diff --git a/bitbake/lib/toaster/toastergui/static/css/default.css b/bitbake/lib/toaster/toastergui/static/css/default.css index 199c7531dc..a3fa0ddf6a 100644 --- a/bitbake/lib/toaster/toastergui/static/css/default.css +++ b/bitbake/lib/toaster/toastergui/static/css/default.css @@ -232,3 +232,4 @@ dd > span { line-height: 20px; } .animate-repeat.ng-enter.ng-enter-active { opacity:1; } +.tab-pane table { margin-top: 10px; } diff --git a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js new file mode 100644 index 0000000000..a5a6330630 --- /dev/null +++ b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js @@ -0,0 +1,404 @@ +"use strict" + +function layerDetailsPageInit (ctx) { + + var layerDepInput = $("#layer-dep-input"); + var layerDepBtn = $("#add-layer-dependency-btn"); + var layerDepsList = $("#layer-deps-list"); + var currentLayerDepSelection; + var addRmLayerBtn = $("#add-remove-layer-btn"); + + /* setup the dependencies typeahead */ + libtoaster.makeTypeahead(layerDepInput, ctx.xhrDataTypeaheadUrl, { type : "layers", project_id: ctx.projectId, include_added: "true" }, function(item){ + currentLayerDepSelection = item; + + layerDepBtn.removeAttr("disabled"); + }); + + function addRemoveDep(depLayerId, add, doneCb) { + var data = { layer_version_id : ctx.layerVersion.id }; + if (add) + data.add_dep = depLayerId; + else + data.rm_dep = depLayerId; + + $.ajax({ + type: "POST", + url: ctx.xhrUpdateLayerUrl, + data: data, + headers: { 'X-CSRFToken' : $.cookie('csrftoken')}, + success: function (data) { + if (data.error != "ok") { + console.warn(data.error); + } else { + doneCb(); + } + }, + error: function (data) { + console.warn("Call failed"); + console.warn(data); + } + }); + } + + function layerRemoveClick() { + var toRemove = $(this).parent().data('layer-id'); + var layerDepItem = $(this); + + addRemoveDep(toRemove, false, function(){ + layerDepItem.parent().fadeOut(function (){ + layerDepItem.remove(); + }); + }); + } + + /* Add dependency layer button click handler */ + layerDepBtn.click(function(){ + if (currentLayerDepSelection == undefined) + return; + + addRemoveDep(currentLayerDepSelection.id, true, function(){ + /* Make a list item for the new layer dependency */ + var newLayerDep = $("
  • "); + + newLayerDep.data('layer-id', currentLayerDepSelection.id); + newLayerDep.children("span").tooltip(); + + var link = newLayerDep.children("a"); + link.attr("href", ctx.layerDetailsUrl+String(currentLayerDepSelection.id)); + link.text(currentLayerDepSelection.name); + link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"}); + + /* Connect up the tash icon */ + var trashItem = newLayerDep.children("span"); + trashItem.click(layerRemoveClick); + + layerDepsList.append(newLayerDep); + /* Clear the current selection */ + layerDepInput.val(""); + currentLayerDepSelection = undefined; + layerDepBtn.attr("disabled","disabled"); + }); + }); + + $(".icon-pencil").click(function (){ + var mParent = $(this).parent("dd"); + mParent.prev().css("margin-top", "10px"); + mParent.children("form").slideDown(); + var currentVal = mParent.children(".current-value"); + currentVal.hide(); + /* Set the current value to the input field */ + mParent.find("textarea,input").val(currentVal.text()); + /* Hides the "Not set" text */ + mParent.children(".muted").hide(); + /* We're editing so hide the delete icon */ + mParent.children(".delete-current-value").hide(); + mParent.find(".cancel").show(); + $(this).hide(); + }); + + $(".delete-current-value").click(function(){ + var mParent = $(this).parent("dd"); + mParent.find("input").val(""); + mParent.find("textarea").val(""); + mParent.find(".change-btn").click(); + }); + + $(".cancel").click(function(){ + var mParent = $(this).parents("dd"); + $(this).hide(); + mParent.children("form").slideUp(function(){ + mParent.children(".current-value").show(); + /* Show the "Not set" text if we ended up with no value */ + if (!mParent.children(".current-value").html()){ + mParent.children(".muted").fadeIn(); + mParent.children(".delete-current-value").hide(); + } else { + mParent.children(".delete-current-value").show(); + } + + mParent.children(".icon-pencil").show(); + mParent.prev().css("margin-top", "0px"); + }); + }); + + $(".build-target-btn").click(function(){ + /* fire a build */ + var target = $(this).data('target-name'); + libtoaster.startABuild(ctx.projectBuildUrl, ctx.projectId, target, null, null); + window.location.replace(ctx.projectPageUrl); + }); + + $(".select-machine-btn").click(function(){ + var data = { machineName : $(this).data('machine-name') }; + libtoaster.editProject(ctx.xhrEditProjectUrl, ctx.projectId, data, + function (){ + window.location.replace(ctx.projectPageUrl); + }, null); + }); + + function defaultAddBtnText(){ + var text = " Add the "+ctx.layerVersion.name+" layer to your project"; + addRmLayerBtn.text(text); + addRmLayerBtn.prepend(""); + addRmLayerBtn.removeClass("btn-danger"); + } + + $("#details-tab").on('show', function(){ + if (!ctx.layerVersion.inCurrentPrj) + defaultAddBtnText(); + + window.location.hash = "details"; + }); + + function targetsTabShow(){ + if (!ctx.layerVersion.inCurrentPrj){ + if (ctx.numTargets > 0) { + var text = " Add the "+ctx.layerVersion.name+" layer to your project "+ + "to enable these targets"; + addRmLayerBtn.text(text); + addRmLayerBtn.prepend(""); + } else { + defaultAddBtnText(); + } + } + + window.location.hash = "targets"; + } + + $("#targets-tab").on('show', targetsTabShow); + + function machinesTabShow(){ + if (!ctx.layerVersion.inCurrentPrj) { + if (ctx.numMachines > 0){ + var text = " Add the "+ctx.layerVersion.name+" layer to your project " + + "to enable these machines"; + addRmLayerBtn.text(text); + addRmLayerBtn.prepend(""); + } else { + defaultAddBtnText(); + } + } + + window.location.hash = "machines"; + } + + $("#machines-tab").on('show', machinesTabShow); + + $(".pagesize").change(function(){ + var search = libtoaster.parseUrlParams(); + search.limit = this.value; + + window.location.search = libtoaster.dumpsUrlParams(search); + }); + + /* Enables the Build target and Select Machine buttons and switches the + * add/remove button + */ + function setLayerInCurrentPrj(added, depsList) { + ctx.layerVersion.inCurrentPrj = added; + var alertMsg = $("#alert-msg"); + /* Reset alert message */ + alertMsg.text(""); + + if (added){ + /* enable and switch all the button states */ + $(".build-target-btn").removeAttr("disabled"); + $(".select-machine-btn").removeAttr("disabled"); + addRmLayerBtn.addClass("btn-danger"); + addRmLayerBtn.data('directive', "remove"); + addRmLayerBtn.text(" Delete the "+ctx.layerVersion.name+" layer from your project"); + addRmLayerBtn.prepend(""); + + if (depsList) { + alertMsg.append("You have added "+(depsList.length+1)+" layers: and its dependencies "); + + /* Build the layer deps list */ + depsList.map(function(layer, i){ + var link = $(""); + + link.attr("href", layer.layerdetailurl); + link.text(layer.name); + link.tooltip({title: layer.tooltip}); + + if (i != 0) + alertMsg.append(", "); + + alertMsg.append(link); + }); + } else { + alertMsg.append("You have added 1 layer: "); + } + } else { + /* disable and switch all the button states */ + $(".build-target-btn").attr("disabled","disabled"); + $(".select-machine-btn").attr("disabled", "disabled"); + addRmLayerBtn.removeClass("btn-danger"); + addRmLayerBtn.data('directive', "add"); + + /* "special" handler so that we get the correct button text which depends + * on which tab is currently visible. Unfortunately we can't just call + * tab('show') as if it's already visible it doesn't run the event. + */ + switch ($(".nav-pills .active a").prop('id')){ + case 'machines-tab': + machinesTabShow(); + break; + case 'targets-tab': + targetsTabShow(); + break; + default: + defaultAddBtnText(); + break; + } + + alertMsg.append("You have deleted 1 layer: "); + } + + alertMsg.children("#layer-affected-name").text(ctx.layerVersion.name); + $("#alert-area").show(); + } + + /* Add or remove this layer from the project */ + addRmLayerBtn.click(function() { + var directive = $(this).data('directive'); + + if (directive == 'add') { + /* If adding get the deps for this layer */ + libtoaster.getLayerDepsForProject(ctx.xhrDataTypeaheadUrl, ctx.projectId, ctx.layerVersion.id, function (data) { + /* got result for dependencies */ + if (data.list.length == 0){ + var editData = { layerAdd : ctx.layerVersion.id }; + libtoaster.editProject(ctx.xhrEditProjectUrl, ctx.projectId, editData, + function() { + setLayerInCurrentPrj(true); + }); + return; + } else { + /* The add deps will include this layer so no need to add it + * separately. + */ + show_layer_deps_modal(ctx.projectId, ctx.layerVersion, data.list, null, null, true, function () { + /* Success add deps and layer */ + setLayerInCurrentPrj(true, data.list); + }); + } + }, null); + } else if (directive == 'remove') { + var editData = { layerDel : ctx.layerVersion.id }; + + libtoaster.editProject(ctx.xhrEditProjectUrl, ctx.projectId, editData, + function () { + /* Success removed layer */ + //window.location.reload(); + setLayerInCurrentPrj(false); + }, function () { + console.warn ("Removing layer from project failed"); + }); + } + }); + + /* Handler for all of the Change buttons */ + $(".change-btn").click(function(){ + var mParent = $(this).parent(); + var prop = $(this).data('layer-prop'); + + /* We have inputs, select and textareas to potentially grab the value + * from. + */ + var entryElement = mParent.find("input"); + if (entryElement.length == 0) + entryElement = mParent.find("textarea"); + if (entryElement.length == 0) + entryElement = mParent.find("select"); + if (entryElement.length == 0) { + console.warn("Could not find element to get data from for this change"); + return; + } + + var data = { layer_version_id: ctx.layerVersion.id }; + data[prop] = entryElement.val(); + + $.ajax({ + type: "POST", + url: ctx.xhrUpdateLayerUrl, + data: data, + headers: { 'X-CSRFToken' : $.cookie('csrftoken')}, + success: function (data) { + if (data.error != "ok") { + console.warn(data.error); + } else { + /* success layer property changed */ + var inputArea = mParent.parents("dd"); + var text; + /* We don't actually want the value from the select option we want + * the text that represents the value to display + */ + text = entryElement.children("option:selected").text(); + if (!text) + text = entryElement.val(); + + /* Hide the "Not set" text if it's visible */ + inputArea.find(".muted").hide(); + inputArea.find(".current-value").text(text); + /* Same behaviour as cancel in that we hide the form/show current + * value. + */ + inputArea.find(".cancel").click(); + } + }, + error: function (data) { + console.warn("Call failed"); + console.warn(data); + } + }); + }); + + /* Disable the change button when we have no data in the input */ + $("dl input, dl textarea").keyup(function() { + if ($(this).val().length == 0) + $(this).parent().children(".change-btn").attr("disabled", "disabled"); + else + $(this).parent().children(".change-btn").removeAttr("disabled"); + }); + + /* This checks to see if the dt's dd has data in it or if the change data + * form is visible, otherwise hide it + */ + $("dl").children().each(function (){ + if ($(this).is("dt")) { + var dd = $(this).next("dd"); + if (!dd.children("form:visible")|| !dd.find(".current-value").html()){ + if (ctx.layerVersion.sourceId == 3){ + /* There's no current value and the layer is editable + * so show the "Not set" and hide the delete icon + */ + dd.find(".muted").show(); + dd.find(".delete-current-value").hide(); + } else { + /* We're not viewing an editable layer so hide the empty dd/dl pair */ + $(this).hide(); + dd.hide(); + } + } + } + }); + + /* Clear the current search selection and reload the results */ + $("#target-search-clear").click(function(){ + $("#target-search").val(""); + $(this).parents("form").submit(); + }); + + $("#machine-search-clear").click(function(){ + $("#machine-search").val(""); + $(this).parents("form").submit(); + }); + + + layerDepsList.find(".icon-trash").click(layerRemoveClick); + layerDepsList.find("a").tooltip(); + $(".icon-trash").tooltip(); + $(".commit").tooltip(); + +} diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js index a2a0abd45b..04264cd8ba 100644 --- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js +++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js @@ -161,6 +161,39 @@ var libtoaster = (function (){ }); }; + /* parses the query string of the current window.location to an object */ + function _parseUrlParams() { + string = window.location.search + string = string.substr(1); + stringArray = string.split ("&"); + obj = {}; + + for (i in stringArray) { + keyVal = stringArray[i].split ("="); + obj[keyVal[0]] = keyVal[1]; + } + + return obj; + }; + + /* takes a flat object and outputs it as a query string + * e.g. the output of dumpsUrlParams + */ + function _dumpsUrlParams(obj) { + var str = "?"; + + for (key in obj){ + if (!obj[key]) + continue; + + str += key+ "="+obj[key].toString(); + str += "&"; + } + + return str; + }; + + return { reload_params : reload_params, startABuild : _startABuild, @@ -169,6 +202,8 @@ var libtoaster = (function (){ getLayerDepsForProject : _getLayerDepsForProject, editProject : _editProject, debug: false, + parseUrlParams : _parseUrlParams, + dumpsUrlParams : _dumpsUrlParams, } })(); diff --git a/bitbake/lib/toaster/toastergui/templates/layerdetails.html b/bitbake/lib/toaster/toastergui/templates/layerdetails.html index 78dc54bfd1..c69f9e945a 100644 --- a/bitbake/lib/toaster/toastergui/templates/layerdetails.html +++ b/bitbake/lib/toaster/toastergui/templates/layerdetails.html @@ -1,159 +1,505 @@ {% extends "baseprojectpage.html" %} {% load projecttags %} {% load humanize %} - +{% load static %} {% block localbreadcrumb %} -
  • Layer Details
  • +
  • All Layers
  • +
  • + {{layerversion.layer.name}} ({{layerversion.commit|truncatechars:13}}) +
  • {% endblock %} - {% block projectinfomain %} - -
    - -
    -
    -
    -
    - - Repository URL -
    -
    -
    -
    - - - -
    - Cloning this Git repository failed -
    -
    -
    - - Repository subdirectory -
    -
    - {{layerversion.dirpath}} - - - -
    -
    Brach, tag or commit
    -
    - {{layerversion.up_branch.name}} - -
    -
    - - Yocto Project compatibility -
    -
    - -
    -
    - - Layer dependencies -
    -
    - - - You can only add layers Toaster knows about -
    -
    -
    -
    -
    - There is no target data for {{layerversion.layer.name}} ... yet
    - Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you - here the targets it provides. -
    -
    -
    -
    - There is no machine data for {{layerversion.layer.name}} ... yet
    - Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you - here the machines it provides. -
    -
    + + + + +{# If this is not an imported layer then hide the edit ui #} +{% if layerversion.layer_source_id != 3 %} + +{% endif %} + +{% include "layers_dep_modal.html" %} +
    +
    +
    +
    -
    -
    -

    About {{layerversion.layer.name}}

    -
    +
    -
    - Summary - -
    -
    - {{layerversion.layer.summary}} - -
    - -
    - Description -
    -
    - {{layerversion.layer.description}} - -
    - -
    - Maintainer(s) -
    -
    - Not set - -
    - -
    +
    +
    + +
    + + {% if layer_in_project == 0 %} + + {% else %} + + {% endif %} + + + +
    +
    +
    + + Repository URL +
    +
    + {{layerversion.layer.vcs_url}} + {% if layerversion.get_vcs_link_url %} + + {% endif %} + + +
    +
    + + Repository subdirectory +
    +
    + + {{layerversion.dirpath}} + {% if layerversion.get_vcs_dirpath_link_url %} + + {% endif %} + + + +
    +
    Brach, tag or commit
    +
    + {{layerversion.commit}} +
    +
    + + + +
    +
    + +
    +
    + + Yocto Project compatibility +
    +
    + {{layerversion.up_branch.name}} +
    +
    + + + +
    +
    + +
    +
    + + Layer dependencies +
    +
    + + + You can only add layers Toaster knows about +
    +
    +
    + +
    + {% if total_targets == 0 %} +
    + There is no target data for {{layerversion.layer.name}} ... yet
    + Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you + here the targets it provides. +
    + {% else %} + +
    + + {% if targets.paginator.count == 0 %} +
    +

    No targets found

    + {% endif %} + + {# only show the search form if we have more than 10 results #} + {% if targets.paginator.count > 10 or request.GET.targets_search %} + {% if targets.paginator.count == 0 %} +
    + {% else %} + + {% endif %} + + + {% if request.GET.targets_search %} + + + + {% endif %} + +
    + {% endif %} + + {% if targets.paginator.count == 0 %} + +
    + +
    + {% else %} + +
    + Show rows: + +
    +
    + + + + + + + + + + + + {% for target in targets %} + + + + + + + {% endfor %} + +
    + + Target + {% if request.GET.targets_search %} + {{targets.paginator.count}} + {% endif %} + + + Target version + DescriptionBuild target
    + {{target.name}} + {% if target.up_id %} + + {% endif %} + {{target.version}}{{target.summary}}
    + + + + {% endif %} + {% endif %} +
    +
    + {% if total_machines == 0 %} +
    + There is no machine data for {{layerversion.layer.name}} ... yet
    + Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you + here the machines it provides. +
    + {% else %} + +
    + + {% if machines.paginator.count == 0 %} +
    +

    No machines found

    + {% endif %} + + {# only show the search form if we have more than 10 results #} + {% if machines.paginator.count > 10 or request.GET.machines_search %} + {% if machines.paginator.count == 0 %} +
    + {% else %} + + {% endif %} + + + {% if request.GET.machines_search %} + + + + {% endif %} + +
    + {% endif %} + + {% if machines.paginator.count == 0 %} + +
    + +
    + {% else %} + +
    + Show rows: + +
    +
    + + + + + + + + + + + {% for machine in machines %} + + + + + + {% endfor %} + +
    + + Machine + {% if request.GET.machines_search %} + {{machines.paginator.count}} + {% endif %} + DescriptionSelect machine
    {{machine.name}}{{machine.description}}
    + + + + {% endif %} + {% endif %} +
    +
    +
    +
    +

    About {{layerversion.layer.name}}

    +
    + +
    + Summary + +
    +
    + + {{layerversion.layer.summary}} +
    + + + Cancel +
    + + +
    +
    + Description +
    +
    + + {{layerversion.layer.description}} +
    + + + Cancel +
    + + +
    + + {% if layerversion.layer.up_id %} +
    Layer index
    +
    + layer index link + +
    + {% endif %} + +
    +
    + +
    + + {% endblock %} diff --git a/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html b/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html index b03fd0b218..8222027d4f 100644 --- a/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html +++ b/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html @@ -18,7 +18,16 @@