bitbake: toaster: Add New Build Button feature
This adds a quick access dropdown menu feature for running builds on a selected project. [YOCTO #6677] (Bitbake rev: e92769b43b00764082a7cb2207e314b40510ef62) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
5b8a62dad7
commit
f26c3cd6f1
|
@ -131,6 +131,10 @@ select { width: auto; }
|
|||
/* make tables Chrome-happy (me, not so much) */
|
||||
#otable { table-layout: fixed; word-wrap: break-word; }
|
||||
|
||||
/* styles for the new build button */
|
||||
.new-build .btn-primary { padding: 4px 30px; }
|
||||
#view-all-projects { display: block; }
|
||||
|
||||
/* Configuration styles */
|
||||
.icon-trash { color: #B94A48; font-size: 16px; padding-left: 2px; }
|
||||
.icon-trash:hover { color: #943A38; text-decoration: none; cursor: pointer; }
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
|
||||
|
||||
function basePageInit (ctx) {
|
||||
|
||||
var newBuildButton = $("#new-build-button");
|
||||
/* Hide the button if we're on the project,newproject or importlyaer page */
|
||||
if (ctx.currentUrl.search('newproject|project/\\d/$|importlayer/$') > 0){
|
||||
newBuildButton.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
newBuildButton.show().removeAttr("disabled");
|
||||
|
||||
_checkProjectBuildable()
|
||||
_setupNewBuildButton();
|
||||
|
||||
|
||||
function _checkProjectBuildable(){
|
||||
libtoaster.getProjectInfo(ctx.projectInfoUrl, ctx.projectId,
|
||||
function(data){
|
||||
if (data.machine.name == undefined || data.layers.length == 0) {
|
||||
/* we can't build anything with out a machine and some layers */
|
||||
$("#new-build-button #targets-form").hide();
|
||||
$("#new-build-button .alert").show();
|
||||
} else {
|
||||
$("#new-build-button #targets-form").show();
|
||||
$("#new-build-button .alert").hide();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
function _setupNewBuildButton() {
|
||||
/* Setup New build button */
|
||||
var newBuildProjectInput = $("#new-build-button #project-name-input");
|
||||
var newBuildTargetBuildBtn = $("#new-build-button #build-button");
|
||||
var newBuildTargetInput = $("#new-build-button #build-target-input");
|
||||
var newBuildProjectSaveBtn = $("#new-build-button #save-project-button");
|
||||
var selectedTarget;
|
||||
var selectedProject;
|
||||
|
||||
/* If we don't have a current project then present the set project
|
||||
* form.
|
||||
*/
|
||||
if (ctx.projectId == undefined) {
|
||||
$('#change-project-form').show();
|
||||
$('#project .icon-pencil').hide();
|
||||
}
|
||||
|
||||
libtoaster.makeTypeahead(newBuildTargetInput, ctx.xhrDataTypeaheadUrl, { type : "targets", project_id: ctx.projectId }, function(item){
|
||||
/* successfully selected a target */
|
||||
selectedTarget = item;
|
||||
});
|
||||
|
||||
|
||||
libtoaster.makeTypeahead(newBuildProjectInput, ctx.xhrDataTypeaheadUrl, { type : "projects" }, function(item){
|
||||
/* successfully selected a project */
|
||||
newBuildProjectSaveBtn.removeAttr("disabled");
|
||||
selectedProject = item;
|
||||
});
|
||||
|
||||
/* Any typing in the input apart from enter key is going to invalidate
|
||||
* the value that has been set by selecting a suggestion from the typeahead
|
||||
*/
|
||||
newBuildProjectInput.keyup(function(event) {
|
||||
if (event.keyCode == 13)
|
||||
return;
|
||||
newBuildProjectSaveBtn.attr("disabled", "disabled");
|
||||
});
|
||||
|
||||
newBuildTargetInput.keyup(function() {
|
||||
if ($(this).val().length == 0)
|
||||
newBuildTargetBuildBtn.attr("disabled", "disabled");
|
||||
else
|
||||
newBuildTargetBuildBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
newBuildTargetBuildBtn.click(function() {
|
||||
if (!newBuildTargetInput.val())
|
||||
return;
|
||||
|
||||
/* fire and forget */
|
||||
libtoaster.startABuild(ctx.projectBuildUrl, ctx.projectId, selectedTarget.name, null, null);
|
||||
window.location.replace(ctx.projectPageUrl+ctx.projectId);
|
||||
});
|
||||
|
||||
newBuildProjectSaveBtn.click(function() {
|
||||
ctx.projectId = selectedProject.id
|
||||
/* Update the typeahead project_id paramater */
|
||||
_checkProjectBuildable();
|
||||
newBuildTargetInput.data('typeahead').options.xhrParams.project_id = ctx.projectId;
|
||||
newBuildTargetInput.val("");
|
||||
|
||||
$("#new-build-button #project a").text(selectedProject.name).attr('href', ctx.projectPageUrl+ctx.projectId);
|
||||
$("#new-build-button .alert a").attr('href', ctx.projectPageUrl+ctx.projectId);
|
||||
|
||||
|
||||
$("#change-project-form").slideUp({ 'complete' : function() {
|
||||
$("#new-build-button #project").show();
|
||||
}});
|
||||
});
|
||||
|
||||
$('#new-build-button #project .icon-pencil').click(function() {
|
||||
newBuildProjectSaveBtn.attr("disabled", "disabled");
|
||||
newBuildProjectInput.val($("#new-build-button #project a").text());
|
||||
$(this).parent().hide();
|
||||
$("#change-project-form").slideDown();
|
||||
});
|
||||
|
||||
$("#new-build-button #cancel-change-project").click(function() {
|
||||
$("#change-project-form").hide(function(){
|
||||
$('#new-build-button #project').show();
|
||||
});
|
||||
|
||||
newBuildProjectInput.val("");
|
||||
newBuildProjectSaveBtn.attr("disabled", "disabled");
|
||||
});
|
||||
|
||||
/* Keep the dropdown open even unless we click outside the dropdown area */
|
||||
$(".new-build").click (function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
};
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
<link rel="stylesheet" href="{% static 'css/font-awesome.min.css' %}" type='text/css'>
|
||||
<link rel="stylesheet" href="{% static 'css/prettify.css' %}" type='text/css'>
|
||||
<link rel="stylesheet" href="{% static 'css/default.css' %}" type='text/css'>
|
||||
<link rel="stylesheet" href="assets/css/jquery-ui-1.10.3.custom.min.css" type='text/css'>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||
<script src="{% static 'js/jquery-2.0.3.min.js' %}">
|
||||
|
@ -20,7 +21,25 @@
|
|||
</script>
|
||||
<script src="{% static 'js/libtoaster.js' %}">
|
||||
</script>
|
||||
<script src="{% static 'js/base.js' %}"></script>
|
||||
{%if MANAGED %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
/* Vars needed for base.js */
|
||||
var ctx = {};
|
||||
ctx.xhrDataTypeaheadUrl = "{% url 'xhr_datatypeahead' %}";
|
||||
ctx.projectBuildUrl = "{% url 'xhr_build' %}";
|
||||
ctx.projectPageUrl = "{% url 'project' %}";
|
||||
ctx.projectInfoUrl = "{% url 'xhr_projectinfo' %}";
|
||||
{% if project %}
|
||||
ctx.projectId = {{project.id}};
|
||||
{% endif %}
|
||||
ctx.currentUrl = "{{request.path|escapejs}}";
|
||||
|
||||
basePageInit(ctx);
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<script>
|
||||
|
||||
</script>
|
||||
|
@ -34,15 +53,55 @@
|
|||
<div class="navbar-inner">
|
||||
<a class="brand logo" href="#"><img src="{% static 'img/logo.png' %}" class="" alt="Yocto logo project"/></a>
|
||||
<a class="brand" href="/">Toaster</a>
|
||||
{%if MANAGED %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn" href="{% url 'newproject' %}">New project</a>
|
||||
</div>
|
||||
{%endif%}
|
||||
<a class="pull-right manual" target="_blank" href="http://www.yoctoproject.org/documentation/toaster-manual">
|
||||
<i class="icon-book"></i>
|
||||
Toaster manual
|
||||
</a>
|
||||
{%if MANAGED %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn" href="{% url 'newproject' %}">New project</a>
|
||||
</div>
|
||||
<!-- New build popover -->
|
||||
<div class="btn-group pull-right" id="new-build-button">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
New build
|
||||
<i class="icon-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu new-build multi-select">
|
||||
<li>
|
||||
<h3>New build</h3>
|
||||
<h6>Project:</h6>
|
||||
<span id="project">
|
||||
<a class="lead" href="{% if project.id %}{% url 'project' project.id %}{% endif %}">{{project.name}}</a>
|
||||
<i class="icon-pencil"></i>
|
||||
</span>
|
||||
<form id="change-project-form" style="display:none;">
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-medium" id="project-name-input" placeholder="Type a project name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead">
|
||||
<button id="save-project-button" class="btn" type="button">Save</button>
|
||||
<a href="#" id="cancel-change-project" class="btn btn-link">Cancel</a>
|
||||
</div>
|
||||
<a id="view-all-projects" href="{% url 'all-projects' %}">View all projects</a>
|
||||
</form>
|
||||
</li>
|
||||
<div class="alert" style="display:none">
|
||||
This project's configuration is incomplete,<br/>so you cannot run builds.<br/>
|
||||
<a href="{% if project.id %}{% url 'project' project.id %}{% endif %}">View project configuration</a>
|
||||
</div>
|
||||
<li id="targets-form">
|
||||
<h6>Target(s):</h6>
|
||||
<form>
|
||||
<input type="text" class="input-xlarge" id="build-target-input" placeholder="Type a target name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead" >
|
||||
<div>
|
||||
<a class="btn btn-primary" id="build-button" disabled="disabled" data-project-id="{{project.id}}">Build</a>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{%endif%}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -80,10 +80,13 @@ urlpatterns = patterns('toastergui.views',
|
|||
url(r'^machines/$', 'machines', name='machines'),
|
||||
|
||||
url(r'^projects/$', 'projects', name='all-projects'),
|
||||
|
||||
url(r'^project/$', 'project', name='project'),
|
||||
url(r'^project/(?P<pid>\d+)/$', 'project', name='project'),
|
||||
url(r'^project/(?P<pid>\d+)/configuration$', 'projectconf', name='projectconf'),
|
||||
url(r'^project/(?P<pid>\d+)/builds$', 'projectbuilds', name='projectbuilds'),
|
||||
|
||||
url(r'^xhr_build/$', 'xhr_build', name='xhr_build'),
|
||||
url(r'^xhr_projectbuild/(?P<pid>\d+)/$', 'xhr_projectbuild', name='xhr_projectbuild'),
|
||||
url(r'^xhr_projectinfo/$', 'xhr_projectinfo', name='xhr_projectinfo'),
|
||||
url(r'^xhr_projectedit/(?P<pid>\d+)/$', 'xhr_projectedit', name='xhr_projectedit'),
|
||||
|
|
|
@ -2015,10 +2015,20 @@ if toastermain.settings.MANAGED:
|
|||
response['Pragma'] = "no-cache"
|
||||
return response
|
||||
|
||||
# This is a wrapper for xhr_projectbuild which allows for a project id
|
||||
# which only becomes known client side.
|
||||
def xhr_build(request):
|
||||
if request.POST.has_key("project_id"):
|
||||
pid = request.POST['project_id']
|
||||
return xhr_projectbuild(request, pid)
|
||||
else:
|
||||
raise BadParameterException("invalid project id")
|
||||
|
||||
def xhr_projectbuild(request, pid):
|
||||
try:
|
||||
if request.method != "POST":
|
||||
raise BadParameterException("invalid method")
|
||||
request.session['project_id'] = pid
|
||||
prj = Project.objects.get(id = pid)
|
||||
|
||||
|
||||
|
@ -2057,6 +2067,8 @@ if toastermain.settings.MANAGED:
|
|||
except Exception as e:
|
||||
return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
||||
|
||||
# This is a wraper for xhr_projectedit which allows for a project id
|
||||
# which only becomes known client side
|
||||
def xhr_projectinfo(request):
|
||||
if request.POST.has_key("project_id") == False:
|
||||
raise BadParameterException("invalid project id")
|
||||
|
@ -2121,8 +2133,12 @@ if toastermain.settings.MANAGED:
|
|||
def xhr_datatypeahead(request):
|
||||
try:
|
||||
prj = None
|
||||
if 'project_id' in request.session:
|
||||
if request.GET.has_key('project_id'):
|
||||
prj = Project.objects.get(pk = request.GET['project_id'])
|
||||
elif 'project_id' in request.session:
|
||||
prj = Project.objects.get(pk = request.session['project_id'])
|
||||
else:
|
||||
raise Exception("No valid project selected")
|
||||
|
||||
# returns layers for current project release that are not in the project set
|
||||
if request.GET['type'] == "layers":
|
||||
|
@ -2188,6 +2204,14 @@ if toastermain.settings.MANAGED:
|
|||
|
||||
}), content_type = "application/json")
|
||||
|
||||
if request.GET['type'] == "projects":
|
||||
queryset_all = Project.objects.all()
|
||||
ret = { "error": "ok",
|
||||
"list": map (lambda x: {"id":x.pk, "name": x.name},
|
||||
queryset_all.filter(name__icontains=request.GET.get('value',''))[:8])}
|
||||
|
||||
return HttpResponse(jsonfilter(ret), content_type = "application/json")
|
||||
|
||||
raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied"))
|
||||
except Exception as e:
|
||||
return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
|
||||
|
@ -2773,6 +2797,12 @@ else:
|
|||
def xhr_projectbuild(request, pid):
|
||||
raise Exception("page not available in interactive mode")
|
||||
|
||||
def xhr_build(request, pid):
|
||||
raise Exception("page not available in interactive mode")
|
||||
|
||||
def xhr_projectinfo(request, pid):
|
||||
raise Exception("page not available in interactive mode")
|
||||
|
||||
def xhr_projectedit(request, pid):
|
||||
raise Exception("page not available in interactive mode")
|
||||
|
||||
|
|
Loading…
Reference in New Issue