summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/base.js81
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/importlayer.js4
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/layerdetails.js5
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/libtoaster.js15
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/projectpage.js429
-rw-r--r--bitbake/lib/toaster/toastergui/templates/base.html7
-rw-r--r--bitbake/lib/toaster/toastergui/templates/baseprojectpage.html2
-rw-r--r--bitbake/lib/toaster/toastergui/templates/importlayer.html12
-rw-r--r--bitbake/lib/toaster/toastergui/templates/machine_btn.html2
-rw-r--r--bitbake/lib/toaster/toastergui/templates/project.html498
-rw-r--r--bitbake/lib/toaster/toastergui/templates/projecttopbar.html26
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py5
12 files changed, 610 insertions, 476 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/base.js b/bitbake/lib/toaster/toastergui/static/js/base.js
index 6fd77ea457..0fb1f8b6bf 100644
--- a/bitbake/lib/toaster/toastergui/static/js/base.js
+++ b/bitbake/lib/toaster/toastergui/static/js/base.js
@@ -6,13 +6,20 @@ function basePageInit(ctx) {
6 var newBuildTargetInput; 6 var newBuildTargetInput;
7 var newBuildTargetBuildBtn; 7 var newBuildTargetBuildBtn;
8 8
9 /* initially the current project is used unless overridden by the new build
10 * button in top right nav
11 */
9 var selectedProject = libtoaster.ctx; 12 var selectedProject = libtoaster.ctx;
13
10 var selectedTarget; 14 var selectedTarget;
11 15
12 var newBuildProjectInput = $("#new-build-button #project-name-input"); 16 var newBuildProjectInput = $("#new-build-button #project-name-input");
13 var newBuildProjectSaveBtn = $("#new-build-button #save-project-button"); 17 var newBuildProjectSaveBtn = $("#new-build-button #save-project-button");
14 18
15 $("#config-nav .nav li a").each(function(){ 19
20 _checkProjectBuildable();
21
22 $("#project-topbar .nav li a").each(function(){
16 if (window.location.pathname === $(this).attr('href')) 23 if (window.location.pathname === $(this).attr('href'))
17 $(this).parent().addClass('active'); 24 $(this).parent().addClass('active');
18 else 25 else
@@ -26,7 +33,7 @@ function basePageInit(ctx) {
26 }); 33 });
27 } 34 }
28 35
29 /* Hide the button if we're on the project,newproject or importlyaer page 36 /* Hide the button if we're on the project,newproject or importlyaer page
30 * or if there are no projects yet defined 37 * or if there are no projects yet defined
31 * only show if there isn't already a build-target-input already 38 * only show if there isn't already a build-target-input already
32 */ 39 */
@@ -37,8 +44,8 @@ function basePageInit(ctx) {
37 newBuildTargetInput = $("#new-build-button .build-target-input"); 44 newBuildTargetInput = $("#new-build-button .build-target-input");
38 newBuildTargetBuildBtn = $("#new-build-button .build-button"); 45 newBuildTargetBuildBtn = $("#new-build-button .build-button");
39 46
40 newBuildButton.show();
41 _setupNewBuildButton(); 47 _setupNewBuildButton();
48 newBuildButton.show();
42 } else if ($(".build-target-input").length > 0) { 49 } else if ($(".build-target-input").length > 0) {
43 newBuildTargetInput = $("#project-topbar .build-target-input"); 50 newBuildTargetInput = $("#project-topbar .build-target-input");
44 newBuildTargetBuildBtn = $("#project-topbar .build-button"); 51 newBuildTargetBuildBtn = $("#project-topbar .build-button");
@@ -52,9 +59,35 @@ function basePageInit(ctx) {
52 $('#project .icon-pencil').hide(); 59 $('#project .icon-pencil').hide();
53 } 60 }
54 61
62 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) {
63 /* successfully selected a target */
64 selectedTarget = item;
65 });
55 66
56 _checkProjectBuildable(); 67 newBuildTargetInput.on('input', function () {
68 if ($(this).val().length === 0) {
69 newBuildTargetBuildBtn.attr("disabled", "disabled");
70 } else {
71 newBuildTargetBuildBtn.removeAttr("disabled");
72 }
73 });
74
75 newBuildTargetBuildBtn.click(function (e) {
76 e.preventDefault();
77
78 if (!newBuildTargetInput.val()) {
79 return;
80 }
57 81
82 if (!selectedTarget) {
83 selectedTarget = { name: newBuildTargetInput.val() };
84 }
85 /* Fire off the build */
86 libtoaster.startABuild(selectedProject.projectBuildsUrl,
87 selectedProject.projectId, selectedTarget.name, function(){
88 window.location.replace(selectedProject.projectBuildsUrl);
89 }, null);
90 });
58 91
59 function _checkProjectBuildable() { 92 function _checkProjectBuildable() {
60 if (selectedProject.projectId === undefined) { 93 if (selectedProject.projectId === undefined) {
@@ -74,21 +107,12 @@ function basePageInit(ctx) {
74 /* we can build this project; enable input fields */ 107 /* we can build this project; enable input fields */
75 newBuildTargetInput.prop("disabled", false); 108 newBuildTargetInput.prop("disabled", false);
76 newBuildTargetBuildBtn.prop("disabled", false); 109 newBuildTargetBuildBtn.prop("disabled", false);
77 110 }
78 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) {
79 /* successfully selected a target */
80 selectedProject.projectPageUrl = item.projectPageUrl;
81 selectedProject.projectName = item.name;
82 selectedProject.projectId = item.id;
83 selectedProject.projectBuildsUrl = item.projectBuildsUrl;
84 });
85
86 }
87 }, null); 111 }, null);
88 } 112 }
89 113
114 /* Setup New build button in the top nav bar */
90 function _setupNewBuildButton() { 115 function _setupNewBuildButton() {
91 /* Setup New build button */
92 116
93 /* If we don't have a current project then present the set project 117 /* If we don't have a current project then present the set project
94 * form. 118 * form.
@@ -98,7 +122,6 @@ function basePageInit(ctx) {
98 $('#project .icon-pencil').hide(); 122 $('#project .icon-pencil').hide();
99 } 123 }
100 124
101
102 libtoaster.makeTypeahead(newBuildProjectInput, selectedProject.projectsUrl, { format : "json" }, function (item) { 125 libtoaster.makeTypeahead(newBuildProjectInput, selectedProject.projectsUrl, { format : "json" }, function (item) {
103 /* successfully selected a project */ 126 /* successfully selected a project */
104 newBuildProjectSaveBtn.removeAttr("disabled"); 127 newBuildProjectSaveBtn.removeAttr("disabled");
@@ -115,40 +138,16 @@ function basePageInit(ctx) {
115 newBuildProjectSaveBtn.attr("disabled", "disabled"); 138 newBuildProjectSaveBtn.attr("disabled", "disabled");
116 }); 139 });
117 140
118 newBuildTargetInput.on('input', function () {
119 if ($(this).val().length === 0) {
120 newBuildTargetBuildBtn.attr("disabled", "disabled");
121 } else {
122 newBuildTargetBuildBtn.removeAttr("disabled");
123 }
124 });
125
126 newBuildTargetBuildBtn.click(function () {
127 if (!newBuildTargetInput.val()) {
128 return;
129 }
130
131 if (!selectedTarget) {
132 selectedTarget = { name: newBuildTargetInput.val() };
133 }
134 /* fire and forget */
135 libtoaster.startABuild(selectedProject.projectBuildsUrl, selectedProject.projectId, selectedTarget.name, null, null);
136 window.location.replace(selectedProject.projectPageUrl);
137 });
138 141
139 newBuildProjectSaveBtn.click(function () { 142 newBuildProjectSaveBtn.click(function () {
140 selectedProject.projectId = selectedProject.pk; 143 selectedProject.projectId = selectedProject.pk;
141 /* Update the typeahead project_id paramater */ 144 /* Update the typeahead project_id paramater */
142 _checkProjectBuildable(); 145 _checkProjectBuildable();
143 146
144 /* we set the effective context of the page to the currently selected project */
145 /* TBD: do we override even if we already have a context project ?? */
146 /* TODO: replace global library context with references to the "selected" project */
147
148 /* we can create a target typeahead only after we have a project selected */
149 newBuildTargetInput.prop("disabled", false); 147 newBuildTargetInput.prop("disabled", false);
150 newBuildTargetBuildBtn.prop("disabled", false); 148 newBuildTargetBuildBtn.prop("disabled", false);
151 149
150 /* Update the typeahead to use the new selectedProject */
152 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) { 151 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) {
153 /* successfully selected a target */ 152 /* successfully selected a target */
154 selectedTarget = item; 153 selectedTarget = item;
diff --git a/bitbake/lib/toaster/toastergui/static/js/importlayer.js b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
index 560e25a01c..03274c00f3 100644
--- a/bitbake/lib/toaster/toastergui/static/js/importlayer.js
+++ b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
@@ -16,8 +16,6 @@ function importLayerPageInit (ctx) {
16 var currentLayerDepSelection; 16 var currentLayerDepSelection;
17 var validLayerName = /^(\w|-)+$/; 17 var validLayerName = /^(\w|-)+$/;
18 18
19 $("#new-project-button").hide();
20
21 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.projectLayersUrl, { include_added: "true" }, function(item){ 19 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.projectLayersUrl, { include_added: "true" }, function(item){
22 currentLayerDepSelection = item; 20 currentLayerDepSelection = item;
23 21
@@ -154,7 +152,7 @@ function importLayerPageInit (ctx) {
154 } else { 152 } else {
155 /* Success layer import now go to the project page */ 153 /* Success layer import now go to the project page */
156 $.cookie('layer-imported-alert', JSON.stringify(data), { path: '/'}); 154 $.cookie('layer-imported-alert', JSON.stringify(data), { path: '/'});
157 window.location.replace(libtoaster.ctx.projectPageUrl+'#/layerimported'); 155 window.location.replace(libtoaster.ctx.projectPageUrl+'?notify=layer-imported');
158 } 156 }
159 }, 157 },
160 error: function (data) { 158 error: function (data) {
diff --git a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
index 291ed98c34..be6bbcd20f 100644
--- a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
+++ b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
@@ -16,14 +16,15 @@ function layerDetailsPageInit (ctx) {
16 }); 16 });
17 17
18 $(".breadcrumb li:first a").click(function(e){ 18 $(".breadcrumb li:first a").click(function(e){
19 e.preventDefault();
19 /* By default this link goes to the project configuration page. However 20 /* By default this link goes to the project configuration page. However
20 * if we have some builds we go there instead of the default href 21 * if we have some builds we go there instead of the default href
21 */ 22 */
22 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){ 23 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){
23 if (prjInfo.builds && prjInfo.builds.length > 0) { 24 if (prjInfo.builds && prjInfo.builds.length > 0) {
24 window.location.replace(libtoaster.ctx.projectBuildsUrl); 25 window.location.replace(libtoaster.ctx.projectBuildsUrl);
25 e.preventDefault(); 26 } else {
26 return; 27 window.location.replace(libtoaster.ctx.projectPageUrl);
27 } 28 }
28 }); 29 });
29 }); 30 });
diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
index afd665ca99..079bbcb0b8 100644
--- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
+++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
@@ -79,11 +79,12 @@ var libtoaster = (function (){
79 data: data, 79 data: data,
80 headers: { 'X-CSRFToken' : $.cookie('csrftoken')}, 80 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
81 success: function (_data) { 81 success: function (_data) {
82 /* No proper reponse YOCTO #7995
82 if (_data.error !== "ok") { 83 if (_data.error !== "ok") {
83 console.warn(_data.error); 84 console.warn(_data.error);
84 } else { 85 } else { */
85 if (onsuccess !== undefined) onsuccess(_data); 86 if (onsuccess !== undefined) onsuccess(_data);
86 } 87 // }
87 }, 88 },
88 error: function (_data) { 89 error: function (_data) {
89 console.warn("Call failed"); 90 console.warn("Call failed");
@@ -180,7 +181,15 @@ var libtoaster = (function (){
180 if (onFail !== undefined) 181 if (onFail !== undefined)
181 onFail(data); 182 onFail(data);
182 } else { 183 } else {
183 onSuccess(data.layerdeps); 184 var deps = {};
185 /* Filter out layer dep ids which are in the
186 * project already.
187 */
188 deps.list = data.layerdeps.list.filter(function(layerObj){
189 return (data.projectlayers.lastIndexOf(layerObj.id) < 0);
190 });
191
192 onSuccess(deps);
184 } 193 }
185 }, function() { 194 }, function() {
186 console.log("E: Failed to make request"); 195 console.log("E: Failed to make request");
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectpage.js b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
new file mode 100644
index 0000000000..b7cb074f11
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
@@ -0,0 +1,429 @@
1"use strict";
2
3function projectPageInit(ctx) {
4
5 var layerAddInput = $("#layer-add-input");
6 var layersInPrjList = $("#layers-in-project-list");
7 var layerAddBtn = $("#add-layer-btn");
8
9 var machineChangeInput = $("#machine-change-input");
10 var machineChangeBtn = $("#machine-change-btn");
11 var machineForm = $("#select-machine-form");
12 var machineChangeFormToggle = $("#change-machine-toggle");
13 var machineNameTitle = $("#project-machine-name");
14 var machineChangeCancel = $("#cancel-machine-change");
15
16 var freqBuildBtn = $("#freq-build-btn");
17 var freqBuildList = $("#freq-build-list");
18
19 var releaseChangeFormToggle = $("#release-change-toggle");
20 var releaseTitle = $("#project-release-title");
21 var releaseForm = $("#change-release-form");
22 var releaseModal = $("#change-release-modal");
23 var cancelReleaseChange = $("#cancel-release-change");
24
25 var currentLayerAddSelection;
26 var currentMachineAddSelection = {};
27
28 var urlParams = libtoaster.parseUrlParams();
29
30 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){
31 updateProjectLayers(prjInfo.layers);
32 updateFreqBuildRecipes(prjInfo.freqtargets);
33 updateProjectRelease(prjInfo.release);
34 updateProjectReleases(prjInfo.releases, prjInfo.release);
35
36 /* If we're receiving a machine set from the url and it's different from
37 * our current machine then activate set machine sequence.
38 */
39 if (urlParams.hasOwnProperty('setMachine') &&
40 urlParams.setMachine !== prjInfo.machine.name){
41 currentMachineAddSelection.name = urlParams.setMachine;
42 machineChangeBtn.click();
43 } else {
44 updateMachineName(prjInfo.machine.name);
45 }
46
47 /* Now we're really ready show the page */
48 $("#project-page").show();
49 });
50
51 (function notificationRequest(){
52
53 if (urlParams.hasOwnProperty('notify')){
54 switch (urlParams.notify){
55 case 'new-project':
56 $("#project-created-notification").show();
57 break;
58 case 'layer-imported':
59 layerImportedNotification();
60 break;
61 default:
62 break;
63 }
64 }
65 })();
66
67 /* Layer imported notification */
68 function layerImportedNotification(){
69 var imported = $.cookie("layer-imported-alert");
70 var message = "Layer imported";
71
72 if (!imported)
73 return;
74 else
75 imported = JSON.parse(imported);
76
77 if (imported.deps_added.length === 0) {
78 message = "You have imported <strong><a href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a></strong> and added it to your project.";
79 } else {
80
81 var links = "<a href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a>, ";
82
83 imported.deps_added.map (function(item, index){
84 links +='<a href="'+item.layerdetailurl+'">'+item.name+'</a>';
85 /*If we're at the last element we don't want the trailing comma */
86 if (imported.deps_added[index+1] !== undefined)
87 links += ', ';
88 });
89
90 /* Length + 1 here to do deps + the imported layer */
91 message = 'You have imported <strong><a href="'+imported.imported_layer.layerdetailurl+'">'+imported.imported_layer.name+'</a></strong> and added <strong>'+(imported.deps_added.length+1)+'</strong> layers to your project: <strong>'+links+'</strong>';
92 }
93
94 libtoaster.showChangeNotification(message);
95
96 $.removeCookie("layer-imported-alert", { path: "/"});
97 }
98
99 /* Add/Rm layer functionality */
100
101 libtoaster.makeTypeahead(layerAddInput, libtoaster.ctx.projectLayersUrl, { include_added: "false" }, function(item){
102 currentLayerAddSelection = item;
103 layerAddBtn.removeAttr("disabled");
104 });
105
106 layerAddBtn.click(function(e){
107 e.preventDefault();
108 var layerObj = currentLayerAddSelection;
109
110 addRmLayer(layerObj, true);
111 /* Reset the text input */
112 layerAddInput.val("");
113 });
114
115 function addRmLayer(layerObj, add){
116
117 libtoaster.addRmLayer(layerObj, add, function(layerDepsList){
118 if (add){
119 updateProjectLayers([layerObj]);
120 updateProjectLayers(layerDepsList);
121 }
122
123 /* Show the alert message */
124 var message = libtoaster.makeLayerAddRmAlertMsg(layerObj, layerDepsList, add);
125 libtoaster.showChangeNotification(message);
126 });
127 }
128
129 function updateProjectLayers(layers){
130
131 /* No layers to add */
132 if (layers.length === 0){
133 updateLayersCount();
134 return;
135 }
136
137 for (var i in layers){
138 var layerObj = layers[i];
139
140 var projectLayer = $("<li><a></a><span class=\"icon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
141
142 projectLayer.data('layer', layerObj);
143 projectLayer.children("span").tooltip();
144
145 var link = projectLayer.children("a");
146
147 link.attr("href", layerObj.layerdetailurl);
148 link.text(layerObj.name);
149 /* YOCTO #8024
150 link.tooltip({title: layerObj.giturl + " | "+ layerObj.branch.name, placement: "right"});
151 branch name not accessible sometimes it is revision instead
152 */
153
154 var trashItem = projectLayer.children("span");
155 trashItem.click(function (e) {
156 e.preventDefault();
157 var layerObjToRm = $(this).parent().data('layer');
158
159 addRmLayer(layerObjToRm, false);
160
161 $(this).parent().fadeOut(function (){
162 $(this).remove();
163 updateLayersCount();
164 });
165 });
166
167 layersInPrjList.append(projectLayer);
168
169 updateLayersCount();
170 }
171 }
172
173 function updateLayersCount(){
174 var count = $("#layers-in-project-list").children().length;
175
176 if (count === 0)
177 $("#no-layers-in-project").fadeIn();
178 else
179 $("#no-layers-in-project").hide();
180
181 $("#project-layers-count").text(count);
182
183 return count;
184 }
185
186 /* Frequent builds functionality */
187 function updateFreqBuildRecipes(recipes) {
188 var noMostBuilt = $("#no-most-built");
189
190 if (recipes.length === 0){
191 noMostBuilt.show();
192 freqBuildBtn.hide();
193 } else {
194 noMostBuilt.hide();
195 freqBuildBtn.show();
196 }
197
198 for (var i in recipes){
199 var freqTargetCheck = $('<li><label class="checkbox"><input type="checkbox" /><span class="freq-target-name"></span></label></li>');
200 freqTargetCheck.find(".freq-target-name").text(recipes[i]);
201 freqTargetCheck.find("input").val(recipes[i]);
202 freqTargetCheck.click(function(){
203 if (freqBuildList.find(":checked").length > 0)
204 freqBuildBtn.removeAttr("disabled");
205 else
206 freqBuildBtn.attr("disabled", "disabled");
207 });
208
209 freqBuildList.append(freqTargetCheck);
210 }
211 }
212
213 freqBuildBtn.click(function(e){
214 e.preventDefault();
215
216 var toBuild = "";
217 freqBuildList.find(":checked").each(function(){
218 toBuild += $(this).val();
219 toBuild += " ";
220 });
221
222 libtoaster.startABuild(libtoaster.ctx.projectBuildsUrl, libtoaster.ctx.projectId, toBuild, function(){
223 /* Build started */
224 window.location.replace(libtoaster.ctx.projectBuildsUrl);
225 },
226 function(){
227 /* Build start failed */
228 /* [YOCTO #7995] */
229 window.location.replace(libtoaster.ctx.projectBuildsUrl);
230 });
231 });
232
233
234 /* Change machine functionality */
235
236 machineChangeFormToggle.click(function(){
237 machineForm.slideDown();
238 machineNameTitle.hide();
239 $(this).hide();
240 });
241
242 machineChangeCancel.click(function(){
243 machineForm.slideUp(function(){
244 machineNameTitle.show();
245 machineChangeFormToggle.show();
246 });
247 });
248
249 function updateMachineName(machineName){
250 machineChangeInput.val(machineName);
251 machineNameTitle.text(machineName);
252 }
253
254 libtoaster.makeTypeahead(machineChangeInput, libtoaster.ctx.projectMachinesUrl, { }, function(item){
255 currentMachineAddSelection = item;
256 machineChangeBtn.removeAttr("disabled");
257 });
258
259 machineChangeBtn.click(function(e){
260 e.preventDefault();
261 if (currentMachineAddSelection.name === undefined)
262 return;
263
264 libtoaster.editCurrentProject({ machineName : currentMachineAddSelection.name },
265 function(){
266 /* Success machine changed */
267 updateMachineName(currentMachineAddSelection.name);
268 machineChangeCancel.click();
269
270 /* Show the alert message */
271 var message = $('<span class="lead">You have changed the machine to: <strong><span id="notify-machine-name"></span></strong></span>');
272 message.find("#notify-machine-name").text(currentMachineAddSelection.name);
273 libtoaster.showChangeNotification(message);
274 },
275 function(){
276 /* Failed machine changed */
277 console.log("failed to change machine");
278 });
279 });
280
281
282 /* Change release functionality */
283 function updateProjectRelease(release){
284 releaseTitle.text(release.description);
285 }
286
287 function updateProjectReleases(releases, current){
288 for (var i in releases){
289 var releaseOption = $("<option></option>");
290
291 releaseOption.val(releases[i].id);
292 releaseOption.text(releases[i].description);
293 releaseOption.data('release', releases[i]);
294
295 if (releases[i].id == current.id)
296 releaseOption.attr("selected", "selected");
297
298 releaseForm.children("select").append(releaseOption);
299 }
300 }
301
302 releaseChangeFormToggle.click(function(){
303 releaseForm.slideDown();
304 releaseTitle.hide();
305 $(this).hide();
306 });
307
308 cancelReleaseChange.click(function(e){
309 e.preventDefault();
310 releaseForm.slideUp(function(){
311 releaseTitle.show();
312 releaseChangeFormToggle.show();
313 });
314 });
315
316 function changeProjectRelease(release, layersToRm){
317 libtoaster.editCurrentProject({ projectVersion : release.id },
318 function(){
319 /* Success */
320 /* Update layers list with new layers */
321 layersInPrjList.addClass('muted');
322 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl,
323 function(prjInfo){
324 layersInPrjList.children().remove();
325 updateProjectLayers(prjInfo.layers);
326 layersInPrjList.removeClass('muted');
327 releaseChangedNotification(release, prjInfo.layers, layersToRm);
328 });
329 updateProjectRelease(release);
330 cancelReleaseChange.click();
331 });
332 }
333
334 /* Create a notification to show the changes to the layer configuration
335 * caused by changing a release.
336 */
337
338 function releaseChangedNotification(release, layers, layersToRm){
339
340 var message;
341
342 if (layers.length === 0 && layersToRm.length === 0){
343 message = $('<span><span class="lead">You have changed the project release to: <strong><span id="notify-release-name"></span></strong>.');
344 message.find("#notify-release-name").text(release.description);
345 libtoaster.showChangeNotification(message);
346 return;
347 }
348
349 /* Create the whitespace separated list of layers removed */
350 var layersDelList = "";
351
352 layersToRm.map(function(layer, i){
353 layersDelList += layer.name;
354 if (layersToRm[i+1] !== undefined)
355 layersDelList += ', ';
356 });
357
358 message = $('<span><span class="lead">You have changed the project release to: <strong><span id="notify-release-name"></span></strong>. This has caused the following changes in your project layers:</span><ul id="notify-layers-changed-list"></ul></span>');
359
360 var changedList = message.find("#notify-layers-changed-list");
361
362 message.find("#notify-release-name").text(release.description);
363
364 /* Manually construct the list item for changed layers */
365 var li = '<li><strong>'+layers.length+'</strong> layers changed to the <strong>'+release.name+'</strong> release: ';
366 for (var i in layers){
367 li += '<a href='+layers[i].layerdetailurl+'>'+layers[i].name+'</a>';
368 if (i !== 0)
369 li += ', ';
370 }
371
372 changedList.append($(li));
373
374 /* Layers removed */
375 if (layersToRm && layersToRm.length > 0){
376 if (layersToRm.length == 1)
377 li = '<li><strong>1</strong> layer deleted: '+layersToRm[0].name+'</li>';
378 else
379 li = '<li><strong>'+layersToRm.length+'</strong> layers deleted: '+layersDelList+'</li>';
380
381 changedList.append($(li));
382 }
383
384 libtoaster.showChangeNotification(message);
385 }
386
387 /* Show the modal dialog which gives the option to remove layers which
388 * aren't compatible with the proposed release
389 */
390 function showReleaseLayerChangeModal(release, layers){
391 var layersToRmList = releaseModal.find("#layers-to-remove-list");
392 layersToRmList.text("");
393
394 releaseModal.find(".proposed-release-change-name").text(release.description);
395 releaseModal.data("layers", layers);
396 releaseModal.data("release", release);
397
398 for (var i in layers){
399 layersToRmList.append($("<li></li>").text(layers[i].name));
400 }
401 releaseModal.modal('show');
402 }
403
404 $("#change-release-btn").click(function(e){
405 e.preventDefault();
406
407 var newRelease = releaseForm.find("option:selected").data('release');
408
409 $.getJSON(ctx.typeaheadUrl,
410 { search: newRelease.id, type: "versionlayers" },
411 function(layers) {
412 if (layers.rows.length === 0){
413 /* No layers to change for this release */
414 changeProjectRelease(newRelease, []);
415 } else {
416 showReleaseLayerChangeModal(newRelease, layers.rows);
417 }
418 });
419 });
420
421 /* Release change modal accept */
422 $("#change-release-and-rm-layers").click(function(){
423 var layers = releaseModal.data("layers");
424 var release = releaseModal.data("release");
425
426 changeProjectRelease(release, layers);
427 });
428
429}
diff --git a/bitbake/lib/toaster/toastergui/templates/base.html b/bitbake/lib/toaster/toastergui/templates/base.html
index 8def2daf8e..4c6676c6e7 100644
--- a/bitbake/lib/toaster/toastergui/templates/base.html
+++ b/bitbake/lib/toaster/toastergui/templates/base.html
@@ -38,6 +38,8 @@
38 projectName : {{project.name|json}}, 38 projectName : {{project.name|json}},
39 projectTargetsUrl: {% url 'projectavailabletargets' project.id as paturl%}{{paturl|json}}, 39 projectTargetsUrl: {% url 'projectavailabletargets' project.id as paturl%}{{paturl|json}},
40 projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}}, 40 projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}},
41 projectLayersUrl: {% url 'projectlayers' project.id as plurl %}{{plurl|json}},
42 projectMachinesUrl: {% url 'projectmachines' project.id as pmurl %}{{pmurl|json}},
41 projectId : {{project.id}}, 43 projectId : {{project.id}},
42 {% else %} 44 {% else %}
43 projectId : undefined, 45 projectId : undefined,
@@ -69,6 +71,11 @@
69 Loading <i class="fa-pulse icon-spinner"></i> 71 Loading <i class="fa-pulse icon-spinner"></i>
70 </div> 72 </div>
71 73
74 <div id="change-notification" class="alert lead alert-info" style="display:none">
75 <button type="button" class="close" id="hide-alert">&times;</button>
76 <span id="change-notification-msg"></span>
77 </div>
78
72 <div class="navbar navbar-fixed-top"> 79 <div class="navbar navbar-fixed-top">
73 <div class="navbar-inner"> 80 <div class="navbar-inner">
74 <div class="container-fluid"> 81 <div class="container-fluid">
diff --git a/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html b/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
index ac32deac2f..0db06a86da 100644
--- a/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
+++ b/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
@@ -29,8 +29,6 @@
29 <li><a href="{% url 'projecttargets' project.id %}">Recipes</a></li> 29 <li><a href="{% url 'projecttargets' project.id %}">Recipes</a></li>
30 <li><a href="{% url 'projectmachines' project.id %}">Machines</a></li> 30 <li><a href="{% url 'projectmachines' project.id %}">Machines</a></li>
31 <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li> 31 <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li>
32 <li class="nav-header">Actions</li>
33 <li><a href="{% url 'importlayer' project.id %}">Import layer</a></li>
34 </ul> 32 </ul>
35 </div> 33 </div>
36 <div class="span10"> 34 <div class="span10">
diff --git a/bitbake/lib/toaster/toastergui/templates/importlayer.html b/bitbake/lib/toaster/toastergui/templates/importlayer.html
index d6984bcc9f..bec5f1aa74 100644
--- a/bitbake/lib/toaster/toastergui/templates/importlayer.html
+++ b/bitbake/lib/toaster/toastergui/templates/importlayer.html
@@ -1,10 +1,11 @@
1{% extends "baseprojectpage.html" %} 1{% extends "base.html" %}
2{% load projecttags %} 2{% load projecttags %}
3{% load humanize %} 3{% load humanize %}
4{% load static %} 4{% load static %}
5{% block pagecontent %}
5 6
7{% include "projecttopbar.html" %}
6 8
7{% block projectinfomain %}
8 9
9 {% if project and project.release %} 10 {% if project and project.release %}
10 <script src="{% static 'js/layerDepsModal.js' %}"></script> 11 <script src="{% static 'js/layerDepsModal.js' %}"></script>
@@ -24,12 +25,10 @@
24 }); 25 });
25 </script> 26 </script>
26 27
27 <h2>Import layer</h2> 28 <form class="span11">
28
29 <form>
30 <span class="help-block">The layer you are importing must be compatible with <strong>{{project.release.description}}</strong>, which is the release you are using in this project.</span>
31 <fieldset class="air"> 29 <fieldset class="air">
32 <legend>Layer repository information</legend> 30 <legend>Layer repository information</legend>
31 <span class="help-block">The layer you are importing must be compatible with <strong>{{project.release.description}}</strong>, which is the release you are using in this project.</span>
33 <div class="alert alert-error" id="import-error" style="display:none"> 32 <div class="alert alert-error" id="import-error" style="display:none">
34 <button type="button" class="close" data-dismiss="alert">&times;</button> 33 <button type="button" class="close" data-dismiss="alert">&times;</button>
35 <h3>&nbsp;</h3> 34 <h3>&nbsp;</h3>
@@ -136,5 +135,4 @@
136 </div> 135 </div>
137 136
138 {% endif %} 137 {% endif %}
139
140{% endblock %} 138{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/machine_btn.html b/bitbake/lib/toaster/toastergui/templates/machine_btn.html
index 54ff5def71..d2cb55bab4 100644
--- a/bitbake/lib/toaster/toastergui/templates/machine_btn.html
+++ b/bitbake/lib/toaster/toastergui/templates/machine_btn.html
@@ -1,4 +1,4 @@
1<a href="{% url 'project' extra.pid %}#/machineselect={{data.name}}" class="btn btn-block layer-exists-{{data.layer_version.id}}" style="margin-top: 5px; display:none"> 1<a href="{% url 'project' extra.pid %}?setMachine={{data.name}}" class="btn btn-block layer-exists-{{data.layer_version.id}}" style="margin-top: 5px; display:none">
2 Select machine</a> 2 Select machine</a>
3<button class="btn btn-block layerbtn layer-add-{{data.layer_version.id}}" data-layer='{ "id": {{data.layer_version.id}}, "name": "{{data.layer_version.layer.name}}", "layerdetailurl": "{%url 'layerdetails' extra.pid data.layer_version.id %}"}' data-directive="add"> 3<button class="btn btn-block layerbtn layer-add-{{data.layer_version.id}}" data-layer='{ "id": {{data.layer_version.id}}, "name": "{{data.layer_version.layer.name}}", "layerdetailurl": "{%url 'layerdetails' extra.pid data.layer_version.id %}"}' data-directive="add">
4 <i class="icon-plus"></i> 4 <i class="icon-plus"></i>
diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html
index 0fbfb599b7..aad79b4860 100644
--- a/bitbake/lib/toaster/toastergui/templates/project.html
+++ b/bitbake/lib/toaster/toastergui/templates/project.html
@@ -1,446 +1,130 @@
1 {% extends "baseprojectpage.html" %} 1{% extends "baseprojectpage.html" %}
2<!-- 2
3vim: expandtab tabstop=2
4-->
5{% load projecttags %} 3{% load projecttags %}
6{% load humanize %} 4{% load humanize %}
7{% load static %} 5{% load static %}
8 6
9
10{% block projectinfomain %} 7{% block projectinfomain %}
11<script src="{% static "js/angular.min.js" %}"></script>
12<script src="{% static "js/angular-animate.min.js" %}"></script>
13<script src="{% static "js/angular-cookies.min.js" %}"></script>
14<script src="{% static "js/angular-route.min.js" %}"></script>
15<script src="{% static "js/angular-sanitize.min.js" %}"></script>
16<script src="{% static "js/ui-bootstrap-tpls-0.11.0.js" %}"></script>
17 8
18{% if lvs_nos == 0 %} 9<script src="{% static 'js/layerDepsModal.js' %}"></script>
10<script src="{% static 'js/projectpage.js' %}"></script>
11<script>
12 $(document).ready(function (){
13 var ctx = {
14 typeaheadUrl : "{% url 'xhr_datatypeahead' project.id %}",
15
16 };
17
18 try {
19 projectPageInit(ctx);
20 } catch (e) {
21 document.write("Sorry, An error has occurred loading this page");
22 console.warn(e);
23 }
24 });
25</script>
19 26
20 <div class="page-header"> 27<div id="change-release-modal" class="modal hide fade in" tabindex="-1" role="dialog" aria-labelledby="change-release-modal" aria-hidden="false">
21 <h1> {{ project.name }} </h1> 28 <div class="modal-header">
29 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
30 <h3>Changing Yocto Project release to <span class="proposed-release-change-name"></span></h3>
22 </div> 31 </div>
23 <div class="alert alert-info lead"> 32 <div class="modal-body">
24 <p>Toaster has no layer information. Without layer information, you cannot run builds. To generate layer information you can:</p> 33 <p>The following added layers do not exist for <span class="proposed-release-change-name"></span>: </p>
25 <ul> 34 <ul id="layers-to-remove-list">
26 <li> <a href="http://www.yoctoproject.org/docs/latest/toaster-manual/toaster-manual.html#layer-source">Configure a layer source</a></li> 35 </ul>
27 <li> <a href="{% url 'importlayer' project.id %}">Import a layer</a></li> 36 <p>If you change the Yocto Project release to <span class="proposed-release-change-name"></span>, the above layers will be deleted from your added layers.</p>
28 </ul>
29</div>
30
31{%else%}
32
33<div id="main" role="main" data-ng-app="project" data-ng-controller="prjCtrl" data-ng-cloak>
34
35 <!-- alerts section 1-->
36 <div data-ng-repeat="a in zone1alerts">
37 <div class="alert alert-dismissible lead" role="alert" data-ng-class="a.type"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span></button>
38 <span data-ng-bind-html="a.text"></span>
39 </div>
40 </div> 37 </div>
38 <div class="modal-footer">
39 <button id="change-release-and-rm-layers" data-dismiss="modal" type="submit" class="btn btn-primary">Change release and delete layers</button>
40 <button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
41 </div>
42</div>
41 43
42 <!-- custom templates for ng -->
43
44 <style>
45 .missing-layer {
46 color: lightgrey;
47 }
48 </style>
49 <script type="text/ng-template" id="recipes_suggestion_details">
50 <a> {[match.model.name]}
51 <span data-ng-class="{'missing-layer':($parent.$parent.$parent.$parent.filterProjectLayerIds().indexOf(match.model.projectcompatible_layer.id) == -1)}">
52 [{[match.model.layer_version__layer__name]}]
53 </span>
54 </a>
55 </script>
56
57 <script type="text/ng-template" id="machines_suggestion_details">
58 <a> {[match.model.name]} <span class="{'missing-layer':(filterProjectLayerIds().indexOf(match.model.layer_version_compatible_id) == -1)}">[{[match.model.layer_version__layer__name]}]</span> </a>
59 </script>
60 44
61 <script type="text/ng-template" id="layers_suggestion_details"> 45<div class="row-fluid" id="project-page" style="display:none">
62 <a> {[match.model['layer__name']]} ( {[match.model.layer__vcs_url]} ) </a> 46 <div class="span6">
63 </script> 47 <div class="well well-transparent" id="machine-section">
48 <h3>Machine</h3>
64 49
50 <p class="lead"><span id="project-machine-name"></span> <i title="" data-original-title="" id="change-machine-toggle" class="icon-pencil"></i></p>
65 51
52 <form id="select-machine-form" style="display:none;">
53 <div class="alert alert-info">
54 <strong>Machine changes have a big impact on build outcome.</strong> You cannot really compare the builds for the new machine with the previous ones.
55 </div>
66 56
57 <div class="input-append">
58 <input id="machine-change-input" autocomplete="off" value="" data-provide="typeahead" data-minlength="1" data-autocomplete="off" type="text">
59 <button id="machine-change-btn" class="btn" type="button">Save</button> <a href="#" id="cancel-machine-change" class="btn btn-link">Cancel</a>
60 </div>
67 61
68 <!-- modal dialogs --> 62 <p><a href="{% url 'projectmachines' project.id %}" class="link">View compatible machines</a></p>
69 <script type="text/ng-template" id="dependencies_modal"> 63 </form>
70 <div class="modal-header">
71 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
72 <h3><span data-ng-bind="layerAddName"></span> dependencies</h3>
73 </div>
74 <div class="modal-body">
75 <p><strong>{[layerAddName]}</strong> depends on some layers that are not added to your project. Select the ones you want to add:</p>
76 <ul class="unstyled">
77 <li data-ng-repeat="ld in items">
78 <label class="checkbox">
79 <input type="checkbox" data-ng-model="selectedItems[ld.id]"> {[ld.name]}
80 </label>
81 </li>
82 </ul>
83 </div>
84 <div class="modal-footer">
85 <button class="btn btn-primary" data-ng-click="ok()">Add layers</button>
86 <button class="btn" data-ng-click="cancel()">Cancel</button>
87 </div> 64 </div>
88 </form>
89 </script>
90 65
66 <div class="well well-transparent">
67 <h3>Most built recipes</h3>
91 68
92 <script type="text/ng-template" id="change_version_modal"> 69 <div class="alert alert-info" style="display:none" id="no-most-built">
93 <div class="modal-header"> 70 <span class="lead">You haven't built any recipes yet</span>
94 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
95 <h3>Changing release to {[releaseDescription]}</h3>
96 </div> 71 </div>
97 <div class="modal-body">
98 <p>The following project layers do not exist for the {[releaseDescription]} release:</p>
99 <ul>
100 <li data-ng-repeat="i in items"><span class="layer-info" data-toggle="tooltip" tooltip="{[i.detail]}">{[i.name]}</span></li>
101 </ul>
102 <p>If you change the release to {[releaseDescription]}, the above layers will be deleted from your project.</p>
103 </div>
104 <div class="modal-footer">
105 <button class="btn btn-primary" data-ng-click="ok()">Change release and delete layers</button>
106 <button class="btn" data-ng-click="cancel()">Cancel</button>
107 </div>
108 </script>
109
110 <script type="text/ng-template" id="target_display">
111 <div data-ng-switch on="t.task.length">
112 <div data-ng-switch-when="undefined">{[t.target]}</div>
113 <div data-ng-switch-default>{[t.target]}:{[t.task]}</div>
114 </div>
115 </script>
116
117
118 <!-- latest builds list -->
119
120 <a id="buildslist"></a>
121 <h2 class="air" data-ng-if="builds.length">Latest builds</h2>
122 <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'queued':'alert-info', 'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
123 <div class="row-fluid">
124 <switch data-ng-switch="b.status">
125
126 <case data-ng-switch-when="Failed">
127 <div class="lead span3">
128 <a data-ng-class="{'Succeeded': 'success', 'Failed': 'error'}[b.status]" href="{[b.br_page_url]}">
129 <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span>
130 </a>
131 </div>
132 <div class="span2 lead">
133 <ngif data-ng-if="b.updated - todaydate > 0">
134 {[b.updated|date:'HH:mm']}
135 </ngif>
136 <ngif data-ng-if="b.updated - todaydate < 0">
137 {[b.updated|date:'dd/MM/yy HH:mm']}
138 </ngif>
139 </div>
140 <div class="span2">
141 <ngif data-ng-if="b.errors.length">
142 <span>
143 <i class="icon-minus-sign red lead"></i>
144 <a href="{[b.br_page_url]}#errors" class="lead error">{[b.errors.length]}
145 <ng-pluralize count="b.errors.length" when="{'1':'error','other':'errors'}"></ng-pluralize></a>
146 </span>
147 </ngif>
148 </div>
149 <div class="span2">
150 <!-- we don't have warnings in this case -->
151 </div>
152 <div> <span class="lead">Build time: {[b.command_time|timediff]}</span>
153 <button class="btn pull-right" data-ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.status]"
154 data-ng-click="buildExistingTarget(b.targets)">Run again</button>
155
156 </div>
157 </case>
158
159 <case data-ng-switch-when="queued">
160 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
161 <div class="span4 lead" >Build queued
162 <i title="This build will start as soon as a build server is available" class="icon-question-sign get-help get-help-blue heading-help" data-toggle="tooltip"></i>
163 </div>
164 <button class="btn pull-right btn-info" data-ng-click="buildCancel(b)">Cancel</button>
165 </case>
166
167 <case data-ng-switch-when="In Progress">
168 <switch data-ng-switch="b.build.length">
169 <case data-ng-switch-when="0">
170 <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
171 <div class="span4 lead">
172 Checking out layers
173 </div>
174 </case>
175 <case data-ng-switch-default="">
176 <div class="lead span3"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
177 <div class="span4 offset1" >
178 <div class="progress" style="margin-top:5px;" data-toggle="tooltip" tooltip="{[b.build[0].completeper]}% of tasks complete">
179 <div style="width: {[b.build[0].completeper]}%;" class="bar"></div>
180 </div>
181 </div>
182 <div class="text-right lead">{[b.build[0].completeper]}% tasks completed</div>
183 </case>
184 </case>
185
186 72
187 <case data-ng-switch-when="Succeeded"> 73 <ul class="unstyled configuration-list" id="freq-build-list">
188 <div class="lead span3"> 74 </ul>
189 <a data-ng-class="{'Succeeded': 'success', 'Failed': 'error'}[b.build[0].status]" href="{[b.build[0].build_page_url]}"> 75 <button class="btn btn-primary" id="freq-build-btn" disabled="disabled">Build selected recipes</button>
190 <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> 76 </div>
191 </a>
192 </div>
193 <div class="span2 lead">
194 <ngif data-ng-if="b.build[0].completed_on - todaydate > 0">
195 {[b.build[0].completed_on|date:'HH:mm']}
196 </ngif>
197 <ngif data-ng-if="b.build[0].completed_on - todaydate < 0">
198 {[b.build[0].completed_on|date:'dd/MM/yy HH:mm']}
199 </ngif>
200 </div>
201 <div class="span2">
202 <ngif data-ng-if="b.build[0].errors">
203 <span>
204 <i class="icon-minus-sign red lead"></i>
205 <a href="{[b.build[0].build_page_url]}#errors" class="lead error">{[b.build[0].errors]}
206 <ng-pluralize count="b.build[0].errors" when="{'1':'error','other':'errors'}"></ng-pluralize></a>
207 </span>
208 </ngif>
209 </div>
210 <div class="span2">
211 <ngif data-ng-if="b.build[0].warnings">
212 <span>
213 <i class="icon-warning-sign yellow lead"></i>
214 <a href="{[b.build[0].build_page_url]}#warnings" class="lead warning">{[b.build[0].warnings]}
215 <ng-pluralize count="b.build[0].warnings" when="{'1':'warning','other':'warnings'}"></ng-pluralize></a>
216 </span>
217 </ngif>
218 </div>
219 <div> <span class="lead">Build time: <a href="{[b.build[0].build_time_page_url]}">{[b.build[0].build_time|timediff]}</a></span>
220 <button class="btn pull-right" data-ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.build[0].status]"
221 data-ng-click="buildExistingTarget(b.targets)">Run again</button>
222 77
223 </div> 78 <div class="well well-transparent">
224 </case> 79 <h3>Project release</h3>
225 80
81 <p class="lead"><span id="project-release-title"></span> <i title="" data-original-title="" id="release-change-toggle" class="icon-pencil"></i></p>
226 82
227 <case data-ng-switch-default=""> 83 <form class="form-inline" id="change-release-form" style="display:none;">
228 <div>FIXME!</div> 84 <select></select>
229 </case> 85 <button class="btn" style="margin-left:5px;" id="change-release-btn">Change</button> <a href="#" id="cancel-release-change" class="btn btn-link">Cancel</a>
230 </switch> 86 </form>
231 <div class="lead pull-right">
232 </div>
233 </div> 87 </div>
234 </div> 88 </div>
235 89
236 <h2 class="air">Project configuration</h2> 90 <div class="span6">
91 <div class="well well-transparent" id="layer-container">
92 <h3>Layers <span class="muted counter">(<span id="project-layers-count"></span>)</span>
93 <i data-original-title="OpenEmbedded organises metadata into modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>" class="icon-question-sign get-help heading-help" title=""></i>
94 </h3>
237 95
238 <!-- alerts section 2 --> 96 <div class="alert lead" id="no-layers-in-project" style="display:none">
239 <div data-ng-repeat="a in zone2alerts"> 97 You need to add some layers. For that you can:
240 <div class="alert alert-dismissible lead" role="alert" data-ng-class="a.type"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span></button> 98 <ul>
241 <span data-ng-bind-html="a.text"></span> 99 <li><a href="{% url 'projectlayers' project.id %}">View all layers compatible with this project</a></li>
242 </div> 100 <li><a href="{% url 'importlayer' project.id %}">Import a layer</a></li>
243 </div> 101 <li><a href="http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#understanding-and-creating-layers" target="_blank">Read about layers in the documentation</a></li>
102 </ul>
103 <p>Or type a layer name below.</p>
104 </div>
244 105
245 <div class="row-fluid"> 106 <form style="margin-top:20px">
107 <!--div class="control-group error"-->
246 108
247 <!-- project layers -->
248 <div id="layer-container" class="well well-transparent span4">
249 <h3>
250 Layers <span class="muted counter">({[layers.length]})</span>
251 <i class="icon-question-sign get-help heading-help" title="Bitbake reads metadata files from modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>"></i>
252 </h3>
253 <div class="alert" data-ng-if="project.release && !layers.length">
254 <b>You need to add some layers </b>
255 <p>
256 You can:
257 <ul>
258 <li> <a href="{% url 'projectlayers' project.id %}">View all compatible layers available in Toaster</a>
259 <li> <a href="{% url 'importlayer' project.id %}">Import a layer</a>
260 <li> <a href="https://www.yoctoproject.org/docs/1.6.1/dev-manual/dev-manual.html#understanding-and-creating-layers" target="_blank">Read about layers in the manual</a>
261 </ul>
262 Or type a layer name below.
263 </p>
264 </div>
265 <form data-ng-submit="layerAdd()">
266 <div class="input-append"> 109 <div class="input-append">
267 <input type="text" style="width: 100%" id="layer" autocomplete="off" placeholder="Type a layer name" data-minLength="1" data-ng-model="layerAddName" data-typeahead="e for e in getLayersAutocompleteSuggestions($viewValue)" data-typeahead-template-url="layers_suggestion_details" data-typeahead-on-select="onLayerSelect($item, $model, $label)" data-typeahead-editable="false" data-ng-class="{ 'has-error': layerAddName.$invalid }" data-ng-disabled="!project.release" /> 110 <input id="layer-add-input" autocomplete="off" placeholder="Type a layer name" data-minlength="1" data-autocomplete="off" data-provide="typeahead" data-source="" type="text">
268 <input type="submit" id="add-layer" class="btn" value="Add" data-ng-disabled="!layerAddName.length"/> 111 <button id="add-layer-btn" class="btn" disabled>Add</button>
269 </div> 112 </div>
270 {% csrf_token %}
271 </form>
272 <p>
273 <a href="{% url 'projectlayers' project.id %}">View all compatible layers</a>
274 <i class="icon-question-sign get-help" title="View all the layers you can build with the release selected for this project, which is {[project.release.desc]}"></i>
275 |
276 <a href="{% url 'importlayer' project.id %}">Import layer</a></p>
277 <ul class="unstyled configuration-list">
278 <li data-ng-repeat="l in layers track by l.id" class="animate-repeat">
279 <a href="{[l.layerdetailurl]}" class="layer-info" data-toggle="tooltip" tooltip-placement="right" tooltip="{[l.giturl]} | {[l.branch.name]}">{[l.name]}</a>
280 <i class="icon-trash" data-ng-click="layerDel(l.id)" tooltip="Delete"></i>
281 </li>
282 </ul>
283 </div>
284 113
285 114 <div id="import-alert" class="alert alert-info" style="display:none;">
286 <!-- project targets --> 115 Toaster does not know about this layer. Please <a href="#">import it</a>
287 <div id="target-container" class="well well-transparent span4">
288 <h3>
289 Recipes
290 <i class="icon-question-sign get-help heading-help" title="What you build, often an image recipe that produces a root file system file. Something like <code>core-image-minimal</code> or <code>core-image-sato</code>"></i>
291 </h3>
292 <form data-ng-submit="buildNamedTarget()">
293 <div class="input-append">
294 <input type="text" style="width: 100%" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-minLength="1" data-ng-model="targetName1" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!project.release || !layers.length">
295 <button type="submit" class="btn btn-primary" data-ng-disabled="!targetName1.length">
296 Build </button>
297 </div>
298 {% csrf_token %}
299 </form>
300 <p>
301 <a href="{% url 'projecttargets' project.id %}">View all compatible recipes</a>
302 <i class="icon-question-sign get-help" title="View all the recipes you can build with the release selected for this project, which is {[project.release.desc]}"></i>
303 </p>
304 <div data-ng-if="frequenttargets.length">
305 <h4 class="air">
306 Most built recipes
307 </h4>
308 <ul class="unstyled configuration-list {[mutedtargets]}">
309 <li data-ng-repeat="t in frequenttargets">
310 <label class="checkbox">
311 <input type="checkbox" data-ng-model="mostBuiltTargets[t]" data-ng-disabled="disableBuildCheckbox(t)" data-ng-checked="mostBuiltTargets[t] && !disableBuildCheckbox(t)">{[t]}
312 </label>
313 </li>
314 </ul>
315 <button class="btn btn-large btn-primary" data-ng-disabled="enableBuildSelectedTargets()" data-ng-click="buildSelectedTargets()">Build selected recipes</button>
316 </div> 116 </div>
317 </div>
318 117
319 <!-- project configuration -->
320 <div id="machine-distro" class="well well-transparent span4">
321 <h3>
322 Machine
323 <i class="icon-question-sign get-help heading-help" title="The machine is the hardware for which you want to build. You can only set one machine per project"></i>
324 </h3>
325 <p class="lead" id="select-machine-opposite">
326 <span>{[machine.name]}</span>
327 <i id="change-machine" class="icon-pencil" data-ng-click="toggle('#select-machine')"></i>
328 </p>
329 <div id="select-machine" style="display: none">
330 <div class="alert alert-info">
331 <strong>Machine changes have a big impact on build outcome.</strong>
332 You cannot really compare the builds for the new machine with the previous ones.
333 </div>
334 <form data-ng-submit="editProjectSettings('#select-machine')" class="input-append">
335 <input type="text" id="machine" autocomplete="off" data-ng-model="machineName" value="{[machine.name]}" data-typeahead="m.name for m in getMachinesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="machines_suggestion_details" />
336 <input type="submit" id="apply-change-machine" class="btn" data-ng-disabled="machineName == machine.name || machineName.length == 0" value="Save"/>
337 <input type="reset" id="cancel-machine" class="btn btn-link" data-ng-click="toggle('#select-machine')" value="Cancel"/>
338 {% csrf_token %}
339 </form>
340 <p> 118 <p>
341 <a href="{% url 'projectmachines' project.id %}" class="link">View all compatible machines</a> 119 <a href="{% url 'projectlayers' project.id %}" id="view-compatible-layers">View compatible layers</a>
342 <i class="icon-question-sign get-help" title="View all the machines you can set with the release selected for this project, which is {[project.release.desc]}"></i> 120 <i data-original-title="View all the layers you can build with the release selected for this project, which is Yocto Project master" class="icon-question-sign get-help" title=""></i>
343 </p> 121 | <a href="{% url 'importlayer' project.id %}">Import layer</a>
344 </div> 122 </p>
345 <p class="link-action">
346 <a href="{% url 'projectconf' project.id %}" class="link">Edit configuration variables</a>
347 <i data-original-title="You can set other project configuration options here. Each option, like everything else in the build system, is a variable - value pair" class="icon-question-sign get-help heading-help" title=""></i>
348 </p>
349 </div>
350 </div>
351
352
353 <h2>Project details</h2>
354
355 <!-- alerts section 3 -->
356 <div data-ng-repeat="a in zone3alerts">
357 <div class="alert alert-dismissible lead" role="alert" data-ng-class="a.type"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span></button>
358 <span data-ng-bind-html="a.text"></span>
359 </div>
360 </div>
361
362
363 <div id="project-details" class="well well-transparent">
364 <h3>Project name</h3>
365 <p class="lead" id="change-project-name-opposite">
366 <span >{[project.name]}</span>
367 <i class="icon-pencil" data-ng-click="toggle('#change-project-name')" ></i>
368 </p>
369 <div id="change-project-name" style="display:none;">
370 <form data-ng-submit="editProjectSettings('#change-project-name')" class="input-append">
371 <input type="text" class="input-xlarge" id="type-project-name" data-ng-model="projectName" value="{[project.name]}">
372 <input type="submit" class="btn" value="Save" data-ng-disabled="project.name == projectName"/>
373 <input type="reset" class="btn btn-link" value="Cancel" data-ng-click="toggle('#change-project-name')">
374 </form>
375 </div>
376
377 <h3 data-ng-if="releases.length > 1">
378 Release
379 <i class="icon-question-sign get-help heading-help" title="The version of the build system you want to use"></i>
380 </h3>
381 <p data-ng-if="releases.length > 1" class="lead" id="change-project-version-opposite">
382 <span id="project-version">{[project.release.desc]}</span>
383 <i id="change-version" class="icon-pencil" data-ng-click="toggle('#change-project-version')" ></i>
384 </p>
385 <div class="div-inline" id="change-project-version" style="display:none;">
386 <form data-ng-submit="testProjectSettingsChange('#change-project-version')" class="input-append">
387 <select id="select-version" data-ng-model="projectVersion">
388 <option data-ng-repeat="r in releases" value="{[r.id]}" data-ng-selected="r.id == project.release.id">{[r.description]}</option>
389 </select>
390 <input type="submit" class="btn" style="margin-left:5px;" value="Save" data-ng-disabled="project.release.id == projectVersion"/>
391 <input type="reset" class="btn btn-link" value="Cancel" data-ng-click="toggle('#change-project-version')"/>
392
393 </form> 123 </form>
124
125 <ul class="unstyled configuration-list" id="layers-in-project-list">
126 </ul>
394 </div> 127 </div>
395 </div> 128 </div>
396 129</div>
397</div> <!-- end main -->
398
399</div> <!-- end row -->
400
401
402
403<!-- load application logic !-->
404<script src="{% static "js/projectapp.js" %}"></script>
405
406<!-- dump initial data for use in the angular app -->
407<script>
408angular.element(document).ready(function() {
409 scope = angular.element("#main").scope();
410 scope.urls = {};
411 scope.urls.xhr_build = "{% url 'projectbuilds' project.id %}";
412 scope.urls.xhr_edit = "{% url 'project' project.id %}?format=json";
413 scope.urls.layers = "{% url 'projectlayers' project.id %}";
414 scope.urls.targets = "{% url 'projectavailabletargets' project.id %}";
415 scope.urls.machines = "{% url 'projectmachines' project.id %}";
416 scope.urls.importlayer = "{% url 'importlayer' project.id %}";
417 scope.urls.xhr_datatypeahead = {% url 'xhr_datatypeahead' project.id as xhrdta %}{{xhrdta|json}};
418 scope.project = {{prj|json}};
419 scope.builds = {{builds|json}};
420 scope.layers = {{layers|json}};
421 scope.targets = {{targets|json}};
422 scope.frequenttargets = {{freqtargets|json}};
423 scope.machine = {{machine|json}};
424 scope.releases = {{releases|json}};
425 scope.layerCount = scope.layers.length;
426 scope.mutedtargets = (scope.layerCount == 0 ? "muted" : "")
427 var now = (new Date()).getTime();
428 scope.todaydate = now - (now % 86400000);
429
430 scope.zone1alerts = [];
431 scope.zone2alerts = [];
432 scope.zone3alerts = [];
433
434 scope.mostBuiltTargets = {};
435
436 scope.updateDisplayWithCommands();
437 scope.validateData();
438
439 scope.init();
440 scope.$digest();
441
442 });
443</script>
444
445{% endif %} {# from lvs_nos check #}
446{% endblock %} 130{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/projecttopbar.html b/bitbake/lib/toaster/toastergui/templates/projecttopbar.html
index 46473cb76b..d4d1951ae4 100644
--- a/bitbake/lib/toaster/toastergui/templates/projecttopbar.html
+++ b/bitbake/lib/toaster/toastergui/templates/projecttopbar.html
@@ -1,3 +1,8 @@
1<div class="alert alert-success lead" id="project-created-notification" style="margin-top:15px; display:none">
2 <button type="button" class="close" data-dismiss="alert">×</button>
3 Your project <strong>{{project.name}}</strong> has been created. You can now <a href="{% url 'projectmachines' project.id %}">select your target machine</a> and <a href="{% url 'projecttargets' project.id %}">choose image recipes</a> to build.
4</div>
5
1<!-- project name --> 6<!-- project name -->
2<div class="row-fluid page-header"> 7<div class="row-fluid page-header">
3 <h1>{{project.name}}</h1> 8 <h1>{{project.name}}</h1>
@@ -7,7 +12,7 @@
7 <ul class="nav nav-pills"> 12 <ul class="nav nav-pills">
8 <li> 13 <li>
9 <a href="{% url 'projectbuilds' project.id %}"> 14 <a href="{% url 'projectbuilds' project.id %}">
10 Builds (<span class="total-builds"></span>) 15 Builds (<span class="total-builds">0</span>)
11 </a> 16 </a>
12 </li> 17 </li>
13 <li id="topbar-configuration-tab"> 18 <li id="topbar-configuration-tab">
@@ -15,6 +20,11 @@
15 Configuration 20 Configuration
16 </a> 21 </a>
17 </li> 22 </li>
23 <li>
24 <a href="{% url 'importlayer' project.id %}">
25 Import layer
26 </a>
27 </li>
18 <!-- Coming soon 28 <!-- Coming soon
19 <li> 29 <li>
20 <a href="my-image-recipes.html"> 30 <a href="my-image-recipes.html">
@@ -23,16 +33,16 @@
23 </li> 33 </li>
24 --> 34 -->
25 <li class="pull-right"> 35 <li class="pull-right">
26 <form class="form-inline" style="margin-bottom: 0">
27 36
28 <i class="icon-question-sign get-help heading-help" data-placement="left" title="" data-original-title="Type the name of one or more recipes you want to build, separated by a space. You can also specify a task by appending a semicolon and a task name to the recipe name, like so: <code>busybox:clean</code>"> 37 <i class="icon-question-sign get-help heading-help" data-placement="left" title="" data-original-title="Type the name of one or more recipes you want to build, separated by a space. You can also specify a task by appending a semicolon and a task name to the recipe name, like so: <code>busybox:clean</code>">
29 </i> 38 </i>
30 <div class="input-append"> 39 <div class="input-append">
31 <input type="text" class="input-xlarge build-target-input" placeholder="Type the recipe you want to build" autocomplete="off"> 40 <form class="form-inline" style="margin-bottom: 0">
32 <button class="btn btn-primary build-button" data-project-id="{{project.id}}" disabled>Build 41 <input type="text" class="input-xlarge input-lg build-target-input" placeholder="Type the recipe you want to build" autocomplete="off" disabled>
33 </button> 42 <button class="btn btn-primary btn-large build-button" data-project-id="{{project.id}}" disabled>Build
43 </button>
44 </form>
34 </div> 45 </div>
35 </form> 46 </li>
36 </li> 47 </ul>
37 </ul>
38</div> 48</div>
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 688a4c276f..b43a01e951 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -2132,7 +2132,7 @@ if True:
2132 prj = Project.objects.create_project(name = request.POST['projectname'], release = release) 2132 prj = Project.objects.create_project(name = request.POST['projectname'], release = release)
2133 prj.user_id = request.user.pk 2133 prj.user_id = request.user.pk
2134 prj.save() 2134 prj.save()
2135 return redirect(reverse(project, args=(prj.pk,)) + "#/newproject") 2135 return redirect(reverse(project, args=(prj.pk,)) + "?notify=new-project")
2136 2136
2137 except (IntegrityError, BadParameterException) as e: 2137 except (IntegrityError, BadParameterException) as e:
2138 # fill in page with previously submitted values 2138 # fill in page with previously submitted values
@@ -2238,7 +2238,8 @@ if True:
2238 } 2238 }
2239 2239
2240 if prj.release is not None: 2240 if prj.release is not None:
2241 context["prj"]["release"] = { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description} 2241 context['release'] = { "id": prj.release.pk, "name": prj.release.name, "description": prj.release.description}
2242
2242 2243
2243 try: 2244 try:
2244 context["machine"] = {"name": prj.projectvariable_set.get(name="MACHINE").value} 2245 context["machine"] = {"name": prj.projectvariable_set.get(name="MACHINE").value}