summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2015-01-14 12:46:52 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-01-16 08:25:31 +0000
commit025533d90b694ed37278c8f5be85afbd05857971 (patch)
tree51bb70fa2873285e1e6d716cf637d78ccb538317 /bitbake
parent2a6f739f1d3a0383d849cb2944c69797b4b3e437 (diff)
downloadpoky-025533d90b694ed37278c8f5be85afbd05857971.tar.gz
bitbake: toaster: Add layer details page feature
This commit adds the layer details page which shows the metadata for the layer such as layer description, machines associated with the layer as well as the targets provided. If the layer is an imported layer this page also allows you to update the layer's configuration. >From this page you can add/remove the layer from the current project (Bitbake rev: c1442bc68ad8ba20c37b1a7cde1400297f4be811) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/toaster/toastergui/static/css/default.css1
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/layerdetails.js404
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/libtoaster.js35
-rw-r--r--bitbake/lib/toaster/toastergui/templates/layerdetails.html644
-rw-r--r--bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html9
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py1
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py94
7 files changed, 1038 insertions, 150 deletions
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; }
232.animate-repeat.ng-enter.ng-enter-active { 232.animate-repeat.ng-enter.ng-enter-active {
233 opacity:1; 233 opacity:1;
234} 234}
235.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 @@
1"use strict"
2
3function layerDetailsPageInit (ctx) {
4
5 var layerDepInput = $("#layer-dep-input");
6 var layerDepBtn = $("#add-layer-dependency-btn");
7 var layerDepsList = $("#layer-deps-list");
8 var currentLayerDepSelection;
9 var addRmLayerBtn = $("#add-remove-layer-btn");
10
11 /* setup the dependencies typeahead */
12 libtoaster.makeTypeahead(layerDepInput, ctx.xhrDataTypeaheadUrl, { type : "layers", project_id: ctx.projectId, include_added: "true" }, function(item){
13 currentLayerDepSelection = item;
14
15 layerDepBtn.removeAttr("disabled");
16 });
17
18 function addRemoveDep(depLayerId, add, doneCb) {
19 var data = { layer_version_id : ctx.layerVersion.id };
20 if (add)
21 data.add_dep = depLayerId;
22 else
23 data.rm_dep = depLayerId;
24
25 $.ajax({
26 type: "POST",
27 url: ctx.xhrUpdateLayerUrl,
28 data: data,
29 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
30 success: function (data) {
31 if (data.error != "ok") {
32 console.warn(data.error);
33 } else {
34 doneCb();
35 }
36 },
37 error: function (data) {
38 console.warn("Call failed");
39 console.warn(data);
40 }
41 });
42 }
43
44 function layerRemoveClick() {
45 var toRemove = $(this).parent().data('layer-id');
46 var layerDepItem = $(this);
47
48 addRemoveDep(toRemove, false, function(){
49 layerDepItem.parent().fadeOut(function (){
50 layerDepItem.remove();
51 });
52 });
53 }
54
55 /* Add dependency layer button click handler */
56 layerDepBtn.click(function(){
57 if (currentLayerDepSelection == undefined)
58 return;
59
60 addRemoveDep(currentLayerDepSelection.id, true, function(){
61 /* Make a list item for the new layer dependency */
62 var newLayerDep = $("<li><a></a><span class=\"icon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
63
64 newLayerDep.data('layer-id', currentLayerDepSelection.id);
65 newLayerDep.children("span").tooltip();
66
67 var link = newLayerDep.children("a");
68 link.attr("href", ctx.layerDetailsUrl+String(currentLayerDepSelection.id));
69 link.text(currentLayerDepSelection.name);
70 link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"});
71
72 /* Connect up the tash icon */
73 var trashItem = newLayerDep.children("span");
74 trashItem.click(layerRemoveClick);
75
76 layerDepsList.append(newLayerDep);
77 /* Clear the current selection */
78 layerDepInput.val("");
79 currentLayerDepSelection = undefined;
80 layerDepBtn.attr("disabled","disabled");
81 });
82 });
83
84 $(".icon-pencil").click(function (){
85 var mParent = $(this).parent("dd");
86 mParent.prev().css("margin-top", "10px");
87 mParent.children("form").slideDown();
88 var currentVal = mParent.children(".current-value");
89 currentVal.hide();
90 /* Set the current value to the input field */
91 mParent.find("textarea,input").val(currentVal.text());
92 /* Hides the "Not set" text */
93 mParent.children(".muted").hide();
94 /* We're editing so hide the delete icon */
95 mParent.children(".delete-current-value").hide();
96 mParent.find(".cancel").show();
97 $(this).hide();
98 });
99
100 $(".delete-current-value").click(function(){
101 var mParent = $(this).parent("dd");
102 mParent.find("input").val("");
103 mParent.find("textarea").val("");
104 mParent.find(".change-btn").click();
105 });
106
107 $(".cancel").click(function(){
108 var mParent = $(this).parents("dd");
109 $(this).hide();
110 mParent.children("form").slideUp(function(){
111 mParent.children(".current-value").show();
112 /* Show the "Not set" text if we ended up with no value */
113 if (!mParent.children(".current-value").html()){
114 mParent.children(".muted").fadeIn();
115 mParent.children(".delete-current-value").hide();
116 } else {
117 mParent.children(".delete-current-value").show();
118 }
119
120 mParent.children(".icon-pencil").show();
121 mParent.prev().css("margin-top", "0px");
122 });
123 });
124
125 $(".build-target-btn").click(function(){
126 /* fire a build */
127 var target = $(this).data('target-name');
128 libtoaster.startABuild(ctx.projectBuildUrl, ctx.projectId, target, null, null);
129 window.location.replace(ctx.projectPageUrl);
130 });
131
132 $(".select-machine-btn").click(function(){
133 var data = { machineName : $(this).data('machine-name') };
134 libtoaster.editProject(ctx.xhrEditProjectUrl, ctx.projectId, data,
135 function (){
136 window.location.replace(ctx.projectPageUrl);
137 }, null);
138 });
139
140 function defaultAddBtnText(){
141 var text = " Add the "+ctx.layerVersion.name+" layer to your project";
142 addRmLayerBtn.text(text);
143 addRmLayerBtn.prepend("<span class=\"icon-plus\"></span>");
144 addRmLayerBtn.removeClass("btn-danger");
145 }
146
147 $("#details-tab").on('show', function(){
148 if (!ctx.layerVersion.inCurrentPrj)
149 defaultAddBtnText();
150
151 window.location.hash = "details";
152 });
153
154 function targetsTabShow(){
155 if (!ctx.layerVersion.inCurrentPrj){
156 if (ctx.numTargets > 0) {
157 var text = " Add the "+ctx.layerVersion.name+" layer to your project "+
158 "to enable these targets";
159 addRmLayerBtn.text(text);
160 addRmLayerBtn.prepend("<span class=\"icon-plus\"></span>");
161 } else {
162 defaultAddBtnText();
163 }
164 }
165
166 window.location.hash = "targets";
167 }
168
169 $("#targets-tab").on('show', targetsTabShow);
170
171 function machinesTabShow(){
172 if (!ctx.layerVersion.inCurrentPrj) {
173 if (ctx.numMachines > 0){
174 var text = " Add the "+ctx.layerVersion.name+" layer to your project " +
175 "to enable these machines";
176 addRmLayerBtn.text(text);
177 addRmLayerBtn.prepend("<span class=\"icon-plus\"></span>");
178 } else {
179 defaultAddBtnText();
180 }
181 }
182
183 window.location.hash = "machines";
184 }
185
186 $("#machines-tab").on('show', machinesTabShow);
187
188 $(".pagesize").change(function(){
189 var search = libtoaster.parseUrlParams();
190 search.limit = this.value;
191
192 window.location.search = libtoaster.dumpsUrlParams(search);
193 });
194
195 /* Enables the Build target and Select Machine buttons and switches the
196 * add/remove button
197 */
198 function setLayerInCurrentPrj(added, depsList) {
199 ctx.layerVersion.inCurrentPrj = added;
200 var alertMsg = $("#alert-msg");
201 /* Reset alert message */
202 alertMsg.text("");
203
204 if (added){
205 /* enable and switch all the button states */
206 $(".build-target-btn").removeAttr("disabled");
207 $(".select-machine-btn").removeAttr("disabled");
208 addRmLayerBtn.addClass("btn-danger");
209 addRmLayerBtn.data('directive', "remove");
210 addRmLayerBtn.text(" Delete the "+ctx.layerVersion.name+" layer from your project");
211 addRmLayerBtn.prepend("<span class=\"icon-trash\"></span>");
212
213 if (depsList) {
214 alertMsg.append("You have added <strong>"+(depsList.length+1)+"</strong> layers: <span id=\"layer-affected-name\"></span> and its dependencies ");
215
216 /* Build the layer deps list */
217 depsList.map(function(layer, i){
218 var link = $("<a></a>");
219
220 link.attr("href", layer.layerdetailurl);
221 link.text(layer.name);
222 link.tooltip({title: layer.tooltip});
223
224 if (i != 0)
225 alertMsg.append(", ");
226
227 alertMsg.append(link);
228 });
229 } else {
230 alertMsg.append("You have added <strong>1</strong> layer: <span id=\"layer-affected-name\"></span>");
231 }
232 } else {
233 /* disable and switch all the button states */
234 $(".build-target-btn").attr("disabled","disabled");
235 $(".select-machine-btn").attr("disabled", "disabled");
236 addRmLayerBtn.removeClass("btn-danger");
237 addRmLayerBtn.data('directive', "add");
238
239 /* "special" handler so that we get the correct button text which depends
240 * on which tab is currently visible. Unfortunately we can't just call
241 * tab('show') as if it's already visible it doesn't run the event.
242 */
243 switch ($(".nav-pills .active a").prop('id')){
244 case 'machines-tab':
245 machinesTabShow();
246 break;
247 case 'targets-tab':
248 targetsTabShow();
249 break;
250 default:
251 defaultAddBtnText();
252 break;
253 }
254
255 alertMsg.append("You have deleted <strong>1</strong> layer: <span id=\"layer-affected-name\"></span>");
256 }
257
258 alertMsg.children("#layer-affected-name").text(ctx.layerVersion.name);
259 $("#alert-area").show();
260 }
261
262 /* Add or remove this layer from the project */
263 addRmLayerBtn.click(function() {
264 var directive = $(this).data('directive');
265
266 if (directive == 'add') {
267 /* If adding get the deps for this layer */
268 libtoaster.getLayerDepsForProject(ctx.xhrDataTypeaheadUrl, ctx.projectId, ctx.layerVersion.id, function (data) {
269 /* got result for dependencies */
270 if (data.list.length == 0){
271 var editData = { layerAdd : ctx.layerVersion.id };
272 libtoaster.editProject(ctx.xhrEditProjectUrl, ctx.projectId, editData,
273 function() {
274 setLayerInCurrentPrj(true);
275 });
276 return;
277 } else {
278 /* The add deps will include this layer so no need to add it
279 * separately.
280 */
281 show_layer_deps_modal(ctx.projectId, ctx.layerVersion, data.list, null, null, true, function () {
282 /* Success add deps and layer */
283 setLayerInCurrentPrj(true, data.list);
284 });
285 }
286 }, null);
287 } else if (directive == 'remove') {
288 var editData = { layerDel : ctx.layerVersion.id };
289
290 libtoaster.editProject(ctx.xhrEditProjectUrl, ctx.projectId, editData,
291 function () {
292 /* Success removed layer */
293 //window.location.reload();
294 setLayerInCurrentPrj(false);
295 }, function () {
296 console.warn ("Removing layer from project failed");
297 });
298 }
299 });
300
301 /* Handler for all of the Change buttons */
302 $(".change-btn").click(function(){
303 var mParent = $(this).parent();
304 var prop = $(this).data('layer-prop');
305
306 /* We have inputs, select and textareas to potentially grab the value
307 * from.
308 */
309 var entryElement = mParent.find("input");
310 if (entryElement.length == 0)
311 entryElement = mParent.find("textarea");
312 if (entryElement.length == 0)
313 entryElement = mParent.find("select");
314 if (entryElement.length == 0) {
315 console.warn("Could not find element to get data from for this change");
316 return;
317 }
318
319 var data = { layer_version_id: ctx.layerVersion.id };
320 data[prop] = entryElement.val();
321
322 $.ajax({
323 type: "POST",
324 url: ctx.xhrUpdateLayerUrl,
325 data: data,
326 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
327 success: function (data) {
328 if (data.error != "ok") {
329 console.warn(data.error);
330 } else {
331 /* success layer property changed */
332 var inputArea = mParent.parents("dd");
333 var text;
334 /* We don't actually want the value from the select option we want
335 * the text that represents the value to display
336 */
337 text = entryElement.children("option:selected").text();
338 if (!text)
339 text = entryElement.val();
340
341 /* Hide the "Not set" text if it's visible */
342 inputArea.find(".muted").hide();
343 inputArea.find(".current-value").text(text);
344 /* Same behaviour as cancel in that we hide the form/show current
345 * value.
346 */
347 inputArea.find(".cancel").click();
348 }
349 },
350 error: function (data) {
351 console.warn("Call failed");
352 console.warn(data);
353 }
354 });
355 });
356
357 /* Disable the change button when we have no data in the input */
358 $("dl input, dl textarea").keyup(function() {
359 if ($(this).val().length == 0)
360 $(this).parent().children(".change-btn").attr("disabled", "disabled");
361 else
362 $(this).parent().children(".change-btn").removeAttr("disabled");
363 });
364
365 /* This checks to see if the dt's dd has data in it or if the change data
366 * form is visible, otherwise hide it
367 */
368 $("dl").children().each(function (){
369 if ($(this).is("dt")) {
370 var dd = $(this).next("dd");
371 if (!dd.children("form:visible")|| !dd.find(".current-value").html()){
372 if (ctx.layerVersion.sourceId == 3){
373 /* There's no current value and the layer is editable
374 * so show the "Not set" and hide the delete icon
375 */
376 dd.find(".muted").show();
377 dd.find(".delete-current-value").hide();
378 } else {
379 /* We're not viewing an editable layer so hide the empty dd/dl pair */
380 $(this).hide();
381 dd.hide();
382 }
383 }
384 }
385 });
386
387 /* Clear the current search selection and reload the results */
388 $("#target-search-clear").click(function(){
389 $("#target-search").val("");
390 $(this).parents("form").submit();
391 });
392
393 $("#machine-search-clear").click(function(){
394 $("#machine-search").val("");
395 $(this).parents("form").submit();
396 });
397
398
399 layerDepsList.find(".icon-trash").click(layerRemoveClick);
400 layerDepsList.find("a").tooltip();
401 $(".icon-trash").tooltip();
402 $(".commit").tooltip();
403
404}
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 (){
161 }); 161 });
162 }; 162 };
163 163
164 /* parses the query string of the current window.location to an object */
165 function _parseUrlParams() {
166 string = window.location.search
167 string = string.substr(1);
168 stringArray = string.split ("&");
169 obj = {};
170
171 for (i in stringArray) {
172 keyVal = stringArray[i].split ("=");
173 obj[keyVal[0]] = keyVal[1];
174 }
175
176 return obj;
177 };
178
179 /* takes a flat object and outputs it as a query string
180 * e.g. the output of dumpsUrlParams
181 */
182 function _dumpsUrlParams(obj) {
183 var str = "?";
184
185 for (key in obj){
186 if (!obj[key])
187 continue;
188
189 str += key+ "="+obj[key].toString();
190 str += "&";
191 }
192
193 return str;
194 };
195
196
164 return { 197 return {
165 reload_params : reload_params, 198 reload_params : reload_params,
166 startABuild : _startABuild, 199 startABuild : _startABuild,
@@ -169,6 +202,8 @@ var libtoaster = (function (){
169 getLayerDepsForProject : _getLayerDepsForProject, 202 getLayerDepsForProject : _getLayerDepsForProject,
170 editProject : _editProject, 203 editProject : _editProject,
171 debug: false, 204 debug: false,
205 parseUrlParams : _parseUrlParams,
206 dumpsUrlParams : _dumpsUrlParams,
172 } 207 }
173})(); 208})();
174 209
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 @@
1{% extends "baseprojectpage.html" %} 1{% extends "baseprojectpage.html" %}
2{% load projecttags %} 2{% load projecttags %}
3{% load humanize %} 3{% load humanize %}
4 4{% load static %}
5{% block localbreadcrumb %} 5{% block localbreadcrumb %}
6<li>Layer Details</li> 6<li><a href="{% url 'layers' %}">All Layers</a></li>
7<li>
8 {{layerversion.layer.name}} ({{layerversion.commit|truncatechars:13}})
9</li>
7{% endblock %} 10{% endblock %}
8
9{% block projectinfomain %} 11{% block projectinfomain %}
10<div class="page-header"> 12
11 <h1>Layer Details</h1> 13
12</div> 14<script src="{% static 'js/layerdetails.js' %}"></script>
13 15<script>
14 <div class="row-fluid span7 tabbable"> 16
15 <ul class="nav nav-pills"> 17 $(document).ready(function (){
16 <li class="active"> 18 var ctx = {
17 <a data-toggle="tab" href="#information">Layer details</a> 19 projectBuildUrl : "{% url 'xhr_build' %}",
18 </li> 20 layerDetailsUrl : "{% url 'layerdetails' %}",
19 <li> 21 projectPageUrl : "{% url 'project' project.id %}",
20 <a data-toggle="tab" href="#targets">Targets (0)</a> 22 xhrEditProjectUrl : "{% url 'xhr_projectedit' project.id %}",
21 </li> 23 xhrDataTypeaheadUrl : "{% url 'xhr_datatypeahead' %}",
22 <li> 24 xhrUpdateLayerUrl : "{% url 'xhr_updatelayer' %}",
23 <a data-toggle="tab" href="#machines">Machines (0)</a> 25 projectId : {{project.id}},
24 </li> 26 numTargets : {{total_targets}},
25 <li> 27 numMachines: {{machines|length}},
26 <a data-toggle="tab" href="#classes">Classes (0)</a> 28 layerVersion : {
27 </li> 29 name : "{{layerversion.layer.name}}",
28 <li> 30 id : {{layerversion.id}},
29 <a data-toggle="tab" href="#bbappends">bbappends (0)</a> 31 commit: "{{layerversion.commit}}",
30 </li> 32 inCurrentPrj : {{layer_in_project}},
31 </ul> 33 url : "{% url 'layerdetails' layerversion.id %}",
32 <div class="tab-content"> 34 sourceId: {{layerversion.layer_source_id}},
33 <div name="information" id="information" class="tab-pane active"> 35 }
34 <dl class="dl-horizontal"> 36 };
35 <dt class=""> 37
36 <i class="icon-question-sign get-help" title="Fetch/clone URL of the repository"></i> 38 try {
37 Repository URL 39 layerDetailsPageInit(ctx);
38 </dt> 40 } catch (e) {
39 <dd> 41 document.write("Sorry, An error has occurred loading this page");
40 <form id="change-repo-form" class="control-group"> 42 console.warn(e);
41 <div class="input-append"> 43 }
42 <input type="text" class="input-xlarge" id="type-repo" value="{{layerversion.layer.vcs_url}}"> 44 });
43 <button id="apply-change-repo" class="btn" type="button">Change</button> 45</script>
44 <!--a href="#" id="cancel-change-repo" class="btn btn-link">Cancel</a--> 46
45 </div> 47{# If this is not an imported layer then hide the edit ui #}
46 <span class="help-block">Cloning this Git repository failed</span> 48{% if layerversion.layer_source_id != 3 %}
47 </form> 49<style>
48 </dd> 50 .icon-pencil {
49 <dt> 51 display:none;
50 <i class="icon-question-sign get-help" title="Subdirectory within the repository where the layer is located, if not in the root (usually only used if the repository contains more than one layer)"></i> 52 }
51 Repository subdirectory 53.delete-current-value{
52 </dt> 54 display: none;
53 <dd> 55}
54 <span id="subdir">{{layerversion.dirpath}}</span> 56 li .icon-trash {
55 <i id="change-subdir" class="icon-pencil"></i> 57 display:none;
56 <i id="delete-subdir" class="icon-trash"></i> 58 }
57 <form id="change-subdir-form" style="display:none;"> 59 .add-deps {
58 <div class="input-append"> 60 display:none;
59 <input type="text" id="type-subdir" value="meta-acer"> 61 }
60 <button id="apply-change-subdir" class="btn" type="button">Change</button> 62</style>
61 <a href="#" id="cancel-change-subdir" class="btn btn-link">Cancel</a> 63{% endif %}
62 </div> 64
63 </form> 65{% include "layers_dep_modal.html" %}
64 </dd> 66 <div class="container-fluid top-padded">
65 <dt>Brach, tag or commit</dt> 67 <div class="row-fluid">
66 <dd> 68 <div class="span11">
67 {{layerversion.up_branch.name}} 69 <div class="page-header">
68 <i class="icon-pencil"></i> 70 <h1>{{layerversion.layer.name}} <small class="commit" data-toggle="tooltip" title="{{layerversion.commit}}">({{layerversion.commit|truncatechars:13}})</small></h1>
69 </dd> 71 </div>
70 <dt>
71 <i class="icon-question-sign get-help" title="The Yocto Project versions with which this layer is compatible. Currently Toaster supports Yocto Project 1.6 and 1.7"></i>
72 Yocto Project compatibility
73 </dt>
74 <dd>
75 <i class="icon-pencil"></i>
76 </dd>
77 <dt>
78 <i class="icon-question-sign get-help" title="Other layers this layer depends upon"></i>
79 Layer dependencies
80 </dt>
81 <dd>
82 <ul class="unstyled">
83 {% for ld in layer.dependencies.all %}
84 <li>
85 <a href="#">openembedded core (meta)</a>
86 <i class="icon-trash"></i>
87 </li>
88 {% endfor %}
89 </ul>
90 <div class="input-append">
91 <input type="text" autocomplete="off" data-minLength="1" data-autocomplete="off"
92 data-provide="typeahead" data-source='
93 '
94 placeholder="Type a layer name" id="layer-dependency">
95 <a class="btn" type="button" id="add-layer-dependency" disabled>
96 Add layer
97 </a>
98 </div>
99 <span class="help-block">You can only add layers Toaster knows about</span>
100 </dd>
101 </dl>
102 </div>
103 <div name="targets" id="targets" class="tab-pane">
104 <div class="alert alert-info">
105 <strong>There is no target data for {{layerversion.layer.name}} ... yet</strong> <br />
106 Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you
107 here the targets it provides.
108 </div>
109 </div>
110 <div name="machines" id="machines" class="tab-pane">
111 <div class="alert alert-info">
112 <strong>There is no machine data for {{layerversion.layer.name}} ... yet</strong> <br />
113 Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you
114 here the machines it provides.
115 </div>
116 </div>
117 </div> 72 </div>
118</div> 73 </div>
119<div class="row span4 well"> 74
120<h2>About {{layerversion.layer.name}}</h2> 75 <div class="row-fluid">
121<dl> 76 <div class="span7">
122 77 <div class="tabbable">
123 <dt> 78 <div class="alert alert-info lead" id="alert-area" style="display:none">
124 Summary 79 <button type="button" class="close" id="dismiss-alert" data-dismiss="alert">&times;</button>
125 <i class="icon-question-sign get-help" title="One-line description of the layer"></i> 80 <span id="alert-msg"></span>
126 </dt> 81 <p style="margin-top:10px;"><a href="{% url 'project' project.id %}">Go to project configuration</a></p>
127 <dd> 82 </div>
128 <span >{{layerversion.layer.summary}}</span> 83 <ul class="nav nav-pills">
129 <i class="icon-pencil"></i> 84 <li class="active">
130 </dd> 85 <a data-toggle="tab" href="#information" id="details-tab">Layer details</a>
131 <!--form> 86 </li>
132 <textarea class="span12" rows="2"></textarea> 87 <li>
133 <button class="btn" type="button">Change</button> 88 <a data-toggle="tab" href="#targets" id="targets-tab">Targets ({{total_targets}})</a>
134 <a href="#" class="btn btn-link">Cancel</a> 89 </li>
135 </form--> 90 <li>
136 <dt> 91 <a data-toggle="tab" href="#machines" id="machines-tab">Machines ({{total_machines}})</a>
137 Description 92 </li>
138 </dt> 93 </ul>
139 <dd> 94 </div>
140 <span >{{layerversion.layer.description}}</span> 95 <div class="tab-content">
141 <i class="icon-pencil"></i> 96 <span class="button-place">
142 </dd> 97 {% if layer_in_project == 0 %}
143 <!--form> 98 <button id="add-remove-layer-btn" data-directive="add" class="btn btn-large btn-block">
144 <textarea class="span12" rows="6"></textarea> 99 <span class="icon-plus"></span>
145 <button class="btn" type="button">Change</button> 100 Add the {{layerversion.layer.name}} layer to your project
146 <a href="#" class="btn btn-link">Cancel</a> 101 </button>
147 </form--> 102 {% else %}
148 <dt> 103 <button id="add-remove-layer-btn" data-directive="remove" class="btn btn-block btn-large btn-danger">
149 Maintainer(s) 104 <span class="icon-trash"></span>
150 </dt> 105 Delete the {{layerversion.layer.name}} layer from your project
151 <dd> 106 </button>
152 <span class="muted">Not set</span> 107 {% endif %}
153 <i class="icon-pencil"></i> 108 </span>
154 </dd> 109
155</dl> 110 <!-- layer details pane -->
156</div> 111 <div name="information" id="information" class="tab-pane active">
112 <dl class="dl-horizontal">
113 <dt class="">
114 <i class="icon-question-sign get-help" title="Fetch/clone URL of the repository"></i>
115 Repository URL
116 </dt>
117 <dd>
118 <span class="current-value">{{layerversion.layer.vcs_url}}</span>
119 {% if layerversion.get_vcs_link_url %}
120 <a href="{{layerversion.get_vcs_link_url}}/" class="icon-share get-info"></a>
121 {% endif %}
122 <form id="change-repo-form" class="control-group" style="display:none">
123 <div class="input-append">
124 <input type="text" class="input-xlarge" value="{{layerversion.layer.vcs_url}}">
125 <button data-layer-prop="vcs_url" class="btn change-btn" type="button">Save</button>
126 <a href="#" style="display:none" class="btn btn-link cancel">Cancel</a>
127 </div>
128 </form>
129 <i class="icon-pencil" ></i>
130 </dd>
131 <dt>
132 <i class="icon-question-sign get-help" title="Subdirectory within the repository where the layer is located, if not in the root (usually only used if the repository contains more than one layer)"></i>
133 Repository subdirectory
134 </dt>
135 <dd>
136 <span class="muted" style="display:none">Not set</span>
137 <span class="current-value">{{layerversion.dirpath}}</span>
138 {% if layerversion.get_vcs_dirpath_link_url %}
139 <a href="{{layerversion.get_vcs_dirpath_link_url}}" class="icon-share get-info"></a>
140 {% endif %}
141 <form id="change-subdir-form" style="display:none;">
142 <div class="input-append">
143 <input type="text" value="{{layerversion.dirpath}}">
144 <button data-layer-prop="dirpath" class="btn change-btn" type="button">Save</button>
145 <a href="#" style="display:none" class="btn btn-link cancel">Cancel</a>
146 </div>
147 </form>
148 <i id="change-subdir" class="icon-pencil"></i>
149 <span class="icon-trash delete-current-value" data-toggle="tooltip" title="Delete"></span>
150 </dd>
151 <dt>Brach, tag or commit</dt>
152 <dd>
153 <span class="current-value">{{layerversion.commit}}</span>
154 <form style="display:none;">
155 <div class="input-append">
156 <input type="text" value="{{layerversion.commit}}">
157 <button data-layer-prop="commit" class="btn change-btn" type="button">Save</button>
158 <a href="#" style="display:none" class="btn btn-link cancel">Cancel</a>
159 </div>
160 </form>
161 <i class="icon-pencil"></i>
162 </dd>
163 <dt>
164 <i class="icon-question-sign get-help" title="The Yocto Project versions with which this layer is compatible. Currently Toaster supports Yocto Project 1.6 and 1.7"></i>
165 Yocto Project compatibility
166 </dt>
167 <dd>
168 <span class="current-value">{{layerversion.up_branch.name}}</span>
169 <form style="display:none">
170 <div class="input-append">
171 <select name="projectversion" id="projectversion">
172 {% for compat in yocto_compat %}
173 <option value="{{compat.id}}" {%if layerversion.up_branch.id == compat.id %} selected{%endif%}>{{compat.name}}</option>
174 {% endfor %}
175 </select>
176 <button data-layer-prop="up_branch" class="btn change-btn" type="button">Save</button>
177 <a href="#" style="display:none" class="btn btn-link cancel">Cancel</a>
178 </div>
179 </form>
180 <i class="icon-pencil"></i>
181 </dd>
182 <dt>
183 <i class="icon-question-sign get-help" title="Other layers this layer depends upon"></i>
184 Layer dependencies
185 </dt>
186 <dd>
187 <ul class="unstyled" id="layer-deps-list">
188 {% for ld in layerversion.dependencies.all %}
189 <span class="current-value">
190 <li data-layer-id="{{ld.depends_on.id}}">
191 <!-- TODO use ld.depends_on.get_vcs_reference instead of commit -->
192 <a data-toggle="tooltip" title="{{ld.depends_on.layer.vcs_url}} | {{ld.depends_on.commit}}" href="{% url 'layerdetails' ld.depends_on.id %}">{{ld.depends_on.layer.name}}</a>
193 <span class="icon-trash " data-toggle="tooltip" title="Delete"></span>
194 </li>
195 </span>
196 {% endfor %}
197 </ul>
198 <div class="input-append add-deps">
199 <input type="text" autocomplete="off" data-minLength="1" data-autocomplete="off" placeholder="Type a layer name" id="layer-dep-input">
200 <a class="btn" type="button" id="add-layer-dependency-btn" disabled>
201 Add layer
202 </a>
203 </div>
204 <span class="help-block add-deps">You can only add layers Toaster knows about</span>
205 </dd>
206 </dl>
207 </div>
208 <!-- targets tab -->
209 <div name="targets" id="targets" class="tab-pane">
210 {% if total_targets == 0 %}
211 <div class="alert alert-info">
212 <strong>There is no target data for {{layerversion.layer.name}} ... yet</strong> <br />
213 Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you
214 here the targets it provides.
215 </div>
216 {% else %}
217
218 <div class="row-fluid">
219
220 {% if targets.paginator.count == 0 %}
221 <div class="alert">
222 <h3>No targets found</h3>
223 {% endif %}
224
225 {# only show the search form if we have more than 10 results #}
226 {% if targets.paginator.count > 10 or request.GET.targets_search %}
227 {% if targets.paginator.count == 0 %}
228 <form class="input-append">
229 {% else %}
230 <form class="navbar-search input-append pull-left">
231 {% endif %}
232
233 <input type="text" id="target-search" name="targets_search" placeholder="Search targets" class="input-xlarge" value="{{request.GET.targets_search}}">
234 {% if request.GET.targets_search %}
235 <a class="add-on btn" id="target-search-clear">
236 <i class="icon-remove"></i>
237 </a>
238 {% endif %}
239 <button type="submit" class="btn">Search</button>
240 </form>
241 {% endif %}
242
243 {% if targets.paginator.count == 0 %}
244 <!-- end alert -->
245 </div>
246 <!-- end row-fluid -->
247 </div>
248 {% else %}
249
250 <div class="pull-right">
251 <span class="help-inline" style="padding-top:5px;">Show rows:</span>
252 <select style="margin-top:5px;margin-bottom:0px;" class="pagesize">
253 {% with "10 25 50 100 150" as list%}
254 {% for i in list.split %}
255 {% if request.session.limit == i %}
256 <option value="{{i}}" selected>{{i}}</option>
257 {% else %}
258 <option value="{{i}}">{{i}}</option>
259 {% endif %}
260 {% endfor %}
261 {% endwith %}
262 </select>
263 </div>
264 </div>
265
266 <table class="table table-bordered table-hover">
267 <thead>
268 <tr>
269 <th>
270 <i class="icon-question-sign get-help" title="Information about a single piece of software, including where to download the source, configuration options, how to compile the source files and how to package the compiled output"></i>
271 Target
272 {% if request.GET.targets_search %}
273 <span class="badge badge-info">{{targets.paginator.count}}</span>
274 {% endif %}
275 </th>
276 <th>
277 <i class="icon-question-sign get-help" title="The recipe version and revision"></i>
278 Target version
279 </th>
280 <th class="span4">Description</th>
281 <th>Build target</th>
282 </tr>
283 </thead>
284 <tbody>
285 {% for target in targets %}
286 <tr>
287 <td>
288 {{target.name}}
289 {% if target.up_id %}
290 <a href="http://layers.openembedded.org/layerindex/recipe/{{target.up_id}}/" class="icon-share get-info"></a>
291 {% endif %}
292 </td>
293 <td>{{target.version}}</td>
294 <td>{{target.summary}}</td>
295 <td><button class="btn btn-block build-target-btn" data-target-name="{{target.name}}" {% if layer_in_project == 0 %}disabled="disabled"{% endif %} >Build Target</button></td>
296 </tr>
297 {% endfor %}
298 </tbody>
299 </table>
300
301 <!-- Show pagination controls -->
302 <div class="pagination">
303 <ul>
304 {%if targets.has_previous %}
305 <li><a href="?tpage={{targets.previous_page_number}}{{request.GET.limit}}#targets">&laquo;</a></li>
306 {%else%}
307 <li class="disabled"><a href="#">&laquo;</a></li>
308 {%endif%}
309 {% for i in targets.paginator.page_range %}
310 <li {%if i == targets.number %} class="active" {%endif%}><a href="?tpage={{i}}#targets">{{i}}</a></li>
311 {% endfor %}
312 {%if targets.has_next%}
313 <li><a href="?tpage={{targets.next_page_number}}#targets">&raquo;</a></li>
314 {%else%}
315 <li class="disabled"><a href="#">&raquo;</a></li>
316 {%endif%}
317 </ul>
318 <div class="pull-right">
319 <span class="help-inline" style="padding-top:5px;">Show rows:</span>
320 <select style="margin-top:5px;margin-bottom:0px;" class="pagesize">
321 {% with "10 25 50 100 150" as list%}
322 {% for i in list.split %}
323 {% if request.session.limit == i %}
324 <option value="{{i}}" selected>{{i}}</option>
325 {% else %}
326 <option value="{{i}}">{{i}}</option>
327 {% endif %}
328 {% endfor %}
329 {% endwith %}
330 </select>
331 </div>
332 </div>
333 {% endif %}
334 {% endif %}
335 </div>
336
337
338 <div name="machines" id="machines" class="tab-pane">
339 {% if total_machines == 0 %}
340 <div class="alert alert-info">
341 <strong>There is no machine data for {{layerversion.layer.name}} ... yet</strong> <br />
342 Toaster learns about layers as they are built. Once you have used {{layerversion.layer.name}} in a build, Toaster will show you
343 here the machines it provides.
344 </div>
345 {% else %}
346
347 <div class="row-fluid">
348
349 {% if machines.paginator.count == 0 %}
350 <div class="alert">
351 <h3>No machines found</h3>
352 {% endif %}
353
354 {# only show the search form if we have more than 10 results #}
355 {% if machines.paginator.count > 10 or request.GET.machines_search %}
356 {% if machines.paginator.count == 0 %}
357 <form class="input-append">
358 {% else %}
359 <form class="navbar-search input-append pull-left">
360 {% endif %}
361
362 <input type="text" id="machine-search" name="machines_search" placeholder="Search machines" class="input-xlarge" value="{{request.GET.machines_search}}">
363 {% if request.GET.machines_search %}
364 <a class="add-on btn" id="machine-search-clear">
365 <i class="icon-remove"></i>
366 </a>
367 {% endif %}
368 <button type="submit" class="btn">Search</button>
369 </form>
370 {% endif %}
371
372 {% if machines.paginator.count == 0 %}
373 <!-- end alert -->
374 </div>
375 <!-- end row-fluid -->
376 </div>
377 {% else %}
378
379 <div class="pull-right">
380 <span class="help-inline" style="padding-top:5px;">Show rows:</span>
381 <select style="margin-top:5px;margin-bottom:0px;" class="pagesize">
382 {% with "10 25 50 100 150" as list%}
383 {% for i in list.split %}
384 {% if request.session.limit == i %}
385 <option value="{{i}}" selected>{{i}}</option>
386 {% else %}
387 <option value="{{i}}">{{i}}</option>
388 {% endif %}
389 {% endfor %}
390 {% endwith %}
391 </select>
392 </div>
393 </div>
394
395 <table class="table table-bordered table-hover">
396 <thead>
397 <tr>
398 <th>
399 <i class="icon-question-sign get-help" title="The machine is the hardware for which you are building"></i>
400 Machine
401 {% if request.GET.machines_search %}
402 <span class="badge badge-info">{{machines.paginator.count}}</span>
403 {% endif %}
404 </th>
405 <th>Description</th>
406 <th>Select machine</th>
407 </tr>
408 </thead>
409 <tbody>
410 {% for machine in machines %}
411 <tr>
412 <td>{{machine.name}}</td>
413 <td>{{machine.description}}</td>
414 <td><button class="btn btn-block select-machine-btn" data-machine-name="{{machine.name}}" {% if layer_in_project == 0 %}disabled="disabled"{% endif %}}>Select machine</button></td>
415 </tr>
416 {% endfor %}
417 </tbody>
418 </table>
419
420 <!-- Show pagination controls -->
421 <div class="pagination">
422 <ul>
423 {%if machines.has_previous %}
424 <li><a href="?mpage={{machines.previous_page_number}}{{request.GET.limit}}#machines">&laquo;</a></li>
425 {%else%}
426 <li class="disabled"><a href="#">&laquo;</a></li>
427 {%endif%}
428 {% for i in machines.paginator.page_range %}
429 <li {%if i == machines.number %} class="active" {%endif%}><a href="?mpage={{i}}#machines">{{i}}</a></li>
430 {% endfor %}
431 {%if machines.has_next%}
432 <li><a href="?mpage={{machines.next_page_number}}#machines">&raquo;</a></li>
433 {%else%}
434 <li class="disabled"><a href="#">&raquo;</a></li>
435 {%endif%}
436 </ul>
437 <div class="pull-right">
438 <span class="help-inline" style="padding-top:5px;">Show rows:</span>
439 <select style="margin-top:5px;margin-bottom:0px;" class="pagesize">
440 {% with "10 25 50 100 150" as list%}
441 {% for i in list.split %}
442 {% if request.session.limit == i %}
443 <option value="{{i}}" selected>{{i}}</option>
444 {% else %}
445 <option value="{{i}}">{{i}}</option>
446 {% endif %}
447 {% endfor %}
448 {% endwith %}
449 </select>
450 </div>
451 </div>
452 {% endif %}
453 {% endif %}
454 </div>
455 </div>
456 </div>
457 <div class="row span4 well">
458 <h2>About {{layerversion.layer.name}}</h2>
459 <dl>
460
461 <dt>
462 Summary
463 <i class="icon-question-sign get-help" title="One-line description of the layer"></i>
464 </dt>
465 <dd>
466 <span class="muted" style="display:none">Not set</span>
467 <span class="current-value">{{layerversion.layer.summary}}</span>
468 <form style="display:none; margin-bottom:20px">
469 <textarea class="span12" rows="2">{% if layerversion.layer.summary %}{{layerversion.layer.summary}}{% endif %}</textarea>
470 <button class="btn change-btn" data-layer-prop="summary" type="button">Save</button>
471 <a href="#" class="btn btn-link cancel">Cancel</a>
472 </form>
473 <i class="icon-pencil"></i>
474 <span class="icon-trash delete-current-value" data-toggle="tooltip" title="Delete"></span>
475 </dd>
476 <dt>
477 Description
478 </dt>
479 <dd>
480 <span class="muted" style="display:none">Not set</span>
481 <span class="current-value">{{layerversion.layer.description}}</span>
482 <form style="display:none; margin-bottom:20px">
483 <textarea class="span12" rows="6">{% if layerversion.layer.description %}{{layerversion.layer.description}}{% endif %}</textarea>
484 <button class="btn change-btn" data-layer-prop="description" type="button" >Save</button>
485 <a href="#" class="btn btn-link cancel">Cancel</a>
486 </form>
487 <i class="icon-pencil"></i>
488 <span class="icon-trash delete-current-value" data-toggle="tooltip" title="Delete"></span>
489 </dd>
490 </dd>
491 {% if layerversion.layer.up_id %}
492 <dt>Layer index</dt>
493 <dd>
494 <a href="http://layers.openembedded.org/layerindex/branch/{{layerversion.up_branch.name}}/layer/{{layerversion.layer.name}}"/>layer index link</a>
495
496 </dd>
497 {% endif %}
157 498
499 </dl>
500 </div>
158 501
502 </div>
503 </div>
504 </div>
159{% endblock %} 505{% 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 @@
18 </div> 18 </div>
19 19
20<script> 20<script>
21 /* projectId: current project
22 * layer: Object representing the parent layer { id: .. name: ... url }
23 * dependencies: array of dependency layer objects { id: .. name: ..}
24 * title: optional override for title
25 * body: optional override for body
26 * addToProject: Whether to add layers to project on accept
27 * successAdd: function to run on success
28 */
21function show_layer_deps_modal(projectId, layer, dependencies, title, body, addToProject, successAdd) { 29function show_layer_deps_modal(projectId, layer, dependencies, title, body, addToProject, successAdd) {
30
22 // update layer name 31 // update layer name
23 if (title) { 32 if (title) {
24 $('#dependencies_modal #title').text(title); 33 $('#dependencies_modal #title').text(title);
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 6e1b0ab913..5c969f814c 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -94,6 +94,7 @@ urlpatterns = patterns('toastergui.views',
94 94
95 url(r'^xhr_datatypeahead/$', 'xhr_datatypeahead', name='xhr_datatypeahead'), 95 url(r'^xhr_datatypeahead/$', 'xhr_datatypeahead', name='xhr_datatypeahead'),
96 url(r'^xhr_importlayer/$', 'xhr_importlayer', name='xhr_importlayer'), 96 url(r'^xhr_importlayer/$', 'xhr_importlayer', name='xhr_importlayer'),
97 url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'),
97 98
98 99
99 # default redirection 100 # default redirection
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 8c21ca48e0..7a9d662b31 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -2336,6 +2336,50 @@ if toastermain.settings.MANAGED:
2336 2336
2337 return HttpResponse(jsonfilter({"error": "ok", "imported_layer" : { "name" : layer.name, "id": layer_version.id }, "deps_added": layers_added }), content_type = "application/json") 2337 return HttpResponse(jsonfilter({"error": "ok", "imported_layer" : { "name" : layer.name, "id": layer_version.id }, "deps_added": layers_added }), content_type = "application/json")
2338 2338
2339 def xhr_updatelayer(request):
2340
2341 def error_response(error):
2342 return HttpResponse(jsonfilter({"error": error}), content_type = "application/json")
2343
2344 if not request.POST.has_key("layer_version_id"):
2345 return error_response("Please specify a layer version id")
2346 try:
2347 layer_version_id = request.POST["layer_version_id"]
2348 layer_version = Layer_Version.objects.get(id=layer_version_id)
2349 except:
2350 return error_response("Cannot find layer to update")
2351
2352
2353 if request.POST.has_key("vcs_url"):
2354 layer_version.layer.vcs_url = request.POST["vcs_url"]
2355 if request.POST.has_key("dirpath"):
2356 layer_version.dirpath = request.POST["dirpath"]
2357 if request.POST.has_key("commit"):
2358 layer_version.commit = request.POST["commit"]
2359 if request.POST.has_key("up_branch"):
2360 layer_version.up_branch_id = int(request.POST["up_branch"])
2361
2362 if request.POST.has_key("add_dep"):
2363 lvd = LayerVersionDependency(layer_version=layer_version, depends_on_id=request.POST["add_dep"])
2364 lvd.save()
2365
2366 if request.POST.has_key("rm_dep"):
2367 rm_dep = LayerVersionDependency.objects.get(layer_version=layer_version, depends_on_id=request.POST["rm_dep"])
2368 rm_dep.delete()
2369
2370 if request.POST.has_key("summary"):
2371 layer_version.layer.summary = request.POST["summary"]
2372 if request.POST.has_key("description"):
2373 layer_version.layer.description = request.POST["description"]
2374
2375 try:
2376 layer_version.layer.save()
2377 layer_version.save()
2378 except:
2379 return error_response("Could not update layer version entry")
2380
2381 return HttpResponse(jsonfilter({"error": "ok",}), content_type = "application/json")
2382
2339 2383
2340 2384
2341 def importlayer(request): 2385 def importlayer(request):
@@ -2439,8 +2483,53 @@ if toastermain.settings.MANAGED:
2439 2483
2440 def layerdetails(request, layerid): 2484 def layerdetails(request, layerid):
2441 template = "layerdetails.html" 2485 template = "layerdetails.html"
2486 limit = 10
2487
2488 if request.GET.has_key("limit"):
2489 request.session['limit'] = request.GET['limit']
2490
2491 if request.session.has_key('limit'):
2492 limit = request.session['limit']
2493
2494 layer_version = Layer_Version.objects.get(pk = layerid)
2495
2496 # Targets tab query functionality
2497 if request.GET.has_key('targets_search'):
2498 targets = Paginator(Recipe.objects.filter(layer_version=layer_version,name__icontains=request.GET['targets_search']).order_by("name"), limit)
2499 else:
2500 targets = Paginator(Recipe.objects.filter(layer_version=layer_version).order_by("name"), limit)
2501
2502 if request.GET.has_key("tpage"):
2503 try:
2504 targets = targets.page(request.GET['tpage'])
2505 except EmptyPage:
2506 targets = targets.page(targets.num_pages)
2507 else:
2508 targets = targets.page(1)
2509
2510 # Machines tab query functionality
2511 if request.GET.has_key('machines_search'):
2512 machines = Paginator(Machine.objects.filter(layer_version=layer_version,name__icontains=request.GET['machines_search']).order_by("name"), limit)
2513 else:
2514 machines = Paginator(Machine.objects.filter(layer_version=layer_version).order_by("name"), limit)
2515
2516 if request.GET.has_key("mpage"):
2517 try:
2518 machines = machines.page(request.GET['mpage'])
2519 except EmptyPage:
2520 machines = machines.page(machines.num_pages)
2521 else:
2522 machines = machines.page(1)
2523
2442 context = { 2524 context = {
2443 'layerversion': Layer_Version.objects.get(pk = layerid), 2525 'layerversion': layer_version,
2526 'layer_in_project' : ProjectLayer.objects.filter(project_id=request.session['project_id'],layercommit=layerid).count(),
2527 'yocto_compat': Branch.objects.filter(layer_source=layer_version.layer_source),
2528 'machines': machines,
2529 'targets': targets,
2530 'total_targets': Recipe.objects.filter(layer_version=layer_version).count(),
2531
2532 'total_machines': Machine.objects.filter(layer_version=layer_version).count(),
2444 } 2533 }
2445 return render(request, template, context) 2534 return render(request, template, context)
2446 2535
@@ -2972,3 +3061,6 @@ else:
2972 3061
2973 def xhr_importlayer(request): 3062 def xhr_importlayer(request):
2974 raise Exception("page not available in interactive mode") 3063 raise Exception("page not available in interactive mode")
3064
3065 def xhr_updatelayer(request):
3066 raise Exception("page not available in interactive mode")