1%{--
2  - Copyright 2016 SimplifyOps, Inc. (http://simplifyops.com)
3  -
4  - Licensed under the Apache License, Version 2.0 (the "License");
5  - you may not use this file except in compliance with the License.
6  - You may obtain a copy of the License at
7  -
8  -     http://www.apache.org/licenses/LICENSE-2.0
9  -
10  - Unless required by applicable law or agreed to in writing, software
11  - distributed under the License is distributed on an "AS IS" BASIS,
12  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  - See the License for the specific language governing permissions and
14  - limitations under the License.
15  --}%
16
17<%@ page import="grails.util.Environment" %>
18<html>
19<head>
20    <g:set var="rkey" value="${g.rkey()}" />
21    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
22    <meta name="layout" content="base"/>
23    <meta name="tabpage" content="jobs"/>
24    <g:set var="projectName" value="${params.project ?: request.project}"></g:set>
25    <g:set var="projectLabel" value="${session.frameworkLabels?session.frameworkLabels[projectName]:projectName}"/>
26    <title><g:message code="gui.menu.Workflows"/> - <g:enc>${projectLabel}</g:enc></title>
27
28    <asset:javascript src="util/yellowfade.js"/>
29    <g:javascript library="pagehistory"/>
30    <g:javascript library="prototype/effects"/>
31    <asset:javascript src="menu/jobs.js"/>
32    <g:if test="${grails.util.Environment.current==grails.util.Environment.DEVELOPMENT}">
33        <asset:javascript src="menu/joboptionsTest.js"/>
34        <asset:javascript src="menu/job-remote-optionsTest.js"/>
35    </g:if>
36    <g:embedJSON data="${projectNames ?: []}" id="projectNamesData"/>
37    <g:embedJSON data="${nextSchedListIds ?: []}" id="nextScheduled"/>
38    <g:embedJSON id="pageParams" data="${[project: params.project?:request.project,]}"/>
39    <g:jsMessages code="Node,Node.plural,job.starting.execution,job.scheduling.execution,option.value.required,options.remote.dependency.missing.required,,option.default.button.title,option.default.button.text,option.select.choose.text"/>
40    <!--[if (gt IE 8)|!(IE)]><!--> <g:javascript library="ace/ace"/><!--<![endif]-->
41    <script type="text/javascript">
42        /** knockout binding for activity */
43        var pageActivity;
44        function showError(message){
45             appendText($('error'),message);
46             $("error").show();
47        }
48        var _jobExecUnloadHandlers=new Array();
49        function _registerJobExecUnloadHandler(handler){
50            _jobExecUnloadHandlers.push(handler);
51        }
52        function unloadExec(){
53            if(_jobExecUnloadHandlers.length>0){
54                for(var i =0;i<_jobExecUnloadHandlers.length;i++){
55                    _jobExecUnloadHandlers[i].call();
56                }
57                _jobExecUnloadHandlers.clear();
58            }
59
60            jQuery('#execDiv').modal('hide');
61            clearHtml('execDivContent');
62
63            $('busy').hide();
64        }
65        function requestError(item,message){
66            unloadExec();
67            showError("Failed request: "+item+". Result: "+message);
68        }
69        function loadExec(id,eparams) {
70            $("error").hide();
71            var params=eparams;
72            if(!params){
73                params={id:id};
74            }
75            jQuery('#execDivContent').load(_genUrl(appLinks.scheduledExecutionExecuteFragment, params),
76                function(response,status,xhr){
77                if (status == "success") {
78                    loadedFormSuccess(!!id,id);
79                } else {
80                    requestError("executeFragment for [" + id + "]",xhr.statusText);
81                }
82            });
83        }
84        function execSubmit(elem, target) {
85            var data = new FormData(jQuery('#' + elem + ' form')[0]);
86            jQuery.ajax({
87                url: target,
88                type: 'POST',
89                data: data,
90                contentType: false,
91                dataType: 'json',
92                processData: false,
93                success: function (result) {
94                    if (result.id) {
95                        if (result.follow && result.href) {
96                            document.location = result.href;
97                        } else {
98                            if (!pageActivity.selected()) {
99                                pageActivity.activateNowRunningTab();
100                            }
101                            unloadExec();
102                        }
103                    } else if (result.error === 'invalid') {
104                        // reload form for validation
105                        loadExec(null, Form.serialize(elem) + "&dovalidate=true");
106                    } else {
107                        unloadExec();
108                        showError(result.message ? result.message : result.error ? result.error : "Failed request");
109                    }
110                },
111                error: function (data, jqxhr, err) {
112                    requestError("runJobInline", err);
113                }
114            });
115        }
116        function loadedFormSuccess(doShow,id){
117            if ($('execFormCancelButton')) {
118                Event.observe($('execFormCancelButton'),'click',function(evt) {
119                    Event.stop(evt);
120                    unloadExec();
121                    return false;
122                },false);
123                $('execFormCancelButton').name = "_x";
124            }
125            if ($('execFormRunButton')) {
126                Event.observe($('execFormRunButton'),'click', function(evt) {
127                    Event.stop(evt);
128                    execSubmit('execDivContent', appLinks.scheduledExecutionRunJobInline);
129                    $('formbuttons').loading(message('job.starting.execution'));
130                    return false;
131                },false);
132            }
133            jQuery('#showScheduler').on('shown.bs.popover', function() {
134                if ($('scheduleAjaxButton')) {
135                    Event.observe($('scheduleAjaxButton'), 'click', function(evt) {
136                        Event.stop(evt);
137                        if (isValidDate()) {
138                            toggleAlert(true);
139		                    execSubmit('execDivContent',
140                                appLinks.scheduledExecutionScheduleJobInline);
141		                    $('formbuttons').loading(message('job.scheduling.execution'));
142                        } else {
143                            toggleAlert(false);
144                        }
145                        return false;
146                    }, false);
147                }
148            });
149
150            //setup option handling
151            //setup option edit
152            var joboptiondata = loadJsonData('jobOptionData');
153            var joboptions = new JobOptions(joboptiondata);
154
155            if (document.getElementById('optionSelect')) {
156                ko.applyBindings(joboptions, document.getElementById('optionSelect'));
157            }
158
159            var remoteoptionloader = new RemoteOptionLoader({
160                url: "${createLink(controller:'scheduledExecution',action:'loadRemoteOptionValues',params:[format:'json'])}",
161                id:id,
162                fieldPrefix: "extra.option."
163            });
164            var remotecontroller = new RemoteOptionController({
165                loader: remoteoptionloader,
166            });
167            remotecontroller.setupOptions(joboptions);
168
169            remotecontroller.loadData(loadJsonData('remoteOptionData'));
170            if (typeof(_registerJobExecUnloadHandler) == 'function') {
171                _registerJobExecUnloadHandler(remotecontroller.unsubscribeAll);
172            }
173            joboptions.remoteoptions = remotecontroller;
174            remotecontroller.begin();
175
176            jQuery('input').on('keydown', function (evt) {
177                return noenter(evt);
178            });
179            if(doShow){
180                jQuery('#execDiv').modal('show');
181            }
182            $('busy').hide();
183        }
184
185
186
187
188        //set box filterselections
189
190        function _setFilterSuccess(data,name){
191            if(data){
192                var bfilters=data.filterpref;
193                //reload page
194                document.location=_genUrl(appLinks.menuJobs , bfilters[name] ? {filterName:bfilters[name]} : {});
195            }
196        }
197
198
199        /** now running section update */
200        function _pageUpdateNowRunning(count){
201        }
202        var lastRunExec=0;
203        /**
204         * Handle embedded content updates
205         */
206        function _updateBoxInfo(name,data){
207            if(name==='events' && data.lastDate){
208                histControl.setHiliteSince(data.lastDate);
209            }
210            if (name == 'nowrunning' && data.lastExecId && data.lastExecId != lastRunExec) {
211                lastRunExec = data.lastExecId;
212            }
213        }
214
215        /////////////
216        // Job context detail popup code
217        /////////////
218
219        var doshow=false;
220        var popvis=false;
221        var lastHref;
222        var targetLink;
223        function popJobDetails(elem){
224            if(doshow && $('jobIdDetailHolder')){
225                new MenuController().showRelativeTo(elem,$('jobIdDetailHolder'));
226                popvis=true;
227                if(targetLink){
228                    $(targetLink).removeClassName('glow');
229                    targetLink=null;
230                }
231                $(elem).addClassName('glow');
232                targetLink=elem;
233            }
234        }
235        var motimer;
236        var mltimer;
237        function bubbleMouseover(evt){
238            if(mltimer){
239                clearTimeout(mltimer);
240                mltimer=null;
241            }
242        }
243        function jobLinkMouseover(elem,evt){
244            if(mltimer){
245                clearTimeout(mltimer);
246                mltimer=null;
247            }
248            if(motimer){
249                clearTimeout(motimer);
250                motimer=null;
251            }
252            if(popvis && lastHref===elem.href){
253                return;
254            }
255            var delay=1500;
256            if(popvis){
257                delay=0;
258            }
259            motimer=setTimeout(showJobDetails.curry(elem),delay);
260        }
261        function doMouseout(){
262            if(popvis && $('jobIdDetailHolder')){
263                popvis=false;
264                Try.these(
265                    function(){
266                        jQuery('#jobIdDetailHolder').fadeOut('fast');
267                    },
268                    function(){$('jobIdDetailHolder').hide();}
269                    );
270            }
271            if(targetLink){
272                $(targetLink).removeClassName('glow');
273                targetLink=null;
274            }
275        }
276        function jobLinkMouseout(elem,evt){
277            //hide job details
278            if(motimer){
279                clearTimeout(motimer);
280                motimer=null;
281            }
282            doshow=false;
283            mltimer=setTimeout(doMouseout,0);
284        }
285        function showJobDetails(elem){
286            //get url
287            var href=elem.href || elem.getAttribute('data-href');
288            lastHref=href;
289            doshow=true;
290            //match is id
291            var matchId = jQuery(elem).data('jobId');
292            if(!matchId){
293                return;
294            }
295            var viewdom=$('jobIdDetailHolder');
296            var bcontent=$('jobIdDetailContent');
297            if(viewdom){
298                viewdom.parentNode.removeChild(viewdom);
299                viewdom=null;
300            }
301            if(!viewdom){
302                viewdom = $(document.createElement('div'));
303                viewdom.addClassName('bubblewrap');
304                viewdom.setAttribute('id','jobIdDetailHolder');
305                viewdom.setAttribute('style','display:none;width:600px;height:250px;');
306
307                Event.observe(viewdom,'click',function(evt){
308                    evt.stopPropagation();
309                },false);
310
311                var btop = new Element('div');
312                btop.addClassName('bubbletop');
313                viewdom.appendChild(btop);
314                bcontent = new Element('div');
315                bcontent.addClassName('bubblecontent');
316                bcontent.setAttribute('id','jobIdDetailContent');
317                viewdom.appendChild(bcontent);
318                document.body.appendChild(viewdom);
319                Event.observe(viewdom,'mouseover',bubbleMouseover);
320                Event.observe(viewdom,'mouseout',jobLinkMouseout.curry(viewdom));
321            }
322            bcontent.loading();
323            var jobNodeFilters;
324            jQuery.ajax({
325                dataType:'json',
326                url:_genUrl(appLinks.scheduledExecutionDetailFragmentAjax, {id: matchId}),
327                success:function(data,status,xhr){
328                    var params={};
329                    if(data.job && data.job.doNodeDispatch) {
330                        if (data.job.filter) {
331                            params.filter = data.job.filter;
332                        }
333                    }else{
334                        params.localNodeOnly=true;
335                        params.emptyMode='localnode';
336                    }
337                    jobNodeFilters=initJobNodeFilters(params);
338                }
339            }).done(
340                    function(){
341                        jQuery('#jobIdDetailContent').load(_genUrl(appLinks.scheduledExecutionDetailFragment, {id: matchId}),
342                                function(response,status,xhr){
343                            if (status=='success') {
344                                var wrapDiv = jQuery('#jobIdDetailHolder').find('.ko-wrap')[0];
345                                if(wrapDiv) {
346                                    ko.applyBindings(jobNodeFilters, wrapDiv);
347                                }
348                                popJobDetails(elem);
349                                $('jobIdDetailContent').select('.apply_ace').each(function (t) {
350                                    _applyAce(t);
351                                });
352                            }else{
353                                clearHtml(bcontent);
354                                viewdom.hide();
355                            }
356                        });
357                    }
358            );
359
360        }
361
362        function initJobIdLinks(){
363            $$('.hover_show_job_info').each(function(e){
364                Event.observe(e,'mouseover',jobLinkMouseover.curry(e));
365                Event.observe(e,'mouseout',jobLinkMouseout.curry(e));
366            });
367
368            jQuery('.act_job_action_dropdown').click(function(){
369                var id=jQuery(this).data('jobId');
370                var el=jQuery(this).parent().find('.dropdown-menu');
371                el.load(
372                    _genUrl(appLinks.scheduledExecutionActionMenuFragment,{id:id})
373                );
374            });
375        }
376         function filterToggle(evt) {
377            ['${enc(js:rkey)}filter','${enc(js:rkey)}filter-toggle'].each(Element.toggle);
378        }
379        function filterToggleSave(evt) {
380            ['${enc(js:rkey)}filter','${enc(js:rkey)}fsave'].each(Element.show);
381            ['${enc(js:rkey)}filter-toggle','${enc(js:rkey)}fsavebtn'].each(Element.hide);
382        }
383        function init(){
384            <g:if test="${!(grailsApplication.config.rundeck?.gui?.enableJobHoverInfo in ['false',false])}">
385            initJobIdLinks();
386            </g:if>
387
388            PageActionHandlers.registerHandler('job_delete_single',function(el){
389                bulkeditor.activateActionForJob(bulkeditor.DELETE,el.data('jobId'));
390            });
391            PageActionHandlers.registerHandler('enable_job_execution_single',function(el){
392                bulkeditor.activateActionForJob(bulkeditor.ENABLE_EXECUTION,el.data('jobId'));
393            });
394            PageActionHandlers.registerHandler('disable_job_execution_single',function(el){
395                bulkeditor.activateActionForJob(bulkeditor.DISABLE_EXECUTION,el.data('jobId'));
396            });
397            PageActionHandlers.registerHandler('disable_job_schedule_single',function(el){
398                bulkeditor.activateActionForJob(bulkeditor.DISABLE_SCHEDULE,el.data('jobId'));
399            });
400            PageActionHandlers.registerHandler('enable_job_schedule_single',function(el){
401                bulkeditor.activateActionForJob(bulkeditor.ENABLE_SCHEDULE,el.data('jobId'));
402            });
403
404            PageActionHandlers.registerHandler('copy_other_project',function(el){
405                jQuery('#jobid').val(el.data('jobId'));
406                jQuery('#selectProject').modal();
407            });
408
409
410            Event.observe(document.body,'click',function(evt){
411                //click outside of popup bubble hides it
412                doMouseout();
413            },false);
414            Event.observe(document,'keydown',function(evt){
415                //escape key hides popup bubble
416                if(evt.keyCode===27 ){
417                    doMouseout();
418                }
419                return true;
420            },false);
421
422            $$('.obs_filtertoggle').each(function(e) {
423                Event.observe(e, 'click', filterToggle);
424            });
425            $$('.obs_filtersave').each(function(e) {
426                Event.observe(e, 'click', filterToggleSave);
427            });
428        }
429        /**
430         * Possible actions for bulk edit jobs, to present in modal dialog
431         * @constructor
432         */
433        function BulkEditor(){
434            var self=this;
435            self.DISABLE_SCHEDULE = 'disable_schedule';
436            self.ENABLE_SCHEDULE = 'enable_schedule';
437            self.ENABLE_EXECUTION= 'enable_execution';
438            self.DISABLE_EXECUTION= 'disable_execution';
439            self.DELETE= 'delete';
440            self.action=ko.observable(null);
441            self.enabled=ko.observable(false);
442            self.beginEdit=function(){
443                self.expandAllComponents();
444                self.enabled(true);
445            };
446            self.cancelEdit=function(){
447                self.enabled(false);
448                self.selectNone();
449            };
450            self.disableSchedule=function(){
451
452                self.action(self.DISABLE_SCHEDULE);
453            };
454            self.isDisableSchedule=ko.pureComputed(function(){
455                return self.action()===self.DISABLE_SCHEDULE;
456            });
457            self.enableSchedule=function(){
458                self.action(self.ENABLE_SCHEDULE);
459            };
460            self.isEnableSchedule=ko.pureComputed(function(){
461                return self.action()===self.ENABLE_SCHEDULE;
462            });
463            self.enableExecution=function(){
464                self.action(self.ENABLE_EXECUTION);
465            };
466            self.isEnableExecution=ko.pureComputed(function(){
467                return self.action()===self.ENABLE_EXECUTION;
468            });
469            self.disableExecution=function(){
470                self.action(self.DISABLE_EXECUTION);
471            };
472            self.isDisableExecution=ko.pureComputed(function(){
473                return self.action()===self.DISABLE_EXECUTION;
474            });
475            self.actionDelete=function(){
476                self.action(self.DELETE);
477            };
478            self.isDelete=ko.pureComputed(function(){
479                return self.action()===self.DELETE;
480            });
481            self.cancel=function(){
482                self.action(null);
483            };
484
485            self.setCheckboxValues=function(ids){
486                //check only the checkbox with this job id by passing an array
487                jQuery('.jobbulkeditfield :input[name=ids]').val(ids);
488            };
489            self.checkboxesForGroup=function(group){
490                return jQuery('.jobbulkeditfield input[type=checkbox][data-job-group="'+group+'"]');
491            };
492            self.allCheckboxes=function(group){
493                return jQuery('.jobbulkeditfield input[type=checkbox]');
494            };
495            self.jobGroupSelectAll=function(e){
496                var jgroup=jQuery(e).data('job-group');
497                if(jgroup){
498                    self.checkboxesForGroup(jgroup).prop('checked', true);
499                }
500            };
501
502            self.jobGroupSelectNone=function(e){
503                var jgroup=jQuery(e).data('job-group');
504                if(jgroup){
505                    self.checkboxesForGroup(jgroup).prop('checked', false);
506                }
507            };
508            self.expandAllComponents=function(){
509                jQuery('.expandComponent').show();
510            };
511            self.collapseAllComponents=function(){
512                jQuery('.topgroup .expandComponent').hide();
513            };
514            self.selectAll=function(){
515                self.expandAllComponents();
516                self.allCheckboxes().prop('checked', true);
517            };
518            self.selectNone=function(){
519                self.expandAllComponents();
520                self.allCheckboxes().prop('checked', false);
521            };
522            self.toggleModal=function(){
523                jQuery('#bulk_del_confirm').modal('toggle');
524            };
525            self.activateActionForJob=function(action,jobid){
526                self.setCheckboxValues([jobid]);
527                self.beginEdit();
528                self.action(action);
529                self.toggleModal();
530            };
531
532            self.scmExportEnabled = ko.observable(false);
533            self.scmImportEnabled = ko.observable(false);
534            self.scmStatus = ko.observable(null);
535            self.scmImportJobStatus = ko.observable(null);
536            self.scmExportStatus = ko.observable(null);
537            self.scmImportStatus = ko.observable(null);
538            self.scmExportActions = ko.observable(null);
539            self.scmImportActions = ko.observable(null);
540            self.scmExportRenamed = ko.observable(null);
541            self.isExportEnabled=ko.pureComputed(function(){
542                return self.scmExportEnabled();
543            });
544
545            self.jobSynchState = function(jobid){
546                var exportStatus = null;
547                var importStatus = null;
548                if(self.scmStatus() && self.scmStatus()[jobid]){
549                    exportStatus = self.scmStatus()[jobid].synchState.name;
550                }
551                if(self.scmImportJobStatus() && self.scmImportJobStatus()[jobid]){
552                    importStatus = self.scmImportJobStatus()[jobid].synchState.name;
553                }
554                if(!exportStatus || exportStatus == "CLEAN"){
555                    return importStatus;
556                }else{
557                    return exportStatus
558                }
559            };
560
561            self.displayBadge = function(jobid){
562                var displayExport = false;
563                var displayImport = false;
564                if(self.scmExportEnabled() || self.scmImportEnabled()){
565                    if(self.scmStatus() && self.scmStatus()[jobid]){
566                        displayExport = self.scmStatus()[jobid].synchState.name != "CLEAN";
567                    }
568                    if(self.scmImportJobStatus() && self.scmImportJobStatus()[jobid]){
569                        displayImport = self.scmImportJobStatus()[jobid].synchState.name != "CLEAN";
570                    }
571                }
572                return (displayExport || displayImport);
573            };
574
575            self.jobText = function(jobid){
576                var exportStatus = null;
577                var importStatus = null;
578                var text = null;
579                if(self.scmStatus() && self.scmStatus()[jobid]){
580                    exportStatus = self.scmStatus()[jobid].synchState.name;
581                    switch(exportStatus) {
582                        case "EXPORT_NEEDED":
583                            text = "${message(code: "scm.export.status.EXPORT_NEEDED.description")}";
584                            break;
585                        case "CREATE_NEEDED":
586                            text = "${message(code: "scm.export.status.CREATE_NEEDED.description")}";
587                            break;
588                        case "CLEAN":
589                            text = "${message(code: "scm.export.status.CLEAN.description")}";
590                            break;
591                        default:
592                            text = exportStatus;
593                    }
594                }
595                if(self.scmImportJobStatus() && self.scmImportJobStatus()[jobid]){
596                    if(text){
597                        text +=', ';
598                    }else{
599                        text = '';
600                    }
601                    importStatus = self.scmImportJobStatus()[jobid].synchState.name;
602                    switch(importStatus) {
603                        case "IMPORT_NEEDED":
604                            text += "${message(code: "scm.import.status.IMPORT_NEEDED.description")}";
605                            break;
606                        case "DELETE_NEEDED":
607                            text += "${message(code: "scm.import.status.DELETE_NEEDED.description")}";
608                            break;
609                        case "CLEAN":
610                            text += "${message(code: "scm.import.status.CLEAN.description")}";
611                            break;
612                        case "REFRESH_NEEDED":
613                            text += "${message(code: "scm.import.status.REFRESH_NEEDED.description")}";
614                            break;
615                        case "UNKNOWN":
616                            text += "${message(code: "scm.import.status.UNKNOWN.description")}";
617                            break;
618                        default:
619                            text += importStatus;
620                    }
621
622                }
623                return text;
624            };
625
626            self.jobClass = function(jobid){
627                switch(self.jobSynchState(jobid)) {
628                    case "EXPORT_NEEDED":
629                        return "text-info";
630                        break;
631                    case "CREATE_NEEDED":
632                        return "text-success";
633                        break;
634                    case "UNKNOWN":
635                        return "text-muted";
636                        break;
637                    case "IMPORT_NEEDED":
638                        return "text-warning";
639                        break;
640                    case "REFRESH_NEEDED":
641                        return "text-warning";
642                        break;
643                    case "DELETED":
644                        return "text-danger";
645                        break;
646                    case "CLEAN":
647                        return "text-muted";
648                        break;
649                }
650                return 'text-muted';
651            };
652
653            self.jobIcon = function(jobid){
654                switch(self.jobSynchState(jobid)) {
655                    case "EXPORT_NEEDED":
656                        return "glyphicon-exclamation-sign";
657                        break;
658                    case "CREATE_NEEDED":
659                        return "glyphicon-exclamation-sign";
660                        break;
661                    case "UNKNOWN":
662                        return "glyphicon-question-sign";
663                        break;
664                    case "IMPORT_NEEDED":
665                        return "glyphicon-exclamation-sign";
666                        break;
667                    case "REFRESH_NEEDED":
668                        return "glyphicon-exclamation-sign";
669                        break;
670                    case "DELETED":
671                        return "glyphicon-minus-sign";
672                        break;
673                    case "CLEAN":
674                        return "glyphicon-ok";
675                        break;
676                }
677                return 'glyphicon-plus';
678            };
679
680            self.exportMessage = function(){
681                if(self.scmExportStatus()){
682                    return self.scmExportStatus().message;
683                }
684                return null;
685            };
686            self.importMessage = function(){
687                if(self.scmImportStatus()){
688                    return self.scmImportStatus().message;
689                }
690                return null;
691            };
692
693            self.exportState = function(){
694                if(self.scmExportStatus()){
695                    return self.scmExportStatus().state.name;
696                }
697                return null;
698            };
699            self.importState = function(){
700                if(self.scmImportStatus()){
701                    return self.scmImportStatus().state.name;
702                }
703                return null;
704            };
705
706            self.jobCommit = function(jobid){
707                return self.scmExportEnabled();
708            };
709
710            self.defaultExportText = function(){
711                if(self.exportState()) {
712                    var text = null;
713                    switch(self.exportState()) {
714                        case "EXPORT_NEEDED":
715                            text = "${message(code: "scm.export.status.EXPORT_NEEDED.display.text")}";
716                            break;
717                        case "CREATE_NEEDED":
718                            text = "${message(code: "scm.export.status.CREATE_NEEDED.display.text")}";
719                            break;
720                        case "REFRESH_NEEDED":
721                            text = "${message(code: "scm.export.status.REFRESH_NEEDED.display.text")}";
722                            break;
723                        case "DELETED":
724                            text = "${message(code: "scm.export.status.DELETED.display.text")}";
725                            break;
726                        case "CLEAN":
727                            text = "${message(code: "scm.export.status.CLEAN.display.text")}";
728                            break;
729                    }
730                    if(!text){
731                        text = self.exportState();
732                    }
733                    return text;
734                }
735                return null;
736            };
737
738            self.defaultImportText = function(){
739                if(self.importState()) {
740                    var text = null;
741                    switch(self.importState()) {
742                        case "IMPORT_NEEDED":
743                            text = "${message(code: "scm.import.status.IMPORT_NEEDED.display.text")}";
744                            break;
745                        case "REFRESH_NEEDED":
746                            text = "${message(code: "scm.import.status.REFRESH_NEEDED.display.text")}";
747                            break;
748                        case "UNKNOWN":
749                            text = "${message(code: "scm.import.status.UNKNOWN.display.text")}";
750                            break;
751                        case "CLEAN":
752                            text = "${message(code: "scm.import.status.CLEAN.display.text")}";
753                            break;
754                    }
755                    if(!text){
756                        text = self.importState();
757                    }
758                    return text;
759                }
760                return null;
761            };
762
763            self.defaultDisplayText = function(){
764                if(self.exportState() != 'CLEAN'){
765                    return self.defaultExportText();
766                }else{
767                    return self.defaultImportText();
768                }
769            };
770
771
772            self.displayExport = function(){
773                return (self.exportState() && self.exportState() != 'CLEAN');
774            };
775
776            self.displayImport = function(){
777                return (self.importState() && self.importState() != 'CLEAN');
778            };
779
780            self.displaySCMMEssage = function(){
781                return (self.displayExport() || self.displayImport());
782            };
783
784        }
785
786
787
788
789
790
791        var bulkeditor;
792        jQuery(document).ready(function () {
793            init();
794            if (jQuery('#activity_section')) {
795                pageActivity = new History(appLinks.reportsEventsAjax, appLinks.menuNowrunningAjax);
796                ko.applyBindings(pageActivity, document.getElementById('activity_section'));
797                setupActivityLinks('activity_section', pageActivity);
798            }
799            jQuery(document).on('click','.act_execute_job',function(evt){
800                evt.preventDefault();
801               loadExec(jQuery(this).data('jobId'));
802            });
803            $$('#wffilterform input').each(function(elem){
804                if(elem.type=='text'){
805                    elem.observe('keypress',noenter);
806                }
807            });
808            bulkeditor=new BulkEditor();
809            ko.applyBindings(bulkeditor,document.getElementById('bulk_del_confirm'));
810            ko.applyBindings(bulkeditor,document.getElementById('bulk_edit_panel'));
811            ko.applyBindings(bulkeditor,document.getElementById('job_action_menu'));
812            ko.applyBindings(bulkeditor,document.getElementById('job_group_tree'));
813            ko.applyBindings(bulkeditor,document.getElementById('group_controls'));
814            ko.applyBindings(bulkeditor,document.getElementById('scm_message'));
815            ko.applyBindings(bulkeditor,document.getElementById('scmStatusPopoverOK'));
816
817
818
819
820            jQuery(document).on('click','#togglescm',function(evt){
821                evt.preventDefault();
822                jQuery.ajax({
823                    dataType:'json',
824                    method: "POST",
825                    url:_genUrl(appLinks.togglescm),
826                    params:nextScheduled,
827                    success:function(data,status,xhr){
828                        console.log(data);
829                    }
830                });
831            });
832
833            var pageParams = loadJsonData('pageParams');
834            var nextScheduled = loadJsonData('nextScheduled');
835            var nextSchedList="";
836            for(var i=0; i< nextScheduled.length; i++){
837                nextSchedList = nextSchedList+nextScheduled[i]+",";
838            }
839
840            jQuery.ajax({
841                dataType:'json',
842                method: "POST",
843                url:_genUrl(appLinks.scmjobs, {nextScheduled:nextSchedList}),
844                params:nextScheduled,
845                success:function(data,status,xhr){
846                    bulkeditor.scmExportEnabled(data.scmExportEnabled);
847                    bulkeditor.scmStatus(data.scmStatus);
848                    bulkeditor.scmExportStatus(data.scmExportStatus);
849                    bulkeditor.scmExportActions(data.scmExportActions);
850                    bulkeditor.scmExportRenamed(data.scmExportRenamed);
851
852                    bulkeditor.scmImportEnabled(data.scmImportEnabled);
853                    bulkeditor.scmImportJobStatus(data.scmImportJobStatus);
854                    bulkeditor.scmImportStatus(data.scmImportStatus);
855                    bulkeditor.scmImportActions(data.scmImportActions);
856                }
857            });
858        });
859
860
861    </script>
862
863    <asset:javascript src="util/yellowfade.js"/>
864    <asset:javascript src="menu/joboptions.js"/>
865    <style type="text/css">
866    .error{
867        color:red;
868    }
869
870        #histcontent table{
871            width:100%;
872        }
873    </style>
874</head>
875<body>
876
877
878<g:if test="${flash.bulkJobResult?.errors}">
879    <div class="alert alert-dismissable alert-warning">
880        <a class="close" data-dismiss="alert" href="#" aria-hidden="true">&times;</a>
881        <ul>
882            <g:if test="${flash.bulkJobResult.errors instanceof org.springframework.validation.Errors}">
883                <g:renderErrors bean="${flash.bulkJobResult.errors}" as="list"/>
884            </g:if>
885            <g:else>
886                <g:each in="${flash.bulkJobResult.errors*.message}" var="message">
887                    <li><g:autoLink>${message}</g:autoLink></li>
888                </g:each>
889            </g:else>
890        </ul>
891    </div>
892</g:if>
893<g:if test="${flash.bulkJobResult?.success}">
894    <div class="alert alert-dismissable alert-info">
895        <a class="close" data-dismiss="alert" href="#" aria-hidden="true">&times;</a>
896        <ul>
897        <g:each in="${flash.bulkJobResult.success*.message}" var="message">
898            <li><g:autoLink>${message}</g:autoLink></li>
899        </g:each>
900        </ul>
901    </div>
902</g:if>
903<div class="runbox primary jobs" id="indexMain">
904    <div id="error" class="alert alert-danger" style="display:none;"></div>
905    <g:render template="workflowsFull" model="${[jobExpandLevel:jobExpandLevel,jobgroups:jobgroups,wasfiltered:wasfiltered?true:false, clusterMap: clusterMap,nextExecutions:nextExecutions,jobauthorizations:jobauthorizations,authMap:authMap,nowrunningtotal:nowrunningtotal,max:max,offset:offset,paginateParams:paginateParams,sortEnabled:true,rkey:rkey, clusterModeEnabled:clusterModeEnabled]}"/>
906</div>
907<div class="modal fade" id="execDiv" role="dialog" aria-labelledby="deleteFilterModalLabel" aria-hidden="true">
908    <div class="modal-dialog modal-lg">
909        <div class="modal-content">
910            <div class="modal-header">
911                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
912                <h4 class="modal-title" id="deleteFilterModalLabel"><g:message code="job.execute.action.button" /></h4>
913            </div>
914
915            <div class="" id="execDivContent">
916
917
918            </div>
919</div>
920</div>
921</div>
922
923<g:render template="/menu/copyModal"
924          model="[projectNames: projectNames]"/>
925
926<div class="row row-space" id="activity_section">
927    <div class="col-sm-12 ">
928        <h4 class="text-muted "><g:message code="page.section.Activity.for.jobs" /></h4>
929        <g:render template="/reports/activityLinks"
930                  model="[filter: [projFilter: params.project ?: request.project, jobIdFilter: '!null',], knockoutBinding: true, showTitle:true]"/>
931    </div>
932</div>
933</body>
934</html>
935