summaryrefslogtreecommitdiffstats
path: root/bitbake/lib
diff options
context:
space:
mode:
authorAlexandru DAMIAN <alexandru.damian@intel.com>2015-05-19 16:14:29 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-06-12 00:01:47 +0100
commitafe06e313eac61f598f65e622ceb5951a9fabc9a (patch)
tree02e37805b7b10a30c77fc3c82987bf435b221423 /bitbake/lib
parent94aa0d5408f71a2e9fd48fd2c0e283acfc9ac2a2 (diff)
downloadpoky-afe06e313eac61f598f65e622ceb5951a9fabc9a.tar.gz
bitbake: toaster: move project data typeahead to the REST API
This patch enables JSON requests on the project REST endpoint, and replaces the universal queries "xhr_datatypeahead" with the `project` type to the REST project endpoint. The patch adds a decorator that takes a context returned by a view and either renders the template specified as the decorator argument, or converts the context to JSON. Normal "search", "filter" and "order" options for view work as normal on the JSON API format. To enable the JSON return, set the "format" GET parameter to "json". In order to demonstrate the functionality, the "New build" button is switched from using the xhr_datatypeahead to the project REST API with JSON formatting. Additionally, the XHR APIs that perform actions with the project id passed as parameter are removed, and the needed URLs are populated from the project JSON API returns after the project has been selected. (Bitbake rev: 15a2274eba13d19b864f337057d61c75ff7849cc) Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/base.js51
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/importlayer.js6
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/layerdetails.js2
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/libtoaster.js12
-rw-r--r--bitbake/lib/toaster/toastergui/templates/base.html24
-rw-r--r--bitbake/lib/toaster/toastergui/templates/basetable_bottom.html2
-rw-r--r--bitbake/lib/toaster/toastergui/templates/detail_pagination_bottom.html2
-rw-r--r--bitbake/lib/toaster/toastergui/templatetags/projecttags.py4
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py3
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py89
10 files changed, 119 insertions, 76 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/base.js b/bitbake/lib/toaster/toastergui/static/js/base.js
index ccc23e0e60..9424b6c328 100644
--- a/bitbake/lib/toaster/toastergui/static/js/base.js
+++ b/bitbake/lib/toaster/toastergui/static/js/base.js
@@ -15,20 +15,21 @@ function basePageInit (ctx) {
15 15
16 /* Hide the change project icon when there is only one project */ 16 /* Hide the change project icon when there is only one project */
17 if (ctx.numProjects == 1){ 17 if (ctx.numProjects == 1){
18 $('#project .icon-pencil').hide(); 18 $('#project .icon-pencil').hide();
19 } 19 }
20 20
21 newBuildButton.show().removeAttr("disabled"); 21 newBuildButton.show().removeAttr("disabled");
22 22
23
23 _checkProjectBuildable() 24 _checkProjectBuildable()
24 _setupNewBuildButton(); 25 _setupNewBuildButton();
25 26
26 27
27 function _checkProjectBuildable(){ 28 function _checkProjectBuildable(){
28 if (currentProjectId == undefined) 29 if (libtoaster.ctx.projectId == undefined)
29 return; 30 return;
30 31
31 libtoaster.getProjectInfo(ctx.projectInfoUrl, currentProjectId, 32 libtoaster.getProjectInfo(ctx.projectInfoUrl, libtoaster.ctx.projectId,
32 function(data){ 33 function(data){
33 if (data.machine.name == undefined || data.layers.length == 0) { 34 if (data.machine.name == undefined || data.layers.length == 0) {
34 /* we can't build anything with out a machine and some layers */ 35 /* we can't build anything with out a machine and some layers */
@@ -53,18 +54,13 @@ function basePageInit (ctx) {
53 /* If we don't have a current project then present the set project 54 /* If we don't have a current project then present the set project
54 * form. 55 * form.
55 */ 56 */
56 if (currentProjectId == undefined) { 57 if (libtoaster.ctx.projectId == undefined) {
57 $('#change-project-form').show(); 58 $('#change-project-form').show();
58 $('#project .icon-pencil').hide(); 59 $('#project .icon-pencil').hide();
59 } 60 }
60 61
61 libtoaster.makeTypeahead(newBuildTargetInput, { type : "targets", project_id: currentProjectId }, function(item){
62 /* successfully selected a target */
63 selectedTarget = item;
64 });
65 62
66 63 libtoaster.makeTypeahead(newBuildProjectInput, libtoaster.ctx.projectsUrl, { format : "json" }, function(item){
67 libtoaster.makeTypeahead(newBuildProjectInput, { type : "projects" }, function(item){
68 /* successfully selected a project */ 64 /* successfully selected a project */
69 newBuildProjectSaveBtn.removeAttr("disabled"); 65 newBuildProjectSaveBtn.removeAttr("disabled");
70 selectedProject = item; 66 selectedProject = item;
@@ -93,20 +89,40 @@ function basePageInit (ctx) {
93 if (!selectedTarget) 89 if (!selectedTarget)
94 selectedTarget = { name: newBuildTargetInput.val() }; 90 selectedTarget = { name: newBuildTargetInput.val() };
95 /* fire and forget */ 91 /* fire and forget */
96 libtoaster.startABuild(ctx.projectBuildUrl, currentProjectId, selectedTarget.name, null, null); 92 libtoaster.startABuild(ctx.projectBuildUrl, libtoaster.ctx.projectId, selectedTarget.name, null, null);
97 window.location.replace(ctx.projectBasePageUrl+currentProjectId); 93 window.location.replace(libtoaster.ctx.projectPageUrl);
98 }); 94 });
99 95
100 newBuildProjectSaveBtn.click(function() { 96 newBuildProjectSaveBtn.click(function() {
101 currentProjectId = selectedProject.id 97 libtoaster.ctx.projectId = selectedProject.pk
102 /* Update the typeahead project_id paramater */ 98 /* Update the typeahead project_id paramater */
103 _checkProjectBuildable(); 99 _checkProjectBuildable();
104 newBuildTargetInput.data('typeahead').options.xhrParams.project_id = currentProjectId;
105 newBuildTargetInput.val("");
106 100
107 $("#new-build-button #project a").text(selectedProject.name).attr('href', ctx.projectBasePageUrl+currentProjectId); 101 /* we set the effective context of the page to the currently selected project */
108 $("#new-build-button .alert a").attr('href', ctx.projectBasePageUrl+currentProjectId); 102 /* TBD: do we override even if we already have a context project ?? */
103 /* TODO: replace global library context with references to the "selected" project */
104 libtoaster.ctx.projectPageUrl = selectedProject.projectPageUrl;
105 libtoaster.ctx.xhrProjectEditUrl = selectedProject.xhrProjectEditUrl;
106 libtoaster.ctx.projectName = selectedProject.name;
107 libtoaster.ctx.projectId = selectedProject.id;
108
109 ctx.projectBuildUrl = selectedProject.projectBuildUrl;
110
111 /* we can create a target typeahead only after we have a project selected */
112 newBuildTargetInput.prop("disabled", false);
113 newBuildTargetBuildBtn.prop("disabled", false);
114
115 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.xhrProjectDataTypeaheadUrl, { type : "targets" }, function(item){
116 /* successfully selected a target */
117 selectedTarget = item;
118 });
119
120 newBuildTargetInput.val("");
109 121
122 /* set up new form aspect */
123 $("#new-build-button #project a").text(selectedProject.name).attr('href', libtoaster.ctx.projectPageUrl);
124 $("#new-build-button .alert a").attr('href', libtoaster.ctx.projectPageUrl);
125 $("#project .icon-pencil").show();
110 126
111 $("#change-project-form").slideUp({ 'complete' : function() { 127 $("#change-project-form").slideUp({ 'complete' : function() {
112 $("#new-build-button #project").show(); 128 $("#new-build-button #project").show();
@@ -116,6 +132,7 @@ function basePageInit (ctx) {
116 $('#new-build-button #project .icon-pencil').click(function() { 132 $('#new-build-button #project .icon-pencil').click(function() {
117 newBuildProjectSaveBtn.attr("disabled", "disabled"); 133 newBuildProjectSaveBtn.attr("disabled", "disabled");
118 newBuildProjectInput.val($("#new-build-button #project a").text()); 134 newBuildProjectInput.val($("#new-build-button #project a").text());
135 $("#cancel-change-project").show();
119 $(this).parent().hide(); 136 $(this).parent().hide();
120 $("#change-project-form").slideDown(); 137 $("#change-project-form").slideDown();
121 }); 138 });
diff --git a/bitbake/lib/toaster/toastergui/static/js/importlayer.js b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
index 875cc342b8..beb2ede3dc 100644
--- a/bitbake/lib/toaster/toastergui/static/js/importlayer.js
+++ b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
@@ -18,7 +18,7 @@ function importLayerPageInit (ctx) {
18 18
19 $("#new-project-button").hide(); 19 $("#new-project-button").hide();
20 20
21 libtoaster.makeTypeahead(layerDepInput, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" }, function(item){ 21 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.xhrProjectDataTypeaheadUrl, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" }, function(item){
22 currentLayerDepSelection = item; 22 currentLayerDepSelection = item;
23 23
24 layerDepBtn.removeAttr("disabled"); 24 layerDepBtn.removeAttr("disabled");
@@ -28,7 +28,7 @@ function importLayerPageInit (ctx) {
28 /* We automatically add "openembedded-core" layer for convenience as a 28 /* We automatically add "openembedded-core" layer for convenience as a
29 * dependency as pretty much all layers depend on this one 29 * dependency as pretty much all layers depend on this one
30 */ 30 */
31 $.getJSON(libtoaster.ctx.xhrDataTypeaheadUrl, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" , value: "openembedded-core" }, function(layer) { 31 $.getJSON(libtoaster.ctx.xhrProjectDataTypeaheadUrl, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" , value: "openembedded-core" }, function(layer) {
32 if (layer.list.length == 1) { 32 if (layer.list.length == 1) {
33 currentLayerDepSelection = layer.list[0]; 33 currentLayerDepSelection = layer.list[0];
34 layerDepBtn.click(); 34 layerDepBtn.click();
@@ -211,7 +211,7 @@ function importLayerPageInit (ctx) {
211 var name = $(this).val(); 211 var name = $(this).val();
212 212
213 /* Check if the layer name exists */ 213 /* Check if the layer name exists */
214 $.getJSON(libtoaster.ctx.xhrDataTypeaheadUrl, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" , value: name }, function(layer) { 214 $.getJSON(libtoaster.ctx.xhrProjectDataTypeaheadUrl, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" , value: name }, function(layer) {
215 if (layer.list.length > 0) { 215 if (layer.list.length > 0) {
216 for (var i in layer.list){ 216 for (var i in layer.list){
217 if (layer.list[i].name == name) { 217 if (layer.list[i].name == name) {
diff --git a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
index 3746ea26e3..8e14b8f277 100644
--- a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
+++ b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
@@ -9,7 +9,7 @@ function layerDetailsPageInit (ctx) {
9 var addRmLayerBtn = $("#add-remove-layer-btn"); 9 var addRmLayerBtn = $("#add-remove-layer-btn");
10 10
11 /* setup the dependencies typeahead */ 11 /* setup the dependencies typeahead */
12 libtoaster.makeTypeahead(layerDepInput, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" }, function(item){ 12 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.xhrProjectDataTypeaheadUrl, { type : "layers", project_id: libtoaster.ctx.projectId, include_added: "true" }, function(item){
13 currentLayerDepSelection = item; 13 currentLayerDepSelection = item;
14 14
15 layerDepBtn.removeAttr("disabled"); 15 layerDepBtn.removeAttr("disabled");
diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
index 99e1f03095..72fb0a93f5 100644
--- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
+++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
@@ -10,16 +10,16 @@ var libtoaster = (function (){
10 * xhrUrl: the url to get the JSON from expects JSON in the form: 10 * xhrUrl: the url to get the JSON from expects JSON in the form:
11 * { "list": [ { "name": "test", "detail" : "a test thing" }, .... ] } 11 * { "list": [ { "name": "test", "detail" : "a test thing" }, .... ] }
12 * xhrParams: the data/parameters to pass to the getJSON url e.g. 12 * xhrParams: the data/parameters to pass to the getJSON url e.g.
13 * { 'type' : 'projects' } the text typed will be passed as 'value'. 13 * { 'type' : 'projects' } the text typed will be passed as 'search'.
14 * selectedCB: function to call once an item has been selected one 14 * selectedCB: function to call once an item has been selected one
15 * arg of the item. 15 * arg of the item.
16 */ 16 */
17 function _makeTypeahead (jQElement, xhrParams, selectedCB) { 17 function _makeTypeahead (jQElement, xhrUrl, xhrParams, selectedCB) {
18 18
19 jQElement.typeahead({ 19 jQElement.typeahead({
20 source: function(query, process){ 20 source: function(query, process){
21 xhrParams.value = query; 21 xhrParams.search = query;
22 $.getJSON(libtoaster.ctx.xhrDataTypeaheadUrl, this.options.xhrParams, function(data){ 22 $.getJSON(xhrUrl, this.options.xhrParams, function(data){
23 if (data.error !== "ok") { 23 if (data.error !== "ok") {
24 console.log("Error getting data from server "+data.error); 24 console.log("Error getting data from server "+data.error);
25 return; 25 return;
@@ -41,7 +41,7 @@ var libtoaster = (function (){
41 return $('<span></span>').text(item.name).get(0); 41 return $('<span></span>').text(item.name).get(0);
42 }, 42 },
43 sorter: function (items) { return items; }, 43 sorter: function (items) { return items; },
44 xhrUrl: libtoaster.ctx.xhrDataTypeaheadUrl, 44 xhrUrl: xhrUrl,
45 xhrParams: xhrParams, 45 xhrParams: xhrParams,
46 }); 46 });
47 47
@@ -172,7 +172,7 @@ var libtoaster = (function (){
172 172
173 function _getLayerDepsForProject(projectId, layerId, onSuccess, onFail){ 173 function _getLayerDepsForProject(projectId, layerId, onSuccess, onFail){
174 /* Check for dependencies not in the current project */ 174 /* Check for dependencies not in the current project */
175 $.getJSON(libtoaster.ctx.xhrDataTypeaheadUrl, 175 $.getJSON(libtoaster.ctx.xhrProjectDataTypeaheadUrl,
176 { type: 'layerdeps', 'value': layerId , project_id: projectId }, 176 { type: 'layerdeps', 'value': layerId , project_id: projectId },
177 function(data) { 177 function(data) {
178 if (data.error != "ok") { 178 if (data.error != "ok") {
diff --git a/bitbake/lib/toaster/toastergui/templates/base.html b/bitbake/lib/toaster/toastergui/templates/base.html
index 230dee49d6..e10dc11673 100644
--- a/bitbake/lib/toaster/toastergui/templates/base.html
+++ b/bitbake/lib/toaster/toastergui/templates/base.html
@@ -31,11 +31,19 @@
31 projectId : {{project.id|default:'undefined'}}, 31 projectId : {{project.id|default:'undefined'}},
32 jsUrl : "{% static 'js/' %}", 32 jsUrl : "{% static 'js/' %}",
33 htmlUrl : "{% static 'html/' %}", 33 htmlUrl : "{% static 'html/' %}",
34 projectsUrl : "{% url 'all-projects' %}",
34 {% if project.id %} 35 {% if project.id %}
35 xhrDataTypeaheadUrl : "{% url 'xhr_datatypeahead' project.id %}", 36 xhrProjectDataTypeaheadUrl : "{% url 'xhr_datatypeahead' project.id %}",
36 xhrProjectEditUrl : "{% url 'xhr_projectedit' project.id %}", 37 xhrProjectEditUrl : "{% url 'xhr_projectedit' project.id %}",
37 projectPageUrl : "{% url 'project' project.id %}", 38 projectPageUrl : "{% url 'project' project.id %}",
38 projectName : "{{project.name}}", 39 projectName : "{{project.name}}",
40 projectId : {{project.id}},
41 {% else %}
42 xhrProjectDataTypeaheadUrl : undefined,
43 xhrProjectEditUrl : undefined,
44 projectPageUrl : undefined,
45 projectName : undefined,
46 projectId : undefined,
39 {% endif %} 47 {% endif %}
40 }; 48 };
41</script> 49</script>
@@ -45,8 +53,6 @@
45 $(document).ready(function () { 53 $(document).ready(function () {
46 /* Vars needed for base.js */ 54 /* Vars needed for base.js */
47 var ctx = {}; 55 var ctx = {};
48 ctx.projectBuildUrl = "{% url 'xhr_build' %}";
49 ctx.projectBasePageUrl = "{% url 'base_project' %}";
50 ctx.projectInfoUrl = "{% url 'xhr_projectinfo' %}"; 56 ctx.projectInfoUrl = "{% url 'xhr_projectinfo' %}";
51 ctx.numProjects = {{projects|length}}; 57 ctx.numProjects = {{projects|length}};
52 ctx.currentUrl = "{{request.path|escapejs}}"; 58 ctx.currentUrl = "{{request.path|escapejs}}";
@@ -99,7 +105,7 @@
99 <div class="input-append"> 105 <div class="input-append">
100 <input type="text" class="input-medium" id="project-name-input" placeholder="Type a project name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead"/> 106 <input type="text" class="input-medium" id="project-name-input" placeholder="Type a project name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead"/>
101 <button id="save-project-button" class="btn" type="button">Save</button> 107 <button id="save-project-button" class="btn" type="button">Save</button>
102 <a href="#" id="cancel-change-project" class="btn btn-link">Cancel</a> 108 <a href="#" id="cancel-change-project" class="btn btn-link" style="display: none">Cancel</a>
103 </div> 109 </div>
104 <p><a id="view-all-projects" href="{% url 'all-projects' %}">View all projects</a></p> 110 <p><a id="view-all-projects" href="{% url 'all-projects' %}">View all projects</a></p>
105 </form> 111 </form>
@@ -111,9 +117,9 @@
111 <li id="targets-form"> 117 <li id="targets-form">
112 <h6>Recipe(s):</h6> 118 <h6>Recipe(s):</h6>
113 <form> 119 <form>
114 <input type="text" class="input-xlarge" id="build-target-input" placeholder="Type a recipe name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead" /> 120 <input type="text" class="input-xlarge" id="build-target-input" placeholder="Type a recipe name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead" disabled/>
115 <div> 121 <div>
116 <a class="btn btn-primary" id="build-button" data-project-id="{{project.id}}">Build</a> 122 <button class="btn btn-primary" id="build-button" data-project-id="{{project.id}}" disabled>Build</button>
117 </div> 123 </div>
118 </form> 124 </form>
119 </li> 125 </li>
diff --git a/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html b/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html
index 4c28cae810..ce023f51af 100644
--- a/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html
+++ b/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html
@@ -57,7 +57,7 @@
57 } 57 }
58 } 58 }
59 59
60 // load cookie for number of entries to be displayed on page 60 // load data for number of entries to be displayed on page
61 if ({{request.GET.count}} != "") { 61 if ({{request.GET.count}} != "") {
62 pagesize = {{request.GET.count}}; 62 pagesize = {{request.GET.count}};
63 } 63 }
diff --git a/bitbake/lib/toaster/toastergui/templates/detail_pagination_bottom.html b/bitbake/lib/toaster/toastergui/templates/detail_pagination_bottom.html
index 434facba93..f40c21d99f 100644
--- a/bitbake/lib/toaster/toastergui/templates/detail_pagination_bottom.html
+++ b/bitbake/lib/toaster/toastergui/templates/detail_pagination_bottom.html
@@ -38,7 +38,7 @@
38<!-- Update page display settings --> 38<!-- Update page display settings -->
39<script> 39<script>
40 $(document).ready(function() { 40 $(document).ready(function() {
41 // load cookie for number of entries to be displayed on page 41 // load data for number of entries to be displayed on page
42 if ({{request.GET.count}} != "") { 42 if ({{request.GET.count}} != "") {
43 pagesize = {{request.GET.count}}; 43 pagesize = {{request.GET.count}};
44 } 44 }
diff --git a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
index 8028ae0022..e79a4e5848 100644
--- a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
@@ -51,11 +51,11 @@ def get_tasks(queryset):
51 51
52 52
53@register.filter(name = "json") 53@register.filter(name = "json")
54def json(value): 54def json(value, default = None):
55 # JSON spec says that "\/" is functionally identical to "/" to allow for HTML-tag embedding in JSON strings 55 # JSON spec says that "\/" is functionally identical to "/" to allow for HTML-tag embedding in JSON strings
56 # unfortunately, I can't find any option in the json module to turn on forward-slash escaping, so we do 56 # unfortunately, I can't find any option in the json module to turn on forward-slash escaping, so we do
57 # it manually here 57 # it manually here
58 return mark_safe(JsonLib.dumps(value, ensure_ascii=False).replace('</', '<\\/')) 58 return mark_safe(JsonLib.dumps(value, default = default, ensure_ascii=False).replace('</', '<\\/'))
59 59
60@register.assignment_tag 60@register.assignment_tag
61def query(qs, **kwargs): 61def query(qs, **kwargs):
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 7a1132febc..4e328dae17 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -77,8 +77,6 @@ urlpatterns = patterns('toastergui.views',
77 77
78 url(r'^projects/$', 'projects', name='all-projects'), 78 url(r'^projects/$', 'projects', name='all-projects'),
79 79
80 url(r'^project/$', lambda x: HttpResponseBadRequest(), name='base_project'),
81
82 url(r'^project/(?P<pid>\d+)/$', 'project', name='project'), 80 url(r'^project/(?P<pid>\d+)/$', 'project', name='project'),
83 url(r'^project/(?P<pid>\d+)/configuration$', 'projectconf', name='projectconf'), 81 url(r'^project/(?P<pid>\d+)/configuration$', 'projectconf', name='projectconf'),
84 url(r'^project/(?P<pid>\d+)/builds/$', 'projectbuilds', name='projectbuilds'), 82 url(r'^project/(?P<pid>\d+)/builds/$', 'projectbuilds', name='projectbuilds'),
@@ -110,7 +108,6 @@ urlpatterns = patterns('toastergui.views',
110 name="all-layers"), 108 name="all-layers"),
111 109
112 110
113 url(r'^xhr_build/$', 'xhr_build', name='xhr_build'),
114 url(r'^xhr_projectbuild/(?P<pid>\d+)$', 'xhr_projectbuild', name='xhr_projectbuild'), 111 url(r'^xhr_projectbuild/(?P<pid>\d+)$', 'xhr_projectbuild', name='xhr_projectbuild'),
115 url(r'^xhr_projectinfo/$', 'xhr_projectinfo', name='xhr_projectinfo'), 112 url(r'^xhr_projectinfo/$', 'xhr_projectinfo', name='xhr_projectinfo'),
116 url(r'^xhr_projectedit/(?P<pid>\d+)$', 'xhr_projectedit', name='xhr_projectedit'), 113 url(r'^xhr_projectedit/(?P<pid>\d+)$', 'xhr_projectedit', name='xhr_projectedit'),
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 0e248a7435..d4a9b4ca0d 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -129,7 +129,19 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs):
129 if not i in params: 129 if not i in params:
130 params[i] = urllib.unquote(str(mandatory_parameters[i])) 130 params[i] = urllib.unquote(str(mandatory_parameters[i]))
131 131
132 return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs) 132 return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, *args, **kwargs)
133
134class RedirectException(Exception):
135 def __init__(self, view, g, mandatory_parameters, *args, **kwargs):
136 super(RedirectException, self).__init__()
137 self.view = view
138 self.g = g
139 self.mandatory_parameters = mandatory_parameters
140 self.oargs = args
141 self.okwargs = kwargs
142
143 def get_redirect_response(self):
144 return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, self.okwargs)
133 145
134FIELD_SEPARATOR = ":" 146FIELD_SEPARATOR = ":"
135AND_VALUE_SEPARATOR = "!" 147AND_VALUE_SEPARATOR = "!"
@@ -2200,14 +2212,6 @@ if toastermain.settings.MANAGED:
2200 response['Pragma'] = "no-cache" 2212 response['Pragma'] = "no-cache"
2201 return response 2213 return response
2202 2214
2203 # This is a wrapper for xhr_projectbuild which allows for a project id
2204 # which only becomes known client side.
2205 def xhr_build(request):
2206 if request.POST.has_key("project_id"):
2207 pid = request.POST['project_id']
2208 return xhr_projectbuild(request, pid)
2209 else:
2210 raise BadParameterException("invalid project id")
2211 2215
2212 def xhr_projectbuild(request, pid): 2216 def xhr_projectbuild(request, pid):
2213 try: 2217 try:
@@ -2333,7 +2337,7 @@ if toastermain.settings.MANAGED:
2333 # returns layers for current project release that are not in the project set, matching the name 2337 # returns layers for current project release that are not in the project set, matching the name
2334 if request.GET['type'] == "layers": 2338 if request.GET['type'] == "layers":
2335 # all layers for the current project 2339 # all layers for the current project
2336 queryset_all = prj.compatible_layerversions().filter(layer__name__icontains=request.GET.get('value','')) 2340 queryset_all = prj.compatible_layerversions().filter(layer__name__icontains=request.GET.get('search',''))
2337 2341
2338 # but not layers with equivalent layers already in project 2342 # but not layers with equivalent layers already in project
2339 if not request.GET.has_key('include_added'): 2343 if not request.GET.has_key('include_added'):
@@ -2348,7 +2352,7 @@ if toastermain.settings.MANAGED:
2348 # returns layer dependencies for a layer, excluding current project layers 2352 # returns layer dependencies for a layer, excluding current project layers
2349 if request.GET['type'] == "layerdeps": 2353 if request.GET['type'] == "layerdeps":
2350 queryset = prj.compatible_layerversions().exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]).filter( 2354 queryset = prj.compatible_layerversions().exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]).filter(
2351 layer__name__in = [ x.depends_on.layer.name for x in LayerVersionDependency.objects.filter(layer_version_id = request.GET['value'])]) 2355 layer__name__in = [ x.depends_on.layer.name for x in LayerVersionDependency.objects.filter(layer_version_id = request.GET['search'])])
2352 2356
2353 final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset]) 2357 final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset])
2354 2358
@@ -2361,7 +2365,7 @@ if toastermain.settings.MANAGED:
2361 2365
2362 retval = [] 2366 retval = []
2363 for i in prj.projectlayer_set.all(): 2367 for i in prj.projectlayer_set.all():
2364 lv = prj.compatible_layerversions(release = Release.objects.get(pk=request.GET['value'])).filter(layer__name = i.layercommit.layer.name) 2368 lv = prj.compatible_layerversions(release = Release.objects.get(pk=request.GET['search'])).filter(layer__name = i.layercommit.layer.name)
2365 # there is no layer_version with the new release id, and the same name 2369 # there is no layer_version with the new release id, and the same name
2366 if lv.count() < 1: 2370 if lv.count() < 1:
2367 retval.append(i) 2371 retval.append(i)
@@ -2374,10 +2378,10 @@ if toastermain.settings.MANAGED:
2374 # returns layer versions that provide the named targets 2378 # returns layer versions that provide the named targets
2375 if request.GET['type'] == "layers4target": 2379 if request.GET['type'] == "layers4target":
2376 # we return data only if the recipe can't be provided by the current project layer set 2380 # we return data only if the recipe can't be provided by the current project layer set
2377 if reduce(lambda x, y: x + y, [x.recipe_layer_version.filter(name=request.GET['value']).count() for x in prj.projectlayer_equivalent_set()], 0): 2381 if reduce(lambda x, y: x + y, [x.recipe_layer_version.filter(name=request.GET['search']).count() for x in prj.projectlayer_equivalent_set()], 0):
2378 final_list = [] 2382 final_list = []
2379 else: 2383 else:
2380 queryset_all = prj.compatible_layerversions().filter(recipe_layer_version__name = request.GET['value']) 2384 queryset_all = prj.compatible_layerversions().filter(recipe_layer_version__name = request.GET['search'])
2381 2385
2382 # exclude layers in the project 2386 # exclude layers in the project
2383 queryset_all = queryset_all.exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]) 2387 queryset_all = queryset_all.exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()])
@@ -2389,7 +2393,7 @@ if toastermain.settings.MANAGED:
2389 2393
2390 # returns targets provided by current project layers 2394 # returns targets provided by current project layers
2391 if request.GET['type'] == "targets": 2395 if request.GET['type'] == "targets":
2392 search_token = request.GET.get('value','') 2396 search_token = request.GET.get('search','')
2393 queryset_all = Recipe.objects.filter(layer_version__layer__name__in = [x.layercommit.layer.name for x in prj.projectlayer_set.all().select_related("layercommit__layer")]).filter(Q(name__icontains=search_token) | Q(layer_version__layer__name__icontains=search_token)) 2397 queryset_all = Recipe.objects.filter(layer_version__layer__name__in = [x.layercommit.layer.name for x in prj.projectlayer_set.all().select_related("layercommit__layer")]).filter(Q(name__icontains=search_token) | Q(layer_version__layer__name__icontains=search_token))
2394 2398
2395# layer_equivalent_set = [] 2399# layer_equivalent_set = []
@@ -2420,7 +2424,7 @@ if toastermain.settings.MANAGED:
2420 if 'project_id' in request.session: 2424 if 'project_id' in request.session:
2421 queryset_all = queryset_all.filter(layer_version__in = prj.projectlayer_equivalent_set()).order_by("name") 2425 queryset_all = queryset_all.filter(layer_version__in = prj.projectlayer_equivalent_set()).order_by("name")
2422 2426
2423 search_token = request.GET.get('value','') 2427 search_token = request.GET.get('search','')
2424 queryset_all = queryset_all.filter(Q(name__icontains=search_token) | Q(description__icontains=search_token)) 2428 queryset_all = queryset_all.filter(Q(name__icontains=search_token) | Q(description__icontains=search_token))
2425 2429
2426 return HttpResponse(jsonfilter({ "error":"ok", 2430 return HttpResponse(jsonfilter({ "error":"ok",
@@ -2432,15 +2436,6 @@ if toastermain.settings.MANAGED:
2432 ) 2436 )
2433 }), content_type = "application/json") 2437 }), content_type = "application/json")
2434 2438
2435 # returns all projects
2436 if request.GET['type'] == "projects":
2437 queryset_all = Project.objects.all()
2438 ret = { "error": "ok",
2439 "list": map (lambda x: {"id":x.pk, "name": x.name},
2440 queryset_all.filter(name__icontains=request.GET.get('value',''))[:8])}
2441
2442 return HttpResponse(jsonfilter(ret), content_type = "application/json")
2443
2444 raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied")) 2439 raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied"))
2445 except Exception as e: 2440 except Exception as e:
2446 return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") 2441 return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
@@ -2866,14 +2861,38 @@ if toastermain.settings.MANAGED:
2866 return build_mru 2861 return build_mru
2867 2862
2868 2863
2869 def projects(request): 2864 def template_renderer(template):
2870 template="projects.html" 2865 def func_wrapper(view):
2866 def returned_wrapper(request, *args, **kwargs):
2867 try:
2868 context = view(request, *args, **kwargs)
2869 except RedirectException as e:
2870 return e.get_redirect_response()
2871
2872 if request.GET.get('format', None) == 'json':
2873 # objects is a special keyword - it's a Page, but we need the actual objects here
2874 # in XHR, the objects come in the "list" property
2875 if "objects" in context:
2876 context["list"] = context["objects"].object_list
2877 del context["objects"]
2878
2879 # we're about to return; to keep up with the XHR API, we set the error to OK
2880 context["error"] = "ok"
2881
2882 return HttpResponse(jsonfilter(context, default=lambda obj: obj.isoformat() if isinstance(obj, datetime) else obj.__dict__ ),
2883 content_type = "application/json; charset=utf-8")
2884 else:
2885 return render(request, template, context)
2886 return returned_wrapper
2887 return func_wrapper
2871 2888
2889 @template_renderer("projects.html")
2890 def projects(request):
2872 (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') 2891 (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-')
2873 mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } 2892 mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }
2874 retval = _verify_parameters( request.GET, mandatory_parameters ) 2893 retval = _verify_parameters( request.GET, mandatory_parameters )
2875 if retval: 2894 if retval:
2876 return _redirect_parameters( 'all-projects', request.GET, mandatory_parameters) 2895 raise RedirectException( 'all-projects', request.GET, mandatory_parameters )
2877 2896
2878 queryset_all = Project.objects.all() 2897 queryset_all = Project.objects.all()
2879 2898
@@ -2886,6 +2905,14 @@ if toastermain.settings.MANAGED:
2886 # retrieve the objects that will be displayed in the table; projects a paginator and gets a page range to display 2905 # retrieve the objects that will be displayed in the table; projects a paginator and gets a page range to display
2887 project_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) 2906 project_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1))
2888 2907
2908 # add fields needed in JSON dumps for API call support
2909 for p in project_info.object_list:
2910 p.id = p.pk
2911 p.xhrProjectDataTypeaheadUrl = reverse('xhr_datatypeahead', args=(p.id,))
2912 p.projectPageUrl = reverse('project', args=(p.id,))
2913 p.xhrProjectEditUrl = reverse('xhr_projectedit', args=(p.id,))
2914 p.projectBuildUrl = reverse('xhr_projectbuild', args=(p.id,))
2915
2889 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) 2916 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
2890 build_mru = _managed_get_latest_builds() 2917 build_mru = _managed_get_latest_builds()
2891 2918
@@ -2965,9 +2992,8 @@ if toastermain.settings.MANAGED:
2965 ] 2992 ]
2966 } 2993 }
2967 2994
2968 response = render(request, template, context)
2969 _set_parameters_values(pagesize, orderby, request) 2995 _set_parameters_values(pagesize, orderby, request)
2970 return response 2996 return context
2971 2997
2972 def buildrequestdetails(request, pid, brid): 2998 def buildrequestdetails(request, pid, brid):
2973 template = "buildrequestdetails.html" 2999 template = "buildrequestdetails.html"
@@ -3185,9 +3211,6 @@ else:
3185 def xhr_projectbuild(request, pid): 3211 def xhr_projectbuild(request, pid):
3186 return render(request, 'landing_not_managed.html') 3212 return render(request, 'landing_not_managed.html')
3187 3213
3188 def xhr_build(request):
3189 return render(request, 'landing_not_managed.html')
3190
3191 def xhr_projectinfo(request): 3214 def xhr_projectinfo(request):
3192 return render(request, 'landing_not_managed.html') 3215 return render(request, 'landing_not_managed.html')
3193 3216