diff --git a/app/assets/images/kuroko2/kuroko-logo-error.png b/app/assets/images/kuroko2/kuroko-logo-error.png
new file mode 100644
index 00000000..fb9830e6
Binary files /dev/null and b/app/assets/images/kuroko2/kuroko-logo-error.png differ
diff --git a/app/assets/images/kuroko2/kuroko-logo-success.png b/app/assets/images/kuroko2/kuroko-logo-success.png
new file mode 100644
index 00000000..04ba8b25
Binary files /dev/null and b/app/assets/images/kuroko2/kuroko-logo-success.png differ
diff --git a/app/assets/javascripts/kuroko2/application.js b/app/assets/javascripts/kuroko2/application.js
index a428e1a8..6b646a09 100644
--- a/app/assets/javascripts/kuroko2/application.js
+++ b/app/assets/javascripts/kuroko2/application.js
@@ -1,6 +1,7 @@
//= require jquery
//= require jquery_ujs
//= require ./bootstrap
+//= require ./js.cookie.js
//= stub kuroko2/instance_linker
//= require_tree
//= require moment
@@ -25,4 +26,42 @@ jQuery(function ($) {
$(".right-side").toggleClass("strech");
}
});
+
+ var showNotificationStatus = function () {
+ if (Notification.permission === 'granted') {
+ if (Cookies.get('notification') === 'on') {
+ $('#notification').html(" on");
+ } else {
+ $('#notification').html(" off");
+ }
+ } else if (Notification.permission === 'denied') {
+ $('#notification').html(" off");
+ }
+ }
+
+ $('#notification').click(function (e) {
+ if (!('Notification' in window)) {
+ return;
+ }
+
+ if (Notification.permission === 'default') {
+ Notification.requestPermission(function (permission) {
+ if (permission === "granted") {
+ Cookies.set('notification', 'on');
+ }
+ showNotificationStatus();
+ });
+ } else if (Notification.permission === 'granted') {
+ if (Cookies.get('notification') === 'on') {
+ Cookies.set('notification', 'off');
+ } else {
+ Cookies.set('notification', 'on');
+ }
+ showNotificationStatus();
+ }
+ });
+
+ if ('Notification' in window) {
+ showNotificationStatus();
+ }
});
diff --git a/app/assets/javascripts/kuroko2/job_instances.js b/app/assets/javascripts/kuroko2/job_instances.js
index 785d12c0..9a9d205d 100644
--- a/app/assets/javascripts/kuroko2/job_instances.js
+++ b/app/assets/javascripts/kuroko2/job_instances.js
@@ -3,11 +3,25 @@
jQuery(function ($) {
var logIntervalId;
+ var notifyIfNeeded = function (status, name, image) {
+ if (!('Notification' in window)) {
+ return;
+ }
+
+ if (Notification.permission === 'granted' && Cookies.get('notification') === 'on') {
+ var notification = new Notification("[" + status + "] " + name, {"icon": image[status.toLowerCase()]});
+ notification.onclick = function () {
+ notification.close();
+ window.focus();
+ };
+ }
+ };
var updateInstance = function () {
var instancePath = $('#instance').data("instance-path");
$.get(instancePath, function (data) {
$('#instance').replaceWith(data);
+ notifyIfNeeded($('#instance-status').text(), $('#definition-name').text(), $('#notification').data());
});
};
var updateLogs = function () {
diff --git a/app/assets/javascripts/kuroko2/js.cookie.js b/app/assets/javascripts/kuroko2/js.cookie.js
new file mode 100644
index 00000000..12fa0eeb
--- /dev/null
+++ b/app/assets/javascripts/kuroko2/js.cookie.js
@@ -0,0 +1,156 @@
+/*!
+ * JavaScript Cookie v2.1.3
+ * https://github.com/js-cookie/js-cookie
+ *
+ * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
+ * Released under the MIT license
+ */
+;(function (factory) {
+ var registeredInModuleLoader = false;
+ if (typeof define === 'function' && define.amd) {
+ define(factory);
+ registeredInModuleLoader = true;
+ }
+ if (typeof exports === 'object') {
+ module.exports = factory();
+ registeredInModuleLoader = true;
+ }
+ if (!registeredInModuleLoader) {
+ var OldCookies = window.Cookies;
+ var api = window.Cookies = factory();
+ api.noConflict = function () {
+ window.Cookies = OldCookies;
+ return api;
+ };
+ }
+}(function () {
+ function extend () {
+ var i = 0;
+ var result = {};
+ for (; i < arguments.length; i++) {
+ var attributes = arguments[ i ];
+ for (var key in attributes) {
+ result[key] = attributes[key];
+ }
+ }
+ return result;
+ }
+
+ function init (converter) {
+ function api (key, value, attributes) {
+ var result;
+ if (typeof document === 'undefined') {
+ return;
+ }
+
+ // Write
+
+ if (arguments.length > 1) {
+ attributes = extend({
+ path: '/'
+ }, api.defaults, attributes);
+
+ if (typeof attributes.expires === 'number') {
+ var expires = new Date();
+ expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
+ attributes.expires = expires;
+ }
+
+ try {
+ result = JSON.stringify(value);
+ if (/^[\{\[]/.test(result)) {
+ value = result;
+ }
+ } catch (e) {}
+
+ if (!converter.write) {
+ value = encodeURIComponent(String(value))
+ .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
+ } else {
+ value = converter.write(value, key);
+ }
+
+ key = encodeURIComponent(String(key));
+ key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
+ key = key.replace(/[\(\)]/g, escape);
+
+ return (document.cookie = [
+ key, '=', value,
+ attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
+ attributes.path ? '; path=' + attributes.path : '',
+ attributes.domain ? '; domain=' + attributes.domain : '',
+ attributes.secure ? '; secure' : ''
+ ].join(''));
+ }
+
+ // Read
+
+ if (!key) {
+ result = {};
+ }
+
+ // To prevent the for loop in the first place assign an empty array
+ // in case there are no cookies at all. Also prevents odd result when
+ // calling "get()"
+ var cookies = document.cookie ? document.cookie.split('; ') : [];
+ var rdecode = /(%[0-9A-Z]{2})+/g;
+ var i = 0;
+
+ for (; i < cookies.length; i++) {
+ var parts = cookies[i].split('=');
+ var cookie = parts.slice(1).join('=');
+
+ if (cookie.charAt(0) === '"') {
+ cookie = cookie.slice(1, -1);
+ }
+
+ try {
+ var name = parts[0].replace(rdecode, decodeURIComponent);
+ cookie = converter.read ?
+ converter.read(cookie, name) : converter(cookie, name) ||
+ cookie.replace(rdecode, decodeURIComponent);
+
+ if (this.json) {
+ try {
+ cookie = JSON.parse(cookie);
+ } catch (e) {}
+ }
+
+ if (key === name) {
+ result = cookie;
+ break;
+ }
+
+ if (!key) {
+ result[name] = cookie;
+ }
+ } catch (e) {}
+ }
+
+ return result;
+ }
+
+ api.set = api;
+ api.get = function (key) {
+ return api.call(api, key);
+ };
+ api.getJSON = function () {
+ return api.apply({
+ json: true
+ }, [].slice.call(arguments));
+ };
+ api.defaults = {};
+
+ api.remove = function (key, attributes) {
+ api(key, '', extend(attributes, {
+ expires: -1
+ }));
+ };
+
+ api.withConverter = init;
+
+ return api;
+ }
+
+ return init(function () {});
+}));
diff --git a/app/assets/stylesheets/kuroko2/application.scss b/app/assets/stylesheets/kuroko2/application.scss
index 910a2e54..0fe49566 100644
--- a/app/assets/stylesheets/kuroko2/application.scss
+++ b/app/assets/stylesheets/kuroko2/application.scss
@@ -88,3 +88,7 @@
font-size: 1.3em;
fill: #666;
}
+
+#notification {
+ cursor: pointer;
+}
diff --git a/app/views/kuroko2/job_instances/_instance.html.slim b/app/views/kuroko2/job_instances/_instance.html.slim
index 741d7408..d266a0e6 100644
--- a/app/views/kuroko2/job_instances/_instance.html.slim
+++ b/app/views/kuroko2/job_instances/_instance.html.slim
@@ -1,6 +1,6 @@
.box#instance data-instance-path="#{naked_job_definition_job_instance_path(@definition, @instance)}"
.box-header
- h2.box-title
+ h2#definition-name.box-title
= link_to "##{@definition.id} #{@definition.name}", job_definition_path(@definition)
.box-body
table.table
diff --git a/app/views/layouts/kuroko2/application.html.slim b/app/views/layouts/kuroko2/application.html.slim
index df4b173c..45201452 100644
--- a/app/views/layouts/kuroko2/application.html.slim
+++ b/app/views/layouts/kuroko2/application.html.slim
@@ -31,6 +31,9 @@ html
img.img-circle src='#{current_user.image}' alt="#{current_user.name}"
.pull-left.info
p Hello, #{current_user.name}
+ p#notification data-success='#{asset_url('kuroko2/kuroko-logo-success.png')}' data-error='#{asset_url('kuroko2/kuroko-logo-error.png')}'
+ i.fa.fa-volume-off
+ | Turn on notification
ul.sidebar-menu
li class=('active' if controller_name == 'dashboard')
= link_to raw(' Dashboard'), root_path
diff --git a/lib/kuroko2/engine.rb b/lib/kuroko2/engine.rb
index b38c0b09..10c1721e 100644
--- a/lib/kuroko2/engine.rb
+++ b/lib/kuroko2/engine.rb
@@ -50,6 +50,8 @@ class Engine < ::Rails::Engine
config.action_mailer.smtp_settings =
Kuroko2.config.action_mailer.smtp_settings.to_h.symbolize_keys || {}
+ app.config.assets.precompile += %w(kuroko2/kuroko-logo-success.png kuroko2/kuroko-logo-error.png)
+
if Kuroko2.config.extentions && Kuroko2.config.extentions.controller
Kuroko2.config.extentions.controller.each do |extention|
Kuroko2::ApplicationController.include(Module.const_get(extention, false))