From 58cd4a14ea81b72dcd9679608e5e2231ec3d3631 Mon Sep 17 00:00:00 2001 From: Alexandru DAMIAN Date: Mon, 8 Jun 2015 18:33:44 +0100 Subject: bitbake: toaster: fixes after refactoring This patch fixes issues brought in by refactoring: * the New Build button is working with pre-set projects * the xhr_datatypeahead is exposed for calls that are not mapable to the REST objects * a new table returing recipes provided by layers currently selected in the project is used to provide recipe suggestions * the field names in json are switched from "list" to "rows" as to maintain consistency with the ToasterTables * the "value" field in xhr_ calls is now named "search" to maintain consistency (Bitbake rev: a5bc29083d4f85a5695f3f62d5badb783c6f7224) Signed-off-by: Alexandru DAMIAN Signed-off-by: Richard Purdie --- bitbake/lib/toaster/toastergui/static/js/base.js | 104 ++++++++++++--------- .../lib/toaster/toastergui/static/js/libtoaster.js | 2 +- .../lib/toaster/toastergui/static/js/projectapp.js | 24 +++-- bitbake/lib/toaster/toastergui/tables.py | 10 +- bitbake/lib/toaster/toastergui/templates/base.html | 7 +- .../lib/toaster/toastergui/templates/project.html | 3 +- bitbake/lib/toaster/toastergui/urls.py | 8 ++ bitbake/lib/toaster/toastergui/views.py | 17 +++- 8 files changed, 114 insertions(+), 61 deletions(-) (limited to 'bitbake/lib/toaster/toastergui') diff --git a/bitbake/lib/toaster/toastergui/static/js/base.js b/bitbake/lib/toaster/toastergui/static/js/base.js index 747442cc9e..06d0676cbf 100644 --- a/bitbake/lib/toaster/toastergui/static/js/base.js +++ b/bitbake/lib/toaster/toastergui/static/js/base.js @@ -1,100 +1,118 @@ +'use strict'; - -function basePageInit (ctx) { +function basePageInit(ctx) { var newBuildButton = $("#new-build-button"); /* Hide the button if we're on the project,newproject or importlyaer page * or if there are no projects yet defined */ - if (ctx.numProjects == 0 || ctx.currentUrl.search('newproject|project/\\d$|importlayer$') > 0){ - newBuildButton.hide(); - return; + if (ctx.numProjects === 0 || ctx.currentUrl.search('newproject|project/\\d$|importlayer$') > 0) { + newBuildButton.hide(); + return; } var currentProjectId = libtoaster.ctx.projectId; /* Hide the change project icon when there is only one project */ - if (ctx.numProjects == 1){ - $('#project .icon-pencil').hide(); + if (ctx.numProjects === 1) { + $('#project .icon-pencil').hide(); } newBuildButton.show().removeAttr("disabled"); - _checkProjectBuildable() + 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; + + _checkProjectBuildable(); _setupNewBuildButton(); - function _checkProjectBuildable(){ - if (libtoaster.ctx.projectId == undefined) + function _checkProjectBuildable() { + if (libtoaster.ctx.projectId === undefined) { return; + } libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, - function(data){ - if (data.machine.name == undefined || data.layers.length == 0) { + 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(); + + /* we can build this project; enable input fields */ + newBuildTargetInput.prop("disabled", false); + newBuildTargetBuildBtn.prop("disabled", false); + + libtoaster.makeTypeahead(newBuildTargetInput, libtoaster.ctx.projectTargetsUrl, { format: "json" }, function (item) { + /* successfully selected a target */ + selectedTarget = item; + }); + } - }, null); + }, 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 (libtoaster.ctx.projectId == undefined) { + if (libtoaster.ctx.projectId === undefined) { $('#change-project-form').show(); $('#project .icon-pencil').hide(); } - libtoaster.makeTypeahead(newBuildProjectInput, libtoaster.ctx.projectsUrl, { format : "json" }, function(item){ - /* successfully selected a project */ - newBuildProjectSaveBtn.removeAttr("disabled"); - selectedProject = item; + libtoaster.makeTypeahead(newBuildProjectInput, libtoaster.ctx.projectsUrl, { format : "json" }, 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.on('input', function(event) { - if (event.keyCode == 13) - return; - newBuildProjectSaveBtn.attr("disabled", "disabled"); + newBuildProjectInput.on('input', function (event) { + if (event.keyCode === 13) { + return; + } + newBuildProjectSaveBtn.attr("disabled", "disabled"); }); - newBuildTargetInput.on('input', function() { - if ($(this).val().length == 0) + newBuildTargetInput.on('input', function () { + if ($(this).val().length === 0) { newBuildTargetBuildBtn.attr("disabled", "disabled"); - else + } else { newBuildTargetBuildBtn.removeAttr("disabled"); + } }); - newBuildTargetBuildBtn.click(function() { - if (!newBuildTargetInput.val()) + newBuildTargetBuildBtn.click(function () { + if (!newBuildTargetInput.val()) { return; + } - if (!selectedTarget) + if (!selectedTarget) { selectedTarget = { name: newBuildTargetInput.val() }; + } /* fire and forget */ - libtoaster.startABuild(ctx.projectBuildsUrl, libtoaster.ctx.projectId, selectedTarget.name, null, null); + libtoaster.startABuild(libtoaster.ctx.projectBuildsUrl, libtoaster.ctx.projectId, selectedTarget.name, null, null); window.location.replace(libtoaster.ctx.projectPageUrl); }); - newBuildProjectSaveBtn.click(function() { - libtoaster.ctx.projectId = selectedProject.pk + newBuildProjectSaveBtn.click(function () { + libtoaster.ctx.projectId = selectedProject.pk; /* Update the typeahead project_id paramater */ _checkProjectBuildable(); @@ -111,10 +129,10 @@ function basePageInit (ctx) { newBuildTargetInput.prop("disabled", false); newBuildTargetBuildBtn.prop("disabled", false); - libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function(item){ + libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) { /* successfully selected a target */ selectedTarget = item; - }); + }); newBuildTargetInput.val(""); @@ -123,12 +141,12 @@ function basePageInit (ctx) { $("#new-build-button .alert a").attr('href', libtoaster.ctx.projectPageUrl); $("#project .icon-pencil").show(); - $("#change-project-form").slideUp({ 'complete' : function() { + $("#change-project-form").slideUp({ 'complete' : function () { $("#new-build-button #project").show(); }}); }); - $('#new-build-button #project .icon-pencil').click(function() { + $('#new-build-button #project .icon-pencil').click(function () { newBuildProjectSaveBtn.attr("disabled", "disabled"); newBuildProjectInput.val($("#new-build-button #project a").text()); $("#cancel-change-project").show(); @@ -136,8 +154,8 @@ function basePageInit (ctx) { $("#change-project-form").slideDown(); }); - $("#new-build-button #cancel-change-project").click(function() { - $("#change-project-form").hide(function(){ + $("#new-build-button #cancel-change-project").click(function () { + $("#change-project-form").hide(function () { $('#new-build-button #project').show(); }); @@ -146,7 +164,7 @@ function basePageInit (ctx) { }); /* Keep the dropdown open even unless we click outside the dropdown area */ - $(".new-build").click (function(event) { + $(".new-build").click (function (event) { event.stopPropagation(); }); }; diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js index b1038cf618..23755a75f2 100644 --- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js +++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js @@ -25,7 +25,7 @@ var libtoaster = (function (){ return; } - return process (data.list); + return process (data.rows); }); }, updater: function(item) { diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js index a915278444..44e244d302 100644 --- a/bitbake/lib/toaster/toastergui/static/js/projectapp.js +++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js @@ -217,13 +217,13 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc $scope.getAutocompleteSuggestions = function(type, currentValue) { var deffered = $q.defer(); - $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: type, value: currentValue}}) + $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: type, search: currentValue}}) .success(function (_data) { if (_data.error != "ok") { console.warn(_data.error); deffered.reject(_data.error); } - deffered.resolve(_data.list); + deffered.resolve(_data.rows); }); return deffered.promise; @@ -534,8 +534,17 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc if (_data.error != "ok") { console.warn(_data.error); } else { - console.log("got layer deps", _data.layerdeps.list); - if (_data.layerdeps.list.length > 0) { + /* filter out layers that are already in the project */ + var filtered_list = []; + var projectlayers_ids = $scope.layers.map(function (e) { return e.id }); + for (var i = 0; i < _data.layerdeps.list.length; i++) { + if (projectlayers_ids.indexOf(_data.layerdeps.list[i].id) == -1) { + filtered_list.push( _data.layerdeps.list[i]); + } + } + + _data.layerdeps.list = filtered_list; + if (_data.layerdeps.list.length > 0) { // activate modal console.log("listing modals"); var modalInstance = $modal.open({ @@ -575,7 +584,6 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc console.log("built modal instance", modalInstance); modalInstance.result.then(function (selectedArray) { - console.log("layer to add", $scope.layerToAdd) selectedArray.push($scope.layerToAdd.id); console.warn("TRC6: selected", selectedArray); @@ -634,13 +642,13 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc $scope.testProjectSettingsChange = function(elementid) { if (elementid != '#change-project-version') throw "Not implemented"; - $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: "versionlayers", value: $scope.projectVersion }}). + $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: "versionlayers", search: $scope.projectVersion }}). success(function (_data) { if (_data.error != "ok") { alert (_data.error); } else { - if (_data.list.length > 0) { + if (_data.rows.length > 0) { // activate modal var modalInstance = $modal.open({ templateUrl: 'change_version_modal', @@ -660,7 +668,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc }, resolve: { items: function () { - return _data.list; + return _data.rows; }, releaseName: function () { return $scope.releases.filter(function (e) { if (e.id == $scope.projectVersion) return e;})[0].name; diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py index 003b924d00..b85527e216 100644 --- a/bitbake/lib/toaster/toastergui/tables.py +++ b/bitbake/lib/toaster/toastergui/tables.py @@ -287,7 +287,7 @@ class RecipesTable(ToasterTable): def setup_queryset(self, *args, **kwargs): prj = Project.objects.get(pk = kwargs['pid']) - self.queryset = Recipe.objects.filter(Q(layer_version__up_branch__name= prj.release.name) | Q(layer_version__build__in = prj.build_set.all())).filter(name__regex=r'.{1,}.*') + self.queryset = Recipe.objects.filter(layer_version__in = prj.compatible_layerversions()) search_maxids = map(lambda i: i[0], list(self.queryset.values('name').distinct().annotate(max_id=Max('id')).values_list('max_id'))) @@ -392,3 +392,11 @@ class LayerRecipesTable(RecipesTable): self.add_column(title="Build recipe", static_data_name="add-del-layers", static_data_template=build_recipe_template) + +class ProjectLayersRecipesTable(RecipesTable): + """ Table that lists only recipes available for layers added to the project """ + + def setup_queryset(self, *args, **kwargs): + super(ProjectLayersRecipesTable, self).setup_queryset(*args, **kwargs) + prj = Project.objects.get(pk = kwargs['pid']) + self.queryset = self.queryset.filter(layer_version__in = prj.projectlayer_equivalent_set()) diff --git a/bitbake/lib/toaster/toastergui/templates/base.html b/bitbake/lib/toaster/toastergui/templates/base.html index 7fee26eb42..6cdc5e8110 100644 --- a/bitbake/lib/toaster/toastergui/templates/base.html +++ b/bitbake/lib/toaster/toastergui/templates/base.html @@ -1,5 +1,6 @@ {% load static %} +{% load projecttags %} {% if objectname %} {{objectname|title}} - {% endif %}Toaster @@ -33,8 +34,10 @@ htmlUrl : "{% static 'html/' %}", projectsUrl : "{% url 'all-projects' %}", {% if project.id %} - projectPageUrl : "{% url 'project' project.id %}", - projectName : "{{project.name}}", + projectPageUrl : {% url 'project' project.id as purl%}{{purl|json}}, + projectName : {{project.name|json}}, + projectTargetsUrl: {% url 'projectavailabletargets' project.id as paturl%}{{paturl|json}}, + projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}}, projectId : {{project.id}}, {% else %} projectPageUrl : undefined, diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html index 7225363c01..bca703a162 100644 --- a/bitbake/lib/toaster/toastergui/templates/project.html +++ b/bitbake/lib/toaster/toastergui/templates/project.html @@ -452,9 +452,10 @@ angular.element(document).ready(function() { scope.urls.xhr_build = "{% url 'projectbuilds' project.id %}"; scope.urls.xhr_edit = "{% url 'project' project.id %}?format=json"; scope.urls.layers = "{% url 'projectlayers' project.id %}"; - scope.urls.targets = "{% url 'projecttargets' project.id %}"; + scope.urls.targets = "{% url 'projectavailabletargets' project.id %}"; scope.urls.machines = "{% url 'projectmachines' project.id %}"; scope.urls.importlayer = "{% url 'importlayer' project.id %}"; + scope.urls.xhr_datatypeahead = {% url 'xhr_datatypeahead' project.id as xhrdta %}{{xhrdta|json}}; scope.project = {{prj|json}}; scope.builds = {{builds|json}}; scope.layers = {{layers|json}}; diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py index 5a79f88eb4..bd3eb401de 100644 --- a/bitbake/lib/toaster/toastergui/urls.py +++ b/bitbake/lib/toaster/toastergui/urls.py @@ -96,6 +96,12 @@ urlpatterns = patterns('toastergui.views', 'title' : 'All compatible recipes' }, name="projecttargets"), + url(r'^project/(?P\d+)/availablerecipes/$', + tables.ProjectLayersRecipesTable.as_view(template_name="generic-toastertable-page.html"), + { 'table_name': tables.ProjectLayersRecipesTable.__name__.lower(), + 'title' : 'Recipes available for layers in the current project' }, + name="projectavailabletargets"), + url(r'^project/(?P\d+)/layers/$', tables.LayersTable.as_view(template_name="generic-toastertable-page.html"), { 'table_name': tables.LayersTable.__name__.lower(), @@ -118,6 +124,8 @@ urlpatterns = patterns('toastergui.views', 'title' : 'All machines in layer' }, name=tables.LayerMachinesTable.__name__.lower()), + + url(r'^xhr_datatypeahead/(?P\d+)$', 'xhr_datatypeahead', name='xhr_datatypeahead'), url(r'^xhr_configvaredit/(?P\d+)$', 'xhr_configvaredit', name='xhr_configvaredit'), url(r'^xhr_importlayer/$', 'xhr_importlayer', name='xhr_importlayer'), diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index b2b263b6e0..4dac62c568 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py @@ -129,9 +129,9 @@ def _template_renderer(template): if request.GET.get('format', None) == 'json': # objects is a special keyword - it's a Page, but we need the actual objects here - # in XHR, the objects come in the "list" property + # in XHR, the objects come in the "rows" property if "objects" in context: - context["list"] = context["objects"].object_list + context["rows"] = context["objects"].object_list del context["objects"] # we're about to return; to keep up with the XHR API, we set the error to OK @@ -2340,7 +2340,7 @@ if toastermain.settings.MANAGED: retval.append(i) return HttpResponse(jsonfilter( {"error":"ok", - "list" : map( _lv_to_dict(prj), map(lambda x: x.layercommit, retval )) + "rows" : map( _lv_to_dict(prj), map(lambda x: x.layercommit, retval )) }), content_type = "application/json") @@ -2358,7 +2358,7 @@ if toastermain.settings.MANAGED: # and show only the selected layers for this project final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset_all]) - return HttpResponse(jsonfilter( { "error":"ok", "list" : map( _lv_to_dict(prj), final_list) }), content_type = "application/json") + return HttpResponse(jsonfilter( { "error":"ok", "rows" : map( _lv_to_dict(prj), final_list) }), content_type = "application/json") raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied")) @@ -2845,7 +2845,7 @@ if toastermain.settings.MANAGED: p.projectPageUrl = reverse('project', args=(p.id,)) p.projectLayersUrl = reverse('projectlayers', args=(p.id,)) p.projectBuildsUrl = reverse('projectbuilds', args=(p.id,)) - p.projectTargetsUrl = reverse('projecttargets', args=(p.id,)) + p.projectTargetsUrl = reverse('projectavailabletargets', args=(p.id,)) # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) build_mru = _managed_get_latest_builds() @@ -3144,6 +3144,13 @@ else: def project(request, pid): return {} + from django.views.decorators.csrf import csrf_exempt + @csrf_exempt + @_template_renderer('landing_not_managed.html') + def xhr_datatypeahead(request, pid): + return {} + + @_template_renderer('landing_not_managed.html') def xhr_configvaredit(request, pid): return {} -- cgit v1.2.3-54-g00ecf