summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2015-09-28 21:45:24 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-09-29 14:11:37 +0100
commitd98c771a9aa047a71a30b570aba544c043d05447 (patch)
tree2fdcddd23547a1a6cea72cb9ed7e576f4d8a5c5e /bitbake/lib/toaster
parent37948cc5d0e9274a2ca0ec70a5b4788fa26e55e5 (diff)
downloadpoky-d98c771a9aa047a71a30b570aba544c043d05447.tar.gz
bitbake: toaster: Add Image customisation frontend feature
Add the Image customisation front end feature to Toaster. Caveat - This feature is currently in development and should not be enabled by default. (Bitbake rev: 543586462b66434741f47f2884b4ccdeda5397b5) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: brian avery <avery.brian@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/customrecipe.js50
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/layerBtn.js13
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/newcustomimage.js49
-rw-r--r--bitbake/lib/toaster/toastergui/templates/baseprojectpage.html7
-rw-r--r--bitbake/lib/toaster/toastergui/templates/customise_btn.html9
-rw-r--r--bitbake/lib/toaster/toastergui/templates/customrecipe.html142
-rw-r--r--bitbake/lib/toaster/toastergui/templates/newcustomimage.html54
-rw-r--r--bitbake/lib/toaster/toastergui/templates/pkg_add_rm_btn.html16
-rw-r--r--bitbake/lib/toaster/toastergui/templates/project.html2
-rw-r--r--bitbake/lib/toaster/toastergui/templates/projecttopbar.html9
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py17
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py9
12 files changed, 368 insertions, 9 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/customrecipe.js b/bitbake/lib/toaster/toastergui/static/js/customrecipe.js
new file mode 100644
index 0000000000..4f6b304dd6
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/customrecipe.js
@@ -0,0 +1,50 @@
1"use strict";
2
3function customRecipePageInit(ctx) {
4
5 var urlParams = libtoaster.parseUrlParams();
6
7 (function notificationRequest(){
8 if (urlParams.hasOwnProperty('notify') && urlParams.notify === 'new'){
9 $("#image-created-notification").show();
10 }
11 })();
12
13 $("#recipeselection").on('table-done', function(e, total, tableParams){
14 /* Table is done so now setup the click handler for the package buttons */
15 $(".add-rm-package-btn").click(function(e){
16 e.preventDefault();
17 addRemovePackage($(this), tableParams);
18 });
19 });
20
21 function addRemovePackage(pkgBtn, tableParams){
22 var pkgBtnData = pkgBtn.data();
23 var method;
24 var buttonToShow;
25
26 if (pkgBtnData.directive == 'add') {
27 method = 'PUT';
28 buttonToShow = '#package-rm-btn-' + pkgBtnData.package;
29 } else if (pkgBtnData.directive == 'remove') {
30 method = 'DELETE';
31 buttonToShow = '#package-add-btn-' + pkgBtnData.package;
32 } else {
33 throw("Unknown package directive: should be add or remove");
34 }
35
36 $.ajax({
37 type: method,
38 url: pkgBtnData.packageUrl,
39 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
40 success: function(data){
41 /* Invalidate the Add | Rm package table's current cache */
42 tableParams.nocache = true;
43 $.get(ctx.tableApiUrl, tableParams);
44 /* Swap the buttons around */
45 pkgBtn.hide();
46 $(buttonToShow).show();
47 }
48 });
49 }
50}
diff --git a/bitbake/lib/toaster/toastergui/static/js/layerBtn.js b/bitbake/lib/toaster/toastergui/static/js/layerBtn.js
index a0509f9aa3..da0241c62b 100644
--- a/bitbake/lib/toaster/toastergui/static/js/layerBtn.js
+++ b/bitbake/lib/toaster/toastergui/static/js/layerBtn.js
@@ -68,6 +68,19 @@ function layerBtnsInit(ctx) {
68 }); 68 });
69 }); 69 });
70 70
71
72 $(".customise-btn").unbind('click');
73 $(".customise-btn").click(function(e){
74 e.preventDefault();
75 var imgCustomModal = $("#new-custom-image-modal");
76
77 if (imgCustomModal.length == 0)
78 throw("Modal new-custom-image not found");
79
80 imgCustomModal.data('recipe', $(this).data('recipe'));
81 imgCustomModal.modal('show');
82 });
83
71 /* Setup the initial state of the buttons */ 84 /* Setup the initial state of the buttons */
72 85
73 for (var i in ctx.projectLayers){ 86 for (var i in ctx.projectLayers){
diff --git a/bitbake/lib/toaster/toastergui/static/js/newcustomimage.js b/bitbake/lib/toaster/toastergui/static/js/newcustomimage.js
new file mode 100644
index 0000000000..935b21ede8
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/newcustomimage.js
@@ -0,0 +1,49 @@
1"use strict";
2
3function newCustomImagePageInit(ctx){
4
5 var newCustomImgBtn = $("#create-new-custom-image-btn");
6 var imgCustomModal = $("#new-custom-image-modal");
7
8 newCustomImgBtn.click(function(e){
9 e.preventDefault();
10
11 var name = imgCustomModal.find('input').val();
12 var baseRecipeId = imgCustomModal.data('recipe');
13
14 if (name.length > 0) {
15 createCustomRecipe(name, baseRecipeId);
16 imgCustomModal.modal('hide');
17 } else {
18 console.warn("TODO No name supplied");
19 }
20 });
21
22 function createCustomRecipe(name, baseRecipeId){
23 var data = {
24 'name' : name,
25 'project' : libtoaster.ctx.projectId,
26 'base' : baseRecipeId,
27 };
28
29 $.ajax({
30 type: "POST",
31 url: ctx.xhrCustomRecipeUrl,
32 data: data,
33 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
34 success: function (ret) {
35 if (ret.error !== "ok") {
36 console.warn(ret.error);
37 } else {
38 window.location.replace(ret.url + '?notify=new');
39 }
40 },
41 error: function (ret) {
42 console.warn("Call failed");
43 console.warn(ret);
44 }
45 });
46 }
47
48
49}
diff --git a/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html b/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
index 668e0bf5ef..88bf8599e4 100644
--- a/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
+++ b/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
@@ -23,8 +23,11 @@
23 <ul class="nav nav-list well"> 23 <ul class="nav nav-list well">
24 <li><a class="nav-parent" href="{% url 'project' project.id %}">Configuration</a></li> 24 <li><a class="nav-parent" href="{% url 'project' project.id %}">Configuration</a></li>
25 <li class="nav-header">Compatible metadata</li> 25 <li class="nav-header">Compatible metadata</li>
26<!-- <li><a href="all-image-recipes.html">Image recipes</a></li> --> 26 {% if CUSTOM_IMAGE %}
27 <li><a href="{% url 'projecttargets' project.id %}">Recipes</a></li> 27 <li><a href="{% url 'projectcustomimages' project.id %}">Custom images</a></li>
28 {% endif %}
29 <li><a href="{% url 'projectimagerecipes' project.id %}">Image recipes</a></li>
30 <li><a href="{% url 'projectsoftwarerecipes' project.id %}">Software recipes</a></li>
28 <li><a href="{% url 'projectmachines' project.id %}">Machines</a></li> 31 <li><a href="{% url 'projectmachines' project.id %}">Machines</a></li>
29 <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li> 32 <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li>
30 <li class="nav-header">Extra configuration</li> 33 <li class="nav-header">Extra configuration</li>
diff --git a/bitbake/lib/toaster/toastergui/templates/customise_btn.html b/bitbake/lib/toaster/toastergui/templates/customise_btn.html
new file mode 100644
index 0000000000..54d05f9ea6
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/customise_btn.html
@@ -0,0 +1,9 @@
1<button class="btn btn-block layer-exists-{{data.layer_version.id}} customise-btn" style="display:none;" data-recipe="{{data.id}}">
2 Customise
3</button>
4
5<button class="btn btn-block layer-add-{{data.layer_version.id}} layerbtn" 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">
6 <i class="icon-plus"></i>
7 Add layer
8</button>
9
diff --git a/bitbake/lib/toaster/toastergui/templates/customrecipe.html b/bitbake/lib/toaster/toastergui/templates/customrecipe.html
new file mode 100644
index 0000000000..823bbd8a1e
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/customrecipe.html
@@ -0,0 +1,142 @@
1{% extends "base.html" %}
2{% load projecttags %}
3{% load humanize %}
4{% load static %}
5{% block pagecontent %}
6
7{% include "projecttopbar.html" %}
8
9<script src="{% static 'js/customrecipe.js' %}"></script>
10<script>
11 $(document).ready(function (){
12 var ctx = {
13 tableApiUrl: "{% url 'recipeselectpackages' project.id recipe.pk %}?format=json"
14 };
15
16 try {
17 customRecipePageInit(ctx);
18 } catch (e) {
19 document.write("Sorry, An error has occurred loading this page");
20 console.warn(e);
21 }
22 });
23</script>
24
25<div class="row-fluid span11">
26 <div class="alert alert-success lead" id="image-created-notification" style="margin-top: 15px; display: none">
27 <button type="button" data-dismiss="alert" class="close">x</button>
28 Your custom image <strong>{{recipe.name}}</strong> has been created. You can now add or remove packages as needed.
29 </div>
30 <div class="page-header air">
31 <h1>
32 {{recipe.name}}
33 <small>({{recipe.base_recipe.name}})</small>
34 </h1>
35 </div>
36</div>
37
38<div class="row-fluid span11">
39 <div class="span8">
40 <div class="button-place btn-group" style="width: 100%">
41 <a class="btn btn-large span6" href="#" id="build-custom-image" style="width: 50%">
42 Build {{recipe.name}}
43 </a>
44 <button class="btn btn-large span6" data-toggle="modal" data-target="#download-file" id="download" style="width: 50%">
45 Download recipe file
46 </button>
47 </div>
48 <div id="no-package-results" class="air" style="display:none;">
49 <div class="alert">
50 <h3>No packages found</h3>
51 <p>You might consider <a href="all-software-recipes.html">searching the list of recipes</a> instead. If you find a recipe that matches the name of the package you want:</p>
52 <ol>
53 <li>Add the layer providing the recipe to your project</li>
54 <li>Build the recipe</li>
55 <li>Once the build completes, come back to this page and search for the package</li>
56 </ol>
57 <form class="input-append no-results">
58 <input type="text" class="input-xlarge" value="search query">
59 <a href="#" class="add-on btn">
60 <i class="icon-remove"></i>
61 </a>
62 <button class="btn">Search</button>
63 <button class="btn btn-link" id="show-all">Show all packages</button>
64 </form>
65 </div>
66 </div>
67 <div id="packages-table">
68 {% url 'recipeselectpackages' project.id recipe.id as xhr_table_url %}
69 {% with 'recipeselection' as table_name %}
70 {% with 'Add | Remove packages' as title %}
71
72 <h2>{{title}} (<span class="table-count-{{table_name}}"></span>) </h2>
73
74 {% include "toastertable.html" %}
75 {% endwith %}
76 {% endwith %}
77 </div>
78 </div>
79 <div class="span4 well">
80 <h2 style="margin-bottom:20px;">About {{recipe.name}}</h2>
81
82 <dl>
83 <dt>
84 Approx. packages included
85 <i class="icon-question-sign get-help" title="" data-original-title="The number of packages included is based on information from previous builds and from parsing layers, so we can never be sure it is 100% accurate"></i>
86 </dt>
87 <dd class="no-packages">{{recipe.packages.count}}</dd>
88 <!-- <dt>
89 Approx. package size
90 <i class="icon-question-sign get-help" title="" data-original-title="Package size is based on information from previous builds, so we can never be sure it is 100% accurate"></i>
91 </dt>
92 <dd>244.3 MB</dd>
93 <dt>Last build</dt>
94 <dd>
95 <i class="icon-ok-sign success"></i>
96 <a href="build-dashboard.html">11/06/15 15:22</a>
97 </dd>
98 <dt>Recipe file</dt>
99 <dd>
100 <code>custom-image-name.bb</code>
101 <a href="#download-file" data-toggle="modal"><i class="icon-download-alt" title="" data-original-title="Download recipe file"></i></a>
102 </dd> -->
103 <dt>Layer</dt>
104 <!-- TODO recipe details page -->
105 <dd><a href="{% url 'layerdetails' project.id recipe.base_recipe.layer_version.pk %}">{{recipe.base_recipe.layer_version.layer.name}}</a></dd>
106 <!--<dt>
107 Summary
108 </dt>
109 <dd>
110 <span class="muted">Not set</span>
111 <i class="icon-pencil" data-original-title="" title=""></i>
112 </dd>
113 <dt>
114 Description
115 </dt>
116 <dd>
117 <span class="muted">Not set</span>
118 <i class="icon-pencil" data-original-title="" title=""></i>
119 </dd>
120 <dt>Version</dt>
121 <dd>
122 1.0
123 <i class="icon-pencil" data-original-title="" title=""></i>
124 </dd>
125 <dt>Section</dt>
126 <dd>
127 base
128 <i class="icon-pencil" data-original-title="" title=""></i>
129 <i class="icon-trash" data-original-title="" title=""></i>
130 </dd>
131 <dt>License</dt>
132 <dd>
133 MIT
134 <i class="icon-question-sign get-help" title="" data-original-title="All custom images have their license set to MIT. This is because the license applies only to the recipe (.bb) file, and not to the image itself. To see which licenses apply to the image you must check the license manifest generated with each build"></i>
135 </dd> -->
136 </dl>
137 <i class="icon-trash no-tooltip"></i>
138 <a href="#" class="error" id="delete">Delete custom image</a>
139 </div>
140</div>
141
142 {% endblock %}
diff --git a/bitbake/lib/toaster/toastergui/templates/newcustomimage.html b/bitbake/lib/toaster/toastergui/templates/newcustomimage.html
new file mode 100644
index 0000000000..4487b3ea08
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/newcustomimage.html
@@ -0,0 +1,54 @@
1{% extends "base.html" %}
2{% load projecttags %}
3{% load humanize %}
4{% load static %}
5{% block pagecontent %}
6
7<script src="{% static 'js/newcustomimage.js' %}"></script>
8<script>
9 $(document).ready(function (){
10 var ctx = {
11 xhrCustomRecipeUrl : "{% url 'xhr_customrecipe' %}",
12 };
13
14 try {
15 newCustomImagePageInit(ctx);
16 } catch (e) {
17 document.write("Sorry, An error has occurred loading this page");
18 console.warn(e);
19 }
20 });
21</script>
22
23</script>
24<div class="modal hide fade in" id="new-custom-image-modal" aria-hidden="false">
25 <div class="modal-header">
26 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
27 <h3>Name your custom image</h3>
28 </div>
29 <div class="modal-body">
30 <div class="row-fluid">
31 <span class="help-block span8">Image names must be unique. They should not contain spaces or capital letters, and the only allowed special character is dash (-).<p></p>
32 </span></div>
33 <div class="control-group controls">
34 <input type="text" class="huge span5" placeholder="Type the name, something like 'core-image-myimage'">
35 <span class="help-block" style="display:none">Image names cannot contain spaces or capital letters. The only allowed special character is dash (-)</span>
36 <span class="help-block" style="display: none">An image with this name already exists. Image names must be unique: try a different one.</span>
37 </div>
38 </div>
39 <div class="modal-footer">
40 <a href="#" id="create-new-custom-image-btn" class="btn btn-primary btn-large" data-original-title="" title="">Create custom image</a>
41 </div>
42</div>
43
44{% include "projecttopbar.html" %}
45
46
47{% url table_name project.id as xhr_table_url %}
48{% include "toastertable.html" %}
49
50
51
52{% endblock %}
53
54
diff --git a/bitbake/lib/toaster/toastergui/templates/pkg_add_rm_btn.html b/bitbake/lib/toaster/toastergui/templates/pkg_add_rm_btn.html
new file mode 100644
index 0000000000..b766aeac93
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/pkg_add_rm_btn.html
@@ -0,0 +1,16 @@
1<button class="btn btn-block btn-danger add-rm-package-btn" id="package-rm-btn-{{data.pk}}" data-directive="remove" data-package="{{data.pk}}" data-package-url="{% url 'xhr_customrecipe_packages' extra.recipe_id data.pk %}" style="
2 {% if data.pk not in extra.current_packages %}
3 display:none
4 {% endif %}
5 ">
6 <i class="icon-trash no-tooltip"></i>
7 Remove package
8</a>
9<button class="btn btn-block add-rm-package-btn" data-directive="add" id="package-add-btn-{{data.pk}}" data-package="{{data.pk}}" data-package-url="{% url 'xhr_customrecipe_packages' extra.recipe_id data.pk %}" style="
10 {% if data.pk in extra.current_packages %}
11 display:none
12 {% endif %}
13 ">
14<i class="icon-plus"></i>
15 Add package
16</button>
diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html
index e8354fd678..2f978bc715 100644
--- a/bitbake/lib/toaster/toastergui/templates/project.html
+++ b/bitbake/lib/toaster/toastergui/templates/project.html
@@ -67,7 +67,7 @@
67 67
68 <div class="alert alert-info" style="display:none" id="no-most-built"> 68 <div class="alert alert-info" style="display:none" id="no-most-built">
69 <span class="lead">You haven't built any recipes yet</span> 69 <span class="lead">You haven't built any recipes yet</span>
70 <p style="margin-top: 10px;"><a href="{% url 'projecttargets' project.id %}">Choose a recipe to build</a></p> 70 <p style="margin-top: 10px;"><a href="{% url 'projectsoftwarerecipes' project.id %}">Choose a recipe to build</a></p>
71 </div> 71 </div>
72 72
73 <ul class="unstyled configuration-list" id="freq-build-list"> 73 <ul class="unstyled configuration-list" id="freq-build-list">
diff --git a/bitbake/lib/toaster/toastergui/templates/projecttopbar.html b/bitbake/lib/toaster/toastergui/templates/projecttopbar.html
index ca2741daf9..a3d1b88edf 100644
--- a/bitbake/lib/toaster/toastergui/templates/projecttopbar.html
+++ b/bitbake/lib/toaster/toastergui/templates/projecttopbar.html
@@ -1,6 +1,6 @@
1<div class="alert alert-success lead" id="project-created-notification" style="margin-top:15px; display:none"> 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> 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. 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 'projectsoftwarerecipes' project.id %}">choose image recipes</a> to build.
4</div> 4</div>
5 5
6<!-- project name --> 6<!-- project name -->
@@ -34,6 +34,13 @@
34 Import layer 34 Import layer
35 </a> 35 </a>
36 </li> 36 </li>
37 {% if CUSTOM_IMAGE %}
38 <li>
39 <a href="{% url 'newcustomimage' project.id %}">
40 New custom image
41 </a>
42 </li>
43 {% endif %}
37 <li class="pull-right"> 44 <li class="pull-right">
38 <form class="form-inline" style="margin-bottom:0px;"> 45 <form class="form-inline" style="margin-bottom:0px;">
39 <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>"></i> 46 <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>"></i>
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index b47a161687..a1adbb7be0 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -103,16 +103,13 @@ urlpatterns = patterns('toastergui.views',
103 tables.NewCustomImagesTable.as_view(template_name="newcustomimage.html"), 103 tables.NewCustomImagesTable.as_view(template_name="newcustomimage.html"),
104 name="newcustomimage"), 104 name="newcustomimage"),
105 105
106 url(r'^project/(?P<pid>\d+)/availablerecipes/$',
107 tables.ProjectLayersRecipesTable.as_view(template_name="generic-toastertable-page.html"),
108 { 'table_name': tables.ProjectLayersRecipesTable.__name__.lower(),
109 'title' : 'Recipes available for layers in the current project' },
110 name="projectavailabletargets"),
111 106
112 url(r'^project/(?P<pid>\d+)/layers/$', 107 url(r'^project/(?P<pid>\d+)/layers/$',
113 tables.LayersTable.as_view(template_name="generic-toastertable-page.html"), 108 tables.LayersTable.as_view(template_name="generic-toastertable-page.html"),
114 name="projectlayers"), 109 name="projectlayers"),
115 110
111
112
116 url(r'^project/(?P<pid>\d+)/layer/(?P<layerid>\d+)$', 113 url(r'^project/(?P<pid>\d+)/layer/(?P<layerid>\d+)$',
117 'layerdetails', name='layerdetails'), 114 'layerdetails', name='layerdetails'),
118 115
@@ -129,6 +126,16 @@ urlpatterns = patterns('toastergui.views',
129 name=tables.LayerMachinesTable.__name__.lower()), 126 name=tables.LayerMachinesTable.__name__.lower()),
130 127
131 128
129 url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipeid>\d+)/selectpackages/$',
130 tables.SelectPackagesTable.as_view(template_name="generic-toastertable-page.html"), name="recipeselectpackages"),
131
132
133 url(r'^project/(?P<pid>\d+)/customrecipe/(?P<recipe_id>\d+)$',
134 'customrecipe',
135 name="customrecipe"),
136
137
138
132 # typeahead api end points 139 # typeahead api end points
133 url(r'^xhr_typeahead/(?P<pid>\d+)/layers$', 140 url(r'^xhr_typeahead/(?P<pid>\d+)/layers$',
134 typeaheads.LayersTypeAhead.as_view(), name='xhr_layerstypeahead'), 141 typeaheads.LayersTypeAhead.as_view(), name='xhr_layerstypeahead'),
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 392e56da2a..b7eddf411d 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -2790,6 +2790,15 @@ if True:
2790 2790
2791 return(vars_managed,sorted(vars_fstypes),vars_blacklist) 2791 return(vars_managed,sorted(vars_fstypes),vars_blacklist)
2792 2792
2793 def customrecipe(request, pid, recipe_id):
2794 project = Project.objects.get(pk=pid)
2795 context = {'project' : project,
2796 'projectlayers': [],
2797 'recipe' : CustomImageRecipe.objects.get(pk=recipe_id)
2798 }
2799
2800 return render(request, "customrecipe.html", context)
2801
2793 @_template_renderer("projectconf.html") 2802 @_template_renderer("projectconf.html")
2794 def projectconf(request, pid): 2803 def projectconf(request, pid):
2795 2804