mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 23:51:09 +03:00
Merge pull request #1451 from jaredevantabor/notifications
Adding notifications into the UI
This commit is contained in:
commit
eee91eea86
59
awx/ui/client/lib/ngToast/.bower.json
Normal file
59
awx/ui/client/lib/ngToast/.bower.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"name": "ngToast",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Angular provider for toast notifications",
|
||||||
|
"main": [
|
||||||
|
"dist/ngToast.js",
|
||||||
|
"dist/ngToast.css"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"angular",
|
||||||
|
"toast",
|
||||||
|
"message",
|
||||||
|
"notification",
|
||||||
|
"toastr"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/tameraydin/ngToast.git"
|
||||||
|
},
|
||||||
|
"homepage": "http://tameraydin.github.io/ngToast",
|
||||||
|
"authors": [
|
||||||
|
"Tamer Aydin (http://tamerayd.in)",
|
||||||
|
"Levi Thomason (http://www.levithomason.com)"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"angular": ">=1.2.15 <1.6",
|
||||||
|
"angular-sanitize": ">=1.2.15 <1.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"angular-animate": ">=1.2.17 <1.6",
|
||||||
|
"bootstrap": "~3.3.2",
|
||||||
|
"Faker": "~2.1.2"
|
||||||
|
},
|
||||||
|
"ignore": [
|
||||||
|
"**/.*",
|
||||||
|
"node_modules",
|
||||||
|
"test",
|
||||||
|
"src",
|
||||||
|
".editorconfig",
|
||||||
|
".gitignore",
|
||||||
|
".gitattributes",
|
||||||
|
".jshintrc",
|
||||||
|
".travis.yml",
|
||||||
|
"Gruntfile.js",
|
||||||
|
"package.json",
|
||||||
|
"index.html"
|
||||||
|
],
|
||||||
|
"_release": "2.0.0",
|
||||||
|
"_resolution": {
|
||||||
|
"type": "version",
|
||||||
|
"tag": "2.0.0",
|
||||||
|
"commit": "8a1951c54a956c33964c99b338f3a4830e652689"
|
||||||
|
},
|
||||||
|
"_source": "git://github.com/tameraydin/ngToast.git",
|
||||||
|
"_target": "~2.0.0",
|
||||||
|
"_originalSource": "ngtoast",
|
||||||
|
"_direct": true
|
||||||
|
}
|
119
awx/ui/client/lib/ngToast/README.md
Normal file
119
awx/ui/client/lib/ngToast/README.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
ngToast [![Code Climate](http://img.shields.io/codeclimate/github/tameraydin/ngToast.svg?style=flat-square)](https://codeclimate.com/github/tameraydin/ngToast/dist/ngToast.js) [![Build Status](http://img.shields.io/travis/tameraydin/ngToast/master.svg?style=flat-square)](https://travis-ci.org/tameraydin/ngToast)
|
||||||
|
=======
|
||||||
|
|
||||||
|
ngToast is a simple Angular provider for toast notifications.
|
||||||
|
|
||||||
|
**[Demo](http://tameraydin.github.io/ngToast)**
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Install via [Bower](http://bower.io/) or [NPM](http://www.npmjs.org):
|
||||||
|
```bash
|
||||||
|
bower install ngtoast --production
|
||||||
|
# or
|
||||||
|
npm install ng-toast --production
|
||||||
|
```
|
||||||
|
or manually [download](https://github.com/tameraydin/ngToast/archive/master.zip).
|
||||||
|
|
||||||
|
2. Include ngToast source files and dependencies ([ngSanitize](http://docs.angularjs.org/api/ngSanitize), [Bootstrap CSS](http://getbootstrap.com/)):
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="bower/bootstrap/dist/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="bower/ngtoast/dist/ngToast.min.css">
|
||||||
|
|
||||||
|
<script src="bower/angular-sanitize/angular-sanitize.min.js"></script>
|
||||||
|
<script src="bower/ngtoast/dist/ngToast.min.js"></script>
|
||||||
|
```
|
||||||
|
*Note: only the [Alerts](http://getbootstrap.com/components/#alerts) component is used as style base, so you don't have to include complete CSS*
|
||||||
|
|
||||||
|
3. Include ngToast as a dependency in your application module:
|
||||||
|
```javascript
|
||||||
|
var app = angular.module('myApp', ['ngToast']);
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Place `toast` element into your HTML:
|
||||||
|
```html
|
||||||
|
<body>
|
||||||
|
<toast></toast>
|
||||||
|
...
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Inject ngToast provider in your controller:
|
||||||
|
```javascript
|
||||||
|
app.controller('myCtrl', function(ngToast) {
|
||||||
|
ngToast.create('a toast message...');
|
||||||
|
});
|
||||||
|
// for more info: http://tameraydin.github.io/ngToast/#api
|
||||||
|
```
|
||||||
|
|
||||||
|
## Animations
|
||||||
|
ngToast comes with optional animations. In order to enable animations in ngToast, you need to include [ngAnimate](http://docs.angularjs.org/api/ngAnimate) module into your app:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="bower/angular-animate/angular-animate.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Built-in**
|
||||||
|
1. Include the ngToast animation stylesheet:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="bower/ngtoast/dist/ngToast-animations.min.css">
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Set the `animation` option.
|
||||||
|
```javascript
|
||||||
|
app.config(['ngToastProvider', function(ngToastProvider) {
|
||||||
|
ngToastProvider.configure({
|
||||||
|
animation: 'slide' // or 'fade'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
Built-in ngToast animations include `slide` & `fade`.
|
||||||
|
|
||||||
|
**Custom**
|
||||||
|
|
||||||
|
See the [plunker](http://plnkr.co/edit/wglAvsCuTLLykLNqVGwU) using [animate.css](http://daneden.github.io/animate.css/).
|
||||||
|
|
||||||
|
1. Using the `additionalClasses` option and [ngAnimate](http://docs.angularjs.org/api/ngAnimate) you can easily add your own animations or wire up 3rd party css animations.
|
||||||
|
```javascript
|
||||||
|
app.config(['ngToastProvider', function(ngToastProvider) {
|
||||||
|
ngToastProvider.configure({
|
||||||
|
additionalClasses: 'my-animation'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Then in your CSS (example using animate.css):
|
||||||
|
```css
|
||||||
|
/* Add any vendor prefixes you need */
|
||||||
|
.my-animation.ng-enter {
|
||||||
|
animation: flipInY 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-animation.ng-leave {
|
||||||
|
animation: flipOutY 1s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Settings & API
|
||||||
|
|
||||||
|
Please find at the [project website](http://tameraydin.github.io/ngToast/#api).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
* Clone the repo or [download](https://github.com/tameraydin/ngToast/archive/master.zip)
|
||||||
|
* Install dependencies: ``npm install && bower install``
|
||||||
|
* Run ``grunt watch``, play on **/src**
|
||||||
|
* Build: ``grunt``
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT [http://tameraydin.mit-license.org/](http://tameraydin.mit-license.org/)
|
||||||
|
|
||||||
|
## Maintainers
|
||||||
|
|
||||||
|
- [Tamer Aydin](http://tamerayd.in)
|
||||||
|
- [Levi Thomason](http://www.levithomason.com)
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
- Add more unit & e2e tests
|
49
awx/ui/client/lib/ngToast/bower.json
Normal file
49
awx/ui/client/lib/ngToast/bower.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "ngToast",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Angular provider for toast notifications",
|
||||||
|
"main": [
|
||||||
|
"dist/ngToast.js",
|
||||||
|
"dist/ngToast.css"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"angular",
|
||||||
|
"toast",
|
||||||
|
"message",
|
||||||
|
"notification",
|
||||||
|
"toastr"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/tameraydin/ngToast.git"
|
||||||
|
},
|
||||||
|
"homepage": "http://tameraydin.github.io/ngToast",
|
||||||
|
"authors": [
|
||||||
|
"Tamer Aydin (http://tamerayd.in)",
|
||||||
|
"Levi Thomason (http://www.levithomason.com)"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"angular": ">=1.2.15 <1.6",
|
||||||
|
"angular-sanitize": ">=1.2.15 <1.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"angular-animate": ">=1.2.17 <1.6",
|
||||||
|
"bootstrap": "~3.3.2",
|
||||||
|
"Faker": "~2.1.2"
|
||||||
|
},
|
||||||
|
"ignore": [
|
||||||
|
"**/.*",
|
||||||
|
"node_modules",
|
||||||
|
"test",
|
||||||
|
"src",
|
||||||
|
".editorconfig",
|
||||||
|
".gitignore",
|
||||||
|
".gitattributes",
|
||||||
|
".jshintrc",
|
||||||
|
".travis.yml",
|
||||||
|
"Gruntfile.js",
|
||||||
|
"package.json",
|
||||||
|
"index.html"
|
||||||
|
]
|
||||||
|
}
|
107
awx/ui/client/lib/ngToast/dist/ngToast-animations.css
vendored
Normal file
107
awx/ui/client/lib/ngToast/dist/ngToast-animations.css
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*!
|
||||||
|
* ngToast v2.0.0 (http://tameraydin.github.io/ngToast)
|
||||||
|
* Copyright 2016 Tamer Aydin (http://tamerayd.in)
|
||||||
|
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-enter,
|
||||||
|
.ng-toast--animate-fade .ng-leave,
|
||||||
|
.ng-toast--animate-fade .ng-move {
|
||||||
|
transition-property: opacity;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
transition-timing-function: ease; }
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-enter {
|
||||||
|
opacity: 0; }
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-enter.ng-enter-active {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-leave {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-leave.ng-leave-active {
|
||||||
|
opacity: 0; }
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-move {
|
||||||
|
opacity: 0.5; }
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-move.ng-move-active {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
|
.ng-toast--animate-slide .ng-enter,
|
||||||
|
.ng-toast--animate-slide .ng-leave,
|
||||||
|
.ng-toast--animate-slide .ng-move {
|
||||||
|
position: relative;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
transition-timing-function: ease; }
|
||||||
|
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message {
|
||||||
|
position: relative;
|
||||||
|
transition-property: top, margin-top, opacity; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-enter {
|
||||||
|
opacity: 0;
|
||||||
|
top: -100px; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-enter.ng-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
top: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-leave {
|
||||||
|
opacity: 1;
|
||||||
|
top: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-leave.ng-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
margin-top: -72px; }
|
||||||
|
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message {
|
||||||
|
position: relative;
|
||||||
|
transition-property: bottom, margin-bottom, opacity; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-enter {
|
||||||
|
opacity: 0;
|
||||||
|
bottom: -100px; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-enter.ng-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
bottom: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-leave {
|
||||||
|
opacity: 1;
|
||||||
|
bottom: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-leave.ng-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: -72px; }
|
||||||
|
|
||||||
|
.ng-toast--animate-slide.ng-toast--right {
|
||||||
|
transition-property: right, margin-right, opacity; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--right .ng-enter {
|
||||||
|
opacity: 0;
|
||||||
|
right: -200%;
|
||||||
|
margin-right: 20px; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--right .ng-enter.ng-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
right: 0;
|
||||||
|
margin-right: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--right .ng-leave {
|
||||||
|
opacity: 1;
|
||||||
|
right: 0;
|
||||||
|
margin-right: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--right .ng-leave.ng-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
right: -200%;
|
||||||
|
margin-right: 20px; }
|
||||||
|
|
||||||
|
.ng-toast--animate-slide.ng-toast--left {
|
||||||
|
transition-property: left, margin-left, opacity; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--left .ng-enter {
|
||||||
|
opacity: 0;
|
||||||
|
left: -200%;
|
||||||
|
margin-left: 20px; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--left .ng-enter.ng-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
margin-left: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--left .ng-leave {
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
margin-left: 0; }
|
||||||
|
.ng-toast--animate-slide.ng-toast--left .ng-leave.ng-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
left: -200%;
|
||||||
|
margin-left: 20px; }
|
7
awx/ui/client/lib/ngToast/dist/ngToast-animations.min.css
vendored
Normal file
7
awx/ui/client/lib/ngToast/dist/ngToast-animations.min.css
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* ngToast v2.0.0 (http://tameraydin.github.io/ngToast)
|
||||||
|
* Copyright 2016 Tamer Aydin (http://tamerayd.in)
|
||||||
|
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ng-toast--animate-fade .ng-enter,.ng-toast--animate-fade .ng-leave,.ng-toast--animate-fade .ng-move{transition-property:opacity;transition-duration:.3s;transition-timing-function:ease}.ng-toast--animate-fade .ng-enter{opacity:0}.ng-toast--animate-fade .ng-enter.ng-enter-active,.ng-toast--animate-fade .ng-leave{opacity:1}.ng-toast--animate-fade .ng-leave.ng-leave-active{opacity:0}.ng-toast--animate-fade .ng-move{opacity:.5}.ng-toast--animate-fade .ng-move.ng-move-active{opacity:1}.ng-toast--animate-slide .ng-enter,.ng-toast--animate-slide .ng-leave,.ng-toast--animate-slide .ng-move{position:relative;transition-duration:.3s;transition-timing-function:ease}.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message{position:relative;transition-property:top,margin-top,opacity}.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-enter{opacity:0;top:-100px}.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-enter.ng-enter-active,.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-leave{opacity:1;top:0}.ng-toast--animate-slide.ng-toast--center.ng-toast--top .ng-toast__message.ng-leave.ng-leave-active{opacity:0;margin-top:-72px}.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message{position:relative;transition-property:bottom,margin-bottom,opacity}.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-enter{opacity:0;bottom:-100px}.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-enter.ng-enter-active,.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-leave{opacity:1;bottom:0}.ng-toast--animate-slide.ng-toast--center.ng-toast--bottom .ng-toast__message.ng-leave.ng-leave-active{opacity:0;margin-bottom:-72px}.ng-toast--animate-slide.ng-toast--right{transition-property:right,margin-right,opacity}.ng-toast--animate-slide.ng-toast--right .ng-enter{opacity:0;right:-200%;margin-right:20px}.ng-toast--animate-slide.ng-toast--right .ng-enter.ng-enter-active,.ng-toast--animate-slide.ng-toast--right .ng-leave{opacity:1;right:0;margin-right:0}.ng-toast--animate-slide.ng-toast--right .ng-leave.ng-leave-active{opacity:0;right:-200%;margin-right:20px}.ng-toast--animate-slide.ng-toast--left{transition-property:left,margin-left,opacity}.ng-toast--animate-slide.ng-toast--left .ng-enter{opacity:0;left:-200%;margin-left:20px}.ng-toast--animate-slide.ng-toast--left .ng-enter.ng-enter-active,.ng-toast--animate-slide.ng-toast--left .ng-leave{opacity:1;left:0;margin-left:0}.ng-toast--animate-slide.ng-toast--left .ng-leave.ng-leave-active{opacity:0;left:-200%;margin-left:20px}
|
60
awx/ui/client/lib/ngToast/dist/ngToast.css
vendored
Normal file
60
awx/ui/client/lib/ngToast/dist/ngToast.css
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*!
|
||||||
|
* ngToast v2.0.0 (http://tameraydin.github.io/ngToast)
|
||||||
|
* Copyright 2016 Tamer Aydin (http://tamerayd.in)
|
||||||
|
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ng-toast {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1080;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center; }
|
||||||
|
.ng-toast.ng-toast--top {
|
||||||
|
top: 0;
|
||||||
|
bottom: auto; }
|
||||||
|
.ng-toast.ng-toast--top .ng-toast__list {
|
||||||
|
top: 0;
|
||||||
|
bottom: auto; }
|
||||||
|
.ng-toast.ng-toast--top.ng-toast--center .ng-toast__list {
|
||||||
|
position: static; }
|
||||||
|
.ng-toast.ng-toast--bottom {
|
||||||
|
top: auto;
|
||||||
|
bottom: 0; }
|
||||||
|
.ng-toast.ng-toast--bottom .ng-toast__list {
|
||||||
|
top: auto;
|
||||||
|
bottom: 0; }
|
||||||
|
.ng-toast.ng-toast--bottom.ng-toast--center .ng-toast__list {
|
||||||
|
pointer-events: none; }
|
||||||
|
.ng-toast.ng-toast--bottom.ng-toast--center .ng-toast__message .alert {
|
||||||
|
pointer-events: auto; }
|
||||||
|
.ng-toast.ng-toast--right .ng-toast__list {
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
|
margin-right: 20px; }
|
||||||
|
.ng-toast.ng-toast--right .ng-toast__message {
|
||||||
|
text-align: right; }
|
||||||
|
.ng-toast.ng-toast--left .ng-toast__list {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
margin-left: 20px; }
|
||||||
|
.ng-toast.ng-toast--left .ng-toast__message {
|
||||||
|
text-align: left; }
|
||||||
|
.ng-toast .ng-toast__list {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none; }
|
||||||
|
.ng-toast .ng-toast__message {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center; }
|
||||||
|
.ng-toast .ng-toast__message .alert {
|
||||||
|
display: inline-block; }
|
||||||
|
.ng-toast .ng-toast__message__count {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 15px 0 5px; }
|
284
awx/ui/client/lib/ngToast/dist/ngToast.js
vendored
Normal file
284
awx/ui/client/lib/ngToast/dist/ngToast.js
vendored
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
/*!
|
||||||
|
* ngToast v2.0.0 (http://tameraydin.github.io/ngToast)
|
||||||
|
* Copyright 2016 Tamer Aydin (http://tamerayd.in)
|
||||||
|
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(window, angular, undefined) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('ngToast.provider', [])
|
||||||
|
.provider('ngToast', [
|
||||||
|
function() {
|
||||||
|
var messages = [],
|
||||||
|
messageStack = [];
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
animation: false,
|
||||||
|
className: 'success',
|
||||||
|
additionalClasses: null,
|
||||||
|
dismissOnTimeout: true,
|
||||||
|
timeout: 4000,
|
||||||
|
dismissButton: false,
|
||||||
|
dismissButtonHtml: '×',
|
||||||
|
dismissOnClick: true,
|
||||||
|
onDismiss: null,
|
||||||
|
compileContent: false,
|
||||||
|
combineDuplications: false,
|
||||||
|
horizontalPosition: 'right', // right, center, left
|
||||||
|
verticalPosition: 'top', // top, bottom,
|
||||||
|
maxNumber: 0,
|
||||||
|
newestOnTop: true
|
||||||
|
};
|
||||||
|
|
||||||
|
function Message(msg) {
|
||||||
|
var id = Math.floor(Math.random()*1000);
|
||||||
|
while (messages.indexOf(id) > -1) {
|
||||||
|
id = Math.floor(Math.random()*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.id = id;
|
||||||
|
this.count = 0;
|
||||||
|
this.animation = defaults.animation;
|
||||||
|
this.className = defaults.className;
|
||||||
|
this.additionalClasses = defaults.additionalClasses;
|
||||||
|
this.dismissOnTimeout = defaults.dismissOnTimeout;
|
||||||
|
this.timeout = defaults.timeout;
|
||||||
|
this.dismissButton = defaults.dismissButton;
|
||||||
|
this.dismissButtonHtml = defaults.dismissButtonHtml;
|
||||||
|
this.dismissOnClick = defaults.dismissOnClick;
|
||||||
|
this.onDismiss = defaults.onDismiss;
|
||||||
|
this.compileContent = defaults.compileContent;
|
||||||
|
|
||||||
|
angular.extend(this, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.configure = function(config) {
|
||||||
|
angular.extend(defaults, config);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$get = [function() {
|
||||||
|
var _createWithClassName = function(className, msg) {
|
||||||
|
msg = (typeof msg === 'object') ? msg : {content: msg};
|
||||||
|
msg.className = className;
|
||||||
|
|
||||||
|
return this.create(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings: defaults,
|
||||||
|
messages: messages,
|
||||||
|
dismiss: function(id) {
|
||||||
|
if (id) {
|
||||||
|
for (var i = messages.length - 1; i >= 0; i--) {
|
||||||
|
if (messages[i].id === id) {
|
||||||
|
messages.splice(i, 1);
|
||||||
|
messageStack.splice(messageStack.indexOf(id), 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
while(messages.length > 0) {
|
||||||
|
messages.pop();
|
||||||
|
}
|
||||||
|
messageStack = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
create: function(msg) {
|
||||||
|
msg = (typeof msg === 'object') ? msg : {content: msg};
|
||||||
|
|
||||||
|
if (defaults.combineDuplications) {
|
||||||
|
for (var i = messageStack.length - 1; i >= 0; i--) {
|
||||||
|
var _msg = messages[i];
|
||||||
|
var _className = msg.className || 'success';
|
||||||
|
|
||||||
|
if (_msg.content === msg.content &&
|
||||||
|
_msg.className === _className) {
|
||||||
|
messages[i].count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaults.maxNumber > 0 &&
|
||||||
|
messageStack.length >= defaults.maxNumber) {
|
||||||
|
this.dismiss(messageStack[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newMsg = new Message(msg);
|
||||||
|
messages[defaults.newestOnTop ? 'unshift' : 'push'](newMsg);
|
||||||
|
messageStack.push(newMsg.id);
|
||||||
|
|
||||||
|
return newMsg.id;
|
||||||
|
},
|
||||||
|
success: function(msg) {
|
||||||
|
return _createWithClassName.call(this, 'success', msg);
|
||||||
|
},
|
||||||
|
info: function(msg) {
|
||||||
|
return _createWithClassName.call(this, 'info', msg);
|
||||||
|
},
|
||||||
|
warning: function(msg) {
|
||||||
|
return _createWithClassName.call(this, 'warning', msg);
|
||||||
|
},
|
||||||
|
danger: function(msg) {
|
||||||
|
return _createWithClassName.call(this, 'danger', msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
})(window, window.angular);
|
||||||
|
|
||||||
|
(function(window, angular) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('ngToast.directives', ['ngToast.provider'])
|
||||||
|
.run(['$templateCache',
|
||||||
|
function($templateCache) {
|
||||||
|
$templateCache.put('ngToast/toast.html',
|
||||||
|
'<div class="ng-toast ng-toast--{{hPos}} ng-toast--{{vPos}} {{animation ? \'ng-toast--animate-\' + animation : \'\'}}">' +
|
||||||
|
'<ul class="ng-toast__list">' +
|
||||||
|
'<toast-message ng-repeat="message in messages" ' +
|
||||||
|
'message="message" count="message.count">' +
|
||||||
|
'<span ng-bind-html="message.content"></span>' +
|
||||||
|
'</toast-message>' +
|
||||||
|
'</ul>' +
|
||||||
|
'</div>');
|
||||||
|
$templateCache.put('ngToast/toastMessage.html',
|
||||||
|
'<li class="ng-toast__message {{message.additionalClasses}}"' +
|
||||||
|
'ng-mouseenter="onMouseEnter()"' +
|
||||||
|
'ng-mouseleave="onMouseLeave()">' +
|
||||||
|
'<div class="alert alert-{{message.className}}" ' +
|
||||||
|
'ng-class="{\'alert-dismissible\': message.dismissButton}">' +
|
||||||
|
'<button type="button" class="close" ' +
|
||||||
|
'ng-if="message.dismissButton" ' +
|
||||||
|
'ng-bind-html="message.dismissButtonHtml" ' +
|
||||||
|
'ng-click="!message.dismissOnClick && dismiss()">' +
|
||||||
|
'</button>' +
|
||||||
|
'<span ng-if="count" class="ng-toast__message__count">' +
|
||||||
|
'{{count + 1}}' +
|
||||||
|
'</span>' +
|
||||||
|
'<span ng-if="!message.compileContent" ng-transclude></span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</li>');
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.directive('toast', ['ngToast', '$templateCache', '$log',
|
||||||
|
function(ngToast, $templateCache, $log) {
|
||||||
|
return {
|
||||||
|
replace: true,
|
||||||
|
restrict: 'EA',
|
||||||
|
templateUrl: 'ngToast/toast.html',
|
||||||
|
compile: function(tElem, tAttrs) {
|
||||||
|
if (tAttrs.template) {
|
||||||
|
var template = $templateCache.get(tAttrs.template);
|
||||||
|
if (template) {
|
||||||
|
tElem.replaceWith(template);
|
||||||
|
} else {
|
||||||
|
$log.warn('ngToast: Provided template could not be loaded. ' +
|
||||||
|
'Please be sure that it is populated before the <toast> element is represented.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(scope) {
|
||||||
|
scope.hPos = ngToast.settings.horizontalPosition;
|
||||||
|
scope.vPos = ngToast.settings.verticalPosition;
|
||||||
|
scope.animation = ngToast.settings.animation;
|
||||||
|
scope.messages = ngToast.messages;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.directive('toastMessage', ['$timeout', '$compile', 'ngToast',
|
||||||
|
function($timeout, $compile, ngToast) {
|
||||||
|
return {
|
||||||
|
replace: true,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'EA',
|
||||||
|
scope: {
|
||||||
|
message: '=',
|
||||||
|
count: '='
|
||||||
|
},
|
||||||
|
controller: ['$scope', 'ngToast', function($scope, ngToast) {
|
||||||
|
$scope.dismiss = function() {
|
||||||
|
ngToast.dismiss($scope.message.id);
|
||||||
|
};
|
||||||
|
}],
|
||||||
|
templateUrl: 'ngToast/toastMessage.html',
|
||||||
|
link: function(scope, element, attrs, ctrl, transclude) {
|
||||||
|
element.attr('data-message-id', scope.message.id);
|
||||||
|
|
||||||
|
var dismissTimeout;
|
||||||
|
var scopeToBind = scope.message.compileContent;
|
||||||
|
|
||||||
|
scope.cancelTimeout = function() {
|
||||||
|
$timeout.cancel(dismissTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.startTimeout = function() {
|
||||||
|
if (scope.message.dismissOnTimeout) {
|
||||||
|
dismissTimeout = $timeout(function() {
|
||||||
|
ngToast.dismiss(scope.message.id);
|
||||||
|
}, scope.message.timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.onMouseEnter = function() {
|
||||||
|
scope.cancelTimeout();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.onMouseLeave = function() {
|
||||||
|
scope.startTimeout();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (scopeToBind) {
|
||||||
|
var transcludedEl;
|
||||||
|
|
||||||
|
transclude(scope, function(clone) {
|
||||||
|
transcludedEl = clone;
|
||||||
|
element.children().append(transcludedEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
$compile(transcludedEl.contents())
|
||||||
|
(typeof scopeToBind === 'boolean' ?
|
||||||
|
scope.$parent : scopeToBind, function(compiledClone) {
|
||||||
|
transcludedEl.replaceWith(compiledClone);
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.startTimeout();
|
||||||
|
|
||||||
|
if (scope.message.dismissOnClick) {
|
||||||
|
element.bind('click', function() {
|
||||||
|
ngToast.dismiss(scope.message.id);
|
||||||
|
scope.$apply();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope.message.onDismiss) {
|
||||||
|
scope.$on('$destroy',
|
||||||
|
scope.message.onDismiss.bind(scope.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
})(window, window.angular);
|
||||||
|
|
||||||
|
(function(window, angular) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular
|
||||||
|
.module('ngToast', [
|
||||||
|
'ngSanitize',
|
||||||
|
'ngToast.directives',
|
||||||
|
'ngToast.provider'
|
||||||
|
]);
|
||||||
|
|
||||||
|
})(window, window.angular);
|
7
awx/ui/client/lib/ngToast/dist/ngToast.min.css
vendored
Normal file
7
awx/ui/client/lib/ngToast/dist/ngToast.min.css
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* ngToast v2.0.0 (http://tameraydin.github.io/ngToast)
|
||||||
|
* Copyright 2016 Tamer Aydin (http://tamerayd.in)
|
||||||
|
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ng-toast{position:fixed;z-index:1080;width:100%;height:0;margin-top:20px;text-align:center}.ng-toast.ng-toast--top,.ng-toast.ng-toast--top .ng-toast__list{top:0;bottom:auto}.ng-toast.ng-toast--top.ng-toast--center .ng-toast__list{position:static}.ng-toast.ng-toast--bottom,.ng-toast.ng-toast--bottom .ng-toast__list{top:auto;bottom:0}.ng-toast.ng-toast--bottom.ng-toast--center .ng-toast__list{pointer-events:none}.ng-toast.ng-toast--bottom.ng-toast--center .ng-toast__message .alert{pointer-events:auto}.ng-toast.ng-toast--right .ng-toast__list{left:auto;right:0;margin-right:20px}.ng-toast.ng-toast--right .ng-toast__message{text-align:right}.ng-toast.ng-toast--left .ng-toast__list{right:auto;left:0;margin-left:20px}.ng-toast.ng-toast--left .ng-toast__message{text-align:left}.ng-toast .ng-toast__list{display:inline-block;position:absolute;right:0;left:0;margin:0 auto;padding:0;list-style:none}.ng-toast .ng-toast__message{display:block;width:100%;text-align:center}.ng-toast .ng-toast__message .alert{display:inline-block}.ng-toast .ng-toast__message__count{display:inline-block;margin:0 15px 0 5px}
|
7
awx/ui/client/lib/ngToast/dist/ngToast.min.js
vendored
Normal file
7
awx/ui/client/lib/ngToast/dist/ngToast.min.js
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* ngToast v2.0.0 (http://tameraydin.github.io/ngToast)
|
||||||
|
* Copyright 2016 Tamer Aydin (http://tamerayd.in)
|
||||||
|
* Licensed under MIT (http://tameraydin.mit-license.org/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(a,b,c){"use strict";b.module("ngToast.provider",[]).provider("ngToast",[function(){function a(a){for(var d=Math.floor(1e3*Math.random());c.indexOf(d)>-1;)d=Math.floor(1e3*Math.random());this.id=d,this.count=0,this.animation=e.animation,this.className=e.className,this.additionalClasses=e.additionalClasses,this.dismissOnTimeout=e.dismissOnTimeout,this.timeout=e.timeout,this.dismissButton=e.dismissButton,this.dismissButtonHtml=e.dismissButtonHtml,this.dismissOnClick=e.dismissOnClick,this.onDismiss=e.onDismiss,this.compileContent=e.compileContent,b.extend(this,a)}var c=[],d=[],e={animation:!1,className:"success",additionalClasses:null,dismissOnTimeout:!0,timeout:4e3,dismissButton:!1,dismissButtonHtml:"×",dismissOnClick:!0,onDismiss:null,compileContent:!1,combineDuplications:!1,horizontalPosition:"right",verticalPosition:"top",maxNumber:0,newestOnTop:!0};this.configure=function(a){b.extend(e,a)},this.$get=[function(){var b=function(a,b){return b="object"==typeof b?b:{content:b},b.className=a,this.create(b)};return{settings:e,messages:c,dismiss:function(a){if(a){for(var b=c.length-1;b>=0;b--)if(c[b].id===a)return c.splice(b,1),void d.splice(d.indexOf(a),1)}else{for(;c.length>0;)c.pop();d=[]}},create:function(b){if(b="object"==typeof b?b:{content:b},e.combineDuplications)for(var f=d.length-1;f>=0;f--){var g=c[f],h=b.className||"success";if(g.content===b.content&&g.className===h)return void c[f].count++}e.maxNumber>0&&d.length>=e.maxNumber&&this.dismiss(d[0]);var i=new a(b);return c[e.newestOnTop?"unshift":"push"](i),d.push(i.id),i.id},success:function(a){return b.call(this,"success",a)},info:function(a){return b.call(this,"info",a)},warning:function(a){return b.call(this,"warning",a)},danger:function(a){return b.call(this,"danger",a)}}}]}])}(window,window.angular),function(a,b){"use strict";b.module("ngToast.directives",["ngToast.provider"]).run(["$templateCache",function(a){a.put("ngToast/toast.html",'<div class="ng-toast ng-toast--{{hPos}} ng-toast--{{vPos}} {{animation ? \'ng-toast--animate-\' + animation : \'\'}}"><ul class="ng-toast__list"><toast-message ng-repeat="message in messages" message="message" count="message.count"><span ng-bind-html="message.content"></span></toast-message></ul></div>'),a.put("ngToast/toastMessage.html",'<li class="ng-toast__message {{message.additionalClasses}}"ng-mouseenter="onMouseEnter()"ng-mouseleave="onMouseLeave()"><div class="alert alert-{{message.className}}" ng-class="{\'alert-dismissible\': message.dismissButton}"><button type="button" class="close" ng-if="message.dismissButton" ng-bind-html="message.dismissButtonHtml" ng-click="!message.dismissOnClick && dismiss()"></button><span ng-if="count" class="ng-toast__message__count">{{count + 1}}</span><span ng-if="!message.compileContent" ng-transclude></span></div></li>')}]).directive("toast",["ngToast","$templateCache","$log",function(a,b,c){return{replace:!0,restrict:"EA",templateUrl:"ngToast/toast.html",compile:function(d,e){if(e.template){var f=b.get(e.template);f?d.replaceWith(f):c.warn("ngToast: Provided template could not be loaded. Please be sure that it is populated before the <toast> element is represented.")}return function(b){b.hPos=a.settings.horizontalPosition,b.vPos=a.settings.verticalPosition,b.animation=a.settings.animation,b.messages=a.messages}}}}]).directive("toastMessage",["$timeout","$compile","ngToast",function(a,b,c){return{replace:!0,transclude:!0,restrict:"EA",scope:{message:"=",count:"="},controller:["$scope","ngToast",function(a,b){a.dismiss=function(){b.dismiss(a.message.id)}}],templateUrl:"ngToast/toastMessage.html",link:function(d,e,f,g,h){e.attr("data-message-id",d.message.id);var i,j=d.message.compileContent;if(d.cancelTimeout=function(){a.cancel(i)},d.startTimeout=function(){d.message.dismissOnTimeout&&(i=a(function(){c.dismiss(d.message.id)},d.message.timeout))},d.onMouseEnter=function(){d.cancelTimeout()},d.onMouseLeave=function(){d.startTimeout()},j){var k;h(d,function(a){k=a,e.children().append(k)}),a(function(){b(k.contents())("boolean"==typeof j?d.$parent:j,function(a){k.replaceWith(a)})},0)}d.startTimeout(),d.message.dismissOnClick&&e.bind("click",function(){c.dismiss(d.message.id),d.$apply()}),d.message.onDismiss&&d.$on("$destroy",d.message.onDismiss.bind(d.message))}}}])}(window,window.angular),function(a,b){"use strict";b.module("ngToast",["ngSanitize","ngToast.directives","ngToast.provider"])}(window,window.angular);
|
@ -112,6 +112,7 @@ var tower = angular.module('Tower', [
|
|||||||
JobTemplates.name,
|
JobTemplates.name,
|
||||||
portalMode.name,
|
portalMode.name,
|
||||||
search.name,
|
search.name,
|
||||||
|
'ngToast',
|
||||||
'templates',
|
'templates',
|
||||||
'Utilities',
|
'Utilities',
|
||||||
'OrganizationFormDefinition',
|
'OrganizationFormDefinition',
|
||||||
@ -210,15 +211,22 @@ var tower = angular.module('Tower', [
|
|||||||
.config(['$pendolyticsProvider', function($pendolyticsProvider) {
|
.config(['$pendolyticsProvider', function($pendolyticsProvider) {
|
||||||
$pendolyticsProvider.doNotAutoStart();
|
$pendolyticsProvider.doNotAutoStart();
|
||||||
}])
|
}])
|
||||||
|
.config(['ngToastProvider', function(ngToastProvider) {
|
||||||
|
ngToastProvider.configure({
|
||||||
|
animation: 'slide',
|
||||||
|
dismissOnTimeout: true,
|
||||||
|
timeout: 4000
|
||||||
|
});
|
||||||
|
}])
|
||||||
.config(['$stateProvider', '$urlRouterProvider', '$breadcrumbProvider', '$urlMatcherFactoryProvider',
|
.config(['$stateProvider', '$urlRouterProvider', '$breadcrumbProvider', '$urlMatcherFactoryProvider',
|
||||||
function ($stateProvider, $urlRouterProvider, $breadcrumbProvider, $urlMatcherFactoryProvider) {
|
function ($stateProvider, $urlRouterProvider, $breadcrumbProvider, $urlMatcherFactoryProvider) {
|
||||||
$urlMatcherFactoryProvider.strictMode(false)
|
$urlMatcherFactoryProvider.strictMode(false);
|
||||||
$breadcrumbProvider.setOptions({
|
$breadcrumbProvider.setOptions({
|
||||||
templateUrl: urlPrefix + 'partials/breadcrumb.html'
|
templateUrl: urlPrefix + 'partials/breadcrumb.html'
|
||||||
});
|
});
|
||||||
|
|
||||||
// route to the details pane of /job/:id/host-event/:eventId if no other child specified
|
// route to the details pane of /job/:id/host-event/:eventId if no other child specified
|
||||||
$urlRouterProvider.when('/jobs/*/host-event/*', '/jobs/*/host-event/*/details')
|
$urlRouterProvider.when('/jobs/*/host-event/*', '/jobs/*/host-event/*/details');
|
||||||
// $urlRouterProvider.otherwise("/home");
|
// $urlRouterProvider.otherwise("/home");
|
||||||
$urlRouterProvider.otherwise(function($injector){
|
$urlRouterProvider.otherwise(function($injector){
|
||||||
var $state = $injector.get("$state");
|
var $state = $injector.get("$state");
|
||||||
|
@ -517,7 +517,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
ReturnToCaller, GetProjectPath, Authorization, CredentialList, LookUpInit,
|
ReturnToCaller, GetProjectPath, Authorization, CredentialList, LookUpInit,
|
||||||
GetChoices, Empty, DebugForm, Wait, SchedulesControllerInit,
|
GetChoices, Empty, DebugForm, Wait, SchedulesControllerInit,
|
||||||
SchedulesListInit, SchedulesList, ProjectUpdate, $state, CreateSelect2,
|
SchedulesListInit, SchedulesList, ProjectUpdate, $state, CreateSelect2,
|
||||||
OrganizationList) {
|
OrganizationList, NotificationsListInit, ToggleNotification) {
|
||||||
|
|
||||||
ClearScope('htmlTemplate');
|
ClearScope('htmlTemplate');
|
||||||
|
|
||||||
@ -604,6 +604,12 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
$scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false;
|
$scope.scmRequired = ($scope.scm_type.value !== 'manual') ? true : false;
|
||||||
$scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch';
|
$scope.scmBranchLabel = ($scope.scm_type.value === 'svn') ? 'Revision #' : 'SCM Branch';
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
|
|
||||||
|
NotificationsListInit({
|
||||||
|
scope: $scope,
|
||||||
|
url: GetBasePath('projects'),
|
||||||
|
id: $scope.project_obj.id
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($scope.removeChoicesReady) {
|
if ($scope.removeChoicesReady) {
|
||||||
@ -710,6 +716,24 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
callback: 'choicesReady'
|
callback: 'choicesReady'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.toggleNotification = function(event, id, column) {
|
||||||
|
var notifier = this.notification;
|
||||||
|
try {
|
||||||
|
$(event.target).tooltip('hide');
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
ToggleNotification({
|
||||||
|
scope: $scope,
|
||||||
|
url: $scope.project_url,
|
||||||
|
id: $scope.project_obj.id,
|
||||||
|
notifier: notifier,
|
||||||
|
column: column,
|
||||||
|
callback: 'NotificationRefresh'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Save changes to the parent
|
// Save changes to the parent
|
||||||
$scope.formSave = function () {
|
$scope.formSave = function () {
|
||||||
var fld, i, params;
|
var fld, i, params;
|
||||||
@ -820,5 +844,6 @@ ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
|
|||||||
'ClearScope', 'GetBasePath', 'ReturnToCaller', 'GetProjectPath',
|
'ClearScope', 'GetBasePath', 'ReturnToCaller', 'GetProjectPath',
|
||||||
'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
|
'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
|
||||||
'DebugForm', 'Wait', 'SchedulesControllerInit', 'SchedulesListInit',
|
'DebugForm', 'Wait', 'SchedulesControllerInit', 'SchedulesListInit',
|
||||||
'SchedulesList', 'ProjectUpdate', '$state', 'CreateSelect2', 'OrganizationList'
|
'SchedulesList', 'ProjectUpdate', '$state', 'CreateSelect2',
|
||||||
|
'OrganizationList', 'NotificationsListInit', 'ToggleNotification'
|
||||||
];
|
];
|
||||||
|
@ -122,7 +122,7 @@ export default
|
|||||||
ask: false,
|
ask: false,
|
||||||
clear: false,
|
clear: false,
|
||||||
hasShowInputButton: true,
|
hasShowInputButton: true,
|
||||||
apiField: 'passwowrd',
|
apiField: 'password',
|
||||||
subForm: 'credentialSubForm'
|
subForm: 'credentialSubForm'
|
||||||
},
|
},
|
||||||
security_token: {
|
security_token: {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('JobTemplateFormDefinition', ['SchedulesListDefinition', 'CompletedJobsDefinition'])
|
angular.module('JobTemplateFormDefinition', [ 'CompletedJobsDefinition'])
|
||||||
|
|
||||||
.value ('JobTemplateFormObject', {
|
.value ('JobTemplateFormObject', {
|
||||||
|
|
||||||
@ -327,6 +327,10 @@ export default
|
|||||||
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
include: "NotificationsList"
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -339,19 +343,23 @@ export default
|
|||||||
permissions: {
|
permissions: {
|
||||||
iterator: 'permission',
|
iterator: 'permission',
|
||||||
url: urls.access_list
|
url: urls.access_list
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
iterator: 'notification',
|
||||||
|
url: '/api/v1/notifiers/'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('JobTemplateForm', ['JobTemplateFormObject', 'SchedulesList', 'CompletedJobsList',
|
.factory('JobTemplateForm', ['JobTemplateFormObject', 'NotificationsList', 'CompletedJobsList',
|
||||||
function(JobTemplateFormObject, SchedulesList, CompletedJobsList) {
|
function(JobTemplateFormObject, NotificationsList, CompletedJobsList) {
|
||||||
return function() {
|
return function() {
|
||||||
var itm;
|
var itm;
|
||||||
|
|
||||||
for (itm in JobTemplateFormObject.related) {
|
for (itm in JobTemplateFormObject.related) {
|
||||||
if (JobTemplateFormObject.related[itm].include === "SchedulesList") {
|
if (JobTemplateFormObject.related[itm].include === "NotificationsList") {
|
||||||
JobTemplateFormObject.related[itm] = SchedulesList;
|
JobTemplateFormObject.related[itm] = NotificationsList;
|
||||||
JobTemplateFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
JobTemplateFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||||
}
|
}
|
||||||
if (JobTemplateFormObject.related[itm].include === "CompletedJobsList") {
|
if (JobTemplateFormObject.related[itm].include === "CompletedJobsList") {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('OrganizationFormDefinition', [])
|
angular.module('OrganizationFormDefinition', [])
|
||||||
.value('OrganizationForm', {
|
.value('OrganizationFormObject', {
|
||||||
|
|
||||||
addTitle: 'New Organization', //Title in add mode
|
addTitle: 'New Organization', //Title in add mode
|
||||||
editTitle: '{{ name }}', //Title in edit mode
|
editTitle: '{{ name }}', //Title in edit mode
|
||||||
@ -171,6 +171,10 @@ export default
|
|||||||
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
include: "NotificationsList"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -179,8 +183,25 @@ export default
|
|||||||
permissions: {
|
permissions: {
|
||||||
iterator: 'permission',
|
iterator: 'permission',
|
||||||
url: urls.access_list
|
url: urls.access_list
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
iterator: 'notification',
|
||||||
|
url: '/api/v1/notifiers/'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}); //OrganizationForm
|
.factory('OrganizationForm', ['OrganizationFormObject', 'NotificationsList',
|
||||||
|
function(OrganizationFormObject, NotificationsList) {
|
||||||
|
return function() {
|
||||||
|
var itm;
|
||||||
|
for (itm in OrganizationFormObject.related) {
|
||||||
|
if (OrganizationFormObject.related[itm].include === "NotificationsList") {
|
||||||
|
OrganizationFormObject.related[itm] = NotificationsList;
|
||||||
|
OrganizationFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OrganizationFormObject;
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
@ -43,9 +43,6 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
|||||||
type: 'lookup',
|
type: 'lookup',
|
||||||
sourceModel: 'organization',
|
sourceModel: 'organization',
|
||||||
sourceField: 'name',
|
sourceField: 'name',
|
||||||
addRequired: true,
|
|
||||||
editRequired: false,
|
|
||||||
excludeMode: 'edit',
|
|
||||||
ngClick: 'lookUpOrganization()',
|
ngClick: 'lookUpOrganization()',
|
||||||
awRequiredWhen: {
|
awRequiredWhen: {
|
||||||
variable: "organizationrequired",
|
variable: "organizationrequired",
|
||||||
@ -151,17 +148,6 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
|||||||
editRequired: false,
|
editRequired: false,
|
||||||
subForm: 'sourceSubForm'
|
subForm: 'sourceSubForm'
|
||||||
},
|
},
|
||||||
organization: {
|
|
||||||
label: 'Organization',
|
|
||||||
type: 'lookup',
|
|
||||||
sourceModel: 'organization',
|
|
||||||
sourceField: 'name',
|
|
||||||
ngClick: 'lookUpOrganization()',
|
|
||||||
awRequiredWhen: {
|
|
||||||
variable: "organizationrequired",
|
|
||||||
init: "true"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
credential: {
|
credential: {
|
||||||
label: 'SCM Credential',
|
label: 'SCM Credential',
|
||||||
type: 'lookup',
|
type: 'lookup',
|
||||||
@ -277,6 +263,9 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
|||||||
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
include: "NotificationsList"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -285,18 +274,22 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
|||||||
permissions: {
|
permissions: {
|
||||||
iterator: 'permission',
|
iterator: 'permission',
|
||||||
url: urls.access_list
|
url: urls.access_list
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
iterator: 'notification',
|
||||||
|
url: '/api/v1/notifiers/'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('ProjectsForm', ['ProjectsFormObject', 'SchedulesList', function(ProjectsFormObject, ScheduleList) {
|
.factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList', function(ProjectsFormObject, NotificationsList) {
|
||||||
return function() {
|
return function() {
|
||||||
var itm;
|
var itm;
|
||||||
for (itm in ProjectsFormObject.related) {
|
for (itm in ProjectsFormObject.related) {
|
||||||
if (ProjectsFormObject.related[itm].include === "SchedulesList") {
|
if (ProjectsFormObject.related[itm].include === "NotificationsList") {
|
||||||
ProjectsFormObject.related[itm] = ScheduleList;
|
ProjectsFormObject.related[itm] = NotificationsList;
|
||||||
ProjectsFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
ProjectsFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ export default
|
|||||||
'SchedulesControllerInit', 'JobsControllerInit', 'JobsListUpdate',
|
'SchedulesControllerInit', 'JobsControllerInit', 'JobsListUpdate',
|
||||||
'GetChoices', 'SchedulesListInit', 'SchedulesList', 'CallbackHelpInit',
|
'GetChoices', 'SchedulesListInit', 'SchedulesList', 'CallbackHelpInit',
|
||||||
'PlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
|
'PlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
|
||||||
|
'ToggleNotification', 'NotificationsListInit',
|
||||||
function(
|
function(
|
||||||
$filter, $scope, $rootScope, $compile,
|
$filter, $scope, $rootScope, $compile,
|
||||||
$location, $log, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
$location, $log, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
||||||
@ -30,7 +31,7 @@ export default
|
|||||||
Empty, Prompt, ParseVariableString, ToJSON, SchedulesControllerInit,
|
Empty, Prompt, ParseVariableString, ToJSON, SchedulesControllerInit,
|
||||||
JobsControllerInit, JobsListUpdate, GetChoices, SchedulesListInit,
|
JobsControllerInit, JobsListUpdate, GetChoices, SchedulesListInit,
|
||||||
SchedulesList, CallbackHelpInit, PlaybookRun, SurveyControllerInit, $state,
|
SchedulesList, CallbackHelpInit, PlaybookRun, SurveyControllerInit, $state,
|
||||||
CreateSelect2
|
CreateSelect2, ToggleNotification, NotificationsListInit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
ClearScope();
|
ClearScope();
|
||||||
@ -60,6 +61,12 @@ export default
|
|||||||
id: id
|
id: id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationsListInit({
|
||||||
|
scope: $scope,
|
||||||
|
url: GetBasePath('job_templates'),
|
||||||
|
id: id
|
||||||
|
});
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
// Make sure the form controller knows there was a change
|
// Make sure the form controller knows there was a change
|
||||||
$scope[form.name + '_form'].$setDirty();
|
$scope[form.name + '_form'].$setDirty();
|
||||||
@ -122,6 +129,24 @@ export default
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.toggleNotification = function(event, notifier_id, column) {
|
||||||
|
var notifier = this.notification;
|
||||||
|
try {
|
||||||
|
$(event.target).tooltip('hide');
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
ToggleNotification({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
id: id,
|
||||||
|
notifier: notifier,
|
||||||
|
column: column,
|
||||||
|
callback: 'NotificationRefresh'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.toggleScanInfo = function() {
|
$scope.toggleScanInfo = function() {
|
||||||
$scope.project_name = 'Default';
|
$scope.project_name = 'Default';
|
||||||
if($scope.project === null){
|
if($scope.project === null){
|
||||||
|
@ -6,61 +6,123 @@
|
|||||||
|
|
||||||
export default
|
export default
|
||||||
[ '$rootScope', 'pagination', '$compile','SchedulerInit', 'Rest', 'Wait',
|
[ '$rootScope', 'pagination', '$compile','SchedulerInit', 'Rest', 'Wait',
|
||||||
'notificationsFormObject', 'ProcessErrors', 'GetBasePath', 'Empty',
|
'NotificationsFormObject', 'ProcessErrors', 'GetBasePath', 'Empty',
|
||||||
'GenerateForm', 'SearchInit' , 'PaginateInit',
|
'GenerateForm', 'SearchInit' , 'PaginateInit', 'LookUpInit',
|
||||||
'LookUpInit', 'OrganizationList', '$scope', '$state',
|
'OrganizationList', '$scope', '$state', 'CreateSelect2', 'GetChoices',
|
||||||
|
'NotificationsTypeChange',
|
||||||
function(
|
function(
|
||||||
$rootScope, pagination, $compile, SchedulerInit, Rest, Wait,
|
$rootScope, pagination, $compile, SchedulerInit, Rest, Wait,
|
||||||
notificationsFormObject, ProcessErrors, GetBasePath, Empty,
|
NotificationsFormObject, ProcessErrors, GetBasePath, Empty,
|
||||||
GenerateForm, SearchInit, PaginateInit,
|
GenerateForm, SearchInit, PaginateInit, LookUpInit,
|
||||||
LookUpInit, OrganizationList, $scope, $state
|
OrganizationList, $scope, $state, CreateSelect2, GetChoices,
|
||||||
|
NotificationsTypeChange
|
||||||
) {
|
) {
|
||||||
var scope = $scope,
|
var generator = GenerateForm,
|
||||||
generator = GenerateForm,
|
form = NotificationsFormObject,
|
||||||
form = notificationsFormObject,
|
url = GetBasePath('notifiers');
|
||||||
url = GetBasePath('notifications');
|
|
||||||
|
|
||||||
generator.inject(form, {
|
generator.inject(form, {
|
||||||
mode: 'add' ,
|
mode: 'add' ,
|
||||||
scope:scope,
|
scope: $scope,
|
||||||
related: false
|
related: false
|
||||||
});
|
});
|
||||||
generator.reset();
|
generator.reset();
|
||||||
|
|
||||||
|
if ($scope.removeChoicesReady) {
|
||||||
|
$scope.removeChoicesReady();
|
||||||
|
}
|
||||||
|
$scope.removeChoicesReady = $scope.$on('choicesReady', function () {
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < $scope.notification_type_options.length; i++) {
|
||||||
|
if ($scope.notification_type_options[i].value === '') {
|
||||||
|
$scope.notification_type_options[i].value="manual";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CreateSelect2({
|
||||||
|
element: '#notifier_notification_type',
|
||||||
|
multiple: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
LookUpInit({
|
LookUpInit({
|
||||||
url: GetBasePath('organization'),
|
url: GetBasePath('organization'),
|
||||||
scope: scope,
|
scope: $scope,
|
||||||
form: form,
|
form: form,
|
||||||
list: OrganizationList,
|
list: OrganizationList,
|
||||||
field: 'organization',
|
field: 'organization',
|
||||||
input_type: 'radio'
|
input_type: 'radio'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save
|
GetChoices({
|
||||||
scope.formSave = function () {
|
scope: $scope,
|
||||||
|
url: url,
|
||||||
|
field: 'notification_type',
|
||||||
|
variable: 'notification_type_options',
|
||||||
|
callback: 'choicesReady'
|
||||||
|
});
|
||||||
|
|
||||||
generator.clearApiErrors();
|
$scope.typeChange = function () {
|
||||||
Wait('start');
|
for(var fld in form.fields){
|
||||||
Rest.setUrl(url);
|
if(form.fields[fld] && form.fields[fld].subForm){
|
||||||
Rest.post({
|
$scope[fld] = null;
|
||||||
name: scope.name,
|
$scope.notifier_form[fld].$setPristine();
|
||||||
description: scope.description,
|
}
|
||||||
organization: scope.organization,
|
}
|
||||||
script: scope.script
|
|
||||||
})
|
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||||
.success(function (data) {
|
$scope[field[0]] = field[1];
|
||||||
$rootScope.addedItem = data.id;
|
|
||||||
$state.go('inventoryScripts', {}, {reload: true});
|
|
||||||
Wait('stop');
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to add new inventory script. POST returned status: ' + status });
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.formCancel = function () {
|
// Save
|
||||||
$state.transitionTo('inventoryScripts');
|
$scope.formSave = function () {
|
||||||
|
var params,
|
||||||
|
v = $scope.notification_type.value;
|
||||||
|
|
||||||
|
generator.clearApiErrors();
|
||||||
|
params = {
|
||||||
|
"name" : $scope.name,
|
||||||
|
"description": $scope.description,
|
||||||
|
"organization": $scope.organization,
|
||||||
|
"notification_type" : v,
|
||||||
|
"notification_configuration": {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function processValue(value, i , field){
|
||||||
|
if(field.type === 'textarea'){
|
||||||
|
$scope[i] = $scope[i].toString().split('\n');
|
||||||
|
}
|
||||||
|
if(field.type === 'checkbox'){
|
||||||
|
$scope[i] = Boolean($scope[i]);
|
||||||
|
}
|
||||||
|
if(field.type === 'number'){
|
||||||
|
$scope[i] = Number($scope[i]);
|
||||||
|
}
|
||||||
|
return $scope[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
params.notification_configuration = _.object(Object.keys(form.fields)
|
||||||
|
.filter(i => (form.fields[i].ngShow && form.fields[i].ngShow.indexOf(v) > -1))
|
||||||
|
.map(i => [i, processValue($scope[i], i , form.fields[i])]));
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.post(params)
|
||||||
|
.success(function (data) {
|
||||||
|
$rootScope.addedItem = data.id;
|
||||||
|
$state.go('notifications', {}, {reload: true});
|
||||||
|
Wait('stop');
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to add new notifier. POST returned status: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.formCancel = function () {
|
||||||
|
$state.transitionTo('notifications');
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,6 @@ export default {
|
|||||||
},
|
},
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
parent: 'notifications',
|
parent: 'notifications',
|
||||||
label: 'Create Notification'
|
label: 'Create Notifier'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,42 +6,43 @@
|
|||||||
|
|
||||||
export default
|
export default
|
||||||
[ 'Rest', 'Wait',
|
[ 'Rest', 'Wait',
|
||||||
'notificationsFormObject', 'ProcessErrors', 'GetBasePath',
|
'NotificationsFormObject', 'ProcessErrors', 'GetBasePath',
|
||||||
'GenerateForm', 'SearchInit' , 'PaginateInit',
|
'GenerateForm', 'SearchInit' , 'PaginateInit',
|
||||||
'LookUpInit', 'OrganizationList', 'inventory_script',
|
'LookUpInit', 'OrganizationList', 'notifier',
|
||||||
'$scope', '$state',
|
'$scope', '$state', 'GetChoices', 'CreateSelect2', 'Empty',
|
||||||
|
'$rootScope', 'NotificationsTypeChange',
|
||||||
function(
|
function(
|
||||||
Rest, Wait,
|
Rest, Wait,
|
||||||
notificationsFormObject, ProcessErrors, GetBasePath,
|
NotificationsFormObject, ProcessErrors, GetBasePath,
|
||||||
GenerateForm, SearchInit, PaginateInit,
|
GenerateForm, SearchInit, PaginateInit,
|
||||||
LookUpInit, OrganizationList, inventory_script,
|
LookUpInit, OrganizationList, notifier,
|
||||||
$scope, $state
|
$scope, $state, GetChoices, CreateSelect2, Empty,
|
||||||
|
$rootScope, NotificationsTypeChange
|
||||||
) {
|
) {
|
||||||
var generator = GenerateForm,
|
var generator = GenerateForm,
|
||||||
id = inventory_script.id,
|
id = notifier.id,
|
||||||
form = notificationsFormObject,
|
form = NotificationsFormObject,
|
||||||
master = {},
|
master = {},
|
||||||
url = GetBasePath('notifications');
|
url = GetBasePath('notifiers');
|
||||||
|
|
||||||
$scope.inventory_script = inventory_script;
|
$scope.notifier = notifier;
|
||||||
generator.inject(form, {
|
generator.inject(form, {
|
||||||
mode: 'edit' ,
|
mode: 'edit' ,
|
||||||
scope:$scope,
|
scope:$scope,
|
||||||
related: false,
|
related: false
|
||||||
activityStream: false
|
|
||||||
});
|
|
||||||
generator.reset();
|
|
||||||
LookUpInit({
|
|
||||||
url: GetBasePath('organization'),
|
|
||||||
scope: $scope,
|
|
||||||
form: form,
|
|
||||||
// hdr: "Select Custom Inventory",
|
|
||||||
list: OrganizationList,
|
|
||||||
field: 'organization',
|
|
||||||
input_type: 'radio'
|
|
||||||
});
|
});
|
||||||
|
if ($scope.removeChoicesReady) {
|
||||||
|
$scope.removeChoicesReady();
|
||||||
|
}
|
||||||
|
$scope.removeChoicesReady = $scope.$on('choicesReady', function () {
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < $scope.notification_type_options.length; i++) {
|
||||||
|
if ($scope.notification_type_options[i].value === '') {
|
||||||
|
$scope.notification_type_options[i].value="manual";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve detail record and prepopulate the form
|
|
||||||
Wait('start');
|
Wait('start');
|
||||||
Rest.setUrl(url + id+'/');
|
Rest.setUrl(url + id+'/');
|
||||||
Rest.get()
|
Rest.get()
|
||||||
@ -53,6 +54,15 @@ export default
|
|||||||
master[fld] = data[fld];
|
master[fld] = data[fld];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(data.notification_configuration[fld]){
|
||||||
|
$scope[fld] = data.notification_configuration[fld];
|
||||||
|
master[fld] = data.notification_configuration[fld];
|
||||||
|
|
||||||
|
if(form.fields[fld].type === 'textarea'){
|
||||||
|
$scope[fld] = $scope[fld].toString().replace(',' , '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||||
data.summary_fields[form.fields[fld].sourceModel]) {
|
data.summary_fields[form.fields[fld].sourceModel]) {
|
||||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||||
@ -61,36 +71,104 @@ export default
|
|||||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
data.notification_type = (Empty(data.notification_type)) ? '' : data.notification_type;
|
||||||
|
for (var i = 0; i < $scope.notification_type_options.length; i++) {
|
||||||
|
if ($scope.notification_type_options[i].value === data.notification_type) {
|
||||||
|
$scope.notification_type = $scope.notification_type_options[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
master.notification_type = $scope.notification_type;
|
||||||
|
CreateSelect2({
|
||||||
|
element: '#notifier_notification_type',
|
||||||
|
multiple: false
|
||||||
|
});
|
||||||
|
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||||
|
$scope[field[0]] = field[1];
|
||||||
|
});
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
})
|
})
|
||||||
.error(function (data, status) {
|
.error(function (data, status) {
|
||||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||||
msg: 'Failed to retrieve inventory script: ' + id + '. GET status: ' + status });
|
msg: 'Failed to retrieve notification: ' + id + '. GET status: ' + status });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
LookUpInit({
|
||||||
|
url: GetBasePath('organization'),
|
||||||
|
scope: $scope,
|
||||||
|
form: form,
|
||||||
|
list: OrganizationList,
|
||||||
|
field: 'organization',
|
||||||
|
input_type: 'radio'
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.formSave = function () {
|
GetChoices({
|
||||||
generator.clearApiErrors();
|
scope: $scope,
|
||||||
Wait('start');
|
url: url,
|
||||||
Rest.setUrl(url+ id+'/');
|
field: 'notification_type',
|
||||||
Rest.put({
|
variable: 'notification_type_options',
|
||||||
name: $scope.name,
|
callback: 'choicesReady'
|
||||||
description: $scope.description,
|
});
|
||||||
organization: $scope.organization,
|
|
||||||
script: $scope.script
|
|
||||||
})
|
|
||||||
.success(function () {
|
|
||||||
$state.transitionTo('inventoryScriptsList');
|
|
||||||
Wait('stop');
|
|
||||||
|
|
||||||
})
|
$scope.typeChange = function () {
|
||||||
.error(function (data, status) {
|
for(var fld in form.fields){
|
||||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
if(form.fields[fld] && form.fields[fld].subForm){
|
||||||
msg: 'Failed to add new inventory script. PUT returned status: ' + status });
|
$scope[fld] = null;
|
||||||
|
$scope.notifier_form[fld].$setPristine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||||
|
$scope[field[0]] = field[1];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.formSave = function(){
|
||||||
|
var params,
|
||||||
|
v = $scope.notification_type.value;
|
||||||
|
|
||||||
|
generator.clearApiErrors();
|
||||||
|
params = {
|
||||||
|
"name" : $scope.name,
|
||||||
|
"description": $scope.description,
|
||||||
|
"organization": $scope.organization,
|
||||||
|
"notification_type" : v,
|
||||||
|
"notification_configuration": {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function processValue(value, i , field){
|
||||||
|
if(field.type === 'textarea'){
|
||||||
|
$scope[i] = $scope[i].toString().split('\n');
|
||||||
|
}
|
||||||
|
if(field.type === 'checkbox'){
|
||||||
|
$scope[i] = Boolean($scope[i]);
|
||||||
|
}
|
||||||
|
return $scope[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
params.notification_configuration = _.object(Object.keys(form.fields)
|
||||||
|
.filter(i => (form.fields[i].ngShow && form.fields[i].ngShow.indexOf(v) > -1))
|
||||||
|
.map(i => [i, processValue($scope[i], i , form.fields[i])]));
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url+ id+'/');
|
||||||
|
Rest.put(params)
|
||||||
|
.success(function (data) {
|
||||||
|
$rootScope.addedItem = data.id;
|
||||||
|
$state.go('notifications', {}, {reload: true});
|
||||||
|
Wait('stop');
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to add new notifier. POST returned status: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
$scope.formCancel = function () {
|
$scope.formCancel = function () {
|
||||||
$state.transitionTo('inventoryScripts');
|
$state.transitionTo('notifications');
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,44 @@ import {templateUrl} from '../../shared/template-url/template-url.factory';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'notifications.edit',
|
name: 'notifications.edit',
|
||||||
route: '/edit',
|
route: '/:notifier_id',
|
||||||
templateUrl: templateUrl('notifications/edit/edit'),
|
templateUrl: templateUrl('notifications/edit/edit'),
|
||||||
controller: 'notificationsEditController',
|
controller: 'notificationsEditController',
|
||||||
resolve: {
|
resolve: {
|
||||||
features: ['FeaturesService', function(FeaturesService) {
|
features: ['FeaturesService', function(FeaturesService) {
|
||||||
return FeaturesService.get();
|
return FeaturesService.get();
|
||||||
}]
|
}],
|
||||||
|
notifier:
|
||||||
|
[ '$state',
|
||||||
|
'$stateParams',
|
||||||
|
'$q',
|
||||||
|
'Rest',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors',
|
||||||
|
function($state, $stateParams, $q, rest, getBasePath, ProcessErrors) {
|
||||||
|
if ($stateParams.notifier) {
|
||||||
|
return $q.when($stateParams.notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifierId = $stateParams.notifier_id;
|
||||||
|
|
||||||
|
var url = getBasePath('notifiers') + notifierId + '/';
|
||||||
|
rest.setUrl(url);
|
||||||
|
return rest.get()
|
||||||
|
.then(function(data) {
|
||||||
|
return data.data;
|
||||||
|
}).catch(function (response) {
|
||||||
|
ProcessErrors(null, response.data, response.status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get inventory script info. GET returned status: ' +
|
||||||
|
response.status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
parent: 'notifications',
|
parent: 'notifications',
|
||||||
label: 'Edit Notification'
|
label: 'Edit Notifier'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
export default
|
|
||||||
[ '$rootScope','Wait', 'generateList', 'notificationsListObject',
|
|
||||||
'GetBasePath' , 'SearchInit' , 'PaginateInit',
|
|
||||||
'Rest' , 'ProcessErrors', 'Prompt', '$state',
|
|
||||||
function(
|
|
||||||
$rootScope,Wait, GenerateList, notificationsListObject,
|
|
||||||
GetBasePath, SearchInit, PaginateInit,
|
|
||||||
Rest, ProcessErrors, Prompt, $state
|
|
||||||
) {
|
|
||||||
var scope = $rootScope.$new(),
|
|
||||||
defaultUrl = GetBasePath('notifications'),
|
|
||||||
list = notificationsListObject,
|
|
||||||
view = GenerateList;
|
|
||||||
|
|
||||||
view.inject( list, {
|
|
||||||
mode: 'edit',
|
|
||||||
scope: scope
|
|
||||||
});
|
|
||||||
|
|
||||||
// SearchInit({
|
|
||||||
// scope: scope,
|
|
||||||
// set: 'notifications',
|
|
||||||
// list: list,
|
|
||||||
// url: defaultUrl
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if ($rootScope.addedItem) {
|
|
||||||
// scope.addedItem = $rootScope.addedItem;
|
|
||||||
// delete $rootScope.addedItem;
|
|
||||||
// }
|
|
||||||
// PaginateInit({
|
|
||||||
// scope: scope,
|
|
||||||
// list: list,
|
|
||||||
// url: defaultUrl
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// scope.search(list.iterator);
|
|
||||||
|
|
||||||
scope.editNotification = function(){
|
|
||||||
$state.transitionTo('notifications.edit',{
|
|
||||||
inventory_script_id: this.inventory_script.id,
|
|
||||||
inventory_script: this.inventory_script
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.deleteNotification = function(id, name){
|
|
||||||
|
|
||||||
var action = function () {
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
Wait('start');
|
|
||||||
var url = defaultUrl + id + '/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.destroy()
|
|
||||||
.success(function () {
|
|
||||||
scope.search(list.iterator);
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var bodyHtml = '<div class="Prompt-bodyQuery">Are you sure you want to delete the inventory script below?</div><div class="Prompt-bodyTarget">' + name + '</div>';
|
|
||||||
Prompt({
|
|
||||||
hdr: 'Delete',
|
|
||||||
body: bodyHtml,
|
|
||||||
action: action,
|
|
||||||
actionText: 'DELETE'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.addNotification = function(){
|
|
||||||
$state.transitionTo('notifications.add');
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
];
|
|
@ -5,18 +5,26 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
import notificationsList from './list/main';
|
import notificationTemplatesList from './notification-templates-list/main';
|
||||||
import notificationsAdd from './add/main';
|
import notificationsAdd from './add/main';
|
||||||
import notificationsEdit from './edit/main';
|
import notificationsEdit from './edit/main';
|
||||||
|
|
||||||
import list from './notifications.list';
|
import list from './notificationTemplates.list';
|
||||||
import form from './notifications.form';
|
import form from './notificationTemplates.form';
|
||||||
|
import notificationsList from './notifications.list';
|
||||||
|
import toggleNotification from './shared/toggle-notification.factory';
|
||||||
|
import notificationsListInit from './shared/notification-list-init.factory';
|
||||||
|
import typeChange from './shared/type-change.service';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('notifications', [
|
angular.module('notifications', [
|
||||||
notificationsList.name,
|
notificationTemplatesList.name,
|
||||||
notificationsAdd.name,
|
notificationsAdd.name,
|
||||||
notificationsEdit.name
|
notificationsEdit.name
|
||||||
])
|
])
|
||||||
.factory('notificationsListObject', list)
|
.factory('NotificationTemplatesList', list)
|
||||||
.factory('notificationsFormObject', form);
|
.factory('NotificationsFormObject', form)
|
||||||
|
.factory('NotificationsList', notificationsList)
|
||||||
|
.factory('ToggleNotification', toggleNotification)
|
||||||
|
.factory('NotificationsListInit', notificationsListInit)
|
||||||
|
.service('NotificationsTypeChange', typeChange);
|
||||||
|
@ -0,0 +1,204 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default
|
||||||
|
[ '$rootScope','Wait', 'generateList', 'NotificationTemplatesList',
|
||||||
|
'GetBasePath' , 'SearchInit' , 'PaginateInit', 'Rest' ,
|
||||||
|
'ProcessErrors', 'Prompt', '$state', 'GetChoices', 'Empty', 'Find',
|
||||||
|
'ngToast', '$compile', '$filter',
|
||||||
|
function(
|
||||||
|
$rootScope,Wait, GenerateList, NotificationTemplatesList,
|
||||||
|
GetBasePath, SearchInit, PaginateInit, Rest,
|
||||||
|
ProcessErrors, Prompt, $state, GetChoices, Empty, Find, ngToast,
|
||||||
|
$compile, $filter) {
|
||||||
|
var scope = $rootScope.$new(),
|
||||||
|
defaultUrl = GetBasePath('notifiers'),
|
||||||
|
list = NotificationTemplatesList,
|
||||||
|
view = GenerateList;
|
||||||
|
|
||||||
|
view.inject( list, {
|
||||||
|
mode: 'edit',
|
||||||
|
scope: scope
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (scope.removePostRefresh) {
|
||||||
|
scope.removePostRefresh();
|
||||||
|
}
|
||||||
|
scope.removePostRefresh = scope.$on('PostRefresh', function () {
|
||||||
|
Wait('stop');
|
||||||
|
if (scope.notifiers) {
|
||||||
|
scope.notifiers.forEach(function(notifier, i) {
|
||||||
|
scope.notification_type_options.forEach(function(type) {
|
||||||
|
if (type.value === notifier.notification_type) {
|
||||||
|
scope.notifiers[i].notification_type = type.label;
|
||||||
|
scope.notifiers[i].status = notifier.summary_fields.recent_notifications[0].status;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeChoicesHere) {
|
||||||
|
scope.removeChoicesHere();
|
||||||
|
}
|
||||||
|
scope.removeChoicesHere = scope.$on('choicesReadyNotifierList', function () {
|
||||||
|
list.fields.notification_type.searchOptions = scope.notification_type_options;
|
||||||
|
|
||||||
|
SearchInit({
|
||||||
|
scope: scope,
|
||||||
|
set: 'notifiers',
|
||||||
|
list: list,
|
||||||
|
url: defaultUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($rootScope.addedItem) {
|
||||||
|
scope.addedItem = $rootScope.addedItem;
|
||||||
|
delete $rootScope.addedItem;
|
||||||
|
}
|
||||||
|
PaginateInit({
|
||||||
|
scope: scope,
|
||||||
|
list: list,
|
||||||
|
url: defaultUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.search(list.iterator);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
GetChoices({
|
||||||
|
scope: scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
field: 'notification_type',
|
||||||
|
variable: 'notification_type_options',
|
||||||
|
callback: 'choicesReadyNotifierList'
|
||||||
|
});
|
||||||
|
|
||||||
|
function attachElem(event, html, title) {
|
||||||
|
var elem = $(event.target).parent();
|
||||||
|
try {
|
||||||
|
elem.tooltip('hide');
|
||||||
|
elem.popover('destroy');
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.popover').each(function() {
|
||||||
|
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
$('.tooltip').each( function() {
|
||||||
|
// close any lingering tool tipss
|
||||||
|
$(this).hide();
|
||||||
|
});
|
||||||
|
elem.attr({
|
||||||
|
"aw-pop-over": html,
|
||||||
|
"data-popover-title": title,
|
||||||
|
"data-placement": "right" });
|
||||||
|
$compile(elem)(scope);
|
||||||
|
elem.on('shown.bs.popover', function() {
|
||||||
|
$('.popover').each(function() {
|
||||||
|
$compile($(this))(scope); //make nested directives work!
|
||||||
|
});
|
||||||
|
$('.popover-content, .popover-title').click(function() {
|
||||||
|
elem.popover('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
elem.popover('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.showSummary = function(event, id) {
|
||||||
|
|
||||||
|
if (!Empty(id)) {
|
||||||
|
var recent_notifications,
|
||||||
|
html, title = "Recent Notifications";
|
||||||
|
|
||||||
|
scope.notifiers.forEach(function(notifier){
|
||||||
|
if(notifier.id === id){
|
||||||
|
recent_notifications = notifier.summary_fields.recent_notifications;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Wait('stop');
|
||||||
|
if (recent_notifications.length > 0) {
|
||||||
|
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
||||||
|
html += "<thead>\n";
|
||||||
|
html += "<tr>";
|
||||||
|
html += "<th>Status</th>";
|
||||||
|
html += "<th>Time</th>";
|
||||||
|
html += "</tr>\n";
|
||||||
|
html += "</thead>\n";
|
||||||
|
html += "<tbody>\n";
|
||||||
|
|
||||||
|
recent_notifications.forEach(function(row) {
|
||||||
|
html += "<tr>\n";
|
||||||
|
html += "<td><i class=\"fa icon-job-" + row.status + "\"></i></td>\n";
|
||||||
|
html += "<td>" + ($filter('longDate')(row.created)).replace(/ /,'<br />') + "</td>\n";
|
||||||
|
html += "</tr>\n";
|
||||||
|
});
|
||||||
|
html += "</tbody>\n";
|
||||||
|
html += "</table>\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
html = "<p>No recent notifications.</p>\n";
|
||||||
|
}
|
||||||
|
attachElem(event, html, title);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.testNotification = function(){
|
||||||
|
var name = this.notifier.name;
|
||||||
|
Rest.setUrl(defaultUrl + this.notifier.id +'/test/');
|
||||||
|
Rest.post({})
|
||||||
|
.then(function () {
|
||||||
|
ngToast.success({
|
||||||
|
content: `<i class="fa fa-check-circle Toast-successIcon"></i> Test Notification Success: <b>${name}</b> `,
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
ngToast.danger({
|
||||||
|
content: 'Test Notification Failure'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.addNotification = function(){
|
||||||
|
$state.transitionTo('notifications.add');
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.editNotification = function(){
|
||||||
|
$state.transitionTo('notifications.edit',{
|
||||||
|
notifier_id: this.notifier.id,
|
||||||
|
notifier: this.notifier
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.deleteNotification = function(id, name){
|
||||||
|
var action = function () {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
Wait('start');
|
||||||
|
var url = defaultUrl + id + '/';
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.destroy()
|
||||||
|
.success(function () {
|
||||||
|
scope.search(list.iterator);
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var bodyHtml = '<div class="Prompt-bodyQuery">Are you sure you want to delete the inventory below?</div><div class="Prompt-bodyTarget">' + name + '</div>';
|
||||||
|
Prompt({
|
||||||
|
hdr: 'Delete',
|
||||||
|
body: bodyHtml,
|
||||||
|
action: action,
|
||||||
|
actionText: 'DELETE'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
@ -9,8 +9,8 @@ import {templateUrl} from '../../shared/template-url/template-url.factory';
|
|||||||
export default {
|
export default {
|
||||||
name: 'notifications',
|
name: 'notifications',
|
||||||
route: '/notifications',
|
route: '/notifications',
|
||||||
templateUrl: templateUrl('notifications/list/list'),
|
templateUrl: templateUrl('notifications/notification-templates-list/list'),
|
||||||
controller: 'notificationsListController',
|
controller: 'notificationTemplatesListController',
|
||||||
resolve: {
|
resolve: {
|
||||||
features: ['FeaturesService', function(FeaturesService) {
|
features: ['FeaturesService', function(FeaturesService) {
|
||||||
return FeaturesService.get();
|
return FeaturesService.get();
|
@ -8,8 +8,8 @@ import route from './list.route';
|
|||||||
import controller from './list.controller';
|
import controller from './list.controller';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('notificationsList', [])
|
angular.module('notificationTemplatesList', [])
|
||||||
.controller('notificationsListController', controller)
|
.controller('notificationTemplatesListController', controller)
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
$stateExtender.addState(route);
|
$stateExtender.addState(route);
|
||||||
}]);
|
}]);
|
349
awx/ui/client/src/notifications/notificationTemplates.form.js
Normal file
349
awx/ui/client/src/notifications/notificationTemplates.form.js
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name forms.function:CustomInventory
|
||||||
|
* @description This form is for adding/editing an organization
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
addTitle: 'New Notification Template',
|
||||||
|
editTitle: '{{ name }}',
|
||||||
|
name: 'notifier',
|
||||||
|
showActions: true,
|
||||||
|
subFormTitles: {
|
||||||
|
typeSubForm: 'Type Details',
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
label: 'Name',
|
||||||
|
type: 'text',
|
||||||
|
addRequired: true,
|
||||||
|
editRequired: true,
|
||||||
|
capitalize: false
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
label: 'Description',
|
||||||
|
type: 'text',
|
||||||
|
addRequired: false,
|
||||||
|
editRequired: false
|
||||||
|
},
|
||||||
|
organization: {
|
||||||
|
label: 'Organization',
|
||||||
|
type: 'lookup',
|
||||||
|
sourceModel: 'organization',
|
||||||
|
sourceField: 'name',
|
||||||
|
ngClick: 'lookUpOrganization()',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "organizationrequired",
|
||||||
|
init: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notification_type: {
|
||||||
|
label: 'Type',
|
||||||
|
type: 'select',
|
||||||
|
addRequired: true,
|
||||||
|
editRequired: true,
|
||||||
|
class: 'NotificationsForm-typeSelect prepend-asterisk',
|
||||||
|
ngOptions: 'type.label for type in notification_type_options track by type.value',
|
||||||
|
ngChange: 'typeChange()',
|
||||||
|
hasSubForm: true
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
label: 'Username',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "email_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'email' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
use_tls: {
|
||||||
|
label: 'Use TLS',
|
||||||
|
type: 'checkbox',
|
||||||
|
ngShow: "notification_type.value == 'email' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
host: {
|
||||||
|
label: 'Host',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "email_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'email' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
sender: {
|
||||||
|
label: 'Sender Email',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "email_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'email' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
recipients: {
|
||||||
|
label: 'Recipient List',
|
||||||
|
type: 'textarea',
|
||||||
|
rows: 3,
|
||||||
|
awPopOver: '<p>Type an option on each line.</p>'+
|
||||||
|
'<p>For example:<br>alias1@email.com<br>\n alias2@email.com<br>\n',
|
||||||
|
dataTitle: 'Recipient List',
|
||||||
|
dataPlacement: 'right',
|
||||||
|
dataContainer: "body",
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "email_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'email' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
labelBind: 'passwordLabel',
|
||||||
|
type: 'sensitive',
|
||||||
|
hasShowInputButton: true,
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "password_required" ,
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'email' || notification_type.value == 'irc' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
use_ssl: {
|
||||||
|
labelBind: 'sslLabel',
|
||||||
|
type: 'checkbox',
|
||||||
|
ngShow: "notification_type.value == 'email' || notification_type.value == 'irc' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
port: {
|
||||||
|
labelBind: 'portLabel',
|
||||||
|
type: 'number',
|
||||||
|
integer: true,
|
||||||
|
spinner: true,
|
||||||
|
'class': "input-small",
|
||||||
|
min: 0,
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "port_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'email' || notification_type.value == 'irc'",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
channels: {
|
||||||
|
label: 'Destination Channels',
|
||||||
|
type: 'textarea',
|
||||||
|
rows: 3,
|
||||||
|
awPopOver: '<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
|
||||||
|
'<p>For example:<br>engineering<br>\n support<br>\n',
|
||||||
|
dataTitle: 'Destination Channels',
|
||||||
|
dataPlacement: 'right',
|
||||||
|
dataContainer: "body",
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "channel_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'slack' || notification_type.value == 'hipchat'",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
token: {
|
||||||
|
labelBind: 'tokenLabel',
|
||||||
|
type: 'sensitive',
|
||||||
|
hasShowInputButton: true,
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "token_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'slack' || notification_type.value == 'pagerduty' || notification_type.value == 'hipchat'",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
account_token: {
|
||||||
|
label: 'Account Token',
|
||||||
|
type: 'sensitive',
|
||||||
|
hasShowInputButton: true,
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "twilio_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'twilio' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
from_number: {
|
||||||
|
label: 'Source Phone Number',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "twilio_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'twilio' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
to_numbers: {
|
||||||
|
label: 'Destination SMS Number',
|
||||||
|
type: 'textarea',
|
||||||
|
rows: 3,
|
||||||
|
awPopOver: '<p>Type an option on each line.</p>'+
|
||||||
|
'<p>For example:<br>alias1@email.com<br>\n alias2@email.com<br>\n',
|
||||||
|
dataTitle: 'Destination Channels',
|
||||||
|
dataPlacement: 'right',
|
||||||
|
dataContainer: "body",
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "twilio_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'twilio' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
account_sid: {
|
||||||
|
label: 'Account SID',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "twilio_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'twilio' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
subdomain: {
|
||||||
|
label: 'Pagerduty subdomain',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "pagerduty_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'pagerduty' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
service_key: {
|
||||||
|
label: 'API Service/Integration Key',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "pagerduty_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'pagerduty' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
client_name: {
|
||||||
|
label: 'Client Identifier',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "pagerduty_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'pagerduty' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
message_from: {
|
||||||
|
label: 'Label to be shown with notification',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "hipchat_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'hipchat' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
api_url: {
|
||||||
|
label: 'API URL (e.g: https://mycompany.hiptchat.com)',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "hipchat_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'hipchat' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
label: 'Notification Color',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "hipchat_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'hipchat' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
notify: {
|
||||||
|
label: 'Notify Channel',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "hipchat_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'hipchat' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
label: 'Target URL',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "webhook_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'webhook' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
label: 'HTTP Headers',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "webhook_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'webhook' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
label: 'IRC Server Address',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "irc_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'irc' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
nickname: {
|
||||||
|
label: 'IRC Nick',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "irc_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'irc' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
targets: {
|
||||||
|
label: 'Destination Channels or Users',
|
||||||
|
type: 'text',
|
||||||
|
awRequiredWhen: {
|
||||||
|
variable: "irc_required",
|
||||||
|
init: "false"
|
||||||
|
},
|
||||||
|
ngShow: "notification_type.value == 'irc' ",
|
||||||
|
subForm: 'typeSubForm'
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
buttons: { //for now always generates <button> tags
|
||||||
|
save: {
|
||||||
|
ngClick: 'formSave()', //$scope.function to call on click, optional
|
||||||
|
ngDisabled: true //Disable when $pristine or $invalid, optional
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
ngClick: 'formCancel()',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function(){
|
||||||
|
return {
|
||||||
|
name: 'notifiers' ,
|
||||||
|
listTitle: 'Notification Templates',
|
||||||
|
iterator: 'notifier',
|
||||||
|
index: false,
|
||||||
|
hover: false,
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
status: {
|
||||||
|
label: '',
|
||||||
|
columnClass: 'List-staticColumn--smallStatus',
|
||||||
|
searchable: false,
|
||||||
|
nosort: true,
|
||||||
|
ngClick: "null",
|
||||||
|
iconOnly: true,
|
||||||
|
excludeModal: true,
|
||||||
|
icons: [{
|
||||||
|
icon: "{{ 'icon-job-' + notifier.status }}",
|
||||||
|
awToolTip: "Click for recent notifications",
|
||||||
|
awTipPlacement: "right",
|
||||||
|
ngClick: "showSummary($event, notifier.id)",
|
||||||
|
ngClass: ""
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
key: true,
|
||||||
|
label: 'Name',
|
||||||
|
columnClass: 'col-md-3 col-sm-9 col-xs-9',
|
||||||
|
linkTo: '/#/notifications/{{notifier.id}}'
|
||||||
|
},
|
||||||
|
notification_type: {
|
||||||
|
label: 'Type',
|
||||||
|
searchType: 'select',
|
||||||
|
searchOptions: [], // will be set by Options call to projects resource
|
||||||
|
excludeModal: true,
|
||||||
|
columnClass: 'col-md-4 hidden-sm hidden-xs'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
add: {
|
||||||
|
mode: 'all', // One of: edit, select, all
|
||||||
|
ngClick: 'addNotification()',
|
||||||
|
awToolTip: 'Create a new custom inventory',
|
||||||
|
actionClass: 'btn List-buttonSubmit',
|
||||||
|
buttonContent: '+ ADD'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldActions: {
|
||||||
|
|
||||||
|
columnClass: 'col-md-2 col-sm-3 col-xs-3',
|
||||||
|
test: {
|
||||||
|
ngClick: "testNotification(notification.id)",
|
||||||
|
icon: 'fa-bell-o',
|
||||||
|
label: 'Edit',
|
||||||
|
"class": 'btn-sm',
|
||||||
|
awToolTip: 'Test notification',
|
||||||
|
dataPlacement: 'top'
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
ngClick: "editNotification(notification.id)",
|
||||||
|
icon: 'fa-edit',
|
||||||
|
label: 'Edit',
|
||||||
|
"class": 'btn-sm',
|
||||||
|
awToolTip: 'Edit notification',
|
||||||
|
dataPlacement: 'top'
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
ngClick: "deleteNotification(notifier.id, notifier.name)",
|
||||||
|
icon: 'fa-trash',
|
||||||
|
label: 'Delete',
|
||||||
|
"class": 'btn-sm',
|
||||||
|
awToolTip: 'Delete notification',
|
||||||
|
dataPlacement: 'top'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
18
awx/ui/client/src/notifications/notifications.block.less
Normal file
18
awx/ui/client/src/notifications/notifications.block.less
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.NotificationsForm-typeSelect{
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NotifierList-lastColumn{
|
||||||
|
text-align: left!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success{
|
||||||
|
background-color: #3cb878;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toast-successIcon{
|
||||||
|
font-size: x-large;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name forms.function:CustomInventory
|
|
||||||
* @description This form is for adding/editing an organization
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function() {
|
|
||||||
return {
|
|
||||||
|
|
||||||
addTitle: 'New Notification',
|
|
||||||
editTitle: '{{ name }}',
|
|
||||||
name: 'notification',
|
|
||||||
showActions: true,
|
|
||||||
|
|
||||||
fields: {
|
|
||||||
name: {
|
|
||||||
label: 'Name',
|
|
||||||
type: 'text',
|
|
||||||
addRequired: true,
|
|
||||||
editRequired: true,
|
|
||||||
capitalize: false
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
label: 'Description',
|
|
||||||
type: 'text',
|
|
||||||
addRequired: false,
|
|
||||||
editRequired: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
buttons: { //for now always generates <button> tags
|
|
||||||
save: {
|
|
||||||
ngClick: 'formSave()', //$scope.function to call on click, optional
|
|
||||||
ngDisabled: true //Disable when $pristine or $invalid, optional
|
|
||||||
},
|
|
||||||
cancel: {
|
|
||||||
ngClick: 'formCancel()',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -4,12 +4,10 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function(){
|
export default function(){
|
||||||
return {
|
return {
|
||||||
name: 'notifications' ,
|
name: 'notifications' ,
|
||||||
listTitle: 'Notifications',
|
title: 'Notifications',
|
||||||
iterator: 'notification',
|
iterator: 'notification',
|
||||||
index: false,
|
index: false,
|
||||||
hover: false,
|
hover: false,
|
||||||
@ -19,45 +17,39 @@ export default function(){
|
|||||||
key: true,
|
key: true,
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
columnClass: 'col-md-3 col-sm-9 col-xs-9',
|
columnClass: 'col-md-3 col-sm-9 col-xs-9',
|
||||||
modalColumnClass: 'col-md-8'
|
linkTo: '/#/notifications/{{notifier.id}}'
|
||||||
},
|
},
|
||||||
description: {
|
notification_type: {
|
||||||
label: 'Description',
|
label: 'Type',
|
||||||
|
searchType: 'select',
|
||||||
|
searchOptions: [],
|
||||||
excludeModal: true,
|
excludeModal: true,
|
||||||
columnClass: 'col-md-4 hidden-sm hidden-xs'
|
columnClass: 'col-md-4 hidden-sm hidden-xs'
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
notifiers_success: {
|
||||||
actions: {
|
label: 'Successful',
|
||||||
add: {
|
flag: 'notifiers_success',
|
||||||
mode: 'all', // One of: edit, select, all
|
type: "toggle",
|
||||||
ngClick: 'addNotification()',
|
ngClick: "toggleNotification($event, notification.id, \"notifiers_success\")",
|
||||||
awToolTip: 'Create a new custom inventory',
|
awToolTip: "{{ schedule.play_tip }}",
|
||||||
actionClass: 'btn List-buttonSubmit',
|
dataTipWatch: "schedule.play_tip",
|
||||||
buttonContent: '+ ADD'
|
dataPlacement: "right",
|
||||||
}
|
searchable: false,
|
||||||
|
nosort: true,
|
||||||
},
|
},
|
||||||
|
notifiers_error: {
|
||||||
fieldActions: {
|
label: 'Failed',
|
||||||
|
columnClass: 'NotifierList-lastColumn',
|
||||||
columnClass: 'col-md-2 col-sm-3 col-xs-3',
|
flag: 'notifiers_error',
|
||||||
|
type: "toggle",
|
||||||
edit: {
|
ngClick: "toggleNotification($event, notification.id, \"notifiers_error\")",
|
||||||
ngClick: "editNotification(inventory_script.id)",
|
awToolTip: "{{ schedule.play_tip }}",
|
||||||
icon: 'fa-edit',
|
dataTipWatch: "schedule.play_tip",
|
||||||
label: 'Edit',
|
dataPlacement: "right",
|
||||||
"class": 'btn-sm',
|
searchable: false,
|
||||||
awToolTip: 'Edit credential',
|
nosort: true,
|
||||||
dataPlacement: 'top'
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
ngClick: "deleteNotification(notification.id, notification.name)",
|
|
||||||
icon: 'fa-trash',
|
|
||||||
label: 'Delete',
|
|
||||||
"class": 'btn-sm',
|
|
||||||
awToolTip: 'Delete credential',
|
|
||||||
dataPlacement: 'top'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
/**
|
||||||
|
* For setting the values and choices for notification lists
|
||||||
|
*
|
||||||
|
* NotificationsListInit({
|
||||||
|
* scope: scope,
|
||||||
|
* id: notification.id to update
|
||||||
|
* url: notifier url off of related object
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest',
|
||||||
|
function(Wait, GetBasePath, ProcessErrors, Rest) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
url = params.url,
|
||||||
|
id = params.id;
|
||||||
|
|
||||||
|
if (scope.relatednotificationsRemove) {
|
||||||
|
scope.relatednotificationsRemove();
|
||||||
|
}
|
||||||
|
scope.relatednotificationsRemove = scope.$on('relatednotifications', function () {
|
||||||
|
var columns = ['/notifiers_success/', '/notifiers_error/'];
|
||||||
|
|
||||||
|
_.map(columns, function(column){
|
||||||
|
var notifier_url = url + id + column;
|
||||||
|
Rest.setUrl(notifier_url);
|
||||||
|
Rest.get()
|
||||||
|
.success( function(data, i, j, obj) {
|
||||||
|
var type = (obj.url.indexOf('success')>0) ? "notifiers_success" : "notifiers_error";
|
||||||
|
if (data.results) {
|
||||||
|
_.forEach(data.results, function(result){
|
||||||
|
_.forEach(scope.notifications, function(notification){
|
||||||
|
if(notification.id === result.id){
|
||||||
|
notification[type] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error( function(data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to update notification ' + data.id + ' PUT returned: ' + status });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}];
|
@ -0,0 +1,55 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
/**
|
||||||
|
* Flip a notifications's enable flag
|
||||||
|
*
|
||||||
|
* ToggleNotification({
|
||||||
|
* scope: scope,
|
||||||
|
* id: schedule.id to update
|
||||||
|
* callback: scope.$emit label to call when update completes
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest',
|
||||||
|
function(Wait, GetBasePath, ProcessErrors, Rest) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
notifier = params.notifier,
|
||||||
|
id = params.id,
|
||||||
|
notifier_id = params.notifier.id,
|
||||||
|
callback = params.callback,
|
||||||
|
column = params.column, // notifiers_success/notifiers_error
|
||||||
|
url = params.url+ id+ "/"+ column + '/';
|
||||||
|
|
||||||
|
if(!notifier[column]){
|
||||||
|
params = {
|
||||||
|
id: notifier_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
params = {
|
||||||
|
id: notifier_id,
|
||||||
|
disassociate: 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.post(params)
|
||||||
|
.success( function(data) {
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback, data.id);
|
||||||
|
notifier[column] = !notifier[column];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error( function(data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to update notification ' + data.id + ' PUT returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}];
|
@ -0,0 +1,73 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default [
|
||||||
|
function () {
|
||||||
|
return{
|
||||||
|
getDetailFields: function(type) {
|
||||||
|
var obj = {};
|
||||||
|
|
||||||
|
obj.email_required = false;
|
||||||
|
obj.slack_required = false;
|
||||||
|
obj.hipchat_required = false;
|
||||||
|
obj.pagerduty_required = false;
|
||||||
|
obj.irc_required = false;
|
||||||
|
obj.twilio_required = false;
|
||||||
|
obj.webhook_required = false;
|
||||||
|
obj.token_required = false;
|
||||||
|
obj.port_required = false;
|
||||||
|
obj.password_required = false;
|
||||||
|
obj.channel_required = false;
|
||||||
|
switch (type) {
|
||||||
|
case 'email':
|
||||||
|
obj.portLabel = ' Port';
|
||||||
|
obj.sslLabel = ' Use SSL';
|
||||||
|
obj.passwordLabel = ' Password';
|
||||||
|
obj.email_required = true;
|
||||||
|
obj.port_required = true;
|
||||||
|
obj.password_required = true;
|
||||||
|
break;
|
||||||
|
case 'slack':
|
||||||
|
obj.tokenLabel =' Token';
|
||||||
|
obj.slack_required = true;
|
||||||
|
obj.token_required = true;
|
||||||
|
obj.channel_required = true;
|
||||||
|
break;
|
||||||
|
case 'hipchat':
|
||||||
|
obj.tokenLabel = ' Token';
|
||||||
|
obj.hipchat_required = true;
|
||||||
|
obj.channel_required = true;
|
||||||
|
obj.token_required = true;
|
||||||
|
break;
|
||||||
|
case 'twilio':
|
||||||
|
obj.twilio_required = true;
|
||||||
|
break;
|
||||||
|
case 'webhook':
|
||||||
|
obj.webhook_required = true;
|
||||||
|
break;
|
||||||
|
case 'pagerduty':
|
||||||
|
obj.tokenLabel = ' API Token';
|
||||||
|
obj.pagerduty_required = true;
|
||||||
|
obj.token_required = true;
|
||||||
|
break;
|
||||||
|
case 'irc':
|
||||||
|
obj.portLabel = ' IRC Server Port';
|
||||||
|
obj.sslLabel = ' SSL Connection';
|
||||||
|
obj.passwordLabel = ' IRC Server Password';
|
||||||
|
obj.irc_required = true;
|
||||||
|
obj.password_required = true;
|
||||||
|
obj.port_required = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make object an array of tuples
|
||||||
|
return Object.keys(obj)
|
||||||
|
.map(i => [i, obj[i]]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}];
|
@ -7,16 +7,17 @@
|
|||||||
export default ['$scope', '$rootScope', '$compile', '$location',
|
export default ['$scope', '$rootScope', '$compile', '$location',
|
||||||
'$log', '$stateParams', 'OrganizationForm', 'GenerateForm', 'Rest', 'Alert',
|
'$log', '$stateParams', 'OrganizationForm', 'GenerateForm', 'Rest', 'Alert',
|
||||||
'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt',
|
'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt',
|
||||||
'ClearScope', 'GetBasePath', 'Wait', '$state',
|
'ClearScope', 'GetBasePath', 'Wait', '$state', 'NotificationsListInit',
|
||||||
|
'ToggleNotification',
|
||||||
function($scope, $rootScope, $compile, $location, $log,
|
function($scope, $rootScope, $compile, $location, $log,
|
||||||
$stateParams, OrganizationForm, GenerateForm, Rest, Alert, ProcessErrors,
|
$stateParams, OrganizationForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||||
RelatedSearchInit, RelatedPaginateInit, Prompt, ClearScope, GetBasePath,
|
RelatedSearchInit, RelatedPaginateInit, Prompt, ClearScope, GetBasePath,
|
||||||
Wait, $state) {
|
Wait, $state, NotificationsListInit, ToggleNotification) {
|
||||||
|
|
||||||
ClearScope();
|
ClearScope();
|
||||||
|
|
||||||
// Inject dynamic view
|
// Inject dynamic view
|
||||||
var form = OrganizationForm,
|
var form = OrganizationForm(),
|
||||||
generator = GenerateForm,
|
generator = GenerateForm,
|
||||||
defaultUrl = GetBasePath('organizations'),
|
defaultUrl = GetBasePath('organizations'),
|
||||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||||
@ -42,6 +43,11 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
$scope.search(relatedSets[set].iterator);
|
$scope.search(relatedSets[set].iterator);
|
||||||
}
|
}
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
|
NotificationsListInit({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
id: id
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Retrieve detail record and prepopulate the form
|
// Retrieve detail record and prepopulate the form
|
||||||
@ -66,6 +72,7 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.extend(relatedSets, form
|
angular.extend(relatedSets, form
|
||||||
.relatedSets(data.related));
|
.relatedSets(data.related));
|
||||||
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
|
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
|
||||||
@ -79,6 +86,23 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
msg: 'Failed to retrieve organization: ' + $stateParams.id + '. GET status: ' + status });
|
msg: 'Failed to retrieve organization: ' + $stateParams.id + '. GET status: ' + status });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.toggleNotification = function(event, id, column) {
|
||||||
|
var notifier = this.notification;
|
||||||
|
try {
|
||||||
|
$(event.target).tooltip('hide');
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
ToggleNotification({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
id: $scope.organization_id,
|
||||||
|
notifier: notifier,
|
||||||
|
column: column,
|
||||||
|
callback: 'NotificationRefresh'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Save changes to the parent
|
// Save changes to the parent
|
||||||
$scope.formSave = function () {
|
$scope.formSave = function () {
|
||||||
@ -150,4 +174,4 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
@ -342,7 +342,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
|
|||||||
var viewValue = elm.val(), label, validity = true;
|
var viewValue = elm.val(), label, validity = true;
|
||||||
if ( scope[attrs.awRequiredWhen] && (elm.attr('required') === null || elm.attr('required') === undefined) ) {
|
if ( scope[attrs.awRequiredWhen] && (elm.attr('required') === null || elm.attr('required') === undefined) ) {
|
||||||
$(elm).attr('required','required');
|
$(elm).attr('required','required');
|
||||||
if ($(elm).hasClass('lookup')) {
|
if ($(elm).hasClass('lookup') || $(elm).hasClass('ui-spinner-input')) {
|
||||||
$(elm).parent().parent().parent().find('label').first().addClass('prepend-asterisk');
|
$(elm).parent().parent().parent().find('label').first().addClass('prepend-asterisk');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1155,6 +1155,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
html += (field.readonly) ? "readonly " : "";
|
html += (field.readonly) ? "readonly " : "";
|
||||||
html += (field.integer) ? "integer " : "";
|
html += (field.integer) ? "integer " : "";
|
||||||
html += (field.disabled) ? "data-disabled=\"true\" " : "";
|
html += (field.disabled) ? "data-disabled=\"true\" " : "";
|
||||||
|
html += (field.awRequiredWhen) ? "data-awrequired-init=\"" + field.awRequiredWhen.init + "\" aw-required-when=\"" +
|
||||||
|
field.awRequiredWhen.variable + "\" " : "";
|
||||||
html += " >\n";
|
html += " >\n";
|
||||||
// Add error messages
|
// Add error messages
|
||||||
if ((options.mode === 'add' && field.addRequired) || (options.mode === 'edit' && field.editRequired)) {
|
if ((options.mode === 'add' && field.addRequired) || (options.mode === 'edit' && field.editRequired)) {
|
||||||
@ -1217,13 +1219,15 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
}
|
}
|
||||||
|
|
||||||
html += "<div class=\"checkbox\">\n";
|
html += "<div class=\"checkbox\">\n";
|
||||||
html += "<label ";
|
html += "<label>";
|
||||||
html += (field.labelBind) ? "ng-bind=\"" + field.labelBind + "\" " : "";
|
|
||||||
//html += "for=\"" + fld + '">';
|
|
||||||
html += ">";
|
|
||||||
html += buildCheckbox(this.form, field, fld, undefined, false);
|
html += buildCheckbox(this.form, field, fld, undefined, false);
|
||||||
html += (field.icon) ? Icon(field.icon) : "";
|
html += (field.icon) ? Icon(field.icon) : "";
|
||||||
html += '<span class=\"Form-inputLabel\">' + field.label + "</span>";
|
if (field.labelBind) {
|
||||||
|
html += "\t\t<span class=\"Form-inputLabel\" ng-bind=\"" + field.labelBind + "\">\n\t\t</span>";
|
||||||
|
} else {
|
||||||
|
html += "<span class=\"Form-inputLabel\">" + field.label + "</span>";
|
||||||
|
}
|
||||||
|
|
||||||
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
|
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
|
||||||
html += "</label>\n";
|
html += "</label>\n";
|
||||||
html += "<div class=\"error api-error\" id=\"" + this.form.name + "-" + fld + "-api-error\" ng-bind=\"" +
|
html += "<div class=\"error api-error\" id=\"" + this.form.name + "-" + fld + "-api-error\" ng-bind=\"" +
|
||||||
|
@ -178,6 +178,9 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
case 'job_details':
|
case 'job_details':
|
||||||
icon = 'fa-list-ul';
|
icon = 'fa-list-ul';
|
||||||
break;
|
break;
|
||||||
|
case 'test':
|
||||||
|
icon = 'fa-bell-o';
|
||||||
|
break;
|
||||||
case 'copy':
|
case 'copy':
|
||||||
icon = "fa-copy";
|
icon = "fa-copy";
|
||||||
break;
|
break;
|
||||||
@ -461,7 +464,13 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
html += "<td class=\"List-tableCell " + fld + "-column";
|
html += "<td class=\"List-tableCell " + fld + "-column";
|
||||||
html += (field['class']) ? " " + field['class'] : "";
|
html += (field['class']) ? " " + field['class'] : "";
|
||||||
html += " " + field.columnClass;
|
html += " " + field.columnClass;
|
||||||
html += "\"><div class='ScheduleToggle' ng-class='{\"is-on\": " + list.iterator + ".enabled\}' aw-tool-tip='" + field.awToolTip + "' data-placement='" + field.dataPlacement + "' data-tip-watch='" + field.dataTipWatch + "'><div ng-show='" + list.iterator + ".enabled' class='ScheduleToggle-switch is-on' ng-click='" + field.ngClick + "'>ON</div><div ng-show='!" + list.iterator + ".enabled' class='ScheduleToggle-switch' ng-click='" + field.ngClick + "'>OFF</div></div></td>";
|
html += "\"><div class='ScheduleToggle' ng-class='{\"is-on\": " + list.iterator + ".";
|
||||||
|
html += (field.flag) ? field.flag : "enabled";
|
||||||
|
html += "\}' aw-tool-tip='" + field.awToolTip + "' data-placement='" + field.dataPlacement + "' data-tip-watch='" + field.dataTipWatch + "'><div ng-show='" + list.iterator + "." ;
|
||||||
|
html += (field.flag) ? field.flag : 'enabled';
|
||||||
|
html += "' class='ScheduleToggle-switch is-on' ng-click='" + field.ngClick + "'>ON</div><div ng-show='!" + list.iterator + "." ;
|
||||||
|
html += (field.flag) ? field.flag : "enabled";
|
||||||
|
html += "' class='ScheduleToggle-switch' ng-click='" + field.ngClick + "'>OFF</div></div></td>";
|
||||||
} else {
|
} else {
|
||||||
html += "<td class=\"List-tableCell " + fld + "-column";
|
html += "<td class=\"List-tableCell " + fld + "-column";
|
||||||
html += (field['class']) ? " " + field['class'] : "";
|
html += (field['class']) ? " " + field['class'] : "";
|
||||||
|
@ -308,7 +308,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
|||||||
|
|
||||||
html += "<div class=\"List-titleText\">" + list.listTitle + "</div>";
|
html += "<div class=\"List-titleText\">" + list.listTitle + "</div>";
|
||||||
// We want to show the list title badge by default and only hide it when the list config specifically passes a false flag
|
// We want to show the list title badge by default and only hide it when the list config specifically passes a false flag
|
||||||
list.listTitleBadge = (typeof list.listTitleBadge === 'boolean' && list.listTitleBadge == false) ? false : true;
|
list.listTitleBadge = (typeof list.listTitleBadge === 'boolean' && list.listTitleBadge === false) ? false : true;
|
||||||
if(list.listTitleBadge) {
|
if(list.listTitleBadge) {
|
||||||
html += "<span class=\"badge List-titleBadge\">{{(" + list.iterator + "_total_rows | number:0)}}</span>";
|
html += "<span class=\"badge List-titleBadge\">{{(" + list.iterator + "_total_rows | number:0)}}</span>";
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<link rel="stylesheet" href="{{ STATIC_URL }}lib/codemirror/theme/elegant.css" />
|
<link rel="stylesheet" href="{{ STATIC_URL }}lib/codemirror/theme/elegant.css" />
|
||||||
<link rel="stylesheet" href="{{ STATIC_URL }}lib/codemirror/addon/lint/lint.css" />
|
<link rel="stylesheet" href="{{ STATIC_URL }}lib/codemirror/addon/lint/lint.css" />
|
||||||
<link rel="stylesheet" href="{{ STATIC_URL }}lib/nvd3/build/nv.d3.css" type="text/css">
|
<link rel="stylesheet" href="{{ STATIC_URL }}lib/nvd3/build/nv.d3.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="{{ STATIC_URL }}lib/ngToast/dist/ngToast.min.css" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ STATIC_URL }}tower.min.css?v={{version}}" type="text/css">
|
<link rel="stylesheet" href="{{ STATIC_URL }}tower.min.css?v={{version}}" type="text/css">
|
||||||
|
|
||||||
@ -40,6 +41,7 @@
|
|||||||
|
|
||||||
<main-menu></main-menu>
|
<main-menu></main-menu>
|
||||||
<bread-crumb></bread-crumb>
|
<bread-crumb></bread-crumb>
|
||||||
|
<toast></toast>
|
||||||
|
|
||||||
<div class="container-fluid" id="content-container">
|
<div class="container-fluid" id="content-container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
Loading…
Reference in New Issue
Block a user