summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2014-11-28 20:12:18 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-12-18 10:24:07 +0000
commit1605cd37dbfcd75043f57dd527e0bfc0a79e1e78 (patch)
tree505bf86cdde6993010c5a153d153b3206ab98cf2 /bitbake
parent13141af70813d84b27e41d7a6e5792c748b3ae90 (diff)
downloadpoky-1605cd37dbfcd75043f57dd527e0bfc0a79e1e78.tar.gz
bitbake: toaster: Add import layer feature.
This feature allows users to import layers from git into their current project and associate it with the release of the current project and the dependencies for the newly imported layer with existing layers. It will also resolve the child dependencies of the dependencies added. [YOCTO #6595] (Bitbake rev: 017f5c746e894f9d87d927c848386459ea332378) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/importlayer.js277
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/projectapp.js6
-rw-r--r--bitbake/lib/toaster/toastergui/templates/importlayer.html109
-rw-r--r--bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html68
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py2
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py107
6 files changed, 533 insertions, 36 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/importlayer.js b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
new file mode 100644
index 0000000000..e2bc1ab607
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
@@ -0,0 +1,277 @@
1"use strict"
2
3function importLayerPageInit (ctx) {
4
5 var layerDepBtn = $("#add-layer-dependency-btn");
6 var importAndAddBtn = $("#import-and-add-btn");
7 var layerNameInput = $("#layer-name");
8 var vcsURLInput = $("#layer-git-repo-url");
9 var gitRefInput = $("#layer-git-ref");
10 var layerDepInput = $("#layer-dependency");
11 var layerNameCtrl = $("#layer-name-ctrl");
12 var duplicatedLayerName = $("#duplicated-layer-name-hint");
13
14 var layerDeps = {};
15 var layerDepsDeps = {};
16 var currentLayerDepSelection;
17 var validLayerName = /^(\w|-)+$/;
18
19 $("#new-project-button").hide();
20
21 libtoaster.makeTypeahead(layerDepInput, ctx.xhrDataTypeaheadUrl, { type : "layers", project_id: ctx.projectId, include_added: "true" }, function(item){
22 currentLayerDepSelection = item;
23
24 layerDepBtn.removeAttr("disabled");
25 });
26
27
28 /* We automatically add "openembedded-core" layer for convenience as a
29 * dependency as pretty much all layers depend on this one
30 */
31 $.getJSON(ctx.xhrDataTypeaheadUrl, { type : "layers", project_id: ctx.projectId, include_added: "true" , value: "openembedded-core" }, function(layer) {
32 if (layer.list.length == 1) {
33 currentLayerDepSelection = layer.list[0];
34 layerDepBtn.click();
35 }
36 });
37
38 layerDepBtn.click(function(){
39 if (currentLayerDepSelection == undefined)
40 return;
41
42 layerDeps[currentLayerDepSelection.id] = currentLayerDepSelection;
43
44 /* Make a list item for the new layer dependency */
45 var newLayerDep = $("<li><a></a><span class=\"icon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
46
47 newLayerDep.data('layer-id', currentLayerDepSelection.id);
48 newLayerDep.children("span").tooltip();
49
50 var link = newLayerDep.children("a");
51 link.attr("href", ctx.layerDetailsUrl+String(currentLayerDepSelection.id));
52 link.text(currentLayerDepSelection.name);
53 link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"});
54
55 var trashItem = newLayerDep.children("span");
56 trashItem.click(function () {
57 var toRemove = $(this).parent().data('layer-id');
58 delete layerDeps[toRemove];
59 $(this).parent().fadeOut(function (){
60 $(this).remove();
61 });
62 });
63
64 $("#layer-deps-list").append(newLayerDep);
65
66 libtoaster.getLayerDepsForProject(ctx.xhrDataTypeaheadUrl, ctx.projectId, currentLayerDepSelection.id, function (data){
67 /* These are the dependencies of the layer added as a dependency */
68 if (data.list.length > 0) {
69 currentLayerDepSelection.url = ctx.layerDetailsUrl+currentLayerDepSelection.id;
70 layerDeps[currentLayerDepSelection.id].deps = data.list
71 }
72
73 /* Clear the current selection */
74 layerDepInput.val("");
75 currentLayerDepSelection = undefined;
76 layerDepBtn.attr("disabled","disabled");
77 }, null);
78 });
79
80 importAndAddBtn.click(function(){
81 /* arrray of all layer dep ids includes parent and child deps */
82 var allDeps = [];
83 /* temporary object to use to do a reduce on the dependencies for each
84 * layer dependency added
85 */
86 var depDeps = {};
87
88 /* the layers that have dependencies have an extra property "deps"
89 * look in this for each layer and reduce this to a unquie object
90 * of deps.
91 */
92 for (var key in layerDeps){
93 if (layerDeps[key].hasOwnProperty('deps')){
94 for (var dep in layerDeps[key].deps){
95 var layer = layerDeps[key].deps[dep];
96 depDeps[layer.id] = layer;
97 }
98 }
99 allDeps.push(layerDeps[key].id);
100 }
101
102 /* we actually want it as an array so convert it now */
103 var depDepsArray = [];
104 for (var key in depDeps)
105 depDepsArray.push (depDeps[key]);
106
107 if (depDepsArray.length > 0) {
108 var layer = { name: layerNameInput.val(), url: "#", id: -1 };
109 show_layer_deps_modal(ctx.projectId, layer, depDepsArray, function(selected){
110 /* Add the accepted dependencies to the allDeps array */
111 if (selected.length > 0){
112 allDeps.concat (selected);
113 }
114 import_and_add ();
115 });
116 } else {
117 import_and_add ();
118 }
119
120 function import_and_add () {
121 /* convert to a csv of all the deps to be added */
122 var layerDepsCsv = allDeps.join(",");
123
124 var layerData = {
125 name: layerNameInput.val(),
126 vcs_url: vcsURLInput.val(),
127 git_ref: gitRefInput.val(),
128 summary: $("#layer-summary").val(),
129 dir_path: $("#layer-subdir").val(),
130 project_id: ctx.projectId,
131 layer_deps: layerDepsCsv,
132 };
133
134 $.ajax({
135 type: "POST",
136 url: ctx.xhrImportLayerUrl,
137 data: layerData,
138 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
139 success: function (data) {
140 if (data.error != "ok") {
141 show_error_message(data, layerData);
142 console.log(data.error);
143 } else {
144 /* Success layer import now go to the project page */
145 window.location.replace(ctx.projectPageUrl+'#/layerimported='+layerData.name);
146 }
147 },
148 error: function (data) {
149 console.log("Call failed");
150 console.log(data);
151 }
152 });
153 }
154 });
155
156 function show_error_message(error, layerData) {
157
158 var errorMsg = $("#import-error").fadeIn();
159 var errorType = error.error;
160 var body = errorMsg.children("span");
161 var title = errorMsg.children("h3");
162 var optionsList = errorMsg.children("ul");
163 var invalidLayerRevision = $("#invalid-layer-revision-hint");
164 var layerRevisionCtrl = $("#layer-revision-ctrl");
165
166 /* remove any existing items */
167 optionsList.children().each(function(){ $(this).remove(); });
168 body.text("");
169 title.text("");
170 invalidLayerRevision.hide();
171 layerNameCtrl.removeClass("error");
172 layerRevisionCtrl.removeClass("error");
173
174 switch (errorType){
175 case 'hint-layer-version-exists':
176 title.text("This layer already exists");
177 body.html("A layer <strong>"+layerData.name+"</strong> already exists with this Git repository URL and this revision. You can:");
178 optionsList.append("<li>Import <strong>"+layerData.name+"</strong> with a different revision </li>");
179 optionsList.append("<li>or <a href=\""+ctx.layerDetailsUrl+error.existing_layer_version+"/\" >change the revision of the existing layer</a></li>");
180
181 layerRevisionCtrl.addClass("error");
182
183 invalidLayerRevision.html("A layer <strong>"+layerData.name+"</strong> already exists with this revision.<br />You can import <strong>"+layerData.name+"</strong> with a different revision");
184 invalidLayerRevision.show();
185 break;
186
187 case 'hint-layer-exists-with-different-url':
188 title.text("This layer already exists");
189 body.html("A layer <strong>"+layerData.name+"</strong> already exists with a different Git repository URL:<br /><br />"+error.current_url+"<br /><br />You Can:");
190 optionsList.append("<li>Import the layer under a different name</li>");
191 optionsList.append("<li>or <a href=\""+ctx.layerDetailsUrl+error.current_id+"/\" >change the Git repository URL of the existing layer</a></li>");
192 duplicatedLayerName.html("A layer <strong>"+layerData.name+"</strong> already exists with a different Git repository URL.<br />To import this layer give it a different name.");
193 duplicatedLayerName.show();
194 layerNameCtrl.addClass("error");
195 break;
196
197 case 'hint-layer-exists':
198 title.text("This layer already exists");
199 body.html("A layer <strong>"+layerData.name+"</strong> already exists: You Can:");
200 optionsList.append("<li>Import the layer under a different name</li>");
201 break;
202 default:
203 title.text("Error")
204 body.text(data.error);
205 }
206 }
207
208 function enable_import_btn (enabled) {
209 var importAndAddHint = $("#import-and-add-hint");
210
211 if (enabled) {
212 importAndAddBtn.removeAttr("disabled");
213 importAndAddHint.hide();
214 return;
215 }
216
217 importAndAddBtn.attr("disabled", "disabled");
218 importAndAddHint.show();
219 }
220
221 function check_form() {
222 var valid = false;
223 var inputs = $("input:required");
224
225 for (var i=0; i<inputs.length; i++){
226 if (!(valid = inputs[i].value)){
227 enable_import_btn(false);
228 break;
229 }
230 }
231
232 if (valid)
233 enable_import_btn(true);
234 }
235
236 vcsURLInput.keyup(function() {
237 check_form();
238 });
239
240 gitRefInput.keyup(function() {
241 check_form();
242 });
243
244 layerNameInput.keyup(function() {
245 if ($(this).val() && !validLayerName.test($(this).val())){
246 layerNameCtrl.addClass("error")
247 $("#invalid-layer-name-hint").show();
248 enable_import_btn(false);
249 return;
250 }
251
252 /* Don't remove the error class if we're displaying the error for another
253 * reason.
254 */
255 if (!duplicatedLayerName.is(":visible"))
256 layerNameCtrl.removeClass("error")
257
258 $("#invalid-layer-name-hint").hide();
259 check_form();
260 });
261
262 /* Have a guess at the layer name */
263 vcsURLInput.focusout(function (){
264 /* If we a layer name specified don't overwrite it or if there isn't a
265 * url typed in yet return
266 */
267 if (layerNameInput.val() || !$(this).val())
268 return;
269
270 if ($(this).val().search("/")){
271 var urlPts = $(this).val().split("/");
272 var suggestion = urlPts[urlPts.length-1].replace(".git","");
273 layerNameInput.val(suggestion);
274 }
275 });
276
277}
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
index e9b07c7848..8e3499a94c 100644
--- a/bitbake/lib/toaster/toastergui/static/js/projectapp.js
+++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
@@ -571,6 +571,12 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
571 "\">select targets</a> you want to build.", "alert-success"); 571 "\">select targets</a> you want to build.", "alert-success");
572 }); 572 });
573 573
574 _cmdExecuteWithParam("/layerimported", function (layer) {
575 $scope.displayAlert($scope.zone2alerts,
576 "You have imported <strong>" + layer +
577 "</strong> and added it to your project.", "alert-success");
578 });
579
574 _cmdExecuteWithParam("/targetbuild=", function (targets) { 580 _cmdExecuteWithParam("/targetbuild=", function (targets) {
575 var oldTargetName = $scope.targetName; 581 var oldTargetName = $scope.targetName;
576 $scope.targetName = targets.split(",").join(" "); 582 $scope.targetName = targets.split(",").join(" ");
diff --git a/bitbake/lib/toaster/toastergui/templates/importlayer.html b/bitbake/lib/toaster/toastergui/templates/importlayer.html
index 7e48eac66e..913f951c28 100644
--- a/bitbake/lib/toaster/toastergui/templates/importlayer.html
+++ b/bitbake/lib/toaster/toastergui/templates/importlayer.html
@@ -1,68 +1,115 @@
1{% extends "baseprojectpage.html" %} 1{% extends "baseprojectpage.html" %}
2{% load projecttags %} 2{% load projecttags %}
3{% load humanize %} 3{% load humanize %}
4{% load static %}
4 5
5{% block localbreadcrumb %} 6{% block localbreadcrumb %}
6<li>Layers</li> 7<li>Import layer</li>
7{% endblock %} 8{% endblock %}
8 9
9{% block projectinfomain %} 10{% block projectinfomain %}
11
12 <script src="{% static 'js/importlayer.js' %}"></script>
13 <script>
14 $(document).ready(function (){
15 var ctx = {};
16 ctx.xhrDataTypeaheadUrl = "{% url 'xhr_datatypeahead' %}";
17 ctx.layerDetailsUrl = "{% url 'layerdetails' %}";
18 ctx.xhrImportLayerUrl = "{% url 'xhr_importlayer' %}";
19 ctx.xhrEditProjectUrl = "{% url 'xhr_projectedit' project.id %}";
20 ctx.projectPageUrl = "{% url 'project' project.id %}";
21 ctx.projectId = {{project.id}};
22
23 try {
24 importLayerPageInit(ctx);
25 } catch(e) {
26 document.write(e.stack);
27 console.log(e);
28 }
29 });
30 </script>
31
10 <div class="page-header"> 32 <div class="page-header">
11 <h1>Import layer</h1> 33 <h1>Import layer</h1>
12 </div> 34 </div>
35
36 {% include "layers_dep_modal.html" %}
13 <form> 37 <form>
14 {% if project %} 38 {% if project %}
15 <span class="help-block" style="padding-left:19px;">The layer you are importing must be compatible with {{project.release.name}} ({{project.release.description}}), which is the release you are using in this project.</span> 39 <span class="help-block" style="padding-left:19px;">The layer you are importing must be compatible with {{project.release.name}} ({{project.release.description}}), which is the release you are using in this project.</span>
16 {% endif %} 40 {% endif %}
17 <fieldset class="air"> 41 <fieldset class="air">
18 <legend>Layer repository information</legend> 42 <legend>Layer repository information</legend>
43 <div class="alert alert-error" id="import-error" style="display:none">
44 <button type="button" class="close" data-dismiss="alert">&times;</button>
45 <h3></h3>
46 <span></span>
47 <ul></ul>
48 </div>
49
50 <div class="control-group" id="layer-name-ctrl">
51 <label class="control-label" for="layer-name">
52 Layer name
53 <span class="icon-question-sign get-help" title="Something like 'meta-mylayer'. Your layer name must be unique and can only include letters, numbers and dashes" />
54 </label>
55 <div class="controls">
56 <input id="layer-name" type="text" required autofocus>
57 <span class="help-inline" style="display: none;" id="invalid-layer-name-hint">A valid layer name can only include letters, numbers and dashes</span>
58 <span class="help-inline" style="display: none;" id="duplicated-layer-name-hint"></span>
59 </div>
60
61 </div>
62
19 <label> 63 <label>
20 Git repository URL 64 Git repository URL
21 <i class="icon-question-sign get-help" title="Fetch/clone URL of the repository. Currently, Toaster only supports Git repositories."></i> 65 <span class="icon-question-sign get-help" title="Fetch/clone URL of the repository. Currently, Toaster only supports Git repositories." />
22 </label> 66 </label>
23 <input id="repo" type="text" class="input-xxlarge" required> 67
24 <label class="project-form"> 68 <input type="text" id="layer-git-repo-url" class="input-xxlarge" required>
69 <label class="project-form" for="layer-subdir">
25 Repository subdirectory 70 Repository subdirectory
26 <span class="muted">(optional)</span> 71 <span class="muted">(optional)</span>
27 <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> 72 <span 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)" />
28 </label>
29 <input type="text" id="subdir">
30 <label class="project-form">Branch, tag or commit</label>
31 <input type="text" class="span4" id="layer-version" required>
32 <label class="project-form">
33 Layer name
34 <i class="icon-question-sign get-help" title="Something like 'meta-mylayer'. Your layer name must be unique and can only include letters, numbers and dashes"></i>
35 </label> 73 </label>
36 <input id="layer-name" type="text" required> 74 <input type="text" id="layer-subdir">
75
76 <div class="control-group" id="layer-revision-ctrl">
77 <label class="control-label" for="layer-git-ref">Revision
78 <span class="icon-question-sign get-help" title="You can provide a Git branch, a tag or a commit SHA as the revision"></span>
79 </label>
80 <div class="controls">
81 <input type="text" class="span4" id="layer-git-ref" required>
82 <span class="help-inline" style="diaply:none;" id="invalid-layer-revision-hint"></span>
83 </div>
84 </div>
85
86 <label class="project-form" for="layer-description">Layer description
87 <span class="muted">(optional)</span>
88 <span class="icon-question-sign get-help" title="Short description for for the layer" />
89 </label>
90 <input id="layer-description" type="text" class="input-xxlarge" />
91
37 </fieldset> 92 </fieldset>
38 <fieldset class="air"> 93 <fieldset class="air">
39 <legend> 94 <legend>
40 Layer dependencies 95 Layer dependencies
41 <span class="muted">(optional)</span> 96 <span class="muted">(optional)</span>
42 <i class="icon-question-sign get-help heading-help" title="Other layers this layer depends upon"></i> 97 <span class="icon-question-sign get-help heading-help" title="Other layers this layer depends upon" />
43 </legend> 98 </legend>
44 <ul class="unstyled configuration-list"> 99 <ul class="unstyled configuration-list" id="layer-deps-list">
45 <li>
46 <a href="" class="layer-info" title="OpenEmbedded | daisy">openembedded-core (meta)</a>
47 <i class="icon-trash"></i>
48 </li>
49 </ul> 100 </ul>
50 <div class="input-append"> 101 <div class="input-append">
51 <input type="text" autocomplete="off" data-minLength="1" data-autocomplete="off" 102 <input type="text" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead" placeholder="Type a layer name" id="layer-dependency" class="input-xlarge">
52 data-provide="typeahead" data-source=' 103 <a class="btn" type="button" id="add-layer-dependency-btn" disabled>
53 []
54 ' placeholder="Type a layer name" id="layer-dependency" class="input-xlarge">
55 <a class="btn" type="button" id="add-layer-dependency" disabled>
56 Add layer 104 Add layer
57 </a> 105 </a>
58 </div> 106 </div>
59 <span class="help-inline">You can only add layers Toaster knows about</span> 107 <span class="help-inline">You can only add layers Toaster knows about</span>
60 </fieldset> 108 </fieldset>
61 <div class="form-actions"> 109 <div class="form-actions" id="form-actions">
62 <a href="#dependencies-message" class="btn btn-primary btn-large" data-toggle="modal" data-target="#dependencies-message" disabled>Import and add to project</a> 110 <button class="btn btn-primary btn-large" data-toggle="modal" id="import-and-add-btn" data-target="#dependencies-message" disabled>Import and add to project</button>
63 <a href="layer-details-just-imported.html" class="btn btn-large" disabled>Just import for the moment</a> 111 <span class="help-inline" id="import-and-add-hint" style="vertical-align: middle;">To import a layer, you need to enter a repository URL, a branch, tag or commit and a layer name</span>
64 <span class="help-inline" style="vertical-align: middle;">To import a layer, you need to enter a repository URL, a branch, tag or commit and a layer name</span>
65 </div> 112 </div>
66 </form> 113 </form>
67 114
68{% endblock %} 115{% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html b/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html
new file mode 100644
index 0000000000..821bbda296
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/layers_dep_modal.html
@@ -0,0 +1,68 @@
1<!-- 'Layer dependencies modal' -->
2 <div id="dependencies_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true">
3 <form id="dependencies_modal_form">
4 <div class="modal-header">
5 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
6 <h3><span class="layer-name"></span> dependencies</h3>
7 </div>
8 <div class="modal-body">
9 <p><strong class="layer-name"></strong> depends on some layers that are not added to your project. Select the ones you want to add:</p>
10 <ul class="unstyled" id="dependencies_list">
11 </ul>
12 </div>
13 <div class="modal-footer">
14 <button class="btn btn-primary" type="submit">Add layers</button>
15 <button class="btn" type="reset" data-dismiss="modal">Cancel</button>
16 </div>
17 </form>
18 </div>
19
20<script>
21function show_layer_deps_modal(projectId, layer, dependencies, successAdd) {
22 // update layer name
23 $('.layer-name').text(layer.name);
24 var deplistHtml = "";
25 for (var i = 0; i < dependencies.length; i++) {
26 deplistHtml += "<li><label class=\"checkbox\"><input name=\"dependencies\" value=\"";
27 deplistHtml += dependencies[i].id;
28 deplistHtml +="\" type=\"checkbox\" checked=\"checked\"/>";
29 deplistHtml += dependencies[i].name;
30 deplistHtml += "</label></li>";
31 }
32 $('#dependencies_list').html(deplistHtml);
33
34 var selected = [layer.id];
35 var layer_link_list = "<a href='"+layer.url+"'>"+layer.name+"</a>";
36
37 $("#dependencies_modal_form").submit(function (e) {
38 e.preventDefault();
39 $("input[name='dependencies']:checked").map(function () { selected.push(parseInt($(this).val()))});
40 if (selected.length > 1) {
41 tooltipUpdateText = "" + selected.length + " layers added";
42 } else {
43 tooltipUpdateText = "1 layer added";
44 }
45
46 for (var i = 0; i < selected.length; i++) {
47 for (var j = 0; j < dependencies.length; j++) {
48 if (dependencies[j].id == selected[i]) {
49 layer_link_list+= ", <a href='"+dependencies[j].layerdetailurl+"'>"+dependencies[j].name+"</a>"
50 break;
51 }
52 }
53 }
54
55 $('#dependencies_modal').modal('hide');
56
57 var editProjectUrl = "{% url 'xhr_projectedit' project.id %}";
58 libtoaster.editProject(editProjectUrl, projectId, { 'layerAdd': selected.join(",") }, function () {
59 if (successAdd) {
60 successAdd(selected);
61 }
62 }, function () {
63 console.log ("Adding layers to project failed");
64 });
65 });
66 $('#dependencies_modal').modal('show');
67}
68</script>
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index b60f7614af..6e1b0ab913 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -76,6 +76,7 @@ urlpatterns = patterns('toastergui.views',
76 76
77 url(r'^layers/$', 'layers', name='layers'), 77 url(r'^layers/$', 'layers', name='layers'),
78 url(r'^layer/(?P<layerid>\d+)/$', 'layerdetails', name='layerdetails'), 78 url(r'^layer/(?P<layerid>\d+)/$', 'layerdetails', name='layerdetails'),
79 url(r'^layer/$', 'layerdetails', name='layerdetails'),
79 url(r'^targets/$', 'targets', name='targets'), 80 url(r'^targets/$', 'targets', name='targets'),
80 url(r'^machines/$', 'machines', name='machines'), 81 url(r'^machines/$', 'machines', name='machines'),
81 82
@@ -92,6 +93,7 @@ urlpatterns = patterns('toastergui.views',
92 url(r'^xhr_projectedit/(?P<pid>\d+)/$', 'xhr_projectedit', name='xhr_projectedit'), 93 url(r'^xhr_projectedit/(?P<pid>\d+)/$', 'xhr_projectedit', name='xhr_projectedit'),
93 94
94 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'),
95 97
96 98
97 # default redirection 99 # default redirection
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 434e1180b0..ec055d392a 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -30,6 +30,7 @@ from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File
30from orm.models import Target_Installed_Package, Target_File, Target_Image_File, BuildArtifact 30from orm.models import Target_Installed_Package, Target_File, Target_Image_File, BuildArtifact
31from django.views.decorators.cache import cache_control 31from django.views.decorators.cache import cache_control
32from django.core.urlresolvers import reverse 32from django.core.urlresolvers import reverse
33from django.core.exceptions import MultipleObjectsReturned
33from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 34from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
34from django.http import HttpResponseBadRequest, HttpResponseNotFound 35from django.http import HttpResponseBadRequest, HttpResponseNotFound
35from django.utils import timezone 36from django.utils import timezone
@@ -2016,8 +2017,9 @@ if toastermain.settings.MANAGED:
2016 "name" : x.layercommit.layer.name, 2017 "name" : x.layercommit.layer.name,
2017 "giturl": x.layercommit.layer.vcs_url, 2018 "giturl": x.layercommit.layer.vcs_url,
2018 "url": x.layercommit.layer.layer_index_url, 2019 "url": x.layercommit.layer.layer_index_url,
2019 "layerdetailurl": reverse("layerdetails", args=(x.layercommit.layer.pk,)), 2020 "layerdetailurl": reverse("layerdetails", args=(x.layercommit.pk,)),
2020 "branch" : { "name" : x.layercommit.up_branch.name, "layersource" : x.layercommit.up_branch.layer_source.name}}, 2021 # This branch name is actually the release
2022 "branch" : { "name" : x.layercommit.commit, "layersource" : x.layercommit.up_branch.layer_source.name}},
2021 prj.projectlayer_set.all().order_by("id")), 2023 prj.projectlayer_set.all().order_by("id")),
2022 "targets" : map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all()), 2024 "targets" : map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all()),
2023 "freqtargets": freqtargets, 2025 "freqtargets": freqtargets,
@@ -2164,7 +2166,7 @@ if toastermain.settings.MANAGED:
2164 2166
2165 2167
2166 def _lv_to_dict(x): 2168 def _lv_to_dict(x):
2167 return {"id": x.pk, "name": x.layer.name, 2169 return {"id": x.pk, "name": x.layer.name, "tooltip": x.layer.vcs_url+" | "+x.commit,
2168 "detail": "(" + x.layer.vcs_url + (")" if x.up_branch == None else " | "+x.up_branch.name+")"), 2170 "detail": "(" + x.layer.vcs_url + (")" if x.up_branch == None else " | "+x.up_branch.name+")"),
2169 "giturl": x.layer.vcs_url, "layerdetailurl" : reverse('layerdetails', args=(x.pk,))} 2171 "giturl": x.layer.vcs_url, "layerdetailurl" : reverse('layerdetails', args=(x.pk,))}
2170 2172
@@ -2174,8 +2176,9 @@ if toastermain.settings.MANAGED:
2174 # all layers for the current project 2176 # all layers for the current project
2175 queryset_all = prj.compatible_layerversions().filter(layer__name__icontains=request.GET.get('value','')) 2177 queryset_all = prj.compatible_layerversions().filter(layer__name__icontains=request.GET.get('value',''))
2176 2178
2177 # but not layers with equivalent layers already in project 2179 # but not layers with equivalent layers already in project
2178 queryset_all = queryset_all.exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()])[:8] 2180 if not request.GET.has_key('include_added'):
2181 queryset_all = queryset_all.exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()])[:8]
2179 2182
2180 # and show only the selected layers for this project 2183 # and show only the selected layers for this project
2181 final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset_all]) 2184 final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset_all])
@@ -2243,6 +2246,100 @@ if toastermain.settings.MANAGED:
2243 return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") 2246 return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
2244 2247
2245 2248
2249 def xhr_importlayer(request):
2250 if (not request.POST.has_key('vcs_url') or
2251 not request.POST.has_key('name') or
2252 not request.POST.has_key('git_ref') or
2253 not request.POST.has_key('project_id')):
2254 return HttpResponse(jsonfilter({"error": "Missing parameters; requires vcs_url, name, git_ref and project_id"}), content_type = "application/json")
2255
2256 # Rudimentary check for any possible html tags
2257 if "<" in request.POST:
2258 return HttpResponse(jsonfilter({"error": "Invalid character <"}), content_type = "application/json")
2259
2260 prj = Project.objects.get(pk=request.POST['project_id'])
2261
2262 # Strip trailing/leading whitespace from all values
2263 # put into a new dict because POST one is immutable
2264 post_data = dict()
2265 for key,val in request.POST.iteritems():
2266 post_data[key] = val.strip()
2267
2268
2269 # We need to know what release the current project is so that we
2270 # can set the imported layer's up_branch_id
2271 prj_branch_name = Release.objects.get(pk=prj.release_id).branch_name
2272 up_branch, branch_created = Branch.objects.get_or_create(name=prj_branch_name, layer_source_id=LayerSource.TYPE_IMPORTED)
2273
2274 layer_source = LayerSource.objects.get(sourcetype=LayerSource.TYPE_IMPORTED)
2275 try:
2276 layer, layer_created = Layer.objects.get_or_create(name=post_data['name'])
2277 except MultipleObjectsReturned:
2278 return HttpResponse(jsonfilter({"error": "hint-layer-exists"}), content_type = "application/json")
2279
2280 if layer:
2281 if layer_created:
2282 layer.layer_source = layer_source
2283 layer.vcs_url = post_data['vcs_url']
2284 if post_data.has_key('summary'):
2285 layer.summary = layer.description = post_data['summary']
2286
2287 layer.up_date = timezone.now()
2288 layer.save()
2289 else:
2290 # We have an existing layer by this name, let's see if the git
2291 # url is the same, if it is then we can just create a new layer
2292 # version for this layer. Otherwise we need to bail out.
2293 if layer.vcs_url != post_data['vcs_url']:
2294 return HttpResponse(jsonfilter({"error": "hint-layer-exists-with-different-url" , "current_url" : layer.vcs_url, "current_id": layer.id }), content_type = "application/json")
2295
2296
2297 layer_version, version_created = Layer_Version.objects.get_or_create(layer_source=layer_source, layer=layer, project=prj, up_branch_id=up_branch.id,branch=post_data['git_ref'], commit=post_data['git_ref'], dirpath=post_data['dir_path'])
2298
2299 if layer_version:
2300 if not version_created:
2301 return HttpResponse(jsonfilter({"error": "hint-layer-version-exists", "existing_layer_version": layer_version.id }), content_type = "application/json")
2302
2303 layer_version.up_date = timezone.now()
2304 layer_version.save()
2305
2306 # Add the dependencies specified for this new layer
2307 if (post_data.has_key("layer_deps") and
2308 version_created and
2309 len(post_data["layer_deps"]) > 0):
2310 for layer_dep_id in post_data["layer_deps"].split(","):
2311
2312 layer_dep_obj = Layer_Version.objects.get(pk=layer_dep_id)
2313 LayerVersionDependency.objects.get_or_create(layer_version=layer_version, depends_on=layer_dep_obj)
2314 # Now add them to the project, we could get an execption
2315 # if the project now contains the exact
2316 # dependency already (like modified on another page)
2317 try:
2318 ProjectLayer.objects.get_or_create(layercommit=layer_dep_obj, project=prj)
2319 except:
2320 pass
2321
2322
2323 # If an old layer version exists in our project then remove it
2324 for prj_layers in ProjectLayer.objects.filter(project=prj):
2325 dup_layer_v = Layer_Version.objects.filter(id=prj_layers.layercommit_id, layer_id=layer.id)
2326 if len(dup_layer_v) >0 :
2327 prj_layers.delete()
2328
2329 # finally add the imported layer (version id) to the project
2330 ProjectLayer.objects.create(layercommit=layer_version, project=prj,optional=1)
2331
2332 else:
2333 # We didn't create a layer version so back out now and clean up.
2334 if layer_created:
2335 layer.delete()
2336
2337 return HttpResponse(jsonfilter({"error": "Uncaught error: Could not create layer version"}), content_type = "application/json")
2338
2339
2340 return HttpResponse(jsonfilter({"error": "ok"}), content_type = "application/json")
2341
2342
2246 2343
2247 def importlayer(request): 2344 def importlayer(request):
2248 template = "importlayer.html" 2345 template = "importlayer.html"