go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/scheduler/appengine/frontend/templates/includes/base.html (about) 1 {{define "base"}} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <!-- Copyright 2015 The LUCI Authors. All rights reserved. 5 Use of this source code is governed under the Apache License, Version 2.0 6 that can be found in the LICENSE file. --> 7 <head> 8 <meta http-equiv="Content-type" content="text/html; charset=UTF-8"> 9 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 10 <meta name="viewport" content="width=device-width, initial-scale=1"> 11 <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet"> 12 <title>{{template "title" .}}</title> 13 <style type="text/css"> 14 body { 15 padding-top: 10px; 16 padding-bottom: 10px; 17 } 18 body.anonymous { 19 background-color: #eee; 20 } 21 .navbar { 22 margin-bottom: 20px; 23 } 24 #account-picture-nav { 25 margin-top: 10px; 26 margin-bottom: 10px; 27 } 28 #account-picture-nav img { 29 border-radius: 6px; 30 } 31 #account-text-nav { 32 margin-left: 8px; 33 margin-right: 0px; 34 } 35 .popover{ 36 max-width: 400px; 37 } 38 footer hr { 39 margin: 10px 0px; 40 } 41 </style> 42 {{template "head" .}} 43 </head> 44 45 {{if .IsAnonymous}} 46 <body class="anonymous"> 47 {{else}} 48 <body> 49 {{end}} 50 <div class="modal fade" tabindex="-1" role="dialog" id="pause-job-modal"> 51 <div class="modal-dialog" role="document"> 52 <div class="modal-content"> 53 <div class="modal-header"> 54 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> 55 <h4 class="modal-title">Pausing</h4> 56 </div> 57 <div class="modal-body"> 58 <form id="pause-job-prompt-form"> 59 <div class="form-group"> 60 <label for="pause-reason" class="control-label"> 61 Provide a reason (it will be recorded): 62 </label> 63 <input type="text" class="form-control" id="pause-reason"> 64 </div> 65 </form> 66 </div> 67 <div class="modal-footer"> 68 <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> 69 <button type="submit" class="btn btn-primary" id="pause-btn" form="pause-job-prompt-form">Pause</button> 70 </div> 71 </div> 72 </div> 73 </div> 74 75 <div class="container"> 76 <div class="navbar navbar-default" role="navigation"> 77 <div class="navbar-header"> 78 <button type="button" class="navbar-toggle" 79 data-toggle="collapse" data-target=".navbar-collapse"> 80 <span class="sr-only">Toggle navigation</span> 81 <span class="icon-bar"></span> 82 <span class="icon-bar"></span> 83 <span class="icon-bar"></span> 84 </button> 85 <span class="navbar-brand"> 86 <span id="progress-spinner" class="not-spinning"> 87 <a href="/">LUCI Scheduler</a> 88 </span> 89 </span> 90 </div> 91 <div class="navbar-collapse collapse"> 92 <ul class="nav navbar-nav"></ul> 93 <p class="nav navbar-text navbar-right" id="account-text-nav"> 94 {{if .IsAnonymous}} 95 <a href="{{.LoginURL}}" class="navbar-link">Login</a> 96 {{else}} 97 <span>{{.User.Email}}</span> 98 <span> |</span> 99 <a href="{{.LogoutURL}}" class="navbar-link">Logout</a> 100 {{end}} 101 {{if .User.Picture}} 102 <p class="nav navbar-right" id="account-picture-nav"> 103 <img src="{{.User.Picture}}" width="30" height="30"> 104 </p> 105 {{end}} 106 </p> 107 </div> 108 </div> 109 110 <div id="content-box"> 111 {{template "content" .}} 112 </div> 113 114 <footer> 115 <hr> 116 <p class="text-right" style="color: #cccccc"> 117 <small>Handled in <span>{{call .HandlerDuration}}</span></small> 118 <small style="margin-left: 20px">Version: <span>{{.AppVersion}}</span></small> 119 </p> 120 </footer> 121 </div> 122 123 <script src="/static/jquery/jquery.min.js"></script> 124 <script src="/static/bootstrap/js/bootstrap.min.js"></script> 125 <script> 126 var xsrfToken = "{{.XsrfToken}}"; 127 128 function jobFromButton(btn) { 129 var form = $(btn).closest("form"); 130 return { 131 project: $("input#projectID", form).val(), 132 job: $("input#jobName", form).val(), 133 }; 134 }; 135 136 function invocationFromButton(btn) { 137 var form = $(btn).closest("form"); 138 return { 139 project: $("input#projectID", form).val(), 140 job: $("input#jobName", form).val(), 141 invocation: $("input#invID", form).val(), 142 }; 143 }; 144 145 function postJobAction(btn, action, reason) { 146 var form = $(btn).closest("form"); 147 if (!form.attr("submitted")) { 148 var job = jobFromButton(btn); 149 var url = "/actions/" + action + "/" + job.project + "/" + job.job; 150 $("#xsrf_token", form).val(xsrfToken); 151 $("#reason", form).val(reason || ""); 152 form.attr("action", url); 153 form.attr("submitted", "yes"); 154 form.submit(); 155 } 156 }; 157 158 function postInvocationAction(btn, action) { 159 var form = $(btn).closest("form"); 160 if (!form.attr("submitted")) { 161 var inv = invocationFromButton(btn); 162 var url = "/actions/" + action + "/" + inv.project + "/" + inv.job + "/" + inv.invocation; 163 $("#xsrf_token", form).val(xsrfToken); 164 form.attr("action", url); 165 form.attr("submitted", "yes"); 166 form.submit(); 167 } 168 }; 169 170 $("#pause-job-modal").on("show.bs.modal", function(event) { 171 // The modal dialog itself. 172 var modal = $(this); 173 // The button that triggered the modal. Indirectly points to the job. 174 // Consumed by postJobAction. 175 var pauseBtn = $(event.relatedTarget); 176 177 // Indicate which job we are pausing. 178 $(".modal-title", modal).text("Pausing " + jobFromButton(pauseBtn).job); 179 180 // Actually pause the job when the prompt is closed affirmatively. 181 modal.unbind("submit"); 182 modal.submit(function(event) { 183 // Disable form buttons and prevent the modal from closing, the page 184 // will reload after postJobAction call. 185 $("button", modal).prop("disabled", true); 186 $("input", modal).prop("disabled", true); 187 $("#pause-btn", modal).text("Pausing"); 188 modal.off("keydown.dismiss.bs.modal"); 189 190 // Initiate the action, this will reload the page eventually. 191 postJobAction(pauseBtn, "pauseJob", $("#pause-reason", modal).val()); 192 193 // Prevent default form submission handler (GET request). 194 event.preventDefault(); 195 }); 196 }); 197 198 // Auto-focus the input box when the modal is shown. 199 $("#pause-job-modal").on("shown.bs.modal", function() { 200 $("#pause-reason", $(this)).focus(); 201 }); 202 203 // Initialize all popovers. 204 $('[data-toggle="popover"]').popover(); 205 </script> 206 </body> 207 208 </html> 209 {{end}} 210 211 212 {{define "job-action-buttons"}} 213 <form style="display: inline" method="POST"> 214 <input type="hidden" id="xsrf_token" name="xsrf_token" value=""> 215 <input type="hidden" id="projectID" value="{{.ProjectID}}"> 216 <input type="hidden" id="jobName" value="{{.JobName}}"> 217 <input type="hidden" id="reason" name="reason" value=""> 218 <div class="btn-group btn-group-xs" style="width: 160px" role="group"> 219 {{if .Paused}} 220 <button {{if not .CanResume}}disabled{{end}} type="button" 221 class="btn btn-primary" 222 onclick="postJobAction(this, 'resumeJob')"> 223 Resume 224 </button> 225 {{else}} 226 <button {{if not .CanPause}}disabled{{end}} type="button" 227 class="btn btn-primary" 228 data-toggle="modal" data-backdrop="static" 229 data-target="#pause-job-modal"> 230 Pause 231 </button> 232 {{end}} 233 <button {{if not .CanAbort}}disabled{{end}} type="button" 234 class="btn btn-danger" 235 onclick="postJobAction(this, 'abortJob')"> 236 Abort 237 </button> 238 <button {{if not .CanTrigger}}disabled{{end}} type="button" 239 class="btn btn-success" 240 onclick="postJobAction(this, 'triggerJob')"> 241 Trigger 242 </button> 243 </div> 244 </form> 245 {{end}} 246 247 248 {{define "invocation-action-buttons"}} 249 <form style="display: inline" method="POST"> 250 <input type="hidden" id="xsrf_token" name="xsrf_token" value=""> 251 <input type="hidden" id="projectID" value="{{.ProjectID}}"> 252 <input type="hidden" id="jobName" value="{{.JobName}}"> 253 <input type="hidden" id="invID" value="{{.InvID}}"> 254 <div class="btn-group btn-group-xs" role="group"> 255 <button {{if not .CanAbort}}disabled{{end}} type="button" 256 class="btn btn-danger" 257 onclick="postInvocationAction(this, 'abortInvocation')"> 258 Abort 259 </button> 260 </div> 261 </form> 262 {{end}} 263 264 265 {{define "job-id-ref"}} 266 <span class="glyphicon {{.JobFlavorIcon}}" aria-hidden="true" title="{{.JobFlavorTitle}}"> 267 </span> 268 <a href="/jobs/{{.ProjectID}}/{{.JobName}}">{{.JobName}}</a> 269 {{end}} 270 271 {{define "job-id-static"}} 272 <span class="glyphicon {{.JobFlavorIcon}}" aria-hidden="true" title="{{.JobFlavorTitle}}"> 273 </span> 274 {{.JobName}} 275 {{end}} 276 277 {{define "triggers-list"}} 278 <ul> 279 {{range .}} 280 <li> 281 <span style="font-family:monospace;"> 282 {{if .URL}} 283 <a href="{{.URL}}" target="_blank">{{.Title}}</a> 284 {{else}} 285 {{.Title}} 286 {{end}} 287 </span> 288 ({{.RelTime}}{{if .EmittedBy}} by <b>{{.EmittedBy}}</b>{{end}}) 289 </li> 290 {{end}} 291 </ul> 292 {{end}}