Commit d3b23cb1 by Kálmán Viktor

Merge branch 'feature-new-tour' into 'master'

Feature new tour

Closes #211

Closes #210

(can't wait for pipeline)

![Kijelölés_020](https://git.ik.bme.hu/uploads/circle/cloud/4e651a54fa/Kijel%C3%B6l%C3%A9s_020.png)

![Kijelölés_021](https://git.ik.bme.hu/uploads/circle/cloud/e603387bb8/Kijel%C3%B6l%C3%A9s_021.png)

![Kijelölés_023](https://git.ik.bme.hu/uploads/circle/cloud/c113cf0cd2/Kijel%C3%B6l%C3%A9s_023.png)

![Kijelölés_022](https://git.ik.bme.hu/uploads/circle/cloud/1d67dc2f03/Kijel%C3%B6l%C3%A9s_022.png)

See merge request !236
parents 6116fd30 87cf344b
/* ===========================================================
# bootstrap-tour - v0.9.1
# http://bootstraptour.com
# ==============================================================
# Copyright 2012-2013 Ulrich Sossou
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/
.tour-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1100;background-color:#000;opacity:.8}.tour-step-backdrop{position:relative;z-index:1101;background:inherit}.tour-step-background{position:absolute;z-index:1100;background:inherit;border-radius:6px}.popover[class*=tour-]{z-index:1100}.popover[class*=tour-] .popover-navigation{padding:9px 14px}.popover[class*=tour-] .popover-navigation [data-role=end]{float:right}.popover[class*=tour-] .popover-navigation [data-role=prev],.popover[class*=tour-] .popover-navigation [data-role=next],.popover[class*=tour-] .popover-navigation [data-role=end]{cursor:pointer}.popover[class*=tour-] .popover-navigation [data-role=prev].disabled,.popover[class*=tour-] .popover-navigation [data-role=next].disabled,.popover[class*=tour-] .popover-navigation [data-role=end].disabled{cursor:default}.popover[class*=tour-].orphan{position:fixed;margin-top:0}.popover[class*=tour-].orphan .arrow{display:none}
\ No newline at end of file
......@@ -688,7 +688,7 @@ textarea[name="new_members"] {
.dashboard-vm-details-connect-command {
/* for mobile view */
margin-bottom: 20px;
padding-bottom: 20px;
}
#store-list-list {
......@@ -961,6 +961,14 @@ textarea[name="new_members"] {
cursor: pointer
}
#vm-details-resources-disk {
padding: 2px 5px 10px 5px;
}
#vm-details-start-template-tour {
margin-right: 5px;
}
#vm-activity-state {
margin-bottom: 15px;
}
......@@ -974,6 +982,24 @@ textarea[name="new_members"] {
color: orange;
}
.introjs-skipbutton {
color: #333;
}
.introjs-button:focus {
text-decoration: none;
color: #333;
outline: none;
}
.introjs-button:hover:not(.introjs-disabled) {
color: #428bca;
}
.introjs-tooltip {
min-width: 250px;
}
#vm-info-pane {
margin-bottom: 20px;
}
......
.introjs-overlay{position:absolute;z-index:999999;background-color:#000;opacity:0;background:-moz-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:-webkit-gradient(radial,center center,0px,center center,100%,color-stop(0%,rgba(0,0,0,0.4)),color-stop(100%,rgba(0,0,0,0.9)));background:-webkit-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:-o-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:-ms-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#66000000',endColorstr='#e6000000',GradientType=1);-ms-filter:"alpha(opacity=50)";filter:alpha(opacity=50);-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out}.introjs-fixParent{z-index:auto !important;opacity:1.0 !important}.introjs-showElement,tr.introjs-showElement>td,tr.introjs-showElement>th{z-index:9999999 !important}.introjs-relativePosition,tr.introjs-showElement>td,tr.introjs-showElement>th{position:relative}.introjs-helperLayer{position:absolute;z-index:9999998;background-color:#FFF;background-color:rgba(255,255,255,.9);border:1px solid #777;border:1px solid rgba(0,0,0,.5);border-radius:4px;box-shadow:0 2px 15px rgba(0,0,0,.4);-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out}.introjs-helperNumberLayer{position:absolute;top:-16px;left:-16px;z-index:9999999999 !important;padding:2px;font-family:Arial,verdana,tahoma;font-size:13px;font-weight:bold;color:white;text-align:center;text-shadow:1px 1px 1px rgba(0,0,0,.3);background:#ff3019;background:-webkit-linear-gradient(top,#ff3019 0,#cf0404 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#ff3019),color-stop(100%,#cf0404));background:-moz-linear-gradient(top,#ff3019 0,#cf0404 100%);background:-ms-linear-gradient(top,#ff3019 0,#cf0404 100%);background:-o-linear-gradient(top,#ff3019 0,#cf0404 100%);background:linear-gradient(to bottom,#ff3019 0,#cf0404 100%);width:20px;height:20px;line-height:20px;border:3px solid white;border-radius:50%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3019',endColorstr='#cf0404',GradientType=0);filter:progid:DXImageTransform.Microsoft.Shadow(direction=135,strength=2,color=ff0000);box-shadow:0 2px 5px rgba(0,0,0,.4)}.introjs-arrow{border:5px solid white;content:'';position:absolute}.introjs-arrow.top{top:-10px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:white;border-left-color:transparent}.introjs-arrow.top-right{top:-10px;right:10px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:white;border-left-color:transparent}.introjs-arrow.top-middle{top:-10px;left:50%;margin-left:-5px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:white;border-left-color:transparent}.introjs-arrow.right{right:-10px;top:10px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:transparent;border-left-color:white}.introjs-arrow.bottom{bottom:-10px;border-top-color:white;border-right-color:transparent;border-bottom-color:transparent;border-left-color:transparent}.introjs-arrow.left{left:-10px;top:10px;border-top-color:transparent;border-right-color:white;border-bottom-color:transparent;border-left-color:transparent}.introjs-tooltip{position:absolute;padding:10px;background-color:white;min-width:200px;max-width:300px;border-radius:3px;box-shadow:0 1px 10px rgba(0,0,0,.4);-webkit-transition:opacity .1s ease-out;-moz-transition:opacity .1s ease-out;-ms-transition:opacity .1s ease-out;-o-transition:opacity .1s ease-out;transition:opacity .1s ease-out}.introjs-tooltipbuttons{text-align:right}.introjs-button{position:relative;overflow:visible;display:inline-block;padding:.3em .8em;border:1px solid #d4d4d4;margin:0;text-decoration:none;text-shadow:1px 1px 0 #fff;font:11px/normal sans-serif;color:#333;white-space:nowrap;cursor:pointer;outline:0;background-color:#ececec;background-image:-webkit-gradient(linear,0 0,0 100%,from(#f4f4f4),to(#ececec));background-image:-moz-linear-gradient(#f4f4f4,#ececec);background-image:-o-linear-gradient(#f4f4f4,#ececec);background-image:linear-gradient(#f4f4f4,#ececec);-webkit-background-clip:padding;-moz-background-clip:padding;-o-background-clip:padding-box;-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;zoom:1;*display:inline;margin-top:10px}.introjs-button:hover{border-color:#bcbcbc;text-decoration:none;box-shadow:0 1px 1px #e3e3e3}.introjs-button:focus,.introjs-button:active{background-image:-webkit-gradient(linear,0 0,0 100%,from(#ececec),to(#f4f4f4));background-image:-moz-linear-gradient(#ececec,#f4f4f4);background-image:-o-linear-gradient(#ececec,#f4f4f4);background-image:linear-gradient(#ececec,#f4f4f4)}.introjs-button::-moz-focus-inner{padding:0;border:0}.introjs-skipbutton{margin-right:5px;color:#7a7a7a}.introjs-prevbutton{-webkit-border-radius:.2em 0 0 .2em;-moz-border-radius:.2em 0 0 .2em;border-radius:.2em 0 0 .2em;border-right:0}.introjs-nextbutton{-webkit-border-radius:0 .2em .2em 0;-moz-border-radius:0 .2em .2em 0;border-radius:0 .2em .2em 0}.introjs-disabled,.introjs-disabled:hover,.introjs-disabled:focus{color:#9a9a9a;border-color:#d4d4d4;box-shadow:none;cursor:default;background-color:#f4f4f4;background-image:none;text-decoration:none}.introjs-bullets{text-align:center}.introjs-bullets ul{clear:both;margin:15px auto 0;padding:0;display:inline-block}.introjs-bullets ul li{list-style:none;float:left;margin:0 2px}.introjs-bullets ul li a{display:block;width:6px;height:6px;background:#ccc;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;text-decoration:none}.introjs-bullets ul li a:hover{background:#999}.introjs-bullets ul li a.active{background:#999}.introjsFloatingElement{position:absolute;height:0;width:0;left:50%;top:50%}
.introjs-helperLayer *,
.introjs-helperLayer *:before,
.introjs-helperLayer *:after {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
-ms-box-sizing: content-box;
-o-box-sizing: content-box;
box-sizing: content-box;
}
......@@ -19,7 +19,7 @@ $(function() {
$('#vm-migrate-node-list li input:checked').closest('li').addClass('panel-primary');
}
});
return false;
e.preventDefault();
});
/* if the operation fails show the modal again */
......
......@@ -28,7 +28,7 @@ $(function() {
});
/* save resources */
$('#vm-details-resources-save').click(function() {
$('#vm-details-resources-save').click(function(e) {
var error = false;
$(".cpu-count-input, .ram-input").each(function() {
if(!$(this)[0].checkValidity()) {
......@@ -61,7 +61,7 @@ $(function() {
}
}
});
return false;
e.preventDefault();
});
/* remove tag */
......@@ -205,11 +205,11 @@ $(function() {
});
/* rename in home tab */
$(".vm-details-home-edit-name-click").click(function() {
$(".vm-details-home-edit-name-click").click(function(e) {
$(".vm-details-home-edit-name-click").hide();
$("#vm-details-home-rename").show();
$("input", $("#vm-details-home-rename")).select();
return false;
e.preventDefault();
});
/* rename ajax */
......@@ -236,7 +236,7 @@ $(function() {
});
/* update description click */
$(".vm-details-home-edit-description-click").click(function() {
$(".vm-details-home-edit-description-click").click(function(e) {
$(".vm-details-home-edit-description-click").hide();
$("#vm-details-home-description").show();
var ta = $("#vm-details-home-description textarea");
......@@ -244,7 +244,7 @@ $(function() {
ta.val("");
ta.focus();
ta.val(tmp)
return false;
e.preventDefault();
});
/* description update ajax */
......@@ -316,6 +316,24 @@ $(function() {
if(Boolean($(this).data("disabled"))) return false;
});
$("#dashboard-tutorial-toggle").click(function() {
var box = $("#alert-new-template");
var list = box.find("ol")
list.stop().slideToggle(function() {
var url = box.find("form").prop("action");
var hidden = list.css("display") === "none";
box.find("button i").prop("class", "fa fa-caret-" + (hidden ? "down" : "up"));
$.ajax({
type: 'POST',
url: url,
data: {'hidden': hidden},
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(re, textStatus, xhr) {}
});
});
return false;
});
});
......@@ -344,10 +362,7 @@ function decideActivityRefresh() {
/* if something is still spinning */
if($('.timeline .activity i').hasClass('fa-spin'))
check = true;
/* if there is only one activity */
if($('#activity-timeline div[class="activity"]').length < 2)
check = true;
return check;
}
......@@ -377,6 +392,7 @@ function checkNewActivity(runs) {
} else {
icon.prop("class", "fa " + data['icon']);
}
$("#vm-details-state").data("status", data['status']);
$("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
if(data['status'] == "RUNNING") {
if(data['connect_uri']) {
......
......@@ -6,7 +6,7 @@
{% block extra_link %}
<link rel="stylesheet" href="{{ STATIC_URL }}dashboard/loopj-jquery-simple-slider/css/simple-slider.css"/>
<link href="{{ STATIC_URL }}dashboard/bootstrap-tour.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ STATIC_URL }}dashboard/introjs/introjs.min.css">
<link href="{{ STATIC_URL }}dashboard/dashboard.css" rel="stylesheet">
{% endblock %}
......
......@@ -6,14 +6,25 @@
{% block content %}
{% if instance.is_base %}
<div class="alert alert-info alert-new-template">
<strong>{% trans "This is the master vm of your new template" %}</strong>
<div id="vm-details-template-tour-button" class="pull-right">
<a href="#" class="btn btn-default btn-lg pull-right vm-details-start-template-tour">
<div class="alert alert-info alert-new-template" id="alert-new-template" style="position: relative;">
<form action="{% url "dashboard.views.vm-toggle-tutorial" pk=instance.pk %}"
method="POST">
{% csrf_token %}
<input name="hidden" type="hidden"
value="{{ hide_tutorial|yesno:"false,true" }}"/>
<button type="submit"
id="dashboard-tutorial-toggle" class="btn btn-sm pull-right btn-success">
<i class="fa fa-caret-{% if hide_tutorial %}down{% else %}up{% endif %}"></i>
{% trans "Toggle tutorial panel" %}
</button>
<a href="#" class="btn btn-default btn-sm pull-right"
id="vm-details-start-template-tour">
<i class="fa fa-play"></i> {% trans "Start template tutorial" %}
</a>
</div>
<ol>
</form>
<strong>{% trans "This is the master vm of your new template" %}</strong>
<ol {% if hide_tutorial %}style="display: none;"{% endif %}>
<li>{% trans "Modify the virtual machine to suit your needs <strong>(optional)</strong>" %}
<ul>
<li>{% trans "Change the description" %}</li>
......@@ -65,7 +76,7 @@
<div class="row">
<div class="col-md-4" id="vm-info-pane">
<div class="big">
<span id="vm-details-state" class="label label-success">
<span id="vm-details-state" class="label label-success" data-status="{{ instance.status }}">
<i class="fa
{% if is_new_state %}
fa-spinner fa-spin
......@@ -202,7 +213,7 @@
{% endblock %}
{% block extra_js %}
<script src="{{ STATIC_URL }}dashboard/bootstrap-tour.min.js"></script>
<script src="{{ STATIC_URL }}dashboard/introjs/intro.min.js"></script>
<script src="{{ STATIC_URL }}dashboard/vm-details.js"></script>
<script src="{{ STATIC_URL }}dashboard/vm-common.js"></script>
<script src="{{ STATIC_URL }}dashboard/vm-console.js"></script>
......
{% load i18n %}
<div class="row">
<div class="col-md-4">
<dl>
<dl id="home_name_and_description">
<dt>{% trans "System" %}:</dt>
<dd><i class="fa fa-{{ os_type_icon }}"></i> {{ instance.system }}</dd>
<dt style="margin-top: 5px;">
......@@ -52,30 +52,34 @@
</dd>
</dl>
<h4>{% trans "Expiration" %} {% if instance.is_expiring %}<i class="fa fa-warning-sign text-danger"></i>{% endif %}
<span id="vm-details-renew-op">
{% with op=op.renew %}{% if op %}
<a href="{{op.get_url}}" class="btn btn-success btn-xs
operation operation-{{op.op}}">
<i class="fa fa-{{op.icon}}"></i>
{{op.name}} </a>
{% endif %}{% endwith %}
</span>
</h4>
<dl>
<dt>{% trans "Suspended at:" %}</dt>
<dd>
<span title="{{ instance.time_of_suspend }}">
<i class="fa fa-moon-o"></i> {{ instance.time_of_suspend|timeuntil }}
<div id="home_expiration_and_lease">
<h4>
{% trans "Expiration" %}
{% if instance.is_expiring %}<i class="fa fa-warning-sign text-danger"></i>{% endif %}
<span id="vm-details-renew-op">
{% with op=op.renew %}{% if op %}
<a href="{{op.get_url}}" class="btn btn-success btn-xs
operation operation-{{op.op}}">
<i class="fa fa-{{op.icon}}"></i>
{{op.name}} </a>
{% endif %}{% endwith %}
</span>
</dd>
<dt>{% trans "Destroyed at:" %}</dt>
<dd>
<span title="{{ instance.time_of_delete }}">
<i class="fa fa-times"></i> {{ instance.time_of_delete|timeuntil }}
</span>
</dd>
</dl>
</h4>
<dl>
<dt>{% trans "Suspended at:" %}</dt>
<dd>
<span title="{{ instance.time_of_suspend }}">
<i class="fa fa-moon-o"></i> {{ instance.time_of_suspend|timeuntil }}
</span>
</dd>
<dt>{% trans "Destroyed at:" %}</dt>
<dd>
<span title="{{ instance.time_of_delete }}">
<i class="fa fa-times"></i> {{ instance.time_of_delete|timeuntil }}
</span>
</dd>
</dl>
</div>
<div style="font-weight: bold;">{% trans "Tags" %}</div>
<div id="vm-details-tags" style="margin-bottom: 20px;">
......
......@@ -18,34 +18,28 @@
{% endif %}
</form>
<hr />
<div class="row" id="vm-details-resources-disk">
<div class="col-sm-11">
<h3>
{% trans "Disks" %}
<div class="pull-right">
<div id="disk-ops">
{% include "dashboard/vm-detail/_disk-operations.html" %}
</div>
<div id="vm-details-resources-disk">
<h3>
{% trans "Disks" %}
<div class="pull-right">
<div id="disk-ops">
{% include "dashboard/vm-detail/_disk-operations.html" %}
</div>
</h3>
<div class="row" id="vm-details-disk-add-for-form"></div>
{% if not instance.disks.all %}
{% trans "No disks are added!" %}
{% endif %}
{% for d in instance.disks.all %}
<h4 class="list-group-item-heading dashboard-vm-details-network-h3">
{% with long_remove=True %}
{% include "dashboard/_disk-list-element.html" %}
{% endwith %}
</h4>
{% endfor %}
</div>
</div>
</h3>
{% if not instance.disks.all %}
{% trans "No disks are added." %}
{% endif %}
{% for d in instance.disks.all %}
<h4 class="list-group-item-heading dashboard-vm-details-network-h3">
{% with long_remove=True %}
{% include "dashboard/_disk-list-element.html" %}
{% endwith %}
</h4>
{% endfor %}
</div>
{% if user.is_superuser %}
......
......@@ -45,6 +45,7 @@ from .views import (
VmTraitsUpdate, VmRawDataUpdate,
GroupPermissionsView,
LeaseAclUpdateView,
toggle_template_tutorial,
ClientCheck, TokenLogin,
VmGraphView, NodeGraphView, NodeListGraphView,
)
......@@ -99,6 +100,8 @@ urlpatterns = patterns(
name='dashboard.views.vm-traits'),
url(r'^vm/(?P<pk>\d+)/raw_data/$', VmRawDataUpdate.as_view(),
name='dashboard.views.vm-raw-data'),
url(r'^vm/(?P<pk>\d+)/toggle_tutorial/$', toggle_template_tutorial,
name='dashboard.views.vm-toggle-tutorial'),
url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'),
url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(),
......
......@@ -24,6 +24,7 @@ from os import getenv
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.core import signing
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.urlresolvers import reverse, reverse_lazy
......@@ -102,13 +103,16 @@ class VmDetailView(GraphMixin, CheckedDetailView):
is_operator = instance.has_level(user, "operator")
is_owner = instance.has_level(user, "owner")
ops = get_operations(instance, user)
hide_tutorial = self.request.COOKIES.get(
"hide_tutorial_for_%s" % instance.pk) == "True"
context.update({
'graphite_enabled': settings.GRAPHITE_URL is not None,
'vnc_url': reverse_lazy("dashboard.views.detail-vnc",
kwargs={'pk': self.object.pk}),
'ops': ops,
'op': {i.op: i for i in ops},
'connect_commands': user.profile.get_connect_commands(instance)
'connect_commands': user.profile.get_connect_commands(instance),
'hide_tutorial': hide_tutorial,
})
# activity data
......@@ -1435,3 +1439,13 @@ class TransferOwnershipConfirmView(LoginRequiredMixin, View):
unicode(user), user.pk, new_owner, key)
raise PermissionDenied()
return (instance, new_owner)
@login_required
def toggle_template_tutorial(request, pk):
hidden = request.POST.get("hidden", "").lower() == "true"
instance = get_object_or_404(Instance, pk=pk)
response = HttpResponseRedirect(instance.get_absolute_url())
response.set_cookie( # for a week
"hide_tutorial_for_%s" % pk, hidden, 7 * 24 * 60 * 60)
return response
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment