diff --git a/www/md_base/guanlecoja/config.coffee b/www/md_base/guanlecoja/config.coffee index de1387e62f7..efc194ea3f2 100644 --- a/www/md_base/guanlecoja/config.coffee +++ b/www/md_base/guanlecoja/config.coffee @@ -120,7 +120,7 @@ gulp.task 'proxy', ['processindex'], -> console.log "[Proxy] #{req.method} #{req.url}" server = http.createServer (req, res) -> - if req.url.match /^\/(api|sse)/ + if req.url.match /^\/(api|sse|avatar)/ proxy.web req, res, {target: 'http://' + argv.host} else filepath = config.dir.build + req.url.split('?')[0] diff --git a/www/md_base/src/app/app.module.coffee b/www/md_base/src/app/app.module.coffee index 65ffaf7a55b..b9614b7cf7f 100644 --- a/www/md_base/src/app/app.module.coffee +++ b/www/md_base/src/app/app.module.coffee @@ -14,3 +14,7 @@ angular.module 'app', [ .config ($mdIconProvider) -> $mdIconProvider.defaultIconSet('/icons/iconset.svg', 512) + + .constant 'angularMomentConfig', { + preprocess: 'unix' + } diff --git a/www/md_base/src/app/builds/builds.less b/www/md_base/src/app/builds/builds.less index 2419f06acf8..d1cff56b54c 100644 --- a/www/md_base/src/app/builds/builds.less +++ b/www/md_base/src/app/builds/builds.less @@ -59,7 +59,6 @@ input { width: 100%; - outline: none; text-align: center; font-size: 12px; line-height: 18px; @@ -85,7 +84,6 @@ } .builder-title { - outline: none; color: rgb(103, 58, 183); display: block; font-size: 20px; @@ -116,13 +114,15 @@ border-radius: 3px; color: #666; background: #ddd; - outline: none; } } } } .sub-content { + background: #fff; + padding: 10px; + .placeholder { text-align: center; color: #999; diff --git a/www/md_base/src/app/builds/changes/builds.changes.tpl.jade b/www/md_base/src/app/builds/changes/builds.changes.tpl.jade index 1f79764e38b..eed0aee444b 100644 --- a/www/md_base/src/app/builds/changes/builds.changes.tpl.jade +++ b/www/md_base/src/app/builds/changes/builds.changes.tpl.jade @@ -1 +1,4 @@ -h1 changes +md-list.changes-list + md-subheader.md-no-sticky Latest changes + md-divider + change-item(change="change", ng-repeat="change in changes.list") diff --git a/www/md_base/src/app/builds/changes/changes.controller.coffee b/www/md_base/src/app/builds/changes/changes.controller.coffee index 43e1f4d3487..d2505d06c61 100644 --- a/www/md_base/src/app/builds/changes/changes.controller.coffee +++ b/www/md_base/src/app/builds/changes/changes.controller.coffee @@ -1,2 +1,5 @@ class Changes extends Controller - constructor: () -> + constructor: ($scope, dataService) -> + opened = dataService.open() + opened.closeOnDestroy($scope) + @list = opened.getChanges(limit: 40, order: '-when_timestamp').getArray() diff --git a/www/md_base/src/app/builds/masters/builds.masters.tpl.jade b/www/md_base/src/app/builds/masters/builds.masters.tpl.jade index d0bc880c52c..b08d38fd6ee 100644 --- a/www/md_base/src/app/builds/masters/builds.masters.tpl.jade +++ b/www/md_base/src/app/builds/masters/builds.masters.tpl.jade @@ -1 +1,14 @@ -h1 masters +md-list.master-list + md-subheader.md-no-sticky Masters + md-divider + md-list-item.master-item(ng-repeat="master in masters.list") + div.master-id + span \#{{ master.id }} + div.master-title(flex) + h3 {{ master.name}} + p(ng-if="master.last_active > 0") Last active at + span(am-time-ago="master.last_active") + p(ng-if="master.last_active == 0") Never been active. + div.master-state + span.active(ng-if="master.active") Active + span.inactive(ng-if="!master.active") Inactive diff --git a/www/md_base/src/app/builds/masters/masters.controller.coffee b/www/md_base/src/app/builds/masters/masters.controller.coffee index c32b9cb303c..a3d53a011c3 100644 --- a/www/md_base/src/app/builds/masters/masters.controller.coffee +++ b/www/md_base/src/app/builds/masters/masters.controller.coffee @@ -1,2 +1,5 @@ class Masters extends Controller - constructor: () -> + constructor: ($scope, dataService) -> + opened = dataService.open() + opened.closeOnDestroy($scope) + @list = opened.getMasters().getArray() diff --git a/www/md_base/src/app/builds/masters/masters.less b/www/md_base/src/app/builds/masters/masters.less new file mode 100644 index 00000000000..4c03e629fbe --- /dev/null +++ b/www/md_base/src/app/builds/masters/masters.less @@ -0,0 +1,41 @@ +.master-list { + .master-item { + margin: 20px 10px; + } + + .master-id { + width: 50px; + font-size: 24px; + color: #999; + } + + .master-title { + white-space: nowrap; + + h3 { + margin: 0; + color: #673ab7; + overflow: hidden; + text-overflow: ellipsis; + } + + p { + margin: 0; + color: #999; + } + } + + .master-state { + font-weight: bold; + width: 50px; + text-align: center; + + .active { + color: #8d4; + } + + .inactive { + color: #ecc; + } + } +} diff --git a/www/md_base/src/app/builds/schedulers/builds.schedulers.tpl.jade b/www/md_base/src/app/builds/schedulers/builds.schedulers.tpl.jade index 2137703152f..31caaf6e905 100644 --- a/www/md_base/src/app/builds/schedulers/builds.schedulers.tpl.jade +++ b/www/md_base/src/app/builds/schedulers/builds.schedulers.tpl.jade @@ -1 +1,7 @@ -h1 schedulers +md-list.scheduler-list + md-subheader.md-no-sticky Schedulers + md-divider + md-list-item.scheduler-item(ng-repeat="scheduler in schedulers.list") + span.scheduler-id \#{{ scheduler.id }} + p.scheduler-name(flex) {{ scheduler.name }} + span.scheduler-master {{ scheduler.master || "No master" }} diff --git a/www/md_base/src/app/builds/schedulers/schedulers.controller.coffee b/www/md_base/src/app/builds/schedulers/schedulers.controller.coffee index 88d0b034461..1ecde3c4aa0 100644 --- a/www/md_base/src/app/builds/schedulers/schedulers.controller.coffee +++ b/www/md_base/src/app/builds/schedulers/schedulers.controller.coffee @@ -1,2 +1,5 @@ class Schedulers extends Controller - constructor: () -> + constructor: ($scope, dataService) -> + opened = dataService.open() + opened.closeOnDestroy($scope) + @list = opened.getSchedulers().getArray() diff --git a/www/md_base/src/app/builds/schedulers/schedulers.less b/www/md_base/src/app/builds/schedulers/schedulers.less new file mode 100644 index 00000000000..a5ceaef00de --- /dev/null +++ b/www/md_base/src/app/builds/schedulers/schedulers.less @@ -0,0 +1,11 @@ +.scheduler-list { + color: #999; + + .scheduler-id { + min-width: 40px; + } + + .scheduler-name { + color: #673ab7; + } +} diff --git a/www/md_base/src/app/builds/schedulers/schedulers.route.coffee b/www/md_base/src/app/builds/schedulers/schedulers.route.coffee index 3ab722f1304..6688023d04d 100644 --- a/www/md_base/src/app/builds/schedulers/schedulers.route.coffee +++ b/www/md_base/src/app/builds/schedulers/schedulers.route.coffee @@ -7,7 +7,7 @@ class State extends Config # Register new state $stateProvider.state controller: "schedulersController" - controllerAs: name + controllerAs: "schedulers" templateUrl: "views/#{name}.html" name: name url: "/schedulers" diff --git a/www/md_base/src/app/builds/slaves/builds.slaves.tpl.jade b/www/md_base/src/app/builds/slaves/builds.slaves.tpl.jade index 0133002bd61..609120cbca1 100644 --- a/www/md_base/src/app/builds/slaves/builds.slaves.tpl.jade +++ b/www/md_base/src/app/builds/slaves/builds.slaves.tpl.jade @@ -1 +1,18 @@ -h1 slaves +md-list.slave-list + md-subheader.md-no-sticky Slaves + md-divider + div.slave-item(ng-repeat="slave in slaves.list", layout="row", layout-md="column") + div.slave-title(flex-gt-md="50") + h3 {{ slave.name }} + p + | Connected to + ng-pluralize( + count="slave.connected_to.length", + when="{'0': 'no master.','one': '1 master.','other': '{} masters.'}") + p + | Configured on + ng-pluralize( + count="slave.configured_on.length", + when="{'0': 'no builder.','one': '1 builder.','other': '{} builders.'}") + div.slave-info(flex-gt-md="50") + inspect-data(data="slave.slaveinfo") diff --git a/www/md_base/src/app/builds/slaves/slaves.controller.coffee b/www/md_base/src/app/builds/slaves/slaves.controller.coffee index d608d84d7d9..0f73402f8c9 100644 --- a/www/md_base/src/app/builds/slaves/slaves.controller.coffee +++ b/www/md_base/src/app/builds/slaves/slaves.controller.coffee @@ -1,2 +1,7 @@ class Slaves extends Controller - constructor: () -> + constructor: ($scope, dataService) -> + opened = dataService.open() + opened.closeOnDestroy($scope) + # TODO: show builder names related to one slave after cache function + # of dataService has been finished. + @list = opened.getBuildslaves().getArray() diff --git a/www/md_base/src/app/builds/slaves/slaves.less b/www/md_base/src/app/builds/slaves/slaves.less new file mode 100644 index 00000000000..8d845cab475 --- /dev/null +++ b/www/md_base/src/app/builds/slaves/slaves.less @@ -0,0 +1,16 @@ +.slave-list { + .slave-item { + margin: 20px 10px; + } + + .slave-title { + h3 { + color: #673ab7; + } + + p { + margin: 5px 0; + color: #999; + } + } +} diff --git a/www/md_base/src/app/common/directives/builditem/builditem.tpl.jade b/www/md_base/src/app/common/directives/builditem/builditem.tpl.jade index 87c2c7afb85..310aa381078 100644 --- a/www/md_base/src/app/common/directives/builditem/builditem.tpl.jade +++ b/www/md_base/src/app/common/directives/builditem/builditem.tpl.jade @@ -3,4 +3,4 @@ div.inner(layout="row") span.builder-name(ng-if="builditem.showBuilder") {{ builditem.builder.name }} span.number(flex) \#{{ builditem.build.number }} span.status-text {{ builditem.build.state_string }} - span.time(am-time-ago="builditem.build.started_at", am-preprocess="unix") + span.time(am-time-ago="builditem.build.started_at") diff --git a/www/md_base/src/app/common/directives/buildstatus/buildstatus.directive.coffee b/www/md_base/src/app/common/directives/buildstatus/buildstatus.directive.coffee index 524a65305e3..42a34eb3457 100644 --- a/www/md_base/src/app/common/directives/buildstatus/buildstatus.directive.coffee +++ b/www/md_base/src/app/common/directives/buildstatus/buildstatus.directive.coffee @@ -19,16 +19,16 @@ class _BuildStatus extends Controller if build.complete is false and build.started_at > 0 @status_class = 'pending' - @icon = 'build-pending' + @icon = 'pending' else if build.results == 0 @status_class = 'success' - @icon = 'build-success' + @icon = 'checkmark' else if build.results >= 1 and build.results <= 6 @status_class = RESULTS_TEXT[build.results].toLowerCase() - @icon = 'build-fail' + @icon = 'crossmark' else @status_class = 'unknown' - @icon = 'build-pending' + @icon = 'pending' $scope.$watch 'status.build', updateBuild, true diff --git a/www/md_base/src/app/common/directives/buildstatus/buildstatus.spec.coffee b/www/md_base/src/app/common/directives/buildstatus/buildstatus.spec.coffee index 47f7866f6d9..c08b623a291 100644 --- a/www/md_base/src/app/common/directives/buildstatus/buildstatus.spec.coffee +++ b/www/md_base/src/app/common/directives/buildstatus/buildstatus.spec.coffee @@ -24,7 +24,7 @@ describe 'buildstatus', -> icon = elem.children().eq(0) # unknown status - expect(icon.attr('md-svg-icon')).toBe('build-pending') + expect(icon.attr('md-svg-icon')).toBe('pending') expect(icon.hasClass('unknown')).toBe(true) expect(icon.hasClass('pending')).toBe(false) for _, text of RESULTS_TEXT @@ -33,7 +33,7 @@ describe 'buildstatus', -> # pending status $rootScope.build.started_at = (new Date()).valueOf() $rootScope.$digest() - expect(icon.attr('md-svg-icon')).toBe('build-pending') + expect(icon.attr('md-svg-icon')).toBe('pending') expect(icon.hasClass('unknown')).toBe(false) expect(icon.hasClass('pending')).toBe(true) for _, text of RESULTS_TEXT @@ -43,7 +43,7 @@ describe 'buildstatus', -> $rootScope.build.complete = true $rootScope.build.results = 0 $rootScope.$digest() - expect(icon.attr('md-svg-icon')).toBe('build-success') + expect(icon.attr('md-svg-icon')).toBe('checkmark') expect(icon.hasClass('unknown')).toBe(false) expect(icon.hasClass('pending')).toBe(false) for code, text of RESULTS_TEXT @@ -53,7 +53,7 @@ describe 'buildstatus', -> for i in [1..6] $rootScope.build.results = i $rootScope.$digest() - expect(icon.attr('md-svg-icon')).toBe('build-fail') + expect(icon.attr('md-svg-icon')).toBe('crossmark') expect(icon.hasClass('unknown')).toBe(false) expect(icon.hasClass('pending')).toBe(false) for code, text of RESULTS_TEXT diff --git a/www/md_base/src/app/common/directives/changeitem/changeitem.directive.coffee b/www/md_base/src/app/common/directives/changeitem/changeitem.directive.coffee new file mode 100644 index 00000000000..c7b6e342726 --- /dev/null +++ b/www/md_base/src/app/common/directives/changeitem/changeitem.directive.coffee @@ -0,0 +1,37 @@ +class ChangeItem extends Directive + + constructor: -> + return { + restrict: 'E' + templateUrl: 'views/changeitem.html' + controller: '_ChangeItemController' + controllerAs: 'changeitem' + bindToController: true + scope: + change: "=" + } + +class _ChangeItem extends Controller + showDetail: false + + toggleDetail: -> + @showDetail = !@showDetail + + constructor: (dataService) -> + @author = @change.author + @revision = @change.revision[0...6] + @comments = @change.comments + @date = @change.when_timestamp + @files = @change.files + + # Official W3C email regular expression + emailRegex = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/ + email = emailRegex.exec @author + + @email = email[0] if email + + @displayData = + 'Repository': @change.repository + 'Branch': @change.branch + 'Revision': @change.revision + diff --git a/www/md_base/src/app/common/directives/changeitem/changeitem.less b/www/md_base/src/app/common/directives/changeitem/changeitem.less new file mode 100644 index 00000000000..03645a1b14a --- /dev/null +++ b/www/md_base/src/app/common/directives/changeitem/changeitem.less @@ -0,0 +1,79 @@ +change-item { + display: block; + padding: 5px; + + .inner { + line-height: 30px; + font-size: 16px; + color: #999; + + span { + padding: 0 5px; + } + } + + .author { + height: 20px; + width: 20px; + overflow: hidden; + border-radius: 50%; + margin: 5px; + + img { + width: 100%; + height: 100%; + } + } + + .comments { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .revision { + width: 80px; + text-align: right; + font-family: monospace; + color: #666; + } + + .detail { + color: #666; + margin-left: 30px; + padding: 10px 0; + + &>div { + border-bottom: 1px solid #eee; + padding-bottom: 10px; + } + } + + .sub-title { + margin: 10px 0; + text-transform: uppercase; + font-size: 12px; + font-weight: bold; + } + + .meta { + .author-name { + font-weight: bold; + } + + .time { + float: right; + } + } + + .full-comment { + + .content { + white-space: pre-wrap; + } + } + + .changed-files { + + } +} diff --git a/www/md_base/src/app/common/directives/changeitem/changeitem.spec.coffee b/www/md_base/src/app/common/directives/changeitem/changeitem.spec.coffee new file mode 100644 index 00000000000..88da6c6840d --- /dev/null +++ b/www/md_base/src/app/common/directives/changeitem/changeitem.spec.coffee @@ -0,0 +1,106 @@ +beforeEach module 'app' + +describe 'changeitem', -> + $compile = $rootScope = $httpBackend = null + injected = ($injector) -> + $compile = $injector.get('$compile') + $rootScope = $injector.get('$rootScope') + webSocketService = $injector.get('webSocketService') + spyOn(webSocketService, 'getWebSocket').and.returnValue({}) + + beforeEach inject injected + + today = new Date() + + # mock object for test + test_change = + author: 'testauthor ' + comments: 'test comments' + when_timestamp: (new Date(today.getFullYear(), today.getMonth(), today.getDate())).getTime()/1000 + repository: 'testrepo' + branch: 'testbranch' + revision: 'abcdefghijk' + files: [ + 'testfile1', + 'testfile2', + 'testfile3', + ] + + + it 'should display information correctly', -> + $rootScope.change = test_change + elem = $compile('')($rootScope) + $rootScope.$digest() + + # test on change-item when it is collapsed (detail not showing) + expect(elem.children().length).toBe(1) + inner = elem.children().eq(0) + expect(inner.children().length).toBe(3) + + author = inner.children().eq(0) + comment = inner.children().eq(1) + revision = inner.children().eq(2) + + expect(author.attr('title')).toBe(test_change.author) + expect(author.attr('href')).toBe('mailto:test@email.com') + expect(comment.text()).toBe(test_change.comments) + expect(revision.text()).toBe('abcdef') + + avatar = author.children().eq(0) + # test img url to the users avatar + expect(avatar.attr('src')).toBe('avatar?email=test@email.com') + + + it 'should show detail correctly', -> + $rootScope.change = test_change + elem = $compile('')($rootScope) + $rootScope.$digest() + + expect(elem.children().length).toBe(1) + inner = elem.children().eq(0) + comment = inner.children().eq(1) + # Expand the directive and show detail + comment.triggerHandler('click') + expect(elem.children().length).toBe(2) + + # Detail should be showing + detail = elem.children().eq(1) + expect(detail.children().length).toBe(4) + + # Retrieve components + meta = detail.children().eq(0) + comment = detail.children().eq(1).children().eq(1) + files = detail.children().eq(2).children() + data = detail.children().eq(3).children().eq(0) + + # Displaying date time + date = meta.children().eq(0) + expect(date.attr('title')).toBe('Today at 12:00 AM') + + # Displaying author + authortitle = meta.children().eq(1) + expect(authortitle.text().trim()).toBe(test_change.author) + + # Displaying comment + expect(comment.text()).toBe(test_change.comments) + + # Displaying changed files + expect(files.length - 1).toBe(test_change.files.length) + for i in [0...3] + expect(files.eq(i + 1).text()).toBe(test_change.files[i]) + + # Displaying extra data + expect(data.children().length).toBe(3) + for i in [0...3] + row = data.children().eq(i) + key = row.children().eq(0).text() + value = row.children().eq(1).text() + switch key + when 'Repository' + expect(value).toBe(test_change.repository) + when 'Branch' + expect(value).toBe(test_change.branch) + when 'Revision' + expect(value).toBe(test_change.revision) + else + expect(key).toBe('') diff --git a/www/md_base/src/app/common/directives/changeitem/changeitem.tpl.jade b/www/md_base/src/app/common/directives/changeitem/changeitem.tpl.jade new file mode 100644 index 00000000000..db0524f0423 --- /dev/null +++ b/www/md_base/src/app/common/directives/changeitem/changeitem.tpl.jade @@ -0,0 +1,18 @@ +div.inner(layout="row") + a.author(title="{{ changeitem.author }}", ng-href="mailto:{{ changeitem.email }}") + img(ng-if="changeitem.email", ng-src="avatar?email={{ changeitem.email }}") + a.comments(flex, ng-click="changeitem.toggleDetail()") {{ changeitem.comments }} + span.revision {{ changeitem.revision }} +div.detail(ng-if="changeitem.showDetail") + div.meta + span.time(am-time-ago="changeitem.date", title="{{ changeitem.date|amCalendar }}") + span.author-name {{ changeitem.author }} + div.full-comment + div.sub-title Comments + div.content {{ changeitem.comments}} + div.changed-files(ng-if="changeitem.files.length > 0") + div.sub-title Changed files + div.file-line(ng-repeat="file in changeitem.files") {{ file }} + + inspect-data(data="changeitem.displayData") + diff --git a/www/md_base/src/app/home/panels/overview/overview_panel.tpl.jade b/www/md_base/src/app/home/panels/overview/overview_panel.tpl.jade index dd62d3e339d..8535811a7e3 100644 --- a/www/md_base/src/app/home/panels/overview/overview_panel.tpl.jade +++ b/www/md_base/src/app/home/panels/overview/overview_panel.tpl.jade @@ -19,4 +19,4 @@ div.tile div.title Schedulers div.count {{ overview.schedulers.count }} div.extra - a(ui-sref="builds") View all schedulers + a(ui-sref="builds.schedulers") View all schedulers diff --git a/www/md_base/src/app/index.jade b/www/md_base/src/app/index.jade index 1b568d9cd61..46939351254 100644 --- a/www/md_base/src/app/index.jade +++ b/www/md_base/src/app/index.jade @@ -21,7 +21,7 @@ html(ng-app="app", ng-controller="appController as app") md-icon(md-svg-icon="navicon") b Buildbot span.title(ng-bind="app.view.title") - md-content.md-padding(flex, ui-view, ng-class="app.view.classname") + md-content.main-content.md-padding(flex, ui-view, ng-class="app.view.classname") h1 Hello buildbot! script(src="scripts.js?_#{(new Date()).getTime()}") diff --git a/www/md_base/src/icons/build-success.svg b/www/md_base/src/icons/checkmark.svg similarity index 100% rename from www/md_base/src/icons/build-success.svg rename to www/md_base/src/icons/checkmark.svg diff --git a/www/md_base/src/icons/build-fail.svg b/www/md_base/src/icons/crossmark.svg similarity index 100% rename from www/md_base/src/icons/build-fail.svg rename to www/md_base/src/icons/crossmark.svg diff --git a/www/md_base/src/icons/build-pending.svg b/www/md_base/src/icons/pending.svg similarity index 100% rename from www/md_base/src/icons/build-pending.svg rename to www/md_base/src/icons/pending.svg diff --git a/www/md_base/src/styles/main-content.less b/www/md_base/src/styles/main-content.less index 6b562130ee7..af3e3c5c158 100644 --- a/www/md_base/src/styles/main-content.less +++ b/www/md_base/src/styles/main-content.less @@ -22,6 +22,6 @@ } } -md-content[ui-view].md-default-theme { +.main-content { background: #efefef; } diff --git a/www/md_base/src/styles/styles.less b/www/md_base/src/styles/styles.less index 22f650e7fbf..f2b7213ddb1 100644 --- a/www/md_base/src/styles/styles.less +++ b/www/md_base/src/styles/styles.less @@ -17,3 +17,8 @@ -ms-user-select: none; user-select: none; } + +*[ng-click] { + // override the outline added by angular material + outline: none !important; +}