diff --git a/CHANGELOG b/CHANGELOG index 2d05a51e77d8df5559a6c323e866e3124e5244e2..65344736e9c19fb53582decbc354d5de76a5cf66 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +v 5.0.0 + - replaced gitolite with gitlab-shell + +v 4.2.0 + - User show page. Via /u/username + - Show help contents on pages for better navigation + v 4.1.0 - Optional Sign-Up - Discussions diff --git a/Gemfile b/Gemfile index 96b5bb6a074f2b86d8748ff4072b51418c07133f..23adddced7569138fe9d5b13462202f5b374f4b6 100644 --- a/Gemfile +++ b/Gemfile @@ -32,9 +32,6 @@ gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap" # Dump db to yml file. Mostly used to migrate from sqlite to mysql gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db" -# Gitolite client (for work with gitolite-admin repo) -gem "gitolite", '1.1.0' - # Syntax highlighter gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master" diff --git a/Gemfile.lock b/Gemfile.lock index 7bf31c95babd5aefafa42bc586678740f6993e40..d91dbf7e82624f2bc7f3e116e7fbd2773e9cf706 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -107,7 +107,6 @@ GEM coderay (>= 1.0.0) erubis (>= 2.7.0) binding_of_caller (0.6.8) - blankslate (3.1.2) bootstrap-sass (2.2.1.1) sass (~> 3.2) builder (3.0.4) @@ -195,10 +194,6 @@ GEM pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.1.1) gitlab_yaml_db (1.0.0) - gitolite (1.1.0) - gratr19 (~> 0.4.4.1) - grit (~> 2.5.0) - hashery (~> 1.5.0) grape (0.2.2) activesupport hashie (~> 1.2) @@ -208,7 +203,6 @@ GEM rack-accept rack-mount virtus - gratr19 (0.4.4.1) growl (1.0.3) guard (1.5.4) listen (>= 0.4.2) @@ -227,8 +221,6 @@ GEM activesupport (>= 3.1, < 4.1) haml (~> 3.1) railties (>= 3.1, < 4.1) - hashery (1.5.0) - blankslate hashie (1.2.0) hike (1.2.1) http_parser.rb (0.5.3) @@ -497,7 +489,6 @@ DEPENDENCIES gitlab_meta (= 4.0) gitlab_omniauth-ldap (= 1.0.2) gitlab_yaml_db (= 1.0.0) - gitolite (= 1.1.0) grack! grape (~> 0.2.1) grit! diff --git a/Procfile b/Procfile index 21dfade1d8a8c469aff48b4d779e722d2994a336..1a4145cc7d3b5a7d45a1a748d030cfc2c8553689 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ web: bundle exec unicorn_rails -p $PORT -worker: bundle exec sidekiq -q post_receive,mailer,system_hook,common,default +worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitolite diff --git a/README.md b/README.md index 1a43be134da284a96be9f02324fbf0f8daa482c5..ee029f9bfcb6875d8ccc96338c7b433c39323b5f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ GitLab is a free project and repository management application - +[](http://ci.gitlab.org/projects/1?ref=master) ## Application details diff --git a/ROADMAP.md b/ROADMAP.md index acfd2eded973edfa616f3022557db83d8839d6d4..d148b518b0ee34028fcbcaa96874a335d48c0dda 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,13 +1,12 @@ ## GitLab Roadmap -### v4.3 March 22 +### v5.0 March 22 -* Jenkins CI integration service +* Replace gitolite with gitlab-shell * Usability improvements * Notification improvements ### v4.2 February 22 -* Campfire integration service * Teams diff --git a/VERSION b/VERSION index 87db9036a822108fd68f9be0ad2186f2760e1155..0ceadf72ef75f83a2758b5be089772584a167cd2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.1.0rc1 +5.0.0pre diff --git a/app/assets/images/home_icon.PNG b/app/assets/images/home_icon.PNG deleted file mode 100644 index b1d60d5935760d7f7c448a6ea3e7270fe405a664..0000000000000000000000000000000000000000 Binary files a/app/assets/images/home_icon.PNG and /dev/null differ diff --git a/app/assets/images/icon-attachment.png b/app/assets/images/icon-attachment.png deleted file mode 100644 index 168ad8dce3765ac98968def2101ba9f298e9f637..0000000000000000000000000000000000000000 Binary files a/app/assets/images/icon-attachment.png and /dev/null differ diff --git a/app/assets/images/onion_skin_sprites.gif b/app/assets/images/onion_skin_sprites.gif new file mode 100644 index 0000000000000000000000000000000000000000..85d20260edcad0f13825b8b70d930133c9e54beb Binary files /dev/null and b/app/assets/images/onion_skin_sprites.gif differ diff --git a/app/assets/images/swipemode_sprites.gif b/app/assets/images/swipemode_sprites.gif new file mode 100644 index 0000000000000000000000000000000000000000..327b3c31ffd3547bbc0e4b9fa7c81c4bcfdfc59e Binary files /dev/null and b/app/assets/images/swipemode_sprites.gif differ diff --git a/app/assets/javascripts/commit/file.js.coffee b/app/assets/javascripts/commit/file.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..a45ee58d1b4924ce3c26ceabb43e066c677de4a5 --- /dev/null +++ b/app/assets/javascripts/commit/file.js.coffee @@ -0,0 +1,7 @@ +class CommitFile + + constructor: (file) -> + if $('.image', file).length + new ImageFile(file) + +this.CommitFile = CommitFile \ No newline at end of file diff --git a/app/assets/javascripts/commit/image-file.js.coffee b/app/assets/javascripts/commit/image-file.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..f901a9e28ffeefebc42593bb07b1f9880efa8512 --- /dev/null +++ b/app/assets/javascripts/commit/image-file.js.coffee @@ -0,0 +1,128 @@ +class ImageFile + + # Width where images must fits in, for 2-up this gets divided by 2 + @availWidth = 900 + @viewModes = ['two-up', 'swipe'] + + constructor: (@file) -> + # Determine if old and new file has same dimensions, if not show 'two-up' view + this.requestImageInfo $('.two-up.view .frame.deleted img', @file), (deletedWidth, deletedHeight) => + this.requestImageInfo $('.two-up.view .frame.added img', @file), (width, height) => + if width == deletedWidth && height == deletedHeight + this.initViewModes() + else + this.initView('two-up') + + initViewModes: -> + viewMode = ImageFile.viewModes[0] + + $('.view-modes', @file).removeClass 'hide' + $('.view-modes-menu', @file).on 'click', 'li', (event) => + unless $(event.currentTarget).hasClass('active') + this.activateViewMode(event.currentTarget.className) + + this.activateViewMode(viewMode) + + activateViewMode: (viewMode) -> + $('.view-modes-menu li', @file) + .removeClass('active') + .filter(".#{viewMode}").addClass 'active' + $(".view:visible:not(.#{viewMode})", @file).fadeOut 200, => + $(".view.#{viewMode}", @file).fadeIn(200) + this.initView viewMode + + initView: (viewMode) -> + this.views[viewMode].call(this) + + prepareFrames = (view) -> + maxWidth = 0 + maxHeight = 0 + $('.frame', view).each (index, frame) => + width = $(frame).width() + height = $(frame).height() + maxWidth = if width > maxWidth then width else maxWidth + maxHeight = if height > maxHeight then height else maxHeight + .css + width: maxWidth + height: maxHeight + + [maxWidth, maxHeight] + + views: + 'two-up': -> + $('.two-up.view .wrap', @file).each (index, wrap) => + $('img', wrap).each -> + currentWidth = $(this).width() + if currentWidth > ImageFile.availWidth / 2 + $(this).width ImageFile.availWidth / 2 + + this.requestImageInfo $('img', wrap), (width, height) -> + $('.image-info .meta-width', wrap).text "#{width}px" + $('.image-info .meta-height', wrap).text "#{height}px" + $('.image-info', wrap).removeClass('hide') + + 'swipe': -> + maxWidth = 0 + maxHeight = 0 + + $('.swipe.view', @file).each (index, view) => + + [maxWidth, maxHeight] = prepareFrames(view) + + $('.swipe-frame', view).css + width: maxWidth + 16 + height: maxHeight + 28 + + $('.swipe-wrap', view).css + width: maxWidth + 1 + height: maxHeight + 2 + + $('.swipe-bar', view).css + left: 0 + .draggable + axis: 'x' + containment: 'parent' + drag: (event) -> + $('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left + stop: (event) -> + $('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left + + 'onion-skin': -> + maxWidth = 0 + maxHeight = 0 + + dragTrackWidth = $('.drag-track', @file).width() - $('.dragger', @file).width() + + $('.onion-skin.view', @file).each (index, view) => + + [maxWidth, maxHeight] = prepareFrames(view) + + $('.onion-skin-frame', view).css + width: maxWidth + 16 + height: maxHeight + 28 + + $('.swipe-wrap', view).css + width: maxWidth + 1 + height: maxHeight + 2 + + $('.dragger', view).css + left: dragTrackWidth + .draggable + axis: 'x' + containment: 'parent' + drag: (event) -> + $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth) + stop: (event) -> + $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth) + + + + requestImageInfo: (img, callback) -> + domImg = img.get(0) + if domImg.complete + callback.call(this, domImg.naturalWidth, domImg.naturalHeight) + else + img.on 'load', => + callback.call(this, domImg.naturalWidth, domImg.naturalHeight) + +this.ImageFile = ImageFile \ No newline at end of file diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js deleted file mode 100644 index b31fe485896865d09f0104af225fac248a144886..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/commits.js +++ /dev/null @@ -1,59 +0,0 @@ -var CommitsList = { - ref:null, - limit:0, - offset:0, - disable:false, - - init: - function(ref, limit) { - $(".day-commits-table li.commit").live('click', function(e){ - if(e.target.nodeName != "A") { - location.href = $(this).attr("url"); - e.stopPropagation(); - return false; - } - }); - - this.ref=ref; - this.limit=limit; - this.offset=limit; - this.initLoadMore(); - $('.loading').show(); - }, - - getOld: - function() { - $('.loading').show(); - $.ajax({ - type: "GET", - url: location.href, - data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref, - complete: function(){ $('.loading').hide()}, - dataType: "script"}); - }, - - append: - function(count, html) { - $("#commits_list").append(html); - if(count > 0) { - this.offset += count; - } else { - this.disable = true; - } - }, - - initLoadMore: - function() { - $(document).endlessScroll({ - bottomPixels: 400, - fireDelay: 1000, - fireOnce:true, - ceaseFire: function() { - return CommitsList.disable; - }, - callback: function(i) { - CommitsList.getOld(); - } - }); - } -} diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..47d6fcf80895f1df1b11512b92866fa2ebe4b3b9 --- /dev/null +++ b/app/assets/javascripts/commits.js.coffee @@ -0,0 +1,54 @@ +class CommitsList + @data = + ref: null + limit: 0 + offset: 0 + @disable = false + + @showProgress: -> + $('.loading').show() + + @hideProgress: -> + $('.loading').hide() + + @init: (ref, limit) -> + $(".day-commits-table li.commit").live 'click', (event) -> + if event.target.nodeName != "A" + location.href = $(this).attr("url") + e.stopPropagation() + return false + + @data.ref = ref + @data.limit = limit + @data.offset = limit + + this.initLoadMore() + this.showProgress(); + + @getOld: -> + this.showProgress() + $.ajax + type: "GET" + url: location.href + data: @data + complete: this.hideProgress + dataType: "script" + + @append: (count, html) -> + $("#commits-list").append(html) + if count > 0 + @data.offset += count + else + @disable = true + + @initLoadMore: -> + $(document).endlessScroll + bottomPixels: 400 + fireDelay: 1000 + fireOnce: true + ceaseFire: => + @disable + callback: => + this.getOld() + +this.CommitsList = CommitsList \ No newline at end of file diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee index f15d09dd475bbf900e4054d9d0df24a9f5a94f9e..6171e0d50fdc0fcc89641c5d95018c3d4b0b324e 100644 --- a/app/assets/javascripts/dashboard.js.coffee +++ b/app/assets/javascripts/dashboard.js.coffee @@ -4,11 +4,11 @@ window.dashboardPage = -> event.preventDefault() toggleFilter $(this) reloadActivities() - + reloadActivities = -> $(".content_list").html '' Pager.init 20, true - + toggleFilter = (sender) -> sender.parent().toggleClass "inactive" event_filters = $.cookie("event_filter") @@ -17,11 +17,11 @@ toggleFilter = (sender) -> event_filters = event_filters.split(",") else event_filters = new Array() - + index = event_filters.indexOf(filter) if index is -1 event_filters.push filter else event_filters.splice index, 1 - + $.cookie "event_filter", event_filters.join(",") diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee index 9c9cc6132b354d9d0220c6ecc1bb1102f9d12bca..65ed817c7c6ee0b76ec265eab10bdbff5ed30be7 100644 --- a/app/assets/javascripts/merge_requests.js.coffee +++ b/app/assets/javascripts/merge_requests.js.coffee @@ -1,6 +1,6 @@ # # * Filter merge requests -# +# @merge_requestsPage = -> $('#assignee_id').chosen() $('#milestone_id').chosen() @@ -8,16 +8,16 @@ $(this).closest('form').submit() class MergeRequest - + constructor: (@opts) -> this.$el = $('.merge-request') @diffs_loaded = false @commits_loaded = false - + this.activateTab(@opts.action) - + this.bindEvents() - + this.initMergeWidget() this.$('.show-all-commits').on 'click', => this.showAllCommits() @@ -28,7 +28,7 @@ class MergeRequest initMergeWidget: -> this.showState( @opts.current_state ) - + if this.$('.automerge_widget').length and @opts.check_enable $.get @opts.url_to_automerge_check, (data) => this.showState( data.state ) @@ -42,12 +42,12 @@ class MergeRequest bindEvents: -> this.$('.nav-tabs').on 'click', 'a', (event) => a = $(event.currentTarget) - + href = a.attr('href') History.replaceState {path: href}, document.title, href - + event.preventDefault() - + this.$('.nav-tabs').on 'click', 'li', (event) => this.activateTab($(event.currentTarget).data('action')) diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 8a7e08dd0e8f30ce06968184679f865c3895cf01..919c6b7f4a2dcb3c4de41dc37d3099825e822f68 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -20,12 +20,12 @@ var NoteList = { if(NoteList.reversed) { var form = $(".js-main-target-form"); - form.find(".buttons, .note_options").hide(); + form.find(".note-form-actions").hide(); var textarea = form.find(".js-note-text"); textarea.css("height", "40px"); textarea.on("focus", function(){ textarea.css("height", "80px"); - form.find(".buttons, .note_options").show(); + form.find(".note-form-actions").show(); }); } diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js deleted file mode 100644 index 7edd6bd61860aa29b0994c451e5c73bfcd848d92..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/pager.js +++ /dev/null @@ -1,56 +0,0 @@ -var Pager = { - limit:0, - offset:0, - disable:false, - - init: - function(limit, preload) { - this.limit=limit; - - if(preload) { - this.offset = 0; - this.getOld(); - } else { - this.offset = limit; - } - - this.initLoadMore(); - }, - - getOld: - function() { - $('.loading').show(); - $.ajax({ - type: "GET", - url: location.href, - data: "limit=" + this.limit + "&offset=" + this.offset, - complete: function(){ $('.loading').hide()}, - dataType: "script"}); - }, - - append: - function(count, html) { - $(".content_list").append(html); - if(count > 0) { - this.offset += count; - } else { - this.disable = true; - } - }, - - initLoadMore: - function() { - $(document).endlessScroll({ - bottomPixels: 400, - fireDelay: 1000, - fireOnce:true, - ceaseFire: function() { - return Pager.disable; - }, - callback: function(i) { - $('.loading').show(); - Pager.getOld(); - } - }); - } -} diff --git a/app/assets/javascripts/pager.js.coffee b/app/assets/javascripts/pager.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..5f606acdf9cc523c8f47eb1a03ca0e58e3d9fc6f --- /dev/null +++ b/app/assets/javascripts/pager.js.coffee @@ -0,0 +1,42 @@ +@Pager = + limit: 0 + offset: 0 + disable: false + init: (limit, preload) -> + @limit = limit + if preload + @offset = 0 + @getOld() + else + @offset = limit + @initLoadMore() + + getOld: -> + $(".loading").show() + $.ajax + type: "GET" + url: location.href + data: "limit=" + @limit + "&offset=" + @offset + complete: -> + $(".loading").hide() + + dataType: "script" + + append: (count, html) -> + $(".content_list").append html + if count > 0 + @offset += count + else + @disable = true + + initLoadMore: -> + $(document).endlessScroll + bottomPixels: 400 + fireDelay: 1000 + fireOnce: true + ceaseFire: -> + Pager.disable + + callback: (i) -> + $(".loading").show() + Pager.getOld() diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index db077048b6cf8283420984a9caf1acb8eaf7f7de..c9a11d0a1acf5efce28dc524998701cd7324218c 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -1,5 +1,5 @@ html { - overflow-y: scroll; + overflow-y: scroll; } /** LAYOUT **/ @@ -277,8 +277,20 @@ p.time { } } +.search-holder { + label, input { + height: 30px; + padding: 0; + font-size: 14px; + } + label { + line-height: 30px; + color: #666; + } +} + .highlight_word { - background: #EEDC94; + border-bottom: 2px solid #F90; } .status_info { @@ -326,10 +338,6 @@ li.note { li { border-bottom:none !important; } - .file { - padding-left: 20px; - background:url("icon-attachment.png") no-repeat left center; - } } } diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss index f53e0e50bab2cb1343291e489a2dc82d3ca1f99d..2ad1bf944a94bdcaa170c412716893936fd79951 100644 --- a/app/assets/stylesheets/gitlab_bootstrap.scss +++ b/app/assets/stylesheets/gitlab_bootstrap.scss @@ -17,6 +17,8 @@ $baseLineHeight: 18px !default; @import "gitlab_bootstrap/variables.scss"; @import "gitlab_bootstrap/fonts.scss"; @import "gitlab_bootstrap/mixins.scss"; +@import "gitlab_bootstrap/avatar.scss"; +@import "gitlab_bootstrap/nav.scss"; @import "gitlab_bootstrap/common.scss"; @import "gitlab_bootstrap/typography.scss"; @import "gitlab_bootstrap/buttons.scss"; diff --git a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss new file mode 100644 index 0000000000000000000000000000000000000000..de1fb1551bf74da2c74f48044b6d5179acf4ffce --- /dev/null +++ b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss @@ -0,0 +1,8 @@ +/** AVATARS **/ +img.avatar { float: left; margin-right: 12px; width: 40px; border: 1px solid #ddd; padding: 1px; } +img.avatar.s16 { width: 16px; height: 16px; margin-right: 6px; } +img.avatar.s24 { width: 24px; height: 24px; margin-right: 8px; } +img.avatar.s32 { width: 32px; height: 32px; margin-right: 10px; } +img.avatar.s90 { width: 90px; height: 90px; margin-right: 15px; } +img.lil_av { padding-left: 4px; padding-right: 3px; } +img.small { width: 80px; } diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss index 8cb1c045778ef3aa8b990c6fae0fc34fe4dbbbdd..4d1b64463629168abed98373526e8a5946afc12a 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss @@ -95,7 +95,11 @@ form { margin-bottom: 0; - margin-top: 3px; + margin-top: 0; + } + + .btn-tiny { + @include box-shadow(0 0px 0px 1px #f1f1f1); } .nav-pills { diff --git a/app/assets/stylesheets/gitlab_bootstrap/buttons.scss b/app/assets/stylesheets/gitlab_bootstrap/buttons.scss index 674481e2cd243528a59ce04b2fcceaaa1e6584ae..03497e32d267c1c9dd4b4282dce7d59fdcd83a89 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/buttons.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/buttons.scss @@ -1,17 +1,16 @@ .btn { - @include linear-gradient(#f7f7f7, #d5d5d5); + @include linear-gradient(#f1f1f1, #e1e1e1); + text-shadow: 0 1px 1px #FFF; border-color: #BBB; + &:hover { - @include bg-gray-gradient; - border-color: #bbb; + background: #f1f1f1; + @include linear-gradient(#fAfAfA, #f1f1f1); + border-color: #AAA; color: #333; } - &.btn-white { - background: #FFF; - } - - &.primary { + &.btn-primary { background: #2a79A3; @include linear-gradient(#47A7b7, #2585b5); border-color: #2A79A3; @@ -58,21 +57,18 @@ } } - &.save-btn { + &.btn-create { @extend .wide; - @extend .primary; + @extend .success; } - &.cancel-btn { - float: right; - } - - &.wide { - padding-left: 30px; - padding-right: 30px; + &.btn-save { + @extend .wide; + @extend .btn-primary; } - &.danger { + &.btn-close, + &.btn-remove { @extend .btn-danger; border-color: #BD362F; @@ -82,8 +78,13 @@ } } - &.danger { - @extend .btn-danger; + &.btn-cancel { + float: right; + } + + &.wide { + padding-left: 20px; + padding-right: 20px; } &.small { @@ -95,7 +96,7 @@ background-color: #ccc; } - &.very_small { + &.btn-tiny { font-size: 11px; padding: 2px 6px; line-height: 16px; diff --git a/app/assets/stylesheets/gitlab_bootstrap/common.scss b/app/assets/stylesheets/gitlab_bootstrap/common.scss index 2b3d14abda8b9c39cc46d110a7a4052c396857d1..cb292bc711fa14123278a3e0bf3590752ebcd97b 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/common.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/common.scss @@ -9,7 +9,6 @@ /** COMMON CLASSES **/ .left { float:left } -.right { float:right!important } .append-bottom-10 { margin-bottom:10px } .append-bottom-20 { margin-bottom:20px } .prepend-top-10 { margin-top:10px } @@ -22,79 +21,13 @@ .light { color: #888 } .tiny { font-weight: normal } -/** PILLS & TABS**/ -.nav-pills { - .active a { - background: $primary_color; - } - - > li > a { - @include border-radius(0); - } - &.nav-stacked { - > li > a { - border-left: 4px solid #EEE; - padding: 12px; - } - > .active > a { - border-color: #29B; - border-radius: 0; - background: #F1F1F1; - color: $style_color; - font-weight: bold; - } - } -} - -.nav-pills > .active > a > i[class^="icon-"] { background: inherit; } - - - -/** - * nav-tabs - * - */ -.nav-tabs > li > a, .nav-pills > li > a { color: $style_color; } -.nav.nav-tabs { - li { - > a { - padding: 8px 20px; - margin-right: 7px; - line-height: 20px; - border-color: #EEE; - color: #888; - border-bottom: 1px solid #ddd; - .badge { - background-color: #eee; - color: #888; - text-shadow: 0 1px 1px #fff; - } - i[class^="icon-"] { - line-height: 14px; - } - } - &.active { - > a { - border-color: #CCC; - border-bottom: 1px solid #fff; - color: #333; - } - } - } -} /** ALERT MESSAGES **/ -.alert-message { @extend .alert; } -.alert-messag.success { @extend .alert-success; } -.alert-message.error { @extend .alert-error; } - -/** AVATARS **/ -img.avatar { float: left; margin-right: 12px; width: 40px; border: 1px solid #ddd; padding: 1px; } -img.avatar.s16 { width: 16px; height: 16px; margin-right: 6px; } -img.avatar.s24 { width: 24px; height: 24px; margin-right: 8px; } -img.avatar.s32 { width: 32px; height: 32px; margin-right: 10px; } -img.lil_av { padding-left: 4px; padding-right: 3px; } -img.small { width: 80px; } +.alert.alert-disabled { + background: #EEE; + color: #777; + border-color: #DDD; +} /** HELPERS **/ .nothing_here_message { diff --git a/app/assets/stylesheets/gitlab_bootstrap/files.scss b/app/assets/stylesheets/gitlab_bootstrap/files.scss index 865a10e1fd7a13f0121a32529d8da08721cf7fcb..279cfcd2103132c3dd6938090f3c640045d19957 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/files.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/files.scss @@ -135,7 +135,7 @@ pre { border: none; border-radius: 0; - font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; + font-family: $monospace_font; font-size: 12px !important; line-height: 16px !important; margin: 0; diff --git a/app/assets/stylesheets/gitlab_bootstrap/fonts.scss b/app/assets/stylesheets/gitlab_bootstrap/fonts.scss index b4217fa9cfa7f2151790533d285a82b3519fe75b..a0c9a6c7b8aba9d7eeb6fa616bfe5a6bb21667c5 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/fonts.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/fonts.scss @@ -4,4 +4,4 @@ } /** Typo **/ -$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; +$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/gitlab_bootstrap/nav.scss new file mode 100644 index 0000000000000000000000000000000000000000..2eaef61ca333bf7f9a8d3a39c7ccb76a05aaf944 --- /dev/null +++ b/app/assets/stylesheets/gitlab_bootstrap/nav.scss @@ -0,0 +1,65 @@ +/** + * nav-pills + * + */ +.nav-pills { + .active a { + background: $primary_color; + } + + > li > a { + @include border-radius(0); + } + &.nav-stacked { + > li > a { + border-left: 4px solid #EEE; + padding: 12px; + } + > .active > a { + border-color: #29B; + border-radius: 0; + background: #F1F1F1; + color: $style_color; + font-weight: bold; + } + } +} + +.nav-pills > .active > a > i[class^="icon-"] { background: inherit; } + + + +/** + * nav-tabs + * + */ +.nav-tabs > li > a, .nav-pills > li > a { color: $style_color; } +.nav.nav-tabs { + li { + > a { + padding: 8px 20px; + margin-right: 7px; + line-height: 20px; + border-color: #EEE; + color: #888; + border-bottom: 1px solid #ddd; + .badge { + background-color: #eee; + color: #888; + text-shadow: 0 1px 1px #fff; + } + i[class^="icon-"] { + line-height: 14px; + } + } + &.active { + > a { + border-color: #CCC; + border-bottom: 1px solid #fff; + color: #333; + } + } + } + + &.nav-small-tabs > li > a { padding: 6px 9px; } +} diff --git a/app/assets/stylesheets/gitlab_bootstrap/typography.scss b/app/assets/stylesheets/gitlab_bootstrap/typography.scss index e74a0de125bedfe09942352c434df1bd193a11e1..781577c214754030f83d11b68794f024faa98d7d 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/typography.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/typography.scss @@ -21,7 +21,7 @@ h6 { /** CODE **/ pre { - font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; + font-family: $monospace_font; &.dark { background: #333; @@ -79,7 +79,7 @@ a:focus { } .monospace { - font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; + font-family: $monospace_font; } /** diff --git a/app/assets/stylesheets/gitlab_bootstrap/variables.scss b/app/assets/stylesheets/gitlab_bootstrap/variables.scss index 869eb168c0dadfb02e1eeb1e09703cae3310cffb..aeabe7ad2e83be87ee201e6971e182ff27b4162e 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/variables.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/variables.scss @@ -1,5 +1,13 @@ -/** Colors **/ +/** + * General Colors + */ $primary_color: #2FA0BB; $link_color: #3A89A3; $style_color: #474D57; $hover: #D9EDF7; + +/** + * Commit Diff Colors + */ +$added: #63c363; +$deleted: #f77; diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index c60aae49eaa12c52c42cd878006a4d55af6f244c..8b93287ed1e25402b649f13d642b3abced087358 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -1,7 +1,5 @@ /** - * - * COMMIT SHOw - * + * Commit file */ .commit-committer-link, .commit-author-link { @@ -12,11 +10,11 @@ } } -.diff_file { +.file { border: 1px solid #CCC; margin-bottom: 1em; - .diff_file_header { + .header { @extend .clearfix; padding: 5px 5px 5px 10px; color: #555; @@ -28,32 +26,35 @@ background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); + a{ + color: $style_color; + } + > span { - font-family: $monospace; + font-family: $monospace_font; font-size: 14px; line-height: 30px; } - a.view-commit{ + a.view-file{ font-weight: bold; } .commit-short-id{ - font-family: $monospace; + font-family: $monospace_font; font-size: smaller; } .file-mode{ - font-family: $monospace; + font-family: $monospace_font; } } - .diff_file_content { + .content { overflow: auto; overflow-y: hidden; - background: #fff; + background: #FFF; color: #333; font-size: 12px; - font-family: $monospace; .old{ span.idiff{ background-color: #FAA; @@ -66,114 +67,274 @@ } table { + font-family: $monospace_font; + border: none; + margin: 0px; + padding: 0px; td { line-height: 18px; + font-size: 12px; + } + } + .old_line, .new_line { + margin: 0px; + padding: 0px; + border: none; + background: #EEE; + color: #666; + padding: 0px 5px; + border-right: 1px solid #ccc; + text-align: right; + min-width: 35px; + max-width: 35px; + width: 35px; + @include user-select(none); + a { + float: left; + width: 35px; + font-weight: normal; + color: #666; + &:hover { + text-decoration: underline; + } + } + } + .line_content { + white-space: pre; + height: 14px; + margin: 0px; + padding: 0px; + border: none; + &.new { + background: #CFD; + } + &.old { + background: #FDD; + } + &.matched { + color: #ccc; + background: #fafafa; } } } - .diff_file_content_image { - background: #eee; + .image { + background: #ddd; text-align: center; - .image { + padding: 30px; + .wrap{ display: inline-block; - margin: 50px; - max-width: 400px; - + } + + .frame { + display: inline-block; + background-color: #fff; + line-height: 0; img{ + border: 1px solid #FFF; background: url('trans_bg.gif'); } + &.deleted { + border: 1px solid $deleted; + } - &.diff_removed { - img{ - border: 1px solid #C00; - } + &.added { + border: 1px solid $added; } + } + .image-info{ + font-size: 12px; + margin: 5px 0 0 0; + color: grey; + } - &.diff_added { - img{ - border: 1px solid #0C0; + .view.swipe{ + position: relative; + + .swipe-frame{ + display: block; + margin: auto; + position: relative; + } + .swipe-wrap{ + overflow: hidden; + border-left: 1px solid #999; + position: absolute; + display: block; + top: 13px; + right: 7px; + } + .frame{ + top: 0; + right: 0; + position: absolute; + &.deleted{ + margin: 0; + display: block; + top: 13px; + right: 7px; } } - - .image-info{ - margin: 5px 0 0 0; + .swipe-bar{ + display: block; + height: 100%; + width: 15px; + z-index: 100; + position: absolute; + cursor: pointer; + &:hover{ + .top-handle{ + background-position: -15px 3px; + } + .bottom-handle{ + background-position: -15px -11px; + } + }; + .top-handle{ + display: block; + height: 14px; + width: 15px; + position: absolute; + top: 0px; + background: url('swipemode_sprites.gif') 0 3px no-repeat; + } + .bottom-handle{ + display: block; + height: 14px; + width: 15px; + position: absolute; + bottom: 0px; + background: url('swipemode_sprites.gif') 0 -11px no-repeat; + } } - } - - &.img_compared { - .image { - max-width: 300px; + } //.view.swipe + .view.onion-skin{ + .onion-skin-frame{ + display: block; + margin: auto; + position: relative; } - } + .frame.added, .frame.deleted { + position: absolute; + display: block; + top: 0px; + left: 0px; + } + .controls{ + display: block; + height: 14px; + width: 300px; + z-index: 100; + position: absolute; + bottom: 0px; + left: 50%; + margin-left: -150px; + + .drag-track{ + display: block; + position: absolute; + left: 12px; + height: 10px; + width: 276px; + background: url('onion_skin_sprites.gif') -4px -20px repeat-x; + } + + .dragger { + display: block; + position: absolute; + left: 0px; + top: 0px; + height: 14px; + width: 14px; + background: url('onion_skin_sprites.gif') 0px -34px repeat-x; + cursor: pointer; + } + + .transparent { + display: block; + position: absolute; + top: 2px; + right: 0px; + height: 10px; + width: 10px; + background: url('onion_skin_sprites.gif') -2px 0px no-repeat; + } + + .opaque { + display: block; + position: absolute; + top: 2px; + left: 0px; + height: 10px; + width: 10px; + background: url('onion_skin_sprites.gif') -2px -10px no-repeat; + } + } + } //.view.onion-skin } -} + .view-modes{ -.diff_file_content{ - table { - border: none; - margin: 0px; - padding: 0px; - tr { - td { - font-size: 12px; - } + padding: 10px; + text-align: center; + + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); + + ul, li{ + list-style: none; + margin: 0; + padding: 0; + display: inline-block; } - } - .new_line, - .old_line, - .notes_line { - margin:0px; - padding:0px; - border:none; - background:#EEE; - color:#666; - padding: 0px 5px; - border-right: 1px solid #ccc; - text-align: right; - min-width: 35px; - max-width: 35px; - width: 35px; - moz-user-select: none; - -khtml-user-select: none; - user-select: none; - - a { - float: left; - width: 35px; - font-weight: normal; - color: #666; - &:hover { + + li{ + color: grey; + border-left: 1px solid #c1c1c1; + padding: 0 12px 0 16px; + cursor: pointer; + &:first-child{ + border-left: none; + } + &:hover{ text-decoration: underline; } - } - } - .line_content { - white-space: pre; - height: 14px; - margin: 0px; - padding: 0px; - border: none; - &.new { - background: #CFD; - } - &.old { - background: #FDD; - } - &.matched { - color: #ccc; - background: #fafafa; + &.active{ + &:hover{ + text-decoration: none; + } + cursor: default; + color: #333; + } + &.disabled{ + display: none; + } } } } /** COMMIT BLOCK **/ -.commit-title{display: block;} -.commit-title{margin-bottom: 10px} -.commit-author, .commit-committer{display: block;color: #999; font-weight: normal; font-style: italic;} -.commit-author strong, .commit-committer strong{font-weight: bold; font-style: normal;} +.commit-title{ + display: block; +} +.commit-title{ + margin-bottom: 10px; +} +.commit-author, .commit-committer{ + display: block; + color: #999; + font-weight: normal; + font-style: italic; +} +.commit-author strong, .commit-committer strong{ + font-weight: bold; + font-style: normal; +} -/** COMMIT ROW **/ +/** + * COMMIT ROW + */ .commit { .browse_code_link_holder { @extend .span2; @@ -199,11 +360,10 @@ float: left; @extend .lined; min-width: 65px; - font-family: $monospace; + font-family: $monospace_font; } } -.diff_file_header a, .file-stats a { color: $style_color; } @@ -237,7 +397,7 @@ font-size: 13px; background: #474D57; color: #fff; - font-family: $monospace; + font-family: $monospace_font; } diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index 7472cb093263e401b902ae5ec12af58657198663..ff8101471783b9f777a0b629c3c87257caa2cd92 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -127,7 +127,7 @@ .btn-new-mr { @extend .btn-info; @extend .small; - @extend .right; + @extend .pull-right; margin: -3px; } } diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index 048a3ffcbb2ad5b3ad5dc8686de010289df4467f..5fe18131828b9e875eefabf51a84d0f432ac22cc 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/sections/header.scss @@ -13,7 +13,7 @@ header { color: $style_color; text-shadow: 0 1px 0 #fff; font-size: 18px; - padding: 11px; + padding: 12px; } /** NAV block with links and profile **/ diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/sections/login.scss index 7536abff44fc5e442f462f3da5ef2c589d3300b8..89b8f1c0055322316b26d48ec58f3d9d60bb1749 100644 --- a/app/assets/stylesheets/sections/login.scss +++ b/app/assets/stylesheets/sections/login.scss @@ -1,7 +1,7 @@ /* Login Page */ -body.login-page{ - padding-top: 10%; - background: #f1f1f1; +body.login-page{ + padding-top: 7%; + background: #666; } .login-box{ diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index 5225a242726b7d0cd2a321daf9f2fd817f17fc49..ff715c0fd1810031274de39762062d7afcb28962 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -77,7 +77,7 @@ li.merge_request { font-size: 14px; background: #474D57; color: #fff; - font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; + font-family: $monospace_font; } .mr_source_commit, diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss index bc19bc75a6797d0a95a99e01696913ba4560f222..50091cd7365f7a37618bf3ccf5dbc8cf4eb924b5 100644 --- a/app/assets/stylesheets/sections/nav.scss +++ b/app/assets/stylesheets/sections/nav.scss @@ -6,8 +6,7 @@ ul.main_menu { margin: auto; margin: 30px 0; margin-top: 10px; - border-bottom: 1px solid #DDD; - height: 37px; + height: 38px; position: relative; overflow: hidden; .count { @@ -33,6 +32,7 @@ ul.main_menu { margin: 0; display: table-cell; width: 1%; + border-bottom: 2px solid #EEE; &.active { border-bottom: 2px solid #474D57; a { @@ -42,10 +42,8 @@ ul.main_menu { &.home { a { - background: url(home_icon.PNG) no-repeat center center; - text-indent:-9999px; - min-width: 20px; - img { + i { + font-size: 20px; position: relative; top: 4px; } @@ -56,7 +54,7 @@ ul.main_menu { display: block; text-align: center; font-weight: normal; - height: 35px; + height: 36px; line-height: 36px; color: #777; text-shadow: 0 1px 1px white; diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index a7fadd4f64103b3227666759a2ab866232278203..648cb210e9cbf4f772d9faaf97cd6253f78599c2 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -40,13 +40,13 @@ ul.notes { .discussion-body { margin-left: 50px; - .diff_file, + .file, .discussion-hidden, .notes { @extend .borders; background-color: #F9F9F9; } - .diff_file .notes { + .file .notes { /* reset */ background: inherit; border: none; @@ -81,14 +81,6 @@ ul.notes { .attachment { font-size: 14px; margin-top: -20px; - - .icon-attachment { - @extend .icon-paper-clip; - font-size: 24px; - position: relative; - text-align: right; - top: 6px; - } } .note-body { margin-left: 45px; @@ -109,7 +101,7 @@ ul.notes { } } -.diff_file .notes_holder { +.file .notes_holder { font-family: $sansFontFamily; font-size: 13px; line-height: 18px; @@ -134,8 +126,6 @@ ul.notes { } } - - /** * Actions for Discussions/Notes */ @@ -171,7 +161,7 @@ ul.notes { } } } -.diff_file .note .note-actions { +.file .note .note-actions { right: 0; top: 0; } @@ -182,7 +172,7 @@ ul.notes { * Line note button on the side of diffs */ -.diff_file tr.line_holder { +.file tr.line_holder { .add-diff-note { background: url("diff_note_add.png") no-repeat left 0; height: 22px; @@ -212,25 +202,25 @@ ul.notes { } } - - /** * Note Form */ -.comment-btn, +.comment-btn { + @extend .btn-create; +} .reply-btn { - @extend .save-btn; + @extend .btn-primary; } -.diff_file, +.file .content tr.line_holder:hover > td { background: $hover !important; } +.file .content tr.line_holder:hover > td .line_note_link { + opacity: 1.0; + filter: alpha(opacity=100); +} +.file, .discussion { .new_note { margin: 8px 5px 8px 0; - - .note_options { - // because of the smaller width and the extra "cancel" button - margin-top: 8px; - } } } .new_note { @@ -243,37 +233,6 @@ ul.notes { .clearfix { margin-bottom: 0; } - .note_options { - h6 { - @extend .left; - line-height: 20px; - padding-right: 16px; - padding-bottom: 16px; - } - label { - padding: 0; - } - - .attachment { - @extend .right; - position: relative; - width: 350px; - height: 50px; - margin:0 0 5px !important; - - // hide the actual file field - input { - display: none; - } - - .choose-btn { - float: right; - } - } - .notify_options { - @extend .right; - } - } .note_text_and_preview { // makes the "absolute" position for links relative to this position: relative; @@ -312,3 +271,17 @@ ul.notes { @extend .thumbnail; margin-left: 45px; } + + +.note-form-actions { + background: #F9F9F9; + height: 45px; + padding: 0 5px; + + .note-form-option { + margin-top: 8px; + margin-left: 15px; + @extend .pull-left; + @extend .span4; + } +} diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss index 4bdc56d2884a62dd66d86b39f3d5550d1ea6373d..28df1b5ac28dfe6f3029d253f75f32033bc88158 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/sections/projects.scss @@ -4,9 +4,8 @@ } .side { - @extend .right; + @extend .pull-right; - .groups_box, .projects_box { > .title { padding: 2px 15px; diff --git a/app/contexts/projects/create_context.rb b/app/contexts/projects/create_context.rb index e644d89a356d78f43845ccc1803f0fbb3196a354..915bd8be8b00626de649a32f49f3d96e3936c685 100644 --- a/app/contexts/projects/create_context.rb +++ b/app/contexts/projects/create_context.rb @@ -32,16 +32,10 @@ module Projects @project.namespace_id = current_user.namespace_id end - Project.transaction do - @project.creator = current_user - @project.save! + @project.creator = current_user - # Add user as project master - @project.users_projects.create!(project_access: UsersProject::MASTER, user: current_user) - - # when project saved no team member exist so - # project repository should be updated after first user add - @project.update_repository + if @project.save + @project.users_projects.create(project_access: UsersProject::MASTER, user: current_user) end @project diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin/application_controller.rb similarity index 82% rename from app/controllers/admin_controller.rb rename to app/controllers/admin/application_controller.rb index bce9f69238577379d926b3c53cfdf427234ebd72..6a8f20f60471fe3722bf630077b07ec5b6cf02e8 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin/application_controller.rb @@ -1,7 +1,7 @@ # Provides a base class for Admin controllers to subclass # # Automatically sets the layout and ensures an administrator is logged in -class AdminController < ApplicationController +class Admin::ApplicationController < ApplicationController layout 'admin' before_filter :authenticate_admin! diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index f97c56b0b31e3e22291a1c31ce8b50d65d604f29..3c27b86180be91c5b45e62f4bedf8055d55a775d 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -1,4 +1,4 @@ -class Admin::DashboardController < AdminController +class Admin::DashboardController < Admin::ApplicationController def index @projects = Project.order("created_at DESC").limit(10) @users = User.order("created_at DESC").limit(10) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 90dbda3eeeafb33e2bb6458caed8e8e485326780..f552fb595b862dc625f0b6e4755e8bc1a39e4dfd 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,4 +1,4 @@ -class Admin::GroupsController < AdminController +class Admin::GroupsController < Admin::ApplicationController before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update] def index diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 91a1d633590df04c31611bdd73e42bc7f0d45812..c5bf76f8c39dabd76a181ed3fec971905a98b74b 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -1,4 +1,4 @@ -class Admin::HooksController < AdminController +class Admin::HooksController < Admin::ApplicationController def index @hooks = SystemHook.all @hook = SystemHook.new diff --git a/app/controllers/admin/logs_controller.rb b/app/controllers/admin/logs_controller.rb index 28c321a9e5231aee09fab2ca653d913e1ac06e6a..b999018dde469e3f32187b94551077c56a56a174 100644 --- a/app/controllers/admin/logs_controller.rb +++ b/app/controllers/admin/logs_controller.rb @@ -1,2 +1,2 @@ -class Admin::LogsController < AdminController +class Admin::LogsController < Admin::ApplicationController end diff --git a/app/controllers/admin/projects/application_controller.rb b/app/controllers/admin/projects/application_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..b3f1539f38760eb94d8c1c3da0372630c645083b --- /dev/null +++ b/app/controllers/admin/projects/application_controller.rb @@ -0,0 +1,11 @@ +# Provides a base class for Admin controllers to subclass +# +# Automatically sets the layout and ensures an administrator is logged in +class Admin::Projects::ApplicationController < Admin::ApplicationController + + protected + + def project + @project ||= Project.find_with_namespace(params[:project_id]) + end +end diff --git a/app/controllers/admin/projects/members_controller.rb b/app/controllers/admin/projects/members_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..d9c0d572bb1ec23c1f72e75ad9bf35a6918b8118 --- /dev/null +++ b/app/controllers/admin/projects/members_controller.rb @@ -0,0 +1,32 @@ +class Admin::Projects::MembersController < Admin::Projects::ApplicationController + def edit + @member = team_member + @project = project + @team_member_relation = team_member_relation + end + + def update + if team_member_relation.update_attributes(params[:team_member]) + redirect_to [:admin, project], notice: 'Project Access was successfully updated.' + else + render action: "edit" + end + end + + def destroy + team_member_relation.destroy + + redirect_to :back + end + + private + + def team_member + @member ||= project.users.find_by_username(params[:id]) + end + + def team_member_relation + team_member.users_projects.find_by_project_id(project) + end + +end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index fc2793a72e7b5959fd1f1910b840b53d06240f27..711817395f1fac05f502de85d62f004b696d0218 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,4 +1,4 @@ -class Admin::ProjectsController < AdminController +class Admin::ProjectsController < Admin::ApplicationController before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] def index @@ -29,7 +29,9 @@ class Admin::ProjectsController < AdminController end def update - status = Projects::UpdateContext.new(project, current_user, params).execute(:admin) + project.creator = current_user unless project.creator + + status = ::Projects::UpdateContext.new(project, current_user, params).execute(:admin) if status redirect_to [:admin, @project], notice: 'Project was successfully updated.' diff --git a/app/controllers/admin/resque_controller.rb b/app/controllers/admin/resque_controller.rb index 9d8e7e3051f0520c288741ce497c49334933bd54..7d489ab4876f226b95800dd0c5390fc7a4f8c62f 100644 --- a/app/controllers/admin/resque_controller.rb +++ b/app/controllers/admin/resque_controller.rb @@ -1,4 +1,4 @@ -class Admin::ResqueController < AdminController +class Admin::ResqueController < Admin::ApplicationController def show end end diff --git a/app/controllers/admin/team_members_controller.rb b/app/controllers/admin/team_members_controller.rb deleted file mode 100644 index 073208057cabce9a48206a9ada82ce1f0dd51284..0000000000000000000000000000000000000000 --- a/app/controllers/admin/team_members_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -class Admin::TeamMembersController < AdminController - def edit - @admin_team_member = UsersProject.find(params[:id]) - end - - def update - @admin_team_member = UsersProject.find(params[:id]) - - if @admin_team_member.update_attributes(params[:team_member]) - redirect_to [:admin, @admin_team_member.project], notice: 'Project Access was successfully updated.' - else - render action: "edit" - end - end - - def destroy - @admin_team_member = UsersProject.find(params[:id]) - @admin_team_member.destroy - - redirect_to :back - end -end diff --git a/app/controllers/admin/teams/application_controller.rb b/app/controllers/admin/teams/application_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..8710821454eaf3a6857893b4f548d410eccfdaa9 --- /dev/null +++ b/app/controllers/admin/teams/application_controller.rb @@ -0,0 +1,11 @@ +# Provides a base class for Admin controllers to subclass +# +# Automatically sets the layout and ensures an administrator is logged in +class Admin::Teams::ApplicationController < Admin::ApplicationController + + private + + def user_team + @team = UserTeam.find_by_path(params[:team_id]) + end +end diff --git a/app/controllers/admin/teams/members_controller.rb b/app/controllers/admin/teams/members_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..e7dbcad568f66c4e304a73f9e07cc26c00d16537 --- /dev/null +++ b/app/controllers/admin/teams/members_controller.rb @@ -0,0 +1,41 @@ +class Admin::Teams::MembersController < Admin::Teams::ApplicationController + def new + @users = User.potential_team_members(user_team) + @users = UserDecorator.decorate @users + end + + def create + unless params[:user_ids].blank? + user_ids = params[:user_ids] + access = params[:default_project_access] + is_admin = params[:group_admin] + user_team.add_members(user_ids, access, is_admin) + end + + redirect_to admin_team_path(user_team), notice: 'Members was successfully added into Team of users.' + end + + def edit + team_member + end + + def update + options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]} + if user_team.update_membership(team_member, options) + redirect_to admin_team_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users." + else + render :edit + end + end + + def destroy + user_team.remove_member(team_member) + redirect_to admin_team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users." + end + + protected + + def team_member + @member ||= user_team.members.find_by_username(params[:id]) + end +end diff --git a/app/controllers/admin/teams/projects_controller.rb b/app/controllers/admin/teams/projects_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..8584a188b20275c8ebb8a24b7dd10f428ec14f13 --- /dev/null +++ b/app/controllers/admin/teams/projects_controller.rb @@ -0,0 +1,41 @@ +class Admin::Teams::ProjectsController < Admin::Teams::ApplicationController + def new + @projects = Project.scoped + @projects = @projects.without_team(user_team) if user_team.projects.any? + #@projects.reject!(&:empty_repo?) + end + + def create + unless params[:project_ids].blank? + project_ids = params[:project_ids] + access = params[:greatest_project_access] + user_team.assign_to_projects(project_ids, access) + end + + redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assgned to projects.' + end + + def edit + team_project + end + + def update + if user_team.update_project_access(team_project, params[:greatest_project_access]) + redirect_to admin_team_path(user_team), notice: 'Access was successfully updated.' + else + render :edit + end + end + + def destroy + user_team.resign_from_project(team_project) + redirect_to admin_team_path(user_team), notice: 'Team of users was successfully reassigned from project.' + end + + protected + + def team_project + @project ||= user_team.projects.find_with_namespace(params[:id]) + end + +end diff --git a/app/controllers/admin/teams_controller.rb b/app/controllers/admin/teams_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..786957cbc594496706a3391001676d2bde27a475 --- /dev/null +++ b/app/controllers/admin/teams_controller.rb @@ -0,0 +1,59 @@ +class Admin::TeamsController < Admin::ApplicationController + def index + @teams = UserTeam.order('name ASC') + @teams = @teams.search(params[:name]) if params[:name].present? + @teams = @teams.page(params[:page]).per(20) + end + + def show + user_team + end + + def new + @team = UserTeam.new + end + + def edit + user_team + end + + def create + @team = UserTeam.new(params[:user_team]) + @team.path = @team.name.dup.parameterize if @team.name + @team.owner = current_user + + if @team.save + redirect_to admin_team_path(@team), notice: 'Team of users was successfully created.' + else + render action: "new" + end + end + + def update + user_team_params = params[:user_team].dup + owner_id = user_team_params.delete(:owner_id) + + if owner_id + user_team.owner = User.find(owner_id) + end + + if user_team.update_attributes(user_team_params) + redirect_to admin_team_path(user_team), notice: 'Team of users was successfully updated.' + else + render action: "edit" + end + end + + def destroy + user_team.destroy + + redirect_to admin_teams_path, notice: 'Team of users was successfully deleted.' + end + + protected + + def user_team + @team ||= UserTeam.find_by_path(params[:id]) + end + +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 8669f5d1d38928dc575872512dfc19431a8da974..400e44e086ddca7622bf7ca6a65bc87cba65b658 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,4 +1,6 @@ -class Admin::UsersController < AdminController +class Admin::UsersController < Admin::ApplicationController + before_filter :admin_user, only: [:show, :edit, :update, :destroy] + def index @admin_users = User.scoped @admin_users = @admin_users.filter(params[:filter]) @@ -7,25 +9,18 @@ class Admin::UsersController < AdminController end def show - @admin_user = User.find(params[:id]) - - @projects = if @admin_user.authorized_projects.empty? - Project - else - Project.without_user(@admin_user) - end.all + @projects = Project.scoped + @projects = @projects.without_user(admin_user) if admin_user.authorized_projects.present? end def team_update - @admin_user = User.find(params[:id]) - UsersProject.add_users_into_projects( params[:project_ids], - [@admin_user.id], + [admin_user.id], params[:project_access] ) - redirect_to [:admin, @admin_user], notice: 'Teams were successfully updated.' + redirect_to [:admin, admin_user], notice: 'Teams were successfully updated.' end @@ -34,13 +29,11 @@ class Admin::UsersController < AdminController end def edit - @admin_user = User.find(params[:id]) + admin_user end def block - @admin_user = User.find(params[:id]) - - if @admin_user.block + if admin_user.block redirect_to :back, alert: "Successfully blocked" else redirect_to :back, alert: "Error occured. User was not blocked" @@ -48,9 +41,7 @@ class Admin::UsersController < AdminController end def unblock - @admin_user = User.find(params[:id]) - - if @admin_user.update_attribute(:blocked, false) + if admin_user.update_attribute(:blocked, false) redirect_to :back, alert: "Successfully unblocked" else redirect_to :back, alert: "Error occured. User was not unblocked" @@ -82,30 +73,34 @@ class Admin::UsersController < AdminController params[:user].delete(:password_confirmation) end - @admin_user = User.find(params[:id]) - @admin_user.admin = (admin && admin.to_i > 0) + admin_user.admin = (admin && admin.to_i > 0) respond_to do |format| - if @admin_user.update_attributes(params[:user], as: :admin) - format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } + if admin_user.update_attributes(params[:user], as: :admin) + format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' } format.json { head :ok } else format.html { render action: "edit" } - format.json { render json: @admin_user.errors, status: :unprocessable_entity } + format.json { render json: admin_user.errors, status: :unprocessable_entity } end end end def destroy - @admin_user = User.find(params[:id]) - if @admin_user.personal_projects.count > 0 + if admin_user.personal_projects.count > 0 redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return end - @admin_user.destroy + admin_user.destroy respond_to do |format| - format.html { redirect_to admin_users_url } + format.html { redirect_to admin_users_path } format.json { head :ok } end end + + protected + + def admin_user + @admin_user ||= User.find_by_username!(params[:id]) + end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3457a1ab1b4a14dd10e6e27d07a6dea884fe4fda..1f211bac9c220d34de02aa23901a23fa024fc89f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,16 +4,12 @@ class ApplicationController < ActionController::Base before_filter :set_current_user_for_observers before_filter :add_abilities before_filter :dev_tools if Rails.env == 'development' + before_filter :default_headers protect_from_forgery helper_method :abilities, :can? - rescue_from Gitlab::Gitolite::AccessDenied do |exception| - log_exception(exception) - render "errors/gitolite", layout: "errors", status: 500 - end - rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) render "errors/encoding", layout: "errors", status: 500 @@ -94,6 +90,18 @@ class ApplicationController < ActionController::Base return access_denied! unless can?(current_user, :download_code, project) end + def authorize_create_team! + return access_denied! unless can?(current_user, :create_team, nil) + end + + def authorize_manage_user_team! + return access_denied! unless user_team.present? && can?(current_user, :manage_user_team, user_team) + end + + def authorize_admin_user_team! + return access_denied! unless user_team.present? && can?(current_user, :admin_user_team, user_team) + end + def access_denied! render "errors/access_denied", layout: "errors", status: 404 end @@ -135,4 +143,9 @@ class ApplicationController < ActionController::Base def dev_tools Rack::MiniProfiler.authorize_request end + + def default_headers + headers['X-Frame-Options'] = 'DENY' + headers['X-XSS-Protection'] = '1; mode=block' + end end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index c0ec4708e0aa1d1b8efb7fb78a9d16c43b224a56..f320e819e26d5f6c091f831c5f6a4016e4ff5095 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,24 +1,15 @@ class DashboardController < ApplicationController respond_to :html - before_filter :projects - before_filter :event_filter, only: :index + before_filter :load_projects + before_filter :event_filter, only: :show - def index + def show @groups = current_user.authorized_groups - @has_authorized_projects = @projects.count > 0 - - @projects = case params[:scope] - when 'personal' then - @projects.personal(current_user) - when 'joined' then - @projects.joined(current_user) - else - @projects - end - - @projects = @projects.page(params[:page]).per(30) + @teams = current_user.authorized_teams + @projects_count = @projects.count + @projects = @projects.limit(20) @events = Event.in_projects(current_user.authorized_projects.pluck(:id)) @events = @event_filter.apply_filter(@events) @@ -33,6 +24,19 @@ class DashboardController < ApplicationController end end + def projects + @projects = case params[:scope] + when 'personal' then + @projects.personal(current_user) + when 'joined' then + @projects.joined(current_user) + else + @projects + end + + @projects = @projects.page(params[:page]).per(30) + end + # Get authored or assigned open merge requests def merge_requests @merge_requests = current_user.cared_merge_requests @@ -55,7 +59,7 @@ class DashboardController < ApplicationController protected - def projects + def load_projects @projects = current_user.authorized_projects.sorted_by_activity end diff --git a/app/controllers/graph_controller.rb b/app/controllers/graph_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..30ec5e89db2e99bee4695b546a4b4bd946f6667a --- /dev/null +++ b/app/controllers/graph_controller.rb @@ -0,0 +1,18 @@ +class GraphController < ProjectResourceController + include ExtractsPath + + # Authorize + before_filter :authorize_read_project! + before_filter :authorize_code_access! + before_filter :require_non_empty_project + + def show + respond_to do |format| + format.html + format.json do + graph = Gitlab::Graph::JsonBuilder.new(project, @ref) + render :json => graph.to_json + end + end + end +end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index c25fc32a62c03c731e264af4e76b757a36265190..7b8649a6bdfc43914bb18563fbc9f764b1c290e7 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,12 +1,32 @@ class GroupsController < ApplicationController respond_to :html - layout 'group' + layout 'group', except: [:new, :create] - before_filter :group - before_filter :projects + before_filter :group, except: [:new, :create] # Authorize - before_filter :authorize_read_group! + before_filter :authorize_read_group!, except: [:new, :create] + before_filter :authorize_admin_group!, only: [:edit, :update, :destroy] + before_filter :authorize_create_group!, only: [:new, :create] + + # Load group projects + before_filter :projects, except: [:new, :create] + + def new + @group = Group.new + end + + def create + @group = Group.new(params[:group]) + @group.path = @group.name.dup.parameterize if @group.name + @group.owner = current_user + + if @group.save + redirect_to @group, notice: 'Group was successfully created.' + else + render action: "new" + end + end def show @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0) @@ -65,6 +85,31 @@ class GroupsController < ApplicationController redirect_to people_group_path(@group), notice: 'Users was successfully added.' end + def edit + end + + def update + group_params = params[:group].dup + owner_id =group_params.delete(:owner_id) + + if owner_id + @group.owner = User.find(owner_id) + end + + if @group.update_attributes(group_params) + redirect_to @group, notice: 'Group was successfully updated.' + else + render action: "edit" + end + end + + def destroy + @group.truncate_teams + @group.destroy + + redirect_to root_path, notice: 'Group was removed.' + end + protected def group @@ -85,4 +130,16 @@ class GroupsController < ApplicationController return render_404 end end + + def authorize_create_group! + unless can?(current_user, :create_group, nil) + return render_404 + end + end + + def authorize_admin_group! + unless can?(current_user, :manage_group, group) + return render_404 + end + end end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..7e4776d2d753d7a1f215e2f2e1f44cc29f2dae33 --- /dev/null +++ b/app/controllers/projects/application_controller.rb @@ -0,0 +1,11 @@ +class Projects::ApplicationController < ApplicationController + + before_filter :authorize_admin_team_member! + + protected + + def user_team + @team ||= UserTeam.find_by_path(params[:id]) + end + +end diff --git a/app/controllers/projects/teams_controller.rb b/app/controllers/projects/teams_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..3ca724aaf4dc5dd7effcc5996791a0a73963ba79 --- /dev/null +++ b/app/controllers/projects/teams_controller.rb @@ -0,0 +1,27 @@ +class Projects::TeamsController < Projects::ApplicationController + + def available + @teams = current_user.is_admin? ? UserTeam.scoped : current_user.user_teams + @teams = @teams.without_project(project) + unless @teams.any? + redirect_to project_team_index_path(project), notice: "No avaliable teams for assigment." + end + end + + def assign + unless params[:team_id].blank? + team = UserTeam.find(params[:team_id]) + access = params[:greatest_project_access] + team.assign_to_project(project, access) + end + redirect_to project_team_index_path(project) + end + + def resign + team = project.user_teams.find_by_path(params[:id]) + team.resign_from_project(project) + + redirect_to project_team_index_path(project) + end + +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 368737d1e0b54d42256f48cf299cc2addceff2e9..7978ea6222c469a01bfa7633363058d941f92a1f 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -19,7 +19,7 @@ class ProjectsController < ProjectResourceController end def create - @project = Projects::CreateContext.new(current_user, params[:project]).execute + @project = ::Projects::CreateContext.new(current_user, params[:project]).execute respond_to do |format| flash[:notice] = 'Project was successfully created.' if @project.saved? @@ -35,7 +35,7 @@ class ProjectsController < ProjectResourceController end def update - status = Projects::UpdateContext.new(project, current_user, params).execute + status = ::Projects::UpdateContext.new(project, current_user, params).execute respond_to do |format| if status @@ -90,16 +90,6 @@ class ProjectsController < ProjectResourceController end end - def graph - respond_to do |format| - format.html - format.json do - graph = Gitlab::Graph::JsonBuilder.new(project) - render :json => graph.to_json - end - end - end - def destroy return access_denied! unless can?(current_user, :remove_project, project) diff --git a/app/controllers/refs_controller.rb b/app/controllers/refs_controller.rb index 09d9eb51b8241ac01623643069a410396c0873a6..0e4dba3dc4b0074ae4d965b15a6979551aa69556 100644 --- a/app/controllers/refs_controller.rb +++ b/app/controllers/refs_controller.rb @@ -13,6 +13,8 @@ class RefsController < ProjectResourceController format.html do new_path = if params[:destination] == "tree" project_tree_path(@project, (@ref + "/" + params[:path])) + elsif params[:destination] == "graph" + project_graph_path(@project, @ref) else project_commits_path(@project, @ref) end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index a23292396a015ec63c103bddc60136c7cc42d386..bbd67df6c704f63e1102fe63a756890f68e9955c 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,6 +1,18 @@ class SearchController < ApplicationController def show - result = SearchContext.new(current_user.authorized_projects.map(&:id), params).execute + project_id = params[:project_id] + group_id = params[:group_id] + + project_ids = current_user.authorized_projects.map(&:id) + + if group_id.present? + group_project_ids = Group.find(group_id).projects.map(&:id) + project_ids.select! { |id| group_project_ids.include?(id)} + elsif project_id.present? + project_ids.select! { |id| id == project_id.to_i} + end + + result = SearchContext.new(project_ids, params).execute @projects = result[:projects] @merge_requests = result[:merge_requests] diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index 8378a8458ffa7193717eab782e117d36ce28ed98..18d4ae3ac96df8fde06d01d54aaa1bc72e59f26f 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -4,15 +4,16 @@ class TeamMembersController < ProjectResourceController before_filter :authorize_admin_project!, except: [:index, :show] def index + @teams = UserTeam.scoped end def show - @team_member = project.users_projects.find(params[:id]) - @events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7) + @user_project_relation = project.users_projects.find_by_user_id(member) + @events = member.recent_events.in_projects(project).limit(7) end def new - @team_member = project.users_projects.new + @user_project_relation = project.users_projects.new end def create @@ -28,18 +29,18 @@ class TeamMembersController < ProjectResourceController end def update - @team_member = project.users_projects.find(params[:id]) - @team_member.update_attributes(params[:team_member]) + @user_project_relation = project.users_projects.find_by_user_id(member) + @user_project_relation.update_attributes(params[:team_member]) - unless @team_member.valid? + unless @user_project_relation.valid? flash[:alert] = "User should have at least one role" end redirect_to project_team_index_path(@project) end def destroy - @team_member = project.users_projects.find(params[:id]) - @team_member.destroy + @user_project_relation = project.users_projects.find_by_user_id(member) + @user_project_relation.destroy respond_to do |format| format.html { redirect_to project_team_index_path(@project) } @@ -54,4 +55,10 @@ class TeamMembersController < ProjectResourceController redirect_to project_team_members_path(project), notice: notice end + + protected + + def member + @member ||= User.find_by_username(params[:id]) + end end diff --git a/app/controllers/teams/application_controller.rb b/app/controllers/teams/application_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..fc23202610c60cfb8ae27a658f1cec56b5617b41 --- /dev/null +++ b/app/controllers/teams/application_controller.rb @@ -0,0 +1,13 @@ +class Teams::ApplicationController < ApplicationController + + layout 'user_team' + + before_filter :authorize_manage_user_team! + + protected + + def user_team + @team ||= UserTeam.find_by_path(params[:team_id]) + end + +end diff --git a/app/controllers/teams/members_controller.rb b/app/controllers/teams/members_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..db218b8ca5e726aa97e87341f442e5001fed2273 --- /dev/null +++ b/app/controllers/teams/members_controller.rb @@ -0,0 +1,49 @@ +class Teams::MembersController < Teams::ApplicationController + + skip_before_filter :authorize_manage_user_team!, only: [:index] + + def index + @members = user_team.members + end + + def new + @users = User.potential_team_members(user_team) + @users = UserDecorator.decorate @users + end + + def create + unless params[:user_ids].blank? + user_ids = params[:user_ids] + access = params[:default_project_access] + is_admin = params[:group_admin] + user_team.add_members(user_ids, access, is_admin) + end + + redirect_to team_members_path(user_team), notice: 'Members was successfully added into Team of users.' + end + + def edit + team_member + end + + def update + options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]} + if user_team.update_membership(team_member, options) + redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users." + else + render :edit + end + end + + def destroy + user_team.remove_member(team_member) + redirect_to team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users." + end + + protected + + def team_member + @member ||= user_team.members.find_by_username(params[:id]) + end + +end diff --git a/app/controllers/teams/projects_controller.rb b/app/controllers/teams/projects_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..e87889b4046324ba5373d99b893628c6da201e05 --- /dev/null +++ b/app/controllers/teams/projects_controller.rb @@ -0,0 +1,57 @@ +class Teams::ProjectsController < Teams::ApplicationController + + skip_before_filter :authorize_manage_user_team!, only: [:index] + + def index + @projects = user_team.projects + @avaliable_projects = current_user.admin? ? Project.without_team(user_team) : current_user.owned_projects.without_team(user_team) + end + + def new + user_team + @avaliable_projects = current_user.owned_projects.scoped + @avaliable_projects = @avaliable_projects.without_team(user_team) if user_team.projects.any? + + redirect_to team_projects_path(user_team), notice: "No avalible projects." unless @avaliable_projects.any? + end + + def create + redirect_to :back if params[:project_ids].blank? + + project_ids = params[:project_ids] + access = params[:greatest_project_access] + + # Reject non-allowed projects + allowed_project_ids = current_user.owned_projects.map(&:id) + project_ids.select! { |id| allowed_project_ids.include?(id.to_i) } + + # Assign projects to team + user_team.assign_to_projects(project_ids, access) + + redirect_to team_projects_path(user_team), notice: 'Team of users was successfully assigned to projects.' + end + + def edit + team_project + end + + def update + if user_team.update_project_access(team_project, params[:greatest_project_access]) + redirect_to team_projects_path(user_team), notice: 'Access was successfully updated.' + else + render :edit + end + end + + def destroy + user_team.resign_from_project(team_project) + redirect_to team_projects_path(user_team), notice: 'Team of users was successfully reassigned from project.' + end + + private + + def team_project + @project ||= user_team.projects.find_with_namespace(params[:id]) + end + +end diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..ef66b77e23290d4f80cf92dd9607078a27647c7c --- /dev/null +++ b/app/controllers/teams_controller.rb @@ -0,0 +1,76 @@ +class TeamsController < ApplicationController + # Authorize + before_filter :authorize_create_team!, only: [:new, :create] + before_filter :authorize_manage_user_team!, only: [:edit, :update] + before_filter :authorize_admin_user_team!, only: [:destroy] + + before_filter :user_team, except: [:new, :create] + + layout 'user_team', except: [:new, :create] + + def show + user_team + projects + @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0) + end + + def edit + user_team + end + + def update + if user_team.update_attributes(params[:user_team]) + redirect_to team_path(user_team) + else + render action: :edit + end + end + + def destroy + user_team.destroy + redirect_to dashboard_path + end + + def new + @team = UserTeam.new + end + + def create + @team = UserTeam.new(params[:user_team]) + @team.owner = current_user unless params[:owner] + @team.path = @team.name.dup.parameterize if @team.name + + if @team.save + redirect_to team_path(@team) + else + render action: :new + end + end + + # Get authored or assigned open merge requests + def merge_requests + projects + @merge_requests = MergeRequest.of_user_team(user_team) + @merge_requests = FilterContext.new(@merge_requests, params).execute + @merge_requests = @merge_requests.recent.page(params[:page]).per(20) + end + + # Get only assigned issues + def issues + projects + @issues = Issue.of_user_team(user_team) + @issues = FilterContext.new(@issues, params).execute + @issues = @issues.recent.page(params[:page]).per(20) + @issues = @issues.includes(:author, :project) + end + + protected + + def projects + @projects ||= user_team.projects.sorted_by_activity + end + + def user_team + @team ||= current_user.authorized_teams.find_by_path(params[:id]) + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..e027057fe65d920da683d7d6176b19ef7aa79937 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,7 @@ +class UsersController < ApplicationController + def show + @user = User.find_by_username!(params[:username]) + @projects = @user.authorized_projects.where('projects.id in (?)', current_user.authorized_projects.map(&:id)) + @events = @user.recent_events.where(project_id: @projects.map(&:id)).limit(20) + end +end diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb index af9c6a63e756147d0c59efa70de112e28a080f51..b781f237352c21fcf96e032df006ed5b6d1788c2 100644 --- a/app/decorators/user_decorator.rb +++ b/app/decorators/user_decorator.rb @@ -8,4 +8,8 @@ class UserDecorator < ApplicationDecorator def tm_of(project) project.team_member_by_id(self.id) end + + def name_with_email + "#{name} (#{email})" + end end diff --git a/app/helpers/admin/teams/members_helper.rb b/app/helpers/admin/teams/members_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..58b9f1896c4a9fd9b190430148ea8f55a3e19bfc --- /dev/null +++ b/app/helpers/admin/teams/members_helper.rb @@ -0,0 +1,5 @@ +module Admin::Teams::MembersHelper + def member_since(team, member) + team.user_team_user_relationships.find_by_user_id(member).created_at + end +end diff --git a/app/helpers/admin/teams/projects_helper.rb b/app/helpers/admin/teams/projects_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..b97cc403337266f1ea19700221047accaafaeb64 --- /dev/null +++ b/app/helpers/admin/teams/projects_helper.rb @@ -0,0 +1,5 @@ +module Admin::Teams::ProjectsHelper + def assigned_since(team, project) + team.user_team_project_relationships.find_by_project_id(project).created_at + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6478982cdad3737a1a5957c7695f1225c98808fc..196105f011900ea89b815e6d751166c9df8510c2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -72,8 +72,9 @@ module ApplicationHelper end def search_autocomplete_source - projects = current_user.authorized_projects.map { |p| { label: p.name_with_namespace, url: project_path(p) } } - groups = current_user.authorized_groups.map { |group| { label: "<group> #{group.name}", url: group_path(group) } } + projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } } + groups = current_user.authorized_groups.map { |group| { label: "group: #{group.name}", url: group_path(group) } } + teams = current_user.authorized_teams.map { |team| { label: "team: #{team.name}", url: team_path(team) } } default_nav = [ { label: "My Profile", url: profile_path }, @@ -83,29 +84,29 @@ module ApplicationHelper ] help_nav = [ - { label: "API Help", url: help_api_path }, - { label: "Markdown Help", url: help_markdown_path }, - { label: "Permissions Help", url: help_permissions_path }, - { label: "Public Access Help", url: help_public_access_path }, - { label: "Rake Tasks Help", url: help_raketasks_path }, - { label: "SSH Keys Help", url: help_ssh_path }, - { label: "System Hooks Help", url: help_system_hooks_path }, - { label: "Web Hooks Help", url: help_web_hooks_path }, - { label: "Workflow Help", url: help_workflow_path }, + { label: "help: API Help", url: help_api_path }, + { label: "help: Markdown Help", url: help_markdown_path }, + { label: "help: Permissions Help", url: help_permissions_path }, + { label: "help: Public Access Help", url: help_public_access_path }, + { label: "help: Rake Tasks Help", url: help_raketasks_path }, + { label: "help: SSH Keys Help", url: help_ssh_path }, + { label: "help: System Hooks Help", url: help_system_hooks_path }, + { label: "help: Web Hooks Help", url: help_web_hooks_path }, + { label: "help: Workflow Help", url: help_workflow_path }, ] project_nav = [] if @project && @project.repository && @project.repository.root_ref project_nav = [ - { label: "#{@project.name} Issues", url: project_issues_path(@project) }, - { label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) }, - { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) }, - { label: "#{@project.name} Milestones", url: project_milestones_path(@project) }, - { label: "#{@project.name} Snippets", url: project_snippets_path(@project) }, - { label: "#{@project.name} Team", url: project_team_index_path(@project) }, - { label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) }, - { label: "#{@project.name} Wall", url: wall_project_path(@project) }, - { label: "#{@project.name} Wiki", url: project_wikis_path(@project) }, + { label: "#{@project.name_with_namespace} - Issues", url: project_issues_path(@project) }, + { label: "#{@project.name_with_namespace} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) }, + { label: "#{@project.name_with_namespace} - Merge Requests", url: project_merge_requests_path(@project) }, + { label: "#{@project.name_with_namespace} - Milestones", url: project_milestones_path(@project) }, + { label: "#{@project.name_with_namespace} - Snippets", url: project_snippets_path(@project) }, + { label: "#{@project.name_with_namespace} - Team", url: project_team_index_path(@project) }, + { label: "#{@project.name_with_namespace} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) }, + { label: "#{@project.name_with_namespace} - Wall", url: wall_project_path(@project) }, + { label: "#{@project.name_with_namespace} - Wiki", url: project_wikis_path(@project) }, ] end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 4b5d8bd96a6fbcd45b68f6046e801b8a1cfd59c1..6d2ce2feea3082e5d258ab0e821df1a58f0b4a50 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -59,9 +59,9 @@ module CommitsHelper def image_diff_class(diff) if diff.deleted_file - "diff_removed" + "deleted" elsif diff.new_file - "diff_added" + "added" else nil end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 0baa5b4108ee3b049c944d375ac18102252e79c9..c759dffa16e1fcd501ad348dfc53509fdd594fa0 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -9,9 +9,9 @@ module DashboardHelper case entity when 'issue' then - dashboard_issues_path(options) + issues_dashboard_path(options) when 'merge_request' - dashboard_merge_requests_path(options) + merge_requests_dashboard_path(options) end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index f4d9b17bce552d212bd490745fb7ddbf6cf563fd..38374e3394d97ef63d311b872f7d2a83b5851d53 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -1,10 +1,9 @@ module EventsHelper def link_to_author(event) - project = event.project - tm = project.team_member_by_id(event.author_id) if project + author = event.author - if tm - link_to event.author_name, project_team_member_path(project, tm) + if author + link_to author.name, user_path(author.username) else event.author_name end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 111982e9147ed5627ca4fda209f25d11263529ab..1a3d34eb886c672b4f66eb79a2e4e297582788c3 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -13,7 +13,13 @@ module GitlabMarkdownHelper def link_to_gfm(body, url, html_options = {}) return "" if body.blank? - gfm_body = gfm(escape_once(body), html_options) + escaped_body = if body =~ /^\<img/ + body + else + escape_once(body) + end + + gfm_body = gfm(escaped_body, html_options) gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match| "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1 diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 158925ba6c03737a7cfac1675d456cf5b37c2687..05303e86ae8977143a6730022007c09b48127119 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -3,8 +3,12 @@ module ProjectsHelper @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) end - def remove_from_team_message(project, member) - "You are going to remove #{member.user_name} from #{project.name}. Are you sure?" + def grouper_project_teams(project) + @project.user_team_project_relationships.sort_by(&:greatest_access).reverse.group_by(&:greatest_access) + end + + def remove_from_project_team_message(project, user) + "You are going to remove #{user.name} from #{project.name} project team. Are you sure?" end def link_to_project project @@ -39,7 +43,7 @@ module ProjectsHelper tm = project.team_member_by_id(author) if tm - link_to author_html, project_team_member_path(project, tm), class: "author_link" + link_to author_html, project_team_member_path(project, tm.user_username), class: "author_link" else author_html end.html_safe @@ -51,7 +55,9 @@ module ProjectsHelper def project_title project if project.group - project.name_with_namespace + content_tag :span do + link_to(project.group.name, group_path(project.group)) + " / " + project.name + end else project.name end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index d52d8af6641962da4f3255b3c5fa3e722d8d5d26..5bd6de896ee565f4601dc15a56f763df6d7aa44a 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -39,7 +39,12 @@ module TabHelper # Returns a list item element String def nav_link(options = {}, &block) if path = options.delete(:path) - c, a, _ = path.split('#') + if path.respond_to?(:each) + c = path.map { |p| p.split('#').first } + a = path.map { |p| p.split('#').last } + else + c, a, _ = path.split('#') + end else c = options.delete(:controller) a = options.delete(:action) diff --git a/app/helpers/user_teams_helper.rb b/app/helpers/user_teams_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..2055bb3c8bcdc6625a58f2ff4c5ca56471189234 --- /dev/null +++ b/app/helpers/user_teams_helper.rb @@ -0,0 +1,26 @@ +module UserTeamsHelper + def team_filter_path(entity, options={}) + exist_opts = { + status: params[:status], + project_id: params[:project_id], + } + + options = exist_opts.merge(options) + + case entity + when 'issue' then + issues_team_path(@team, options) + when 'merge_request' + merge_requests_team_path(@team, options) + end + end + + def grouped_user_team_members(team) + team.user_team_user_relationships.sort_by(&:permission).reverse.group_by(&:permission) + end + + def remove_from_user_team_message(team, member) + "You are going to remove #{member.name} from #{team.name}. Are you sure?" + end + +end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 0ed06475fa8090fcfb2b5e10d90b03267ed7286c..08f7e01aab11c192ec46d6040283dfa42a635e2c 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -10,6 +10,10 @@ class Notify < ActionMailer::Base default from: Gitlab.config.gitlab.email_from + # Just send email with 3 seconds delay + def self.delay + delay_for(2.seconds) + end # @@ -63,12 +67,12 @@ class Notify < ActionMailer::Base # Note # - def note_commit_email(commit_autor_email, note_id) + def note_commit_email(recipient_id, note_id) @note = Note.find(note_id) @commit = @note.noteable @commit = CommitDecorator.decorate(@commit) @project = @note.project - mail(to: commit_autor_email, subject: subject("note for commit #{@commit.short_id}", @commit.title)) + mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) end def note_issue_email(recipient_id, note_id) diff --git a/app/models/ability.rb b/app/models/ability.rb index 9d33501fdbc57a0cb2c79b245a7396e8b9d77500..6d087a959a9320f9f744f217619b997fcbd8942c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,15 +1,25 @@ class Ability class << self - def allowed(object, subject) + def allowed(user, subject) + return [] unless user.kind_of?(User) + case subject.class.name - when "Project" then project_abilities(object, subject) - when "Issue" then issue_abilities(object, subject) - when "Note" then note_abilities(object, subject) - when "Snippet" then snippet_abilities(object, subject) - when "MergeRequest" then merge_request_abilities(object, subject) - when "Group", "Namespace" then group_abilities(object, subject) + when "Project" then project_abilities(user, subject) + when "Issue" then issue_abilities(user, subject) + when "Note" then note_abilities(user, subject) + when "Snippet" then snippet_abilities(user, subject) + when "MergeRequest" then merge_request_abilities(user, subject) + when "Group", "Namespace" then group_abilities(user, subject) + when "UserTeam" then user_team_abilities(user, subject) else [] - end + end.concat(global_abilities(user)) + end + + def global_abilities(user) + rules = [] + rules << :create_group if user.can_create_group + rules << :create_team if user.can_create_team + rules end def project_abilities(user, project) @@ -110,6 +120,22 @@ class Ability rules.flatten end + def user_team_abilities user, team + rules = [] + + # Only group owner and administrators can manage group + if team.owner == user || team.admin?(user) || user.admin? + rules << [ :manage_user_team ] + end + + if team.owner == user || user.admin? + rules << [ :admin_user_team ] + end + + rules.flatten + end + + [:issue, :note, :snippet, :merge_request].each do |name| define_method "#{name}_abilities" do |user, subject| if subject.author == user diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index d1717d3bbeef3dff620f486ab5f3b674a046a2a0..8872cf59a2cfa17fed8f461dc7e9037090911082 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -22,6 +22,7 @@ module Issuable scope :opened, where(closed: false) scope :closed, where(closed: true) scope :of_group, ->(group) { where(project_id: group.project_ids) } + scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } scope :assigned, ->(u) { where(assignee_id: u.id)} scope :recent, order("created_at DESC") diff --git a/app/models/issue.rb b/app/models/issue.rb index 40a6c01577d3b5477f3b4e4fe1818bae3a666efc..07c0401143c8050f35aad274ee2e71abdbf7a091 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -24,8 +24,6 @@ class Issue < ActiveRecord::Base acts_as_taggable_on :labels - validates :description, length: { within: 0..10000 } - def self.open_for(user) opened.assigned(user) end diff --git a/app/models/key.rb b/app/models/key.rb index 2bf50f5656572122bb8bf0e47ac8d31efc7e9f34..895e8d6cb9ccbab7693ceb422fa19529286760b0 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -24,8 +24,8 @@ class Key < ActiveRecord::Base before_save :set_identifier validates :title, presence: true, length: { within: 0..255 } - validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / } - validate :unique_key, :fingerprintable_key + validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true + validate :fingerprintable_key delegate :name, :email, to: :user, prefix: true @@ -33,14 +33,6 @@ class Key < ActiveRecord::Base self.key = self.key.strip unless self.key.blank? end - def unique_key - query = Key.where(key: key) - query = query.where('(project_id IS NULL OR project_id = ?)', project_id) if project_id - if (query.count > 0) - errors.add :key, 'already exist.' - end - end - def fingerprintable_key return true unless key # Don't test if there is no key. # `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected @@ -65,7 +57,7 @@ class Key < ActiveRecord::Base end def is_deploy_key - true if project_id + !!project_id end # projects that has this key @@ -77,7 +69,7 @@ class Key < ActiveRecord::Base end end - def last_deploy? - Key.where(identifier: identifier).count == 0 + def shell_id + "key-#{self.id}" end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index ad04d0ef99b88045176cc6d3e7eec5c28e6e2249..f17d8f65183895cddf57189fe66c3a8ae725daba 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -27,7 +27,6 @@ class Namespace < ActiveRecord::Base after_create :ensure_dir_exist after_update :move_dir - after_commit :update_gitolite, on: :update, if: :require_update_gitolite after_destroy :rm_dir scope :root, where('type IS NULL') @@ -89,11 +88,6 @@ class Namespace < ActiveRecord::Base end end - def update_gitolite - @require_update_gitolite = false - projects.each(&:update_repository) - end - def rm_dir dir_path = File.join(Gitlab.config.gitolite.repos_path, path) FileUtils.rm_r( dir_path, force: true ) diff --git a/app/models/project.rb b/app/models/project.rb index fa38093b7d5364e44cdc2d530fed762a4642d749..e6be2d2ce840fefcc406a77833c970fb969ea06b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -8,7 +8,6 @@ # description :text # created_at :datetime not null # updated_at :datetime not null -# private_flag :boolean default(TRUE), not null # creator_id :integer # default_branch :string(255) # issues_enabled :boolean default(TRUE), not null @@ -16,6 +15,7 @@ # merge_requests_enabled :boolean default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null # namespace_id :integer +# public :boolean default(FALSE), not null # require "grit" @@ -33,28 +33,31 @@ class Project < ActiveRecord::Base attr_accessor :error_code # Relations - belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" + belongs_to :creator, foreign_key: "creator_id", class_name: "User" + belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" belongs_to :namespace - belongs_to :creator, - class_name: "User", - foreign_key: "creator_id" - - has_many :users, through: :users_projects - has_many :events, dependent: :destroy - has_many :merge_requests, dependent: :destroy - has_many :issues, dependent: :destroy, order: "closed, created_at DESC" - has_many :milestones, dependent: :destroy - has_many :users_projects, dependent: :destroy - has_many :notes, dependent: :destroy - has_many :snippets, dependent: :destroy - has_many :deploy_keys, dependent: :destroy, foreign_key: "project_id", class_name: "Key" - has_many :hooks, dependent: :destroy, class_name: "ProjectHook" - has_many :wikis, dependent: :destroy - has_many :protected_branches, dependent: :destroy has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id' has_one :gitlab_ci_service, dependent: :destroy + has_many :events, dependent: :destroy + has_many :merge_requests, dependent: :destroy + has_many :issues, dependent: :destroy, order: "closed, created_at DESC" + has_many :milestones, dependent: :destroy + has_many :users_projects, dependent: :destroy + has_many :notes, dependent: :destroy + has_many :snippets, dependent: :destroy + has_many :deploy_keys, dependent: :destroy, class_name: "Key", foreign_key: "project_id" + has_many :hooks, dependent: :destroy, class_name: "ProjectHook" + has_many :wikis, dependent: :destroy + has_many :protected_branches, dependent: :destroy + has_many :user_team_project_relationships, dependent: :destroy + + has_many :users, through: :users_projects + has_many :user_teams, through: :user_team_project_relationships + has_many :user_team_user_relationships, through: :user_teams + has_many :user_teams_members, through: :user_team_user_relationships + delegate :name, to: :owner, allow_nil: true, prefix: true # Validations @@ -77,6 +80,8 @@ class Project < ActiveRecord::Base # Scopes scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) } scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) } + scope :without_team, ->(team) { team.projects.present? ? where("id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped } + scope :in_team, ->(team) { where("id IN (:ids)", ids: team.projects.map(&:id)) } scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) } scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } @@ -122,7 +127,7 @@ class Project < ActiveRecord::Base end def team - @team ||= Team.new(self) + @team ||= ProjectTeam.new(self) end def repository @@ -257,8 +262,6 @@ class Project < ActiveRecord::Base Gitlab::ProjectMover.new(self, old_dir, new_dir).execute - gitolite.move_repository(old_repo, self) - save! end rescue Gitlab::ProjectMover::ProjectMoveError => ex @@ -294,6 +297,9 @@ class Project < ActiveRecord::Base def trigger_post_receive(oldrev, newrev, ref, user) data = post_receive_data(oldrev, newrev, ref, user) + # Create satellite + self.satellite.create unless self.satellite.exists? + # Create push event self.observe_push(data) @@ -308,9 +314,6 @@ class Project < ActiveRecord::Base self.execute_services(data.dup) end - # Create satellite - self.satellite.create unless self.satellite.exists? - # Discover the default branch, but only if it hasn't already been set to # something else if repository && default_branch.nil? @@ -335,7 +338,7 @@ class Project < ActiveRecord::Base end def execute_hooks(data) - hooks.each { |hook| hook.execute(data) } + hooks.each { |hook| hook.async_execute(data) } end def execute_services(data) @@ -454,14 +457,6 @@ class Project < ActiveRecord::Base namespace.try(:path) || '' end - def update_repository - gitolite.update_repository(self) - end - - def destroy_repository - gitolite.remove_repository(self) - end - def repo_exists? @repo_exists ||= (repository && repository.branches.present?) rescue @@ -489,6 +484,11 @@ class Project < ActiveRecord::Base http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') end + def project_access_human(member) + project_user_relation = self.users_projects.find_by_user_id(member.id) + self.class.access_options.key(project_user_relation.project_access) + end + # Check if current branch name is marked as protected in the system def protected_branch? branch_name protected_branches.map(&:name).include?(branch_name) diff --git a/app/models/team.rb b/app/models/project_team.rb similarity index 95% rename from app/models/team.rb rename to app/models/project_team.rb index f235d20ebdbfc7285bd8d98db0aebcb907b0e3d1..c2cf83c0ca831177a54c85a26df30b0baeb32295 100644 --- a/app/models/team.rb +++ b/app/models/project_team.rb @@ -1,4 +1,4 @@ -class Team +class ProjectTeam attr_accessor :project def initialize(project) @@ -21,6 +21,10 @@ class Team end end + def get_tm user_id + project.users_projects.find_by_user_id(user_id) + end + def add_user(user, access) add_users_ids([user.id], access) end @@ -108,7 +112,6 @@ class Team source_team.each do |tm| tm.save end - target_project.update_repository end true diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 3308caf360a38e12cac1ab85e3bee63cdf09d40a..57229d507594cd1fa95d95479df6cfca2d57e662 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -18,13 +18,6 @@ class ProtectedBranch < ActiveRecord::Base validates :name, presence: true validates :project, presence: true - after_save :update_repository - after_destroy :update_repository - - def update_repository - gitolite.update_repository(project) - end - def commit project.repository.commit(self.name) end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8d7eb788abb6e35f353c67ccd5efdc62d5499c22..806d346cf1084d0e4c3f2d96894ee62c084b1de2 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -28,7 +28,7 @@ class Snippet < ActiveRecord::Base validates :project, presence: true validates :title, presence: true, length: { within: 0..255 } validates :file_name, presence: true, length: { within: 0..255 } - validates :content, presence: true, length: { within: 0..10000 } + validates :content, presence: true # Scopes scope :fresh, order("created_at DESC") diff --git a/app/models/user.rb b/app/models/user.rb index 35a693fdb1c0beb4d58591af2685805fabfb4757..5b0df09a439780bbcc90ae731ffabb3e8af6bac6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -31,6 +31,8 @@ # extern_uid :string(255) # provider :string(255) # username :string(255) +# can_create_group :boolean default(TRUE), not null +# can_create_team :boolean default(TRUE), not null # class User < ActiveRecord::Base @@ -40,23 +42,32 @@ class User < ActiveRecord::Base attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, :skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password, :extern_uid, :provider, as: [:default, :admin] - attr_accessible :projects_limit, as: :admin + attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin attr_accessor :force_random_password # Namespace for personal projects - has_one :namespace, class_name: "Namespace", foreign_key: :owner_id, conditions: 'type IS NULL', dependent: :destroy - has_many :groups, class_name: "Group", foreign_key: :owner_id - - has_many :keys, dependent: :destroy - has_many :users_projects, dependent: :destroy - has_many :issues, foreign_key: :author_id, dependent: :destroy - has_many :notes, foreign_key: :author_id, dependent: :destroy - has_many :merge_requests, foreign_key: :author_id, dependent: :destroy - has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy - has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC" - has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy - has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy + has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL' + + has_many :keys, dependent: :destroy + has_many :users_projects, dependent: :destroy + has_many :issues, dependent: :destroy, foreign_key: :author_id + has_many :notes, dependent: :destroy, foreign_key: :author_id + has_many :merge_requests, dependent: :destroy, foreign_key: :author_id + has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event" + has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue" + has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest" + + has_many :groups, class_name: "Group", foreign_key: :owner_id + has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC" + + has_many :projects, through: :users_projects + + has_many :user_team_user_relationships, dependent: :destroy + + has_many :user_teams, through: :user_team_user_relationships + has_many :user_team_project_relationships, through: :user_teams + has_many :team_projects, through: :user_team_project_relationships validates :name, presence: true validates :bio, length: { within: 0..255 } @@ -80,6 +91,9 @@ class User < ActiveRecord::Base scope :blocked, where(blocked: true) scope :active, where(blocked: false) scope :alphabetically, order('name ASC') + scope :in_team, ->(team){ where(id: team.member_ids) } + scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } + scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } # # Class methods @@ -131,6 +145,11 @@ class User < ActiveRecord::Base # # Instance methods # + + def to_param + username + end + def generate_password if self.force_random_password self.password = self.password_confirmation = Devise.friendly_token.first(8) @@ -220,7 +239,7 @@ class User < ActiveRecord::Base end def can_create_group? - is_admin? + can?(:create_group, nil) end def abilities @@ -283,4 +302,15 @@ class User < ActiveRecord::Base def namespace_id namespace.try :id end + + def authorized_teams + @authorized_teams ||= begin + ids = [] + ids << UserTeam.with_member(self).pluck('user_teams.id') + ids << UserTeam.created_by(self).pluck('user_teams.id') + ids.flatten + + UserTeam.where(id: ids) + end + end end diff --git a/app/models/user_team.rb b/app/models/user_team.rb new file mode 100644 index 0000000000000000000000000000000000000000..dc8cf9eeb22dede1712ef000cd49f72544a99843 --- /dev/null +++ b/app/models/user_team.rb @@ -0,0 +1,109 @@ +# == Schema Information +# +# Table name: user_teams +# +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# owner_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +class UserTeam < ActiveRecord::Base + attr_accessible :name, :owner_id, :path + + belongs_to :owner, class_name: User + + has_many :user_team_project_relationships, dependent: :destroy + has_many :user_team_user_relationships, dependent: :destroy + + has_many :projects, through: :user_team_project_relationships + has_many :members, through: :user_team_user_relationships, source: :user + + validates :name, presence: true, uniqueness: true + validates :owner, presence: true + validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, + format: { with: Gitlab::Regex.path_regex, + message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } + + scope :with_member, ->(user){ joins(:user_team_user_relationships).where(user_team_user_relationships: {user_id: user.id}) } + scope :with_project, ->(project){ joins(:user_team_project_relationships).where(user_team_project_relationships: {project_id: project})} + scope :without_project, ->(project){ where("user_teams.id NOT IN (:ids)", ids: (a = with_project(project); a.blank? ? 0 : a))} + scope :created_by, ->(user){ where(owner_id: user) } + + class << self + def search query + where("name LIKE :query OR path LIKE :query", query: "%#{query}%") + end + + def global_id + 'GLN' + end + + def access_roles + UsersProject.access_roles + end + end + + def to_param + path + end + + def assign_to_projects(projects, access) + projects.each do |project| + assign_to_project(project, access) + end + end + + def assign_to_project(project, access) + Gitlab::UserTeamManager.assign(self, project, access) + end + + def resign_from_project(project) + Gitlab::UserTeamManager.resign(self, project) + end + + def add_members(users, access, group_admin) + users.each do |user| + add_member(user, access, group_admin) + end + end + + def add_member(user, access, group_admin) + Gitlab::UserTeamManager.add_member_into_team(self, user, access, group_admin) + end + + def remove_member(user) + Gitlab::UserTeamManager.remove_member_from_team(self, user) + end + + def update_membership(user, options) + Gitlab::UserTeamManager.update_team_user_membership(self, user, options) + end + + def update_project_access(project, permission) + Gitlab::UserTeamManager.update_project_greates_access(self, project, permission) + end + + def max_project_access(project) + user_team_project_relationships.find_by_project_id(project).greatest_access + end + + def human_max_project_access(project) + self.class.access_roles.invert[max_project_access(project)] + end + + def default_projects_access(member) + user_team_user_relationships.find_by_user_id(member).permission + end + + def human_default_projects_access(member) + self.class.access_roles.invert[default_projects_access(member)] + end + + def admin?(member) + user_team_user_relationships.with_user(member).first.group_admin? + end + +end diff --git a/app/models/user_team_project_relationship.rb b/app/models/user_team_project_relationship.rb new file mode 100644 index 0000000000000000000000000000000000000000..a7aa88970c77f5faeeed7534d4dc5ea48032d828 --- /dev/null +++ b/app/models/user_team_project_relationship.rb @@ -0,0 +1,40 @@ +# == Schema Information +# +# Table name: user_team_project_relationships +# +# id :integer not null, primary key +# project_id :integer +# user_team_id :integer +# greatest_access :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +class UserTeamProjectRelationship < ActiveRecord::Base + attr_accessible :greatest_access, :project_id, :user_team_id + + belongs_to :user_team + belongs_to :project + + validates :project, presence: true + validates :user_team, presence: true + validate :check_greatest_access + + scope :with_project, ->(project){ where(project_id: project.id) } + + def team_name + user_team.name + end + + private + + def check_greatest_access + errors.add(:base, :incorrect_access_code) unless correct_access? + end + + def correct_access? + return false if greatest_access.blank? + return true if UsersProject.access_roles.has_value?(greatest_access) + false + end +end diff --git a/app/models/user_team_user_relationship.rb b/app/models/user_team_user_relationship.rb new file mode 100644 index 0000000000000000000000000000000000000000..1f7e2625f5f4665472270932068b963521fa2846 --- /dev/null +++ b/app/models/user_team_user_relationship.rb @@ -0,0 +1,32 @@ +# == Schema Information +# +# Table name: user_team_user_relationships +# +# id :integer not null, primary key +# user_id :integer +# user_team_id :integer +# group_admin :boolean +# permission :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +class UserTeamUserRelationship < ActiveRecord::Base + attr_accessible :group_admin, :permission, :user_id, :user_team_id + + belongs_to :user_team + belongs_to :user + + validates :user_team, presence: true + validates :user, presence: true + + scope :with_user, ->(user) { where(user_id: user.id) } + + def user_name + user.name + end + + def access_human + UsersProject.access_roles.invert[permission] + end +end diff --git a/app/models/users_project.rb b/app/models/users_project.rb index 79146289836cfd5bfd70b8721917b6c572fe0490..dd8ceb9da70e51ef7a16c548530948435c665d92 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -25,21 +25,21 @@ class UsersProject < ActiveRecord::Base attr_accessor :skip_git - after_save :update_repository, unless: :skip_git? - after_destroy :update_repository, unless: :skip_git? - validates :user, presence: true validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" } validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true validates :project, presence: true - delegate :name, :email, to: :user, prefix: true + delegate :name, :username, :email, to: :user, prefix: true scope :guests, where(project_access: GUEST) scope :reporters, where(project_access: REPORTER) scope :developers, where(project_access: DEVELOPER) scope :masters, where(project_access: MASTER) + scope :in_project, ->(project) { where(project_id: project.id) } + scope :in_projects, ->(projects) { where(project_id: project_ids) } + scope :with_user, ->(user) { where(user_id: user.id) } class << self @@ -79,7 +79,6 @@ class UsersProject < ActiveRecord::Base users_project.save end end - Gitlab::Gitolite.new.update_repositories(Project.where(id: project_ids)) end true @@ -94,7 +93,6 @@ class UsersProject < ActiveRecord::Base users_project.skip_git = true users_project.destroy end - Gitlab::Gitolite.new.update_repositories(Project.where(id: project_ids)) end true @@ -125,10 +123,6 @@ class UsersProject < ActiveRecord::Base end end - def update_repository - gitolite.update_repository(project) - end - def project_access_human Project.access_options.key(self.project_access) end diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index df58fa93b7e4d5c1a9f724f8ed7d30ff9184ddd1..efa27f31982d9faa16313271d7124169651668af 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -34,4 +34,8 @@ class WebHook < ActiveRecord::Base basic_auth: {username: parsed_url.user, password: parsed_url.password}) end end + + def async_execute(data) + Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data) + end end diff --git a/app/observers/key_observer.rb b/app/observers/key_observer.rb index bf5fa647647d9d628ef7bae009525246fd5261c8..4146216d82f16b96e96bc8640de6050905611165 100644 --- a/app/observers/key_observer.rb +++ b/app/observers/key_observer.rb @@ -2,11 +2,18 @@ class KeyObserver < ActiveRecord::Observer include Gitolited def after_save(key) - gitolite.set_key(key.identifier, key.key, key.projects) + GitoliteWorker.perform_async( + :add_key, + key.shell_id, + key.key + ) end def after_destroy(key) - return if key.is_deploy_key && !key.last_deploy? - gitolite.remove_key(key.identifier, key.projects) + GitoliteWorker.perform_async( + :remove_key, + key.shell_id, + key.key, + ) end end diff --git a/app/observers/note_observer.rb b/app/observers/note_observer.rb index 2ec644ef7c1f0e76f4af121b7653a9f5d85948a9..4ee9fadf4dad84d2ba2aa5aee2ceff339492d9c0 100644 --- a/app/observers/note_observer.rb +++ b/app/observers/note_observer.rb @@ -11,7 +11,9 @@ class NoteObserver < ActiveRecord::Observer notify_team(note) elsif note.notify_author # Notify only author of resource - Notify.delay.note_commit_email(note.noteable.author_email, note.id) + if note.commit_author + Notify.delay.note_commit_email(note.commit_author.id, note.id) + end else # Otherwise ignore it nil diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index b1c694569d7d4cd51bd2d299eb24d2810b776725..cc2a0224b722d2a9a59f5687fac11cac8b82327c 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,6 +1,11 @@ class ProjectObserver < ActiveRecord::Observer def after_create(project) - project.update_repository + GitoliteWorker.perform_async( + :add_repository, + project.path_with_namespace + ) + + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") end def after_update(project) @@ -8,13 +13,14 @@ class ProjectObserver < ActiveRecord::Observer end def after_destroy(project) - log_info("Project \"#{project.name}\" was removed") + GitoliteWorker.perform_async( + :remove_repository, + project.path_with_namespace + ) - project.destroy_repository - end + project.satellite.destroy - def after_create project - log_info("#{project.owner.name} created a new project \"#{project.name}\"") + log_info("Project \"#{project.name}\" was removed") end protected diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 9a5e7edea0fd4281c2eb74fcafe487319b4e677c..46a876294ceb745f93098bfe2b8fdc147bcb2d51 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -6,7 +6,7 @@ = link_to admin_projects_path do %h1= Project.count %hr - = link_to 'New Project', new_project_path, class: "btn small" + = link_to 'New Project', new_project_path, class: "btn btn-small" .span4 .ui-box %h5.title Groups @@ -14,7 +14,7 @@ = link_to admin_groups_path do %h1= Group.count %hr - = link_to 'New Group', new_admin_group_path, class: "btn small" + = link_to 'New Group', new_admin_group_path, class: "btn btn-small" .span4 .ui-box %h5.title Users @@ -22,7 +22,7 @@ = link_to admin_users_path do %h1= User.count %hr - = link_to 'New User', new_admin_user_path, class: "btn small" + = link_to 'New User', new_admin_user_path, class: "btn btn-small" .row .span4 @@ -31,7 +31,7 @@ - @projects.each do |project| %p = link_to project.name_with_namespace, [:admin, project] - %span.light.right + %span.light.pull-right = time_ago_in_words project.created_at ago @@ -42,7 +42,7 @@ %p = link_to [:admin, user] do = user.name - %span.light.right + %span.light.pull-right = time_ago_in_words user.created_at ago @@ -51,25 +51,25 @@ %hr %p Issues - %span.light.right + %span.light.pull-right = Issue.count %p Merge Requests - %span.light.right + %span.light.pull-right = MergeRequest.count %p Notes - %span.light.right + %span.light.pull-right = Note.count %p Snippets - %span.light.right + %span.light.pull-right = Snippet.count %p SSH Keys - %span.light.right + %span.light.pull-right = Key.count %p Milestones - %span.light.right + %span.light.pull-right = Milestone.count diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml index 901d07e74f36371212e4d97b49e5aa1a8cd5a9e1..dce044956c36558805fd007e125a70cbdb3acf75 100644 --- a/app/views/admin/groups/edit.html.haml +++ b/app/views/admin/groups/edit.html.haml @@ -2,7 +2,7 @@ %hr = form_for [:admin, @group] do |f| - if @group.errors.any? - .alert-message.block-message.error + .alert.alert-error %span= @group.errors.full_messages.first .clearfix.group_name_holder = f.label :name do @@ -24,5 +24,5 @@ %li It will change the git path to repositories under this group. .form-actions - = f.submit 'Rename group', class: "btn danger" - = link_to 'Cancel', admin_groups_path, class: "btn cancel-btn" + = f.submit 'Rename group', class: "btn btn-remove" + = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 49acedc8c79979413016533633ce36a5c90d45e0..25ce66575bfa79def312b6315fcfa9b0dbea7ce2 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -4,11 +4,11 @@ allows you to keep projects organized. Use groups for uniting related projects. - = link_to 'New Group', new_admin_group_path, class: "btn small right" + = link_to 'New Group', new_admin_group_path, class: "btn btn-small pull-right" %br = form_tag admin_groups_path, method: :get, class: 'form-inline' do = text_field_tag :name, params[:name], class: "xlarge" - = submit_tag "Search", class: "btn submit primary" + = submit_tag "Search", class: "btn submit btn-primary" %table %thead @@ -30,6 +30,6 @@ %td = link_to group.owner_name, admin_user_path(group.owner_id) %td.bgred - = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small" - = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger" + = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small" + = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" = paginate @groups, theme: "admin" diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml index 6ff0e781d1740461cbb40d9e9b6f0e8859a9edf8..60c6fa5ad090683891ccc8b4792f9fe607ce7496 100644 --- a/app/views/admin/groups/new.html.haml +++ b/app/views/admin/groups/new.html.haml @@ -2,7 +2,7 @@ %hr = form_for [:admin, @group] do |f| - if @group.errors.any? - .alert-message.block-message.error + .alert.alert-error %span= @group.errors.full_messages.first .clearfix = f.label :name do @@ -10,7 +10,7 @@ .input = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" - = f.submit 'Create group', class: "btn primary" + = f.submit 'Create group', class: "btn btn-primary" %hr .padded %ul diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 0a25b125905365af537a9c8790e8ddf7d4ef58bf..6ae8a75d696992efd65e7e1cf812d50b5714e7a3 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -14,7 +14,7 @@ %td = @group.name - = link_to edit_admin_group_path(@group), class: "btn btn-small right" do + = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do %i.icon-edit Rename %tr @@ -29,7 +29,7 @@ Owner: %td = @group.owner_name - .right + .pull-right = link_to "#", class: "btn btn-small change-owner-link" do %i.icon-edit Change owner @@ -42,7 +42,7 @@ = form_for [:admin, @group] do |f| = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} %div - = f.submit 'Change Owner', class: "btn danger" + = f.submit 'Change Owner', class: "btn btn-remove" = link_to "Cancel", "#", class: "btn change-owner-cancel-link" - if @group.projects.any? @@ -63,7 +63,7 @@ %span.monospace= project.path_with_namespace + ".git" %td= project.users.count %td.bgred - = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn danger small" + = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small" = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do %table.zebra-striped @@ -72,22 +72,23 @@ %th Users %th Project Access: - - @group.users.each do |u| - %tr{class: "user_#{u.id}"} - %td.name= link_to u.name, admin_user_path(u) + - @group.users.each do |user| + - next unless user + %tr{class: "user_#{user.id}"} + %td.name= link_to user.name, admin_user_path(user) %td.projects_access - - u.authorized_projects.in_namespace(@group).each do |project| - - u_p = u.users_projects.in_project(project).first + - user.authorized_projects.in_namespace(@group).each do |project| + - u_p = user.users_projects.in_project(project).first - next unless u_p %span - = project.name - = link_to "(#{ u_p.project_access_human })", edit_admin_team_member_path(u_p) + = project.name_with_namespace + = link_to "(#{ u_p.project_access_human })", edit_admin_project_member_path(project, user) %tr %td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} %tr - %td= submit_tag 'Add user to projects in group', class: "btn primary" + %td= submit_tag 'Add user to projects in group', class: "btn btn-primary" %td Read more about project permissions %strong= link_to "here", help_permissions_path, class: "vlink" @@ -109,7 +110,7 @@ .input = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' .form-actions - = submit_tag 'Add', class: "btn primary" + = submit_tag 'Add', class: "btn btn-primary" :javascript $(function(){ diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index f17355fb2c2996f77851c7fa5704e50c454c52bb..acbf7a108b85d52168935a8fc33c689407ad619e 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -7,7 +7,7 @@ = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f| -if @hook.errors.any? - .alert-message.block-message.error + .alert.alert-error - @hook.errors.full_messages.each do |msg| %p= msg .clearfix @@ -15,7 +15,7 @@ .input = f.text_field :url, class: "text_field xxlarge" - = f.submit "Add System Hook", class: "btn primary" + = f.submit "Add System Hook", class: "btn btn-primary" %hr -if @hooks.any? @@ -33,7 +33,7 @@ %td = link_to admin_hook_path(hook) do %strong= hook.url - = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn small right" + = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small pull-right" %td POST %td - = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small right" + = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small pull-right" diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index c8be2ffa43cd923fd2c0206af7e16ab3e5a3ad6d..9ddd781c6ec4656e317c43cbb0f3be4d5e48bf88 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -15,7 +15,7 @@ .file_title %i.icon-file githost.log - .right + .pull-right = link_to '#', class: 'log-bottom' do %i.icon-arrow-down Scroll down @@ -29,7 +29,7 @@ .file_title %i.icon-file application.log - .right + .pull-right = link_to '#', class: 'log-bottom' do %i.icon-arrow-down Scroll down @@ -43,7 +43,7 @@ .file_title %i.icon-file production.log - .right + .pull-right = link_to '#', class: 'log-bottom' do %i.icon-arrow-down Scroll down @@ -57,7 +57,7 @@ .file_title %i.icon-file sidekiq.log - .right + .pull-right = link_to '#', class: 'log-bottom' do %i.icon-arrow-down Scroll down diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml index 0c7cf68ef439334b80834e96481eb842d978345a..ebf69924a2590e744d821c889cb4e94e3d22f653 100644 --- a/app/views/admin/projects/_form.html.haml +++ b/app/views/admin/projects/_form.html.haml @@ -1,6 +1,6 @@ = form_for [:admin, project] do |f| -if project.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - project.errors.full_messages.each do |msg| %li= msg @@ -65,8 +65,8 @@ .actions - = f.submit 'Save Project', class: "btn save-btn" - = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn" + = f.submit 'Save Project', class: "btn btn-save" + = link_to 'Cancel', admin_projects_path, class: "btn btn-cancel" diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index e47cda766369833b3867ef72fef4b95608ed6a90..15b2778252a367aa5e5f74b6062127cd3d8738c0 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -1,6 +1,6 @@ %h3.page_title Projects - = link_to 'New Project', new_project_path, class: "btn small right" + = link_to 'New Project', new_project_path, class: "btn btn-small pull-right" %hr @@ -37,7 +37,7 @@ .form-actions - = submit_tag "Search", class: "btn submit primary" + = submit_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_projects_path, class: "btn" .span8 .ui-box @@ -51,9 +51,9 @@ - else %i.icon-lock.cgreen = link_to project.name_with_namespace, [:admin, project] - .right - = link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small" - = link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger" + .pull-right + = link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" - if @projects.blank? %p.nothing_here_message 0 projects matches - else diff --git a/app/views/admin/projects/members/_form.html.haml b/app/views/admin/projects/members/_form.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..8041202980dabce7914b5b2dac95b7598637621a --- /dev/null +++ b/app/views/admin/projects/members/_form.html.haml @@ -0,0 +1,16 @@ += form_for @team_member_relation, as: :team_member, url: admin_project_member_path(@project, @member) do |f| + -if @team_member_relation.errors.any? + .alert.alert-error + %ul + - @team_member_relation.errors.full_messages.each do |msg| + %li= msg + + .clearfix + %label Project Access: + .input + = f.select :project_access, options_for_select(Project.access_options, @team_member_relation.project_access), {}, class: "project-access-select chosen span3" + + %br + .actions + = f.submit 'Save', class: "btn btn-primary" + = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/projects/members/edit.html.haml b/app/views/admin/projects/members/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..2d76deb2aca195691d20e80b9524b5322e4d900c --- /dev/null +++ b/app/views/admin/projects/members/edit.html.haml @@ -0,0 +1,8 @@ +%p.slead + Edit access for + = link_to @member.name, admin_user_path(@member) + in + = link_to @project.name_with_namespace, admin_project_path(@project) + +%hr += render 'form' diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 8e0d82328dfb6e25265658407212cdfaf735dd9e..b9294bbafbe250e09eb558ff1b8a4542323cd44c 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -1,6 +1,6 @@ %h3.page_title Project: #{@project.name_with_namespace} - = link_to edit_admin_project_path(@project), class: "btn right" do + = link_to edit_admin_project_path(@project), class: "btn pull-right" do %i.icon-edit Edit @@ -114,9 +114,9 @@ %h5 Team %small - (#{@project.users_projects.count}) + (#{@project.users.count}) %br -%table.zebra-striped +%table.zebra-striped.team_members %thead %tr %th Name @@ -124,13 +124,13 @@ %th Repository Access %th - - @project.users_projects.each do |tm| + - @project.users.each do |tm| %tr %td - = link_to tm.user_name, admin_user_path(tm.user) - %td= tm.project_access_human - %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" - %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small" + = link_to tm.name, admin_user_path(tm) + %td= @project.project_access_human(tm) + %td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn btn-small" + %td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove small" %br %h5 Add new team member @@ -147,7 +147,7 @@ %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} %tr - %td= submit_tag 'Add', class: "btn primary" + %td= submit_tag 'Add', class: "btn btn-primary" %td Read more about project permissions %strong= link_to "here", help_permissions_path, class: "vlink" diff --git a/app/views/admin/team_members/_form.html.haml b/app/views/admin/team_members/_form.html.haml deleted file mode 100644 index 9cd94fdd30f11e7cb40943b930f084031accd709..0000000000000000000000000000000000000000 --- a/app/views/admin/team_members/_form.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= form_for @admin_team_member, as: :team_member, url: admin_team_member_path(@admin_team_member) do |f| - -if @admin_team_member.errors.any? - .alert-message.block-message.error - %ul - - @admin_team_member.errors.full_messages.each do |msg| - %li= msg - - .clearfix - %label Project Access: - .input - = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3" - - %br - .actions - = f.submit 'Save', class: "btn primary" - = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/team_members/edit.html.haml b/app/views/admin/team_members/edit.html.haml deleted file mode 100644 index aea9bd70a79d29f6d658979a62976ac5f597beb2..0000000000000000000000000000000000000000 --- a/app/views/admin/team_members/edit.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%p.slead - Edit access for - = link_to @admin_team_member.user_name, admin_user_path(@admin_team_member) - in - = link_to @admin_team_member.project.name_with_namespace, admin_project_path(@admin_team_member) - -%hr -= render 'form' diff --git a/app/views/admin/teams/edit.html.haml b/app/views/admin/teams/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..9282398ce5bfbfa4be6754ceaaf3277fa1c980a5 --- /dev/null +++ b/app/views/admin/teams/edit.html.haml @@ -0,0 +1,23 @@ +%h3.page_title Rename Team +%hr += form_for @team, url: admin_team_path(@team), method: :put do |f| + - if @team.errors.any? + .alert.alert-error + %span= @team.errors.full_messages.first + .clearfix.team_name_holder + = f.label :name do + Team name is + .input + = f.text_field :name, placeholder: "Example Team", class: "xxlarge" + + .clearfix.team_name_holder + = f.label :path do + %span.cred Team path is + .input + = f.text_field :path, placeholder: "example-team", class: "xxlarge danger" + %ul.cred + %li It will change web url for access team and team projects. + + .form-actions + = f.submit 'Rename team', class: "btn btn-remove" + = link_to 'Cancel', admin_teams_path, class: "btn btn-cancel" diff --git a/app/views/admin/teams/index.html.haml b/app/views/admin/teams/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..1f2f4763f76e4a2cd71ae6d346193177bbc4636a --- /dev/null +++ b/app/views/admin/teams/index.html.haml @@ -0,0 +1,38 @@ +%h3.page_title + Teams + %small + simple Teams description + + = link_to 'New Team', new_admin_team_path, class: "btn btn-small pull-right" + %br + += form_tag admin_teams_path, method: :get, class: 'form-inline' do + = text_field_tag :name, params[:name], class: "xlarge" + = submit_tag "Search", class: "btn submit btn-primary" + +%table + %thead + %tr + %th + Name + %i.icon-sort-down + %th Path + %th Projects + %th Members + %th Owner + %th.cred Danger Zone! + + - @teams.each do |team| + %tr + %td + %strong= link_to team.name, admin_team_path(team) + %td= team.path + %td= team.projects.count + %td= team.members.count + %td + = link_to team.owner.name, admin_user_path(team.owner_id) + %td.bgred + = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small" + = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" + += paginate @teams, theme: "admin" diff --git a/app/views/admin/teams/members/_form.html.haml b/app/views/admin/teams/members/_form.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..f1388aab4bbf82cef500b5de5da583bb82df153d --- /dev/null +++ b/app/views/admin/teams/members/_form.html.haml @@ -0,0 +1,20 @@ += form_tag admin_team_member_path(@team, @member), method: :put do + -if @member.errors.any? + .alert.alert-error + %ul + - @member.errors.full_messages.each do |msg| + %li= msg + + .clearfix + %label Default access for Team projects: + .input + = select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3" + .clearfix + %label Team admin? + .input + = check_box_tag :group_admin, true, @team.admin?(@member) + + %br + .actions + = submit_tag 'Save', class: "btn btn-primary" + = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/teams/members/edit.html.haml b/app/views/admin/teams/members/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..a82847ee5f86a0492944193de17b72ca79e6b987 --- /dev/null +++ b/app/views/admin/teams/members/edit.html.haml @@ -0,0 +1,16 @@ +%h3 + Edit access #{@member.name} in #{@team.name} team + +%hr +%table.zebra-striped + %tr + %td User: + %td= @member.name + %tr + %td Team: + %td= @team.name + %tr + %td Since: + %td= member_since(@team, @member).stamp("Nov 11, 2010") + += render 'form' diff --git a/app/views/admin/teams/members/new.html.haml b/app/views/admin/teams/members/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..a37c941db5351b79603dfb7bf9c6d3d3929d885b --- /dev/null +++ b/app/views/admin/teams/members/new.html.haml @@ -0,0 +1,29 @@ +%h3.page_title + Team: #{@team.name} + +%fieldset + %legend Members (#{@team.members.count}) + = form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do + %table#members_list + %thead + %tr + %th User name + %th Default project access + %th Team access + %th + - @team.members.each do |member| + %tr.member + %td + = link_to [:admin, member] do + = member.name + %small= "(#{member.email})" + %td= @team.human_default_projects_access(member) + %td= @team.admin?(member) ? "Admin" : "Member" + %td + %tr + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_email), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' + %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } + %td + %span= check_box_tag :group_admin + %span Admin? + %td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team diff --git a/app/views/admin/teams/new.html.haml b/app/views/admin/teams/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5d55a7975eecb477afb2e2612639b02c9730029e --- /dev/null +++ b/app/views/admin/teams/new.html.haml @@ -0,0 +1,19 @@ +%h3.page_title New Team +%hr += form_for @team, url: admin_teams_path do |f| + - if @team.errors.any? + .alert.alert-error + %span= @team.errors.full_messages.first + .clearfix + = f.label :name do + Team name is + .input + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" + + = f.submit 'Create team', class: "btn btn-primary" + %hr + .padded + %ul + %li All created teams are public (users can view who enter into team and which project are assigned for this team) + %li People within a team see only projects they have access to + %li You will be able to assign existing projects for team diff --git a/app/views/admin/teams/projects/_form.html.haml b/app/views/admin/teams/projects/_form.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5b79d518d42f24e9655b8485af1eb93f604518b2 --- /dev/null +++ b/app/views/admin/teams/projects/_form.html.haml @@ -0,0 +1,16 @@ += form_tag admin_team_project_path(@team, @project), method: :put do + -if @project.errors.any? + .alert.alert-error + %ul + - @project.errors.full_messages.each do |msg| + %li= msg + + .clearfix + %label Max access for Team members: + .input + = select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3" + + %br + .actions + = submit_tag 'Save', class: "btn btn-primary" + = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/admin/teams/projects/edit.html.haml b/app/views/admin/teams/projects/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..b91a4982b81205f039d84f68eafeafbfe5f9281c --- /dev/null +++ b/app/views/admin/teams/projects/edit.html.haml @@ -0,0 +1,16 @@ +%h3 + Edit max access in #{@project.name} for #{@team.name} team + +%hr +%table.zebra-striped + %tr + %td Project: + %td= @project.name + %tr + %td Team: + %td= @team.name + %tr + %td Since: + %td= assigned_since(@team, @project).stamp("Nov 11, 2010") + += render 'form' diff --git a/app/views/admin/teams/projects/new.html.haml b/app/views/admin/teams/projects/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..b60dad35214f9a793568f1166a8c5c0c8e9011af --- /dev/null +++ b/app/views/admin/teams/projects/new.html.haml @@ -0,0 +1,23 @@ +%h3.page_title + Team: #{@team.name} + +%fieldset + %legend Projects (#{@team.projects.count}) + = form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do + %table#projects_list + %thead + %tr + %th Project name + %th Max access + %th + - @team.projects.each do |project| + %tr.project + %td + = link_to project.name_with_namespace, [:admin, project] + %td + %span= @team.human_max_project_access(project) + %td + %tr + %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' + %td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } + %td= submit_tag 'Add', class: "btn btn-primary", id: :assign_projects_to_team diff --git a/app/views/admin/teams/show.html.haml b/app/views/admin/teams/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..e5d079981c0f9b98d1c658ac09256e0144f2c21e --- /dev/null +++ b/app/views/admin/teams/show.html.haml @@ -0,0 +1,101 @@ +%h3.page_title + Team: #{@team.name} + +%br +%table.zebra-striped + %thead + %tr + %th Team + %th + %tr + %td + %b + Name: + %td + = @team.name + + = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do + %i.icon-edit + Rename + %tr + %td + %b + Owner: + %td + = @team.owner.name + .pull-right + = link_to "#", class: "btn btn-small change-owner-link" do + %i.icon-edit + Change owner + + %tr.change-owner-holder.hide + %td.bgred + %b.cred + New Owner: + %td.bgred + = form_for @team, url: admin_team_path(@team) do |f| + = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} + %div + = f.submit 'Change Owner', class: "btn btn-remove" + = link_to "Cancel", "#", class: "btn change-owner-cancel-link" + +%fieldset + %legend + Members (#{@team.members.count}) + %span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn btn-primary btn-small pull-right", id: :add_members_to_team + - if @team.members.any? + %table#members_list + %thead + %tr + %th User name + %th Default project access + %th Team access + %th.cred.span3 Danger Zone! + - @team.members.each do |member| + %tr.member{ class: "user_#{member.id}"} + %td + = link_to [:admin, member] do + = member.name + %small= "(#{member.email})" + %td= @team.human_default_projects_access(member) + %td= @team.admin?(member) ? "Admin" : "Member" + %td.bgred + = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn btn-small" + + = link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn btn-remove btn-small", id: "remove_member_#{member.id}" + +%fieldset + %legend + Projects (#{@team.projects.count}) + %span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn btn-primary btn-small pull-right", id: :assign_projects_to_team + - if @team.projects.any? + %table#projects_list + %thead + %tr + %th Project name + %th Max access + %th.cred.span3 Danger Zone! + - @team.projects.each do |project| + %tr.project + %td + = link_to project.name_with_namespace, [:admin, project] + %td + %span= @team.human_max_project_access(project) + %td.bgred + = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn btn-small" + + = link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn btn-remove small", id: "relegate_project_#{project.id}" + +:javascript + $(function(){ + var modal = $('.change-owner-holder'); + $('.change-owner-link').bind("click", function(){ + $(this).hide(); + modal.show(); + }); + $('.change-owner-cancel-link').bind("click", function(){ + modal.hide(); + $('.change-owner-link').show(); + }) + }) + diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 45195152cb707d3a83dbf5466f0d28b76b651523..51b05c05993cf647530ffeea93da4546b4ad017e 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -46,6 +46,14 @@ = f.label :projects_limit .input= f.number_field :projects_limit + .clearfix + = f.label :can_create_group + .input= f.check_box :can_create_group + + .clearfix + = f.label :can_create_team + .input= f.check_box :can_create_team + .clearfix = f.label :admin do %strong.cred Administrator @@ -55,10 +63,10 @@ .alert.alert-error - if @admin_user.blocked %p This user is blocked and is not able to login to GitLab - = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn small" + = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small" - else %p Blocked users will be removed from all projects & will not be able to login to GitLab. - = link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger" + = link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove" %fieldset %legend Profile .clearfix @@ -72,8 +80,8 @@ .input= f.text_field :twitter .actions - = f.submit 'Save', class: "btn save-btn" + = f.submit 'Save', class: "btn btn-save" - if @admin_user.new_record? - = link_to 'Cancel', admin_users_path, class: "btn cancel-btn" + = link_to 'Cancel', admin_users_path, class: "btn btn-cancel" - else - = link_to 'Cancel', admin_user_path(@admin_user), class: "btn cancel-btn" + = link_to 'Cancel', admin_user_path(@admin_user), class: "btn btn-cancel" diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 87290abe7a64ad14eb60575bf038938edc0de459..87d6309aefdfb8d1d1a2b45716e0c1e61c6baf65 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -1,11 +1,11 @@ %h3.page_title Users - = link_to 'New User', new_admin_user_path, class: "btn small right" + = link_to 'New User', new_admin_user_path, class: "btn btn-small pull-right" %br = form_tag admin_users_path, method: :get, class: 'form-inline' do = text_field_tag :name, params[:name], class: "xlarge" - = submit_tag "Search", class: "btn submit primary" + = submit_tag "Search", class: "btn submit btn-primary" %ul.nav.nav-tabs %li{class: "#{'active' unless params[:filter]}"} = link_to admin_users_path do @@ -44,15 +44,15 @@ %td= user.username %td= user.email %td= user.users_projects.count - %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small" + %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small" %td.bgred - if user == current_user %span.cred It's you! - else - if user.blocked - = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success" + = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success" - else - = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger" - = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger" + = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove" + = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove" = paginate @admin_users, theme: "admin" diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index a3be6614136818f04a1b9c56d351b791f0c891d7..08201abd7d52b8a04e57cfb033b0f73352706e69 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -4,7 +4,7 @@ %small Blocked - if @admin_user.admin %small Administrator - = link_to edit_admin_user_path(@admin_user), class: "btn right" do + = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do %i.icon-edit Edit @@ -86,7 +86,7 @@ %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3" %tr - %td= submit_tag 'Add', class: "btn primary" + %td= submit_tag 'Add', class: "btn btn-primary" %td Read more about project permissions %strong= link_to "here", help_permissions_path, class: "vlink" @@ -123,5 +123,5 @@ %tr %td= link_to project.name_with_namespace, admin_project_path(project) %td= tm.project_access_human - %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" - %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" + %td= link_to 'Edit Access', edit_admin_project_member_path(project, tm.user), class: "btn btn-small" + %td= link_to 'Remove from team', admin_project_member_path(project, tm.user), confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove" diff --git a/app/views/blame/_head.html.haml b/app/views/blame/_head.html.haml index 85da18052b4aca1045b90c5f264fa060d4073299..ef9e6c9c53288280e7147b2f57b419ef3f150046 100644 --- a/app/views/blame/_head.html.haml +++ b/app/views/blame/_head.html.haml @@ -3,5 +3,5 @@ = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]} = nav_link(controller: :refs) do = link_to 'Source', project_tree_path(@project, @ref) - %li.right + %li.pull-right = render "shared/clone_panel" diff --git a/app/views/commit/huge_commit.html.haml b/app/views/commit/huge_commit.html.haml index ba97a7c572c79b234fd1978c0ed80476e72e30e3..7f0bcf380375a18ab529ff072aae5b2a612b3c6f 100644 --- a/app/views/commit/huge_commit.html.haml +++ b/app/views/commit/huge_commit.html.haml @@ -1,3 +1,3 @@ = render "commits/commit_box" -.alert-message.block-message.error +.alert.alert-error %h4 Commit diffs are too big to be displayed diff --git a/app/views/commit/show.html.haml b/app/views/commit/show.html.haml index f920534e03a30aec6425576d29402e501892e9c1..485f2d1e67c6261e5a7ec27725cfca2d2e2ad464 100644 --- a/app/views/commit/show.html.haml +++ b/app/views/commit/show.html.haml @@ -1,6 +1,6 @@ = render "commits/commit_box" -%p.right.cgray +%p.pull-right.cgray This commit has %span.cgreen #{@commit.stats.additions} additions and @@ -11,19 +11,7 @@ :javascript $(function(){ - var w, h; - $('.diff_file').each(function(){ - $('.image.diff_removed img', this).on('load', $.proxy(function(event){ - var w = event.currentTarget.naturalWidth - , h = event.currentTarget.naturalHeight; - $('.image.diff_removed .image-info', this).append(' | <b>W:</b> ' + w + 'px | <b>H:</b> ' + h + 'px'); - }, this)); - $('.image.diff_added img', this).on('load', $.proxy(function(event){ - var w = event.currentTarget.naturalWidth - , h = event.currentTarget.naturalHeight; - $('.image.diff_added .image-info', this).append(' | <b>W:</b> ' + w + 'px | <b>H:</b> ' + h + 'px'); - }, this)); - + $('.files .file').each(function(){ + new CommitFile(this); }); - }); diff --git a/app/views/commits/_commit_box.html.haml b/app/views/commits/_commit_box.html.haml index 0544a1d10fee4a3f6bce9ecc37bd76b15bdca748..4c80c13ced14a481268f7e6ce4f839e154b46b9d 100644 --- a/app/views/commits/_commit_box.html.haml +++ b/app/views/commits/_commit_box.html.haml @@ -1,6 +1,6 @@ .ui-box.ui-box-show .ui-box-head - .right + .pull-right - if @notes_count > 0 %span.btn.disabled.grouped %i.icon-comment @@ -13,7 +13,7 @@ %ul.dropdown-menu %li= link_to "Email Patches", project_commit_path(@project, @commit, format: :patch) %li= link_to "Plain Diff", project_commit_path(@project, @commit, format: :diff) - = link_to project_tree_path(@project, @commit), class: "btn primary grouped" do + = link_to project_tree_path(@project, @commit), class: "btn btn-primary grouped" do %span Browse Code » %h3.commit-title.page_title = gfm escape_once(@commit.title) diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index 0dc6664c1d6c078ae06db3f32c1843f2ae8836a3..191320933d35ee94b3faa6080b83375f6b71bae5 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -2,5 +2,5 @@ %div.ui-box %h5.title %i.icon-calendar - = day.stamp("28 Aug, 2010") + %span= day.stamp("28 Aug, 2010") %ul.well-list= render commits diff --git a/app/views/commits/_diffs.html.haml b/app/views/commits/_diffs.html.haml index 7fe45aa25bc23149b9aacc29dfa4617b4ec40bf3..76f9f267b4166f350f8976155e50a04e4600fa3e 100644 --- a/app/views/commits/_diffs.html.haml +++ b/app/views/commits/_diffs.html.haml @@ -1,7 +1,7 @@ - if @suppress_diff - .alert-message.block-message + .alert.alert-block %p - %strong Warning! Large commit with more then #{Commit::DIFF_SAFE_SIZE} files changed. + %strong Warning! Large commit with more than #{Commit::DIFF_SAFE_SIZE} files changed. %p To prevent performance issue we rejected diff information. %p But if you still want to see diff @@ -12,50 +12,38 @@ .file-stats = render "commits/diff_head", diffs: diffs -- unless @suppress_diff - - diffs.each_with_index do |diff, i| - - next if diff.diff.empty? - - file = (@commit.tree / diff.new_path) - - file = (@commit.prev_commit.tree / diff.old_path) unless file - - next unless file - .diff_file{id: "diff-#{i}"} - .diff_file_header - - if diff.deleted_file - %span= diff.old_path +.files + - unless @suppress_diff + - diffs.each_with_index do |diff, i| + - next if diff.diff.empty? + - file = (@commit.tree / diff.new_path) + - file = (@commit.prev_commit.tree / diff.old_path) unless file + - next unless file + .file{id: "diff-#{i}"} + .header + - if diff.deleted_file + %span= diff.old_path - - if @commit.prev_commit - = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn right view-commit'} do + - if @commit.prev_commit + = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn pull-right view-file'} do + View file @ + %span.commit-short-id= @commit.short_id(6) + - else + %span= diff.new_path + - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode + %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" + + = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do View file @ %span.commit-short-id= @commit.short_id(6) - - else - %span= diff.new_path - - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode - %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" - - = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-commit'} do - View file @ - %span.commit-short-id= @commit.short_id(6) - %br/ - .diff_file_content - -# Skip all non-supported blobs - - next unless file.respond_to?('text?') - - if file.text? - = render "commits/text_diff", diff: diff, index: i - - elsif file.image? - - old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil? - - if diff.renamed_file || diff.new_file || diff.deleted_file - .diff_file_content_image - .image{class: image_diff_class(diff)} - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - %div.image-info= "#{number_to_human_size file.size}" + .content + -# Skipp all non non-supported blobs + - next unless file.respond_to?('text?') + - if file.text? + = render "commits/text_file", diff: diff, index: i + - elsif file.image? + - old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil? + = render "commits/image", diff: diff, old_file: old_file, file: file, index: i - else - .diff_file_content_image.img_compared - .image.diff_removed - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"} - %div.image-info= "#{number_to_human_size file.size}" - .image.diff_added - %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - %div.image-info= "#{number_to_human_size file.size}" - - else - %p.nothing_here_message No preview for this file type + %p.nothing_here_message No preview for this file type diff --git a/app/views/commits/_head.html.haml b/app/views/commits/_head.html.haml index a5f3fdf5c5e47170a0040b14ff33c4773b4acf49..02debe426fefefa84886887b6d840d6bfedfd97c 100644 --- a/app/views/commits/_head.html.haml +++ b/app/views/commits/_head.html.haml @@ -22,7 +22,7 @@ - if current_controller?(:commits) && current_user.private_token - %li.right + %li.pull-right %span.rss-icon = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do = image_tag "rss_ui.png", title: "feed" diff --git a/app/views/commits/_image.html.haml b/app/views/commits/_image.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..db02fa333b93779587b9414e406df83f10a6c902 --- /dev/null +++ b/app/views/commits/_image.html.haml @@ -0,0 +1,63 @@ +- if diff.renamed_file || diff.new_file || diff.deleted_file + .image + %span.wrap + .frame{class: image_diff_class(diff)} + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + %p.image-info= "#{number_to_human_size file.size}" +- else + .image + %div.two-up.view + %span.wrap + .frame.deleted + %a{href: project_tree_path(@project, tree_join(@commit.id, diff.old_path))} + %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} + %p.image-info.hide + %span.meta-filesize= "#{number_to_human_size old_file.size}" + | + %b W: + %span.meta-width + | + %b H: + %span.meta-height + %span.wrap + .frame.added + %a{href: project_tree_path(@project, tree_join(@commit.id, diff.new_path))} + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + %p.image-info.hide + %span.meta-filesize= "#{number_to_human_size file.size}" + | + %b W: + %span.meta-width + | + %b H: + %span.meta-height + + %div.swipe.view.hide + .swipe-frame + .frame.deleted + %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} + .swipe-wrap + .frame.added + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + %span.swipe-bar + %span.top-handle + %span.bottom-handle + + %div.onion-skin.view.hide + .onion-skin-frame + .frame.deleted + %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} + .frame.added + %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} + .controls + .transparent + .drag-track + .dragger{:style => "left: 0px;"} + .opaque + + + .view-modes.hide + %ul.view-modes-menu + %li.two-up{data: {mode: 'two-up'}} 2-up + %li.swipe{data: {mode: 'swipe'}} Swipe + %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin \ No newline at end of file diff --git a/app/views/commits/_text_diff.html.haml b/app/views/commits/_text_file.html.haml similarity index 95% rename from app/views/commits/_text_diff.html.haml rename to app/views/commits/_text_file.html.haml index 8afad96bde2702338c536f9199c315df87daea23..760fd07ed8b9f7175158de5acdd924d9e54eeb94 100644 --- a/app/views/commits/_text_diff.html.haml +++ b/app/views/commits/_text_file.html.haml @@ -2,7 +2,7 @@ - if too_big %a.supp_diff_link Diff suppressed. Click to show -%table{class: "#{'hide' if too_big}"} +%table.text-file{class: "#{'hide' if too_big}"} - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old| %tr.line_holder{ id: line_code } - if type == "match" diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 9451a038df057692cd6ac6672d72305bf091d576..d180b8ec4260e9f07bad7a56c0354ebb4eb8f69a 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -5,7 +5,7 @@ = breadcrumbs %div{id: dom_id(@project)} - #commits_list= render "commits" + #commits-list= render "commits" .clear .loading{ style: "display:none;"} diff --git a/app/views/compare/_form.html.haml b/app/views/compare/_form.html.haml index 0915782dddc70d637ecac033a0678dc9d1523c28..7c0688a2287b8732d1ba716ccda28ff09aa0b3b1 100644 --- a/app/views/compare/_form.html.haml +++ b/app/views/compare/_form.html.haml @@ -19,7 +19,7 @@ = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" .pull-left - = submit_tag "Compare", class: "btn primary wide commits-compare-btn" + = submit_tag "Compare", class: "btn btn-primary wide commits-compare-btn" - if @refs_are_same .alert %span Refs are the same diff --git a/app/views/dashboard/_filter.html.haml b/app/views/dashboard/_filter.html.haml index 4624af799487e5486fa7a630a24776d7d74f4d88..82e679d5927c422c0284bdb99f116e917b2f4cbf 100644 --- a/app/views/dashboard/_filter.html.haml +++ b/app/views/dashboard/_filter.html.haml @@ -25,9 +25,9 @@ %li{class: ("active" if params[:project_id] == project.id.to_s)} = link_to dashboard_filter_path(entity, project_id: project.id) do = project.name_with_namespace - %small.right= entities_per_project(project, entity) + %small.pull-right= entities_per_project(project, entity) %fieldset %hr - = link_to "Reset", dashboard_filter_path(entity), class: 'btn right' + = link_to "Reset", dashboard_filter_path(entity), class: 'btn pull-right' diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml index 7f544406761afaf88de0c4a11e8aa960ad0c0764..ba8d3029eaf4f5949d6f832961dc092ba6b712f4 100644 --- a/app/views/dashboard/_groups.html.haml +++ b/app/views/dashboard/_groups.html.haml @@ -1,11 +1,11 @@ -.groups_box +.ui-box %h5.title Groups %small (#{groups.count}) - if current_user.can_create_group? - %span.right - = link_to new_admin_group_path, class: "btn very_small info" do + %span.pull-right + = link_to new_group_path, class: "btn btn-tiny info" do %i.icon-plus New Group %ul.well-list @@ -13,8 +13,6 @@ %li = link_to group_path(id: group.path), class: dom_class(group) do %strong.well-title= truncate(group.name, length: 35) - %span.arrow - → - %span.last_activity - %strong Projects: - %span= current_user.authorized_projects.where(namespace_id: group.id).count + %span.pull-right.light + - if group.owner == current_user + %i.icon-wrench diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml index 6c1304ee4a852d187a6c3b7cd4d355e6e75e5108..30fb726801452d5fe9e9b6b24c1451c4f7b7f280 100644 --- a/app/views/dashboard/_projects.html.haml +++ b/app/views/dashboard/_projects.html.haml @@ -2,19 +2,12 @@ %h5.title Projects %small - (#{projects.total_count}) + (#{@projects_count}) - if current_user.can_create_project? - %span.right - = link_to new_project_path, class: "btn very_small info" do + %span.pull-right + = link_to new_project_path, class: "btn btn-tiny info" do %i.icon-plus New Project - %ul.nav.nav-projects-tabs - = nav_tab :scope, nil do - = link_to "All", dashboard_path - = nav_tab :scope, 'personal' do - = link_to "Personal", dashboard_path(scope: 'personal') - = nav_tab :scope, 'joined' do - = link_to "Joined", dashboard_path(scope: 'joined') %ul.well-list - projects.each do |project| @@ -33,4 +26,6 @@ - if projects.blank? %li %h3.nothing_here_message There are no projects here. - .bottom= paginate projects, theme: "gitlab" + - if @projects_count > 20 + %li.bottom + %strong= link_to "show all projects", projects_dashboard_path diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml index 9830cdf4f6b85ee1d1d46116edd240a7945c02e3..7c6daf6ec311868e254dd795a96979ac84d93245 100644 --- a/app/views/dashboard/_sidebar.html.haml +++ b/app/views/dashboard/_sidebar.html.haml @@ -1,3 +1,5 @@ +- if @teams.present? + = render "teams", teams: @teams - if @groups.present? = render "groups", groups: @groups = render "projects", projects: @projects diff --git a/app/views/dashboard/_teams.html.haml b/app/views/dashboard/_teams.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..f56115856a741f55afab3662c7ed93184255f2c5 --- /dev/null +++ b/app/views/dashboard/_teams.html.haml @@ -0,0 +1,20 @@ +.ui-box.teams-box + %h5.title + Teams + %small + (#{@teams.count}) + %span.pull-right + = link_to new_team_path, class: "btn btn-tiny info" do + %i.icon-plus + New Team + %ul.well-list + - @teams.each do |team| + %li + = link_to team_path(id: team.path), class: dom_class(team) do + %strong.well-title= truncate(team.name, length: 35) + %span.pull-right.light + - if team.owner == current_user + %i.icon-wrench + - tm = current_user.user_team_user_relationships.find_by_user_team_id(team.id) + - if tm + = tm.access_human diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml index d1676ed11fadc6d7ec51a788c413ea0db96457d0..4b0d0d6873d13a3ddc1d0186bd9d8debd090908c 100644 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/_zero_authorized_projects.html.haml @@ -6,7 +6,7 @@ = current_user.projects_limit projects. Click on button below to add a new one .link_holder - = link_to new_project_path, class: "btn primary" do + = link_to new_project_path, class: "btn btn-primary" do New Project » - else If you will be added to project - it will be displayed here diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 28bdc5ed814e8083c300f63d9c2d9d45403f3f11..0f0f3466e92dce54addf87855d1a39f993673f1c 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -1,9 +1,9 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{current_user.name} issues" - xml.link :href => dashboard_issues_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml" - xml.link :href => dashboard_issues_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html" - xml.id dashboard_issues_url(:private_token => current_user.private_token) + xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml" + xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html" + xml.id issues_dashboard_url(:private_token => current_user.private_token) xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 307d0d85ea3ab18f49dc82c8d0e9b0e81412bd30..affe01a7ef94fba499d04eedd928adc65e35ff9d 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -1,7 +1,7 @@ %h3.page_title Issues %small (assigned to you) - %small.right #{@issues.total_count} issues + %small.pull-right #{@issues.total_count} issues %hr diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index 0c4d6e0aadfbe30a6aa3984a88e3dd1558ea6821..a311729dd4d3386c3afa4f17e26729a98f3292c4 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -1,7 +1,7 @@ %h3.page_title Merge Requests %small (authored by or assigned to you) - %small.right #{@merge_requests.total_count} merge requests + %small.pull-right #{@merge_requests.total_count} merge requests %hr .row diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..8e21b0c7e023e032be82d739187a1da933bd385b --- /dev/null +++ b/app/views/dashboard/projects.html.haml @@ -0,0 +1,56 @@ +%h3.page_title + Projects + %span + (#{@projects.total_count}) + - if current_user.can_create_project? + %span.pull-right + = link_to new_project_path, class: "btn btn-tiny info" do + %i.icon-plus + New Project + + +%hr +.row + .span3 + %ul.nav.nav-pills.nav-stacked + = nav_tab :scope, nil do + = link_to "All", projects_dashboard_path + = nav_tab :scope, 'personal' do + = link_to "Personal", projects_dashboard_path(scope: 'personal') + = nav_tab :scope, 'joined' do + = link_to "Joined", projects_dashboard_path(scope: 'joined') + + .span9 + = form_tag projects_dashboard_path, method: 'get' do + %fieldset.dashboard-search-filter + = hidden_field_tag "scope", params[:scope] + = search_field_tag "search", params[:search], { placeholder: 'Search', class: 'left input-xxlarge' } + = button_tag type: 'submit', class: 'btn' do + %i.icon-search + + %ul.well-list + - @projects.each do |project| + %li.clearfix + .left + = link_to project_path(project), class: dom_class(project) do + - if project.namespace + = project.namespace.human_name + \/ + %strong.well-title + = truncate(project.name, length: 25) + %br + %small.light + %strong Last activity: + %span= project_last_activity(project) + .pull-right.light + - if project.owner == current_user + %i.icon-wrench + - tm = project.team.get_tm(current_user.id) + - if tm + = tm.project_access_human + + - if @projects.blank? + %li + %h3.nothing_here_message There are no projects here. + .bottom= paginate @projects, theme: "gitlab" + diff --git a/app/views/dashboard/index.atom.builder b/app/views/dashboard/show.atom.builder similarity index 100% rename from app/views/dashboard/index.atom.builder rename to app/views/dashboard/show.atom.builder diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/show.html.haml similarity index 100% rename from app/views/dashboard/index.html.haml rename to app/views/dashboard/show.html.haml diff --git a/app/views/dashboard/index.js.haml b/app/views/dashboard/show.js.haml similarity index 100% rename from app/views/dashboard/index.js.haml rename to app/views/dashboard/show.js.haml diff --git a/app/views/deploy_keys/_form.html.haml b/app/views/deploy_keys/_form.html.haml index 6beba562a9508967fa429e3a30dafe00dbbf94f5..5fb83021dc08a94e1d1e8856162bc10e159a7161 100644 --- a/app/views/deploy_keys/_form.html.haml +++ b/app/views/deploy_keys/_form.html.haml @@ -1,7 +1,7 @@ %div = form_for [@project, @key], url: project_deploy_keys_path do |f| -if @key.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @key.errors.full_messages.each do |msg| %li= msg @@ -18,6 +18,6 @@ = link_to "here", help_ssh_path .actions - = f.submit 'Save', class: "save-btn btn" - = link_to "Cancel", project_deploy_keys_path(@project), class: "btn cancel-btn" + = f.submit 'Save', class: "btn-save btn" + = link_to "Cancel", project_deploy_keys_path(@project), class: "btn btn-cancel" diff --git a/app/views/deploy_keys/_show.html.haml b/app/views/deploy_keys/_show.html.haml index a5314ae92ade74b90fb77c1ed5e63dda373b22cb..635054350ecec96baa4fdf756e7f6f852c3a111d 100644 --- a/app/views/deploy_keys/_show.html.haml +++ b/app/views/deploy_keys/_show.html.haml @@ -8,5 +8,5 @@ = time_ago_in_words(key.created_at) ago %td - = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key small right" + = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key btn-small pull-right" diff --git a/app/views/deploy_keys/index.html.haml b/app/views/deploy_keys/index.html.haml index b9c654a1abe5cbbc7d974f0f3ba66638e430daaf..db167f4e2f29b115c943ca139b17ca70065d9df8 100644 --- a/app/views/deploy_keys/index.html.haml +++ b/app/views/deploy_keys/index.html.haml @@ -4,7 +4,7 @@ Deploy keys allow read-only access to repository. It matches perfectly for CI, staging or production servers. - if can? current_user, :admin_project, @project - = link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do + = link_to new_project_deploy_key_path(@project), class: "btn btn-small", title: "New Deploy Key" do Add Deploy Key - if @keys.any? %table diff --git a/app/views/deploy_keys/show.html.haml b/app/views/deploy_keys/show.html.haml index c94cf10dde09a62dd15258c9cad2ca5108d741b6..227afecb0619a60156a17359ea52df3caf5c680c 100644 --- a/app/views/deploy_keys/show.html.haml +++ b/app/views/deploy_keys/show.html.haml @@ -10,5 +10,5 @@ ← To keys list %hr %pre= @key.key -.right - = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key" +.pull-right + = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "btn-remove btn delete-key" diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index 31d355673ab2b0c9f5f7cfe4a9157b219a83d6a4..e5800025c6de847e49120dba55e06a9b3e0d6cbf 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -8,5 +8,5 @@ %div = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password" %div - = f.submit "Change my password", class: "btn primary" - .right= render partial: "devise/shared/links" + = f.submit "Change my password", class: "btn btn-primary" + .pull-right= render partial: "devise/shared/links" diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index 860d67d19e7a9c8f5bd70d1a13115c70aabbf7e7..0e39f3187265d6c76327b9d45fa1574d1578516b 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,9 +1,9 @@ -<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :class => "login-box", :method => :post }) do |f| %> - <%= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" %> +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { class: "login-box", method: :post }) do |f| %> + <%= image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" %> <%= devise_error_messages! %> - <%= f.email_field :email, :placeholder => "Email", :class => "text" %> + <%= f.email_field :email, placeholder: "Email", class: "text" %> <br/> <br/> - <%= f.submit "Reset password", :class => "primary btn" %> - <div class="right"> <%= link_to "Sign in", new_session_path(resource_name), :class => "btn" %><br /></div> + <%= f.submit "Reset password", class: "btn-primary btn" %> + <div class="pull-right"> <%= link_to "Sign in", new_session_path(resource_name), class: "btn" %><br /></div> <% end %> diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index 81eb26222615109a82fcf8beddfc5b07383be2d6..12b0438229b9d6071a0ed9ae45f04f48f6b5777f 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -1,19 +1,18 @@ -= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "login-box" }) do |f| - = image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" += form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "login-box" }) do |f| + = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" = devise_error_messages! %div - = f.text_field :name, :class => "text top", :placeholder => "Name", :required => true + = f.text_field :name, class: "text top", placeholder: "Name", required: true %div - = f.text_field :username, :class => "text middle", :placeholder => "Username", :required => true + = f.text_field :username, class: "text middle", placeholder: "Username", required: true %div - = f.email_field :email, :class => "text middle", :placeholder => "Email", :required => true + = f.email_field :email, class: "text middle", placeholder: "Email", required: true %div - = f.password_field :password, :class => "text middle", :placeholder => "Password", :required => true + = f.password_field :password, class: "text middle", placeholder: "Password", required: true %div - = f.password_field :password_confirmation, :class => "text bottom", :placeholder => "Confirm password", :required => true + = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm password", required: true %div - = f.submit "Sign up", :class => "primary btn wide" - %br + = f.submit "Sign up", class: "btn-create btn" %hr = link_to "Sign in", new_session_path(resource_name) - = link_to "Forgot your password?", new_password_path(resource_name), :class => "right" + = link_to "Forgot your password?", new_password_path(resource_name), class: "pull-right" diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 4233aa61ecb5d80bab88186b737bb3435670eba0..7968b0e9c9fdcb7a4222baacd4bf1cf128c7d027 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -3,11 +3,11 @@ = text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"} = password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"} %br/ - = submit_tag "LDAP Sign in", :class => "primary btn" + = submit_tag "LDAP Sign in", :class => "btn-primary btn" - if devise_mapping.omniauthable? - (resource_class.omniauth_providers - [:ldap]).each do |provider| %hr/ - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" + = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn btn-primary" %br/ %hr/ %a#other_form_toggle{:href => "#", :onclick => "javascript:$('#new_user').toggle();"} Other Sign in @@ -24,6 +24,6 @@ = f.check_box :remember_me %span Remember me %br/ - = f.submit "Sign in", :class => "primary btn" - .right + = f.submit "Sign in", :class => "btn-primary btn" + .pull-right = render :partial => "devise/shared/links" diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 0983f3157c4eeee3bd6690309e41acaa179825ae..d904e701b8ab25102483cbf08d26a4d876dd81f8 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -11,18 +11,18 @@ = f.check_box :remember_me %span Remember me %br/ - = f.submit "Sign in", :class => "primary btn wide" - .right + = f.submit "Sign in", :class => "btn-create btn" + .pull-right = link_to "Forgot your password?", new_password_path(resource_name), :class => "btn" %br/ - %br/ - if Gitlab.config.gitlab.signup_enabled %hr/ Don't have an account? = link_to "Sign up", new_registration_path(resource_name) - .clearfix - if devise_mapping.omniauthable? && resource_class.omniauth_providers.present? + %hr %div + %span Sign in with: - resource_class.omniauth_providers.each do |provider| %span = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 191aed0747e0b3c39cb1bf775a6683bf72401523..719f6c3787f982752d118d097001381e89772718 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -1,6 +1,6 @@ - if event.proper? %div.event-item - %span.cgray.right + %span.cgray.pull-right #{time_ago_in_words(event.created_at)} ago. = image_tag gravatar_icon(event.author_email), class: "avatar s24" diff --git a/app/views/projects/graph.html.haml b/app/views/graph/show.html.haml similarity index 51% rename from app/views/projects/graph.html.haml rename to app/views/graph/show.html.haml index 4e0b0e36c343afc6caf338217ed8c3c350e5268d..ca3a8706313ee75af30ccab845ae850aac356760 100644 --- a/app/views/projects/graph.html.haml +++ b/app/views/graph/show.html.haml @@ -1,5 +1,7 @@ %h3.page_title Project Network Graph %br += render partial: 'shared/ref_switcher', locals: {destination: 'graph', path: @path} +%br .graph_holder %h4 %small You can move around the graph by using the arrow keys. @@ -10,7 +12,8 @@ var branch_graph; $(function(){ branch_graph = new BranchGraph($("#holder"), { - url: '#{url_for controller: 'projects', action: 'graph', format: :json}', - commit_url: '#{url_for controller: 'projects', action: 'show'}/commits/%s' + url: '#{project_graph_path(@project, @ref, format: :json)}', + commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}', + ref: '#{@ref}' }); }); diff --git a/app/views/groups/_filter.html.haml b/app/views/groups/_filter.html.haml index c8b0ad0f4339c8772b67a64d07fbc3e8a68da8d2..c14fc8e5e4b725440a35f3a5e27d3266b8d4bd5c 100644 --- a/app/views/groups/_filter.html.haml +++ b/app/views/groups/_filter.html.haml @@ -25,9 +25,9 @@ %li{class: ("active" if params[:project_id] == project.id.to_s)} = link_to group_filter_path(entity, project_id: project.id) do = project.name_with_namespace - %small.right= entities_per_project(project, entity) + %small.pull-right= entities_per_project(project, entity) %fieldset %hr - = link_to "Reset", group_filter_path(entity), class: 'btn right' + = link_to "Reset", group_filter_path(entity), class: 'btn pull-right' diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml index 2d599816e2ac584be3447b7adbb048f018b69fcf..9cdbea60370ba69c13a201e941b6577bcc470145 100644 --- a/app/views/groups/_new_group_member.html.haml +++ b/app/views/groups/_new_group_member.html.haml @@ -14,5 +14,5 @@ .form-actions = hidden_field_tag :redirect_to, people_group_path(@group) - = f.submit 'Add', class: "btn save-btn" + = f.submit 'Add', class: "btn btn-save" diff --git a/app/views/groups/_new_member.html.haml b/app/views/groups/_new_member.html.haml index 89ac05e774e760433e2ab000df3e94e928356f47..b3424b01bcb5ab54dc9e9202592a20a9035f0ea8 100644 --- a/app/views/groups/_new_member.html.haml +++ b/app/views/groups/_new_member.html.haml @@ -14,5 +14,5 @@ .form-actions = hidden_field_tag :redirect_to, people_group_path(@group, project_id: @project.id) - = f.submit 'Add', class: "btn save-btn" + = f.submit 'Add', class: "btn btn-save" diff --git a/app/views/groups/_people_filter.html.haml b/app/views/groups/_people_filter.html.haml index 79a1b01a5faad5763ce877525c3b2a0b39598461..901a037adf32f46cb534887cf839284b6763fbbb 100644 --- a/app/views/groups/_people_filter.html.haml +++ b/app/views/groups/_people_filter.html.haml @@ -6,9 +6,9 @@ %li{class: ("active" if params[:project_id] == project.id.to_s)} = link_to people_group_path(@group, project_id: project.id) do = project.name_with_namespace - %small.right= project.users.count + %small.pull-right= project.users.count %fieldset %hr - = link_to "Reset", people_group_path(@group), class: 'btn right' + = link_to "Reset", people_group_path(@group), class: 'btn pull-right' diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 040d1ae94aae6a79f0da1817f4d1f9f368936873..4fa4a1779835cd8aeaaf2c25fbff7156628f6f66 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -4,8 +4,8 @@ %small (#{projects.count}) - if can? current_user, :manage_group, @group - %span.right - = link_to new_project_path(namespace_id: @group.id), class: "btn very_small info" do + %span.pull-right + = link_to new_project_path(namespace_id: @group.id), class: "btn btn-tiny info" do %i.icon-plus New Project %ul.well-list diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..7202ef26c70af5f4a3f7f06f38d46e480bf7ab5d --- /dev/null +++ b/app/views/groups/edit.html.haml @@ -0,0 +1,50 @@ +%h3.page_title Edit Group +%hr += form_for @group do |f| + - if @group.errors.any? + .alert.alert-error + %span= @group.errors.full_messages.first + .clearfix + = f.label :name do + Group name is + .input + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" + + = f.submit 'Save group', class: "btn btn-save" +%hr + + +.row + .span7 + .ui-box + %h5.title Projects + %ul.well-list + - @group.projects.each do |project| + %li + - if project.public + %i.icon-share + - else + %i.icon-lock.cgreen + = link_to project.name_with_namespace, project + .pull-right + = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" + + .span5 + .ui-box + %h5.title Transfer group + .padded + %p + Transferring group will cause loss of admin control over group and all child projects + = form_for @group do |f| + = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'} + = f.submit 'Transfer group', class: "btn btn-small" + .ui-box + %h5.title Remove group + .padded.bgred + %p + Remove of group will cause removing all child projects and resources + %br + Removed group can not be restored! + = link_to 'Remove Group', @group, confirm: 'Removed group can not be restored! Are you sure?', method: :delete, class: "btn btn-remove btn-small" diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index 5bd07bcd89ff2352c63d43feb9c3d71cdad6e495..701747bdbc3242cfd894e95ed4666dc4b0331c16 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -1,9 +1,9 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@user.name} issues" - xml.link :href => dashboard_issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml" - xml.link :href => dashboard_issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html" - xml.id dashboard_issues_url(:private_token => @user.private_token) + xml.link :href => issues_dashboard_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml" + xml.link :href => issues_dashboard_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html" + xml.id issues_dashboard_url(:private_token => @user.private_token) xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 9e8642f3b2c6abb7c1003c6724db220640667ffe..94682bdd51e035611cef5b90fbcd72d70d8b00c6 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,7 +1,7 @@ %h3.page_title Issues %small (assigned to you) - %small.right #{@issues.total_count} issues + %small.pull-right #{@issues.total_count} issues %hr .row diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 0c4d6e0aadfbe30a6aa3984a88e3dd1558ea6821..a311729dd4d3386c3afa4f17e26729a98f3292c4 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,7 +1,7 @@ %h3.page_title Merge Requests %small (authored by or assigned to you) - %small.right #{@merge_requests.total_count} merge requests + %small.pull-right #{@merge_requests.total_count} merge requests %hr .row diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..73be474e2780821721906e49f9687231ae890abb --- /dev/null +++ b/app/views/groups/new.html.haml @@ -0,0 +1,21 @@ +%h3.page_title New Group +%hr += form_for @group do |f| + - if @group.errors.any? + .alert.alert-error + %span= @group.errors.full_messages.first + .clearfix + = f.label :name do + Group name is + .input + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" + + = f.submit 'Create group', class: "btn btn-create" + %hr + .padded + %ul + %li Group is kind of directory for several projects + %li All created groups are private + %li People within a group see only projects they have access to + %li All projects of group will be stored in group directory + %li You will be able to move existing projects into group diff --git a/app/views/groups/people.html.haml b/app/views/groups/people.html.haml index 0bceeaa3ceba6c37a24fb6812bcbb2b972114a57..3e4eb082f564b37b2992d4d6b03221862f4d62d0 100644 --- a/app/views/groups/people.html.haml +++ b/app/views/groups/people.html.haml @@ -16,5 +16,5 @@ %strong= user.name %span.cgray= user.email - if @group.owner == user - %span.btn.btn-small.disabled.right Group Owner + %span.btn.btn-small.disabled.pull-right Group Owner diff --git a/app/views/groups/search.html.haml b/app/views/groups/search.html.haml index 1ba4707aa52d73d3569989485e0e79f4e2290552..f56bbadeac071cb911ab51df5d02e16f5ed4dc0f 100644 --- a/app/views/groups/search.html.haml +++ b/app/views/groups/search.html.haml @@ -4,6 +4,6 @@ %strong Looking for .input = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search" - = submit_tag 'Search', class: "btn primary wide" + = submit_tag 'Search', class: "btn btn-primary wide" - if params[:search].present? = render 'search/result' diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 84dd17c045deb11ca973a407578360e66e791ea1..a140b401b9d54048d7f092b5eb2dc0673cc50ff3 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,7 +1,7 @@ .projects .activities.span8 = render "events/event_last_push", event: @last_push - = link_to dashboard_path, class: 'btn very_small' do + = link_to dashboard_path, class: 'btn btn-tiny' do ← To dashboard %span.cgray You will only see events from projects in this group diff --git a/app/views/help/_layout.html.haml b/app/views/help/_layout.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..fa5e3a30b2945fbcdad32825c028900593d28ca0 --- /dev/null +++ b/app/views/help/_layout.html.haml @@ -0,0 +1,34 @@ +.row + .span3{:"data-spy" => 'affix'} + .ui-box + .title + %h5 Help + %ul.well-list + %li + %strong= link_to "Workflow", help_workflow_path + %li + %strong= link_to "SSH keys", help_ssh_path + + %li + %strong= link_to "GitLab Markdown", help_markdown_path + + %li + %strong= link_to "Permissions", help_permissions_path + + %li + %strong= link_to "API", help_api_path + + %li + %strong= link_to "Web Hooks", help_web_hooks_path + + %li + %strong= link_to "Rake Tasks", help_raketasks_path + + %li + %strong= link_to "System Hooks", help_system_hooks_path + + %li + %strong= link_to "Public Access", help_public_access_path + + .span9.pull-right + = yield diff --git a/app/views/help/api.html.haml b/app/views/help/api.html.haml index 3f16637dd2e3538b96b6a41e4fc1ae394fc4d21f..d771f1e9f38109b9525f8a027823d71f217bb778 100644 --- a/app/views/help/api.html.haml +++ b/app/views/help/api.html.haml @@ -1,107 +1,105 @@ -%h3.page_title API -.back_link - = link_to help_path do - ← to index -%br += render layout: 'help/layout' do + %h3.page_title API + %br -%ul.nav.nav-tabs.log-tabs - %li.active - = link_to "README", "#README", 'data-toggle' => 'tab' - %li - = link_to "Projects", "#projects", 'data-toggle' => 'tab' - %li - = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' - %li - = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' - %li - = link_to "Users", "#users", 'data-toggle' => 'tab' - %li - = link_to "Session", "#session", 'data-toggle' => 'tab' - %li - = link_to "Issues", "#issues", 'data-toggle' => 'tab' - %li - = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' - %li - = link_to "Notes", "#notes", 'data-toggle' => 'tab' + %ul.nav.nav-tabs.log-tabs.nav-small-tabs + %li.active + = link_to "README", "#README", 'data-toggle' => 'tab' + %li + = link_to "Projects", "#projects", 'data-toggle' => 'tab' + %li + = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' + %li + = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' + %li + = link_to "Users", "#users", 'data-toggle' => 'tab' + %li + = link_to "Session", "#session", 'data-toggle' => 'tab' + %li + = link_to "Issues", "#issues", 'data-toggle' => 'tab' + %li + = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' + %li + = link_to "Notes", "#notes", 'data-toggle' => 'tab' -.tab-content - .tab-pane.active#README - .file_holder - .file_title - %i.icon-file - README - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "README.md")) + .tab-content + .tab-pane.active#README + .file_holder + .file_title + %i.icon-file + README + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "README.md")) - .tab-pane#projects - .file_holder - .file_title - %i.icon-file - Projects - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "projects.md")) + .tab-pane#projects + .file_holder + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "projects.md")) - .tab-pane#snippets - .file_holder - .file_title - %i.icon-file - Projects Snippets - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) + .tab-pane#snippets + .file_holder + .file_title + %i.icon-file + Projects Snippets + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) - .tab-pane#repositories - .file_holder - .file_title - %i.icon-file - Projects - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) + .tab-pane#repositories + .file_holder + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) - .tab-pane#users - .file_holder - .file_title - %i.icon-file - Users - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "users.md")) + .tab-pane#users + .file_holder + .file_title + %i.icon-file + Users + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "users.md")) - .tab-pane#session - .file_holder - .file_title - %i.icon-file - Session - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "session.md")) + .tab-pane#session + .file_holder + .file_title + %i.icon-file + Session + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "session.md")) - .tab-pane#issues - .file_holder - .file_title - %i.icon-file - Issues - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "issues.md")) + .tab-pane#issues + .file_holder + .file_title + %i.icon-file + Issues + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "issues.md")) - .tab-pane#milestones - .file_holder - .file_title - %i.icon-file - Milestones - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) + .tab-pane#milestones + .file_holder + .file_title + %i.icon-file + Milestones + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) - .tab-pane#notes - .file_holder - .file_title - %i.icon-file - Notes - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "notes.md")) + .tab-pane#notes + .file_holder + .file_title + %i.icon-file + Notes + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "notes.md")) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 28791b321f1e1808b87a18ba06406ac3f3101b0e..1a4411c8f30e8393022188fbd82cfd66bb0f55c0 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,6 +1,6 @@ %h3.page_title GITLAB - .right + .pull-right %span= Gitlab::Version %small= Gitlab::Revision %hr diff --git a/app/views/help/markdown.html.haml b/app/views/help/markdown.html.haml index 0419f7c131aa01e3debe2238c78e6e81c77a6b8a..92c1e49be49568233510a43ba756ca60b3aea305 100644 --- a/app/views/help/markdown.html.haml +++ b/app/views/help/markdown.html.haml @@ -1,129 +1,127 @@ -%h3.page_title GitLab Flavored Markdown -.back_link - = link_to help_path do - ← to index -%hr - -.row - .span8 - %p - For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). - It extends the standard Markdown in a few significant ways adds some useful functionality. - - %p You can use GFM in: - %ul - %li commit messages - %li comments - %li wall posts - %li issues - %li merge requests - %li milestones - %li wiki pages - - .span4 - .alert.alert-info += render layout: 'help/layout' do + %h3.page_title GitLab Flavored Markdown + %br + + .row + .span8 %p - If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent - %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax" - at Daring Fireball. + For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). + It extends the standard Markdown in a few significant ways adds some useful functionality. + + %p You can use GFM in: + %ul + %li commit messages + %li comments + %li wall posts + %li issues + %li merge requests + %li milestones + %li wiki pages + + .span4 + .alert.alert-info + %p + If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent + %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax" + at Daring Fireball. + + .row + .span8 + %h3 Differences from traditional Markdown + + %h4 Newlines -.row - .span8 - %h3 Differences from traditional Markdown + %p + The biggest difference that GFM introduces is in the handling of linebreaks. + With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. + GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended. - %h4 Newlines - %p - The biggest difference that GFM introduces is in the handling of linebreaks. - With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. - GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended. + %p The next paragraph contains two phrases separated by a single newline character: + %pre= "Roses are red\nViolets are blue" + %p becomes + = markdown "Roses are red\nViolets are blue" + %h4 Multiple underscores in words - %p The next paragraph contains two phrases separated by a single newline character: - %pre= "Roses are red\nViolets are blue" - %p becomes - = markdown "Roses are red\nViolets are blue" + %p + It is not reasonable to italicize just <em>part</em> of a word, especially when you're dealing with code and names often appear with multiple underscores. + Therefore, GFM ignores multiple underscores in words. - %h4 Multiple underscores in words + %pre= "perform_complicated_task\ndo_this_and_do_that_and_another_thing" + %p becomes + = markdown "perform_complicated_task\ndo_this_and_do_that_and_another_thing" - %p - It is not reasonable to italicize just <em>part</em> of a word, especially when you're dealing with code and names often appear with multiple underscores. - Therefore, GFM ignores multiple underscores in words. + %h4 URL autolinking - %pre= "perform_complicated_task\ndo_this_and_do_that_and_another_thing" - %p becomes - = markdown "perform_complicated_task\ndo_this_and_do_that_and_another_thing" + %p + GFM will autolink standard URLs you copy and paste into your text. + So if you want to link to a URL (instead of a textual link), you can simply put the URL in verbatim and it will be turned into a link to that URL. - %h4 URL autolinking + %h4 Fenced code blocks - %p - GFM will autolink standard URLs you copy and paste into your text. - So if you want to link to a URL (instead of a textual link), you can simply put the URL in verbatim and it will be turned into a link to that URL. + %p + Markdown converts text with four spaces at the front of each line to code blocks. + GFM supports that, but we also support fenced blocks. + Just wrap your code blocks in <code>```</code> and you won't need to indent manually to trigger a code block. - %h4 Fenced code blocks + %pre= %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} + %p becomes + = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} - %p - Markdown converts text with four spaces at the front of each line to code blocks. - GFM supports that, but we also support fenced blocks. - Just wrap your code blocks in <code>```</code> and you won't need to indent manually to trigger a code block. + %h4 Emoji - %pre= %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} - %p becomes - = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} + .row + .span8 + :ruby + puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you: - %h4 Emoji + :exclamation: You can use emoji anywhere GFM is supported. :sunglasses: -.row - .span8 - :ruby - puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you: + You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that. - :exclamation: You can use emoji anywhere GFM is supported. :sunglasses: + If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes. + } - You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that. + .span4 + .alert.alert-info + %p + Consult the + %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/" + for a list of all supported emoji codes. - If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes. - } + .row + .span8 + %h4 Special GitLab references - .span4 - .alert.alert-info %p - Consult the - %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/" - for a list of all supported emoji codes. - -.row - .span8 - %h4 Special GitLab references - - %p - GFM recognizes special references. - You can easily reference e.g. a team member, an issue or a commit within a project. - GFM will turn that reference into a link so you can navigate between them easily. - - %p GFM will recognize the following references: - %ul - %li - %code @foo - for team members - %li - %code #123 - for issues - %li - %code !123 - for merge request - %li - %code $123 - for snippets - %li - %code 1234567 - for commits - - -# this example will only be shown if the user has a project with at least one issue - - if @project = current_user.authorized_projects.first - - if issue = @project.issues.first - %p For example in your #{link_to @project.name, project_path(@project)} project, writing: - %pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." - %p becomes: - = markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." - - @project = nil # Prevent this from bubbling up to page title + GFM recognizes special references. + You can easily reference e.g. a team member, an issue or a commit within a project. + GFM will turn that reference into a link so you can navigate between them easily. + + %p GFM will recognize the following references: + %ul + %li + %code @foo + for team members + %li + %code #123 + for issues + %li + %code !123 + for merge request + %li + %code $123 + for snippets + %li + %code 1234567 + for commits + + -# this example will only be shown if the user has a project with at least one issue + - if @project = current_user.authorized_projects.first + - if issue = @project.issues.first + %p For example in your #{link_to @project.name, project_path(@project)} project, writing: + %pre= "This is related to ##{issue.id}. @#{current_user.username} is working on solving it." + %p becomes: + = markdown "This is related to ##{issue.id}. @#{current_user.username} is working on solving it." + - @project = nil # Prevent this from bubbling up to page title diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml index b56251f35ebcddd2df84498d93258eff32b6c287..2075753e82a10b9d8b26d05cc4dbc42534b02371 100644 --- a/app/views/help/permissions.html.haml +++ b/app/views/help/permissions.html.haml @@ -1,68 +1,66 @@ -%h3.page_title Permissions -.back_link - = link_to help_path do - ← to index -%hr += render layout: 'help/layout' do + %h3.page_title Permissions + %br -%fieldset - %legend Guest - %ul - %li Create new issue - %li Leave comments - %li Write on project wall + %fieldset + %legend Guest + %ul + %li Create new issue + %li Leave comments + %li Write on project wall -%fieldset - %legend Reporter - %ul - %li Create new issue - %li Leave comments - %li Write on project wall - %li Pull project code - %li Download project - %li Create a code snippets + %fieldset + %legend Reporter + %ul + %li Create new issue + %li Leave comments + %li Write on project wall + %li Pull project code + %li Download project + %li Create a code snippets -%fieldset - %legend Developer - %ul - %li Create new issue - %li Leave comments - %li Write on project wall - %li Pull project code - %li Download project - %li Create new merge request - %li Create a code snippets - %li Create new branches - %li Push to non-protected branches - %li Remove non-protected branches - %li Add tags - %li Write a wiki + %fieldset + %legend Developer + %ul + %li Create new issue + %li Leave comments + %li Write on project wall + %li Pull project code + %li Download project + %li Create new merge request + %li Create a code snippets + %li Create new branches + %li Push to non-protected branches + %li Remove non-protected branches + %li Add tags + %li Write a wiki -%fieldset - %legend Master - %ul - %li Create new issue - %li Leave comments - %li Write on project wall - %li Pull project code - %li Download project - %li Create new merge request - %li Create a code snippets - %li Create new branches - %li Push to non-protected branches - %li Remove non-protected branches - %li Add tags - %li Write a wiki - %li Add new team members - %li Push to protected branches - %li Remove protected branches - %li Push with force option - %li Edit project - %li Add Deploy Keys to project - %li Configure Project Hooks + %fieldset + %legend Master + %ul + %li Create new issue + %li Leave comments + %li Write on project wall + %li Pull project code + %li Download project + %li Create new merge request + %li Create a code snippets + %li Create new branches + %li Push to non-protected branches + %li Remove non-protected branches + %li Add tags + %li Write a wiki + %li Add new team members + %li Push to protected branches + %li Remove protected branches + %li Push with force option + %li Edit project + %li Add Deploy Keys to project + %li Configure Project Hooks -%fieldset - %legend Owner - %ul - %li Transfer project to another namespace - %li Remove project + %fieldset + %legend Owner + %ul + %li Transfer project to another namespace + %li Remove project diff --git a/app/views/help/public_access.html.haml b/app/views/help/public_access.html.haml index 60d8d59761773e42eca0b0252d0b2db7de13707a..66de17a34edc62819e82702005f0c5f8c3738e06 100644 --- a/app/views/help/public_access.html.haml +++ b/app/views/help/public_access.html.haml @@ -1,18 +1,16 @@ -%h3.page_title Public Access -.back_link - = link_to help_path do - ← to index -%hr += render layout: 'help/layout' do + %h3.page_title Public Access + %br -%p - GitLab allows you to open selected projects to be accessed publicly. - These projects will be clonable - %em without any - authentication. - Also they will be listed on the #{link_to "public access directory", public_root_path}. + %p + GitLab allows you to open selected projects to be accessed publicly. + These projects will be clonable + %em without any + authentication. + Also they will be listed on the #{link_to "public access directory", public_root_path}. -%ol - %li Go to your project dashboard - %li Click on the "Edit" tab - %li Select "Public clone access" + %ol + %li Go to your project dashboard + %li Click on the "Edit" tab + %li Select "Public clone access" diff --git a/app/views/help/raketasks.html.haml b/app/views/help/raketasks.html.haml index f015451a673d3288114e6f0eb207dd6ec78ad0b4..bcc874fc3902618778903d604e9e0706b812106b 100644 --- a/app/views/help/raketasks.html.haml +++ b/app/views/help/raketasks.html.haml @@ -1,66 +1,64 @@ -%h3.page_title GitLab Rake Tasks -.back_link - = link_to help_path do - ← to index -%hr += render layout: 'help/layout' do + %h3.page_title GitLab Rake Tasks + %br -%p.slead - GitLab provides some specific rake tasks to enable special features or perform maintenance tasks. + %p.slead + GitLab provides some specific rake tasks to enable special features or perform maintenance tasks. -%ul.nav.nav-tabs.log-tabs - %li.active - = link_to "Features", "#features", 'data-toggle' => 'tab' - %li - = link_to "Maintenance", "#maintenance", 'data-toggle' => 'tab' - %li - = link_to "User Management", "#user_management", 'data-toggle' => 'tab' - %li - = link_to "Backup & Restore", "#backup_restore", 'data-toggle' => 'tab' - %li - = link_to "Cleanup", "#cleanup", 'data-toggle' => 'tab' + %ul.nav.nav-tabs.log-tabs + %li.active + = link_to "Features", "#features", 'data-toggle' => 'tab' + %li + = link_to "Maintenance", "#maintenance", 'data-toggle' => 'tab' + %li + = link_to "User Management", "#user_management", 'data-toggle' => 'tab' + %li + = link_to "Backup & Restore", "#backup_restore", 'data-toggle' => 'tab' + %li + = link_to "Cleanup", "#cleanup", 'data-toggle' => 'tab' -.tab-content - .tab-pane.active#features - .file_holder - .file_title - %i.icon-file - Features - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "features.md")) + .tab-content + .tab-pane.active#features + .file_holder + .file_title + %i.icon-file + Features + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "raketasks", "features.md")) - .tab-pane#maintenance - .file_holder - .file_title - %i.icon-file - Maintenance - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "maintenance.md")) + .tab-pane#maintenance + .file_holder + .file_title + %i.icon-file + Maintenance + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "raketasks", "maintenance.md")) - .tab-pane#user_management - .file_holder - .file_title - %i.icon-file - User Management - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "user_management.md")) + .tab-pane#user_management + .file_holder + .file_title + %i.icon-file + User Management + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "raketasks", "user_management.md")) - .tab-pane#cleanup - .file_holder - .file_title - %i.icon-file - Cleanup - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "cleanup.md")) + .tab-pane#cleanup + .file_holder + .file_title + %i.icon-file + Cleanup + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "raketasks", "cleanup.md")) - .tab-pane#backup_restore - .file_holder - .file_title - %i.icon-file - Backup & Restore - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "raketasks", "backup_restore.md")) + .tab-pane#backup_restore + .file_holder + .file_title + %i.icon-file + Backup & Restore + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "raketasks", "backup_restore.md")) diff --git a/app/views/help/ssh.html.haml b/app/views/help/ssh.html.haml index 3f082333a76303f10f3510c1ce25d304edd3c012..114415977b4e14a04b45a48957b66384550e9371 100644 --- a/app/views/help/ssh.html.haml +++ b/app/views/help/ssh.html.haml @@ -1,25 +1,23 @@ -%h3.page_title SSH Keys -.back_link - = link_to help_path do - ← to index -%hr += render layout: 'help/layout' do + %h3.page_title SSH Keys + %br -%p.slead - SSH key allows you to establish a secure connection between your computer and GitLab + %p.slead + SSH key allows you to establish a secure connection between your computer and GitLab -%p.slead - To generate a new SSH key just open your terminal and use code below. + %p.slead + To generate a new SSH key just open your terminal and use code below. -%pre.dark - ssh-keygen -t rsa -C "#{current_user.email}" + %pre.dark + ssh-keygen -t rsa -C "#{current_user.email}" - \# Creates a new ssh key using the provided email - \# Generating public/private rsa key pair... + \# Creates a new ssh key using the provided email + \# Generating public/private rsa key pair... -%p.slead - Next just use code below to dump your public key and add to GitLab SSH Keys + %p.slead + Next just use code below to dump your public key and add to GitLab SSH Keys -%pre.dark - cat ~/.ssh/id_rsa.pub + %pre.dark + cat ~/.ssh/id_rsa.pub - \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc.... + \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc.... diff --git a/app/views/help/system_hooks.html.haml b/app/views/help/system_hooks.html.haml index c25b60deeb090ac1814485113ae42cfa115d0ecc..c49011a22698c89c23a81b10234fce1537660247 100644 --- a/app/views/help/system_hooks.html.haml +++ b/app/views/help/system_hooks.html.haml @@ -1,14 +1,12 @@ -%h3 System hooks -.back_link - = link_to :back do - ← back -%hr - -%p.slead - Your GitLab instance can perform HTTP POST requests on the following events: create_project, delete_project, create_user, delete_user, change_team_member. - %br += render layout: 'help/layout' do + %h3.page_title System hooks %br - System Hooks can be used, e.g. for logging or changing information in a LDAP server. - %br -%h5 Hooks request example: -= render "admin/hooks/data_ex" + + %p.slead + Your GitLab instance can perform HTTP POST requests on the following events: create_project, delete_project, create_user, delete_user, change_team_member. + %br + %br + System Hooks can be used, e.g. for logging or changing information in a LDAP server. + %br + %h5 Hooks request example: + = render "admin/hooks/data_ex" diff --git a/app/views/help/web_hooks.html.haml b/app/views/help/web_hooks.html.haml index 65036613fa7474c53781dfa88a6c8b55f0cad2ac..09745f7361434d94580fdfdeb896dd86ba109002 100644 --- a/app/views/help/web_hooks.html.haml +++ b/app/views/help/web_hooks.html.haml @@ -1,15 +1,13 @@ -%h3.page_title Web hooks -.back_link - = link_to help_path do - ← to index -%hr - -%p.slead - Every GitLab project can trigger a web server whenever the repo is pushed to. - %br - Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. += render layout: 'help/layout' do + %h3.page_title Web hooks %br - GitLab will send POST request with commits information on every push. -%h5 Hooks request example: -= render "hooks/data_ex" + + %p.slead + Every GitLab project can trigger a web server whenever the repo is pushed to. + %br + Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. + %br + GitLab will send POST request with commits information on every push. + %h5 Hooks request example: + = render "hooks/data_ex" diff --git a/app/views/help/workflow.html.haml b/app/views/help/workflow.html.haml index 6062ca091bd87b7d6be2598897567b424bbb34e3..495b7c6e6fc0955fa4bb966f2911640618378aee 100644 --- a/app/views/help/workflow.html.haml +++ b/app/views/help/workflow.html.haml @@ -1,40 +1,38 @@ -%h3.page_title Workflow -.back_link - = link_to help_path do - ← to index -%hr - -%ol.help - %li - %p Clone project - .bash - %pre.dark - git clone git@example.com:project-name.git - - %li - %p Create branch with your feature - .bash - %pre.dark - git checkout -b $feature_name - - %li - %p Write code. Commit changes - .bash - %pre.dark - git commit -am "My feature is ready" - - %li - %p Push your branch to GitLab - .bash - %pre.dark - git push origin $feature_name - - %li - %p Review your code on Commits page - - %li - %p Create a merge request - - %li - %p Your team lead will review code & merge it to main branch += render layout: 'help/layout' do + %h3.page_title Workflow + %br + + %ol.help + %li + %p Clone project + .bash + %pre.dark + git clone git@example.com:project-name.git + + %li + %p Create branch with your feature + .bash + %pre.dark + git checkout -b $feature_name + + %li + %p Write code. Commit changes + .bash + %pre.dark + git commit -am "My feature is ready" + + %li + %p Push your branch to GitLab + .bash + %pre.dark + git push origin $feature_name + + %li + %p Review your code on Commits page + + %li + %p Create a merge request + + %li + %p Your team lead will review code & merge it to main branch diff --git a/app/views/hooks/index.html.haml b/app/views/hooks/index.html.haml index 1fcf6e1c57a6345cb0e888373c71b5e6d98a283c..88a5a7dcffe7939d357d3e0f85ea1bdbad779d1a 100644 --- a/app/views/hooks/index.html.haml +++ b/app/views/hooks/index.html.haml @@ -10,7 +10,7 @@ = form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f| -if @hook.errors.any? - .alert-message.block-message.error + .alert.alert-error - @hook.errors.full_messages.each do |msg| %p= msg .clearfix @@ -18,7 +18,7 @@ .input = f.text_field :url, class: "text_field xxlarge" - = f.submit "Add Web Hook", class: "btn primary" + = f.submit "Add Web Hook", class: "btn btn-primary" %hr -if @hooks.any? @@ -37,6 +37,6 @@ → %span.monospace= hook.url %td - .right - = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small grouped" - = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small grouped" + .pull-right + = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped" + = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped" diff --git a/app/views/issues/_filter.html.haml b/app/views/issues/_filter.html.haml index 779e55bb7af6670b0f2c1a389d21f5dec9b21b79..21efaa5357cbef9d5b1fe3282856539854af28b8 100644 --- a/app/views/issues/_filter.html.haml +++ b/app/views/issues/_filter.html.haml @@ -16,5 +16,5 @@ %fieldset %hr - = link_to "Reset", project_issues_path(@project), class: 'btn right' + = link_to "Reset", project_issues_path(@project), class: 'btn pull-right' diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml index bef235f2dea8a4d29239cf5021463f3cf142b204..6d7613a700dd67db93de5b058e9fb346cf5e8450 100644 --- a/app/views/issues/_form.html.haml +++ b/app/views/issues/_form.html.haml @@ -2,7 +2,7 @@ %h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}" = form_for [@project, @issue] do |f| -if @issue.errors.any? - .alert-message.block-message.error + .alert.alert-error - @issue.errors.full_messages.each do |msg| %span= msg %br @@ -44,12 +44,12 @@ .actions - if @issue.new_record? - = f.submit 'Submit new issue', class: "btn success" + = f.submit 'Submit new issue', class: "btn btn-create" -else - = f.submit 'Save changes', class: "save-btn btn" + = f.submit 'Save changes', class: "btn-save btn" - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue) - = link_to "Cancel", cancel_path, class: 'btn cancel-btn' + = link_to "Cancel", cancel_path, class: 'btn btn-cancel' diff --git a/app/views/issues/_head.html.haml b/app/views/issues/_head.html.haml index 4294503c211aaff062d43447e2900526b13f547a..7e0b2cde074ae8c484bc5736d18e90f8588e249c 100644 --- a/app/views/issues/_head.html.haml +++ b/app/views/issues/_head.html.haml @@ -5,7 +5,7 @@ = link_to 'Milestones', project_milestones_path(@project), class: "tab" = nav_link(controller: :labels) do = link_to 'Labels', project_labels_path(@project), class: "tab" - %li.right + %li.pull-right %span.rss-icon = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do = image_tag "rss_ui.png", title: "feed" diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml index 8821dbb8d9803af9fe0b70a1ba41bf1061529eb7..3bbd293dba2897aa41e93f739d0a89b9624fc730 100644 --- a/app/views/issues/_issues.html.haml +++ b/app/views/issues/_issues.html.haml @@ -4,7 +4,7 @@ - if @issues.present? %li.bottom .left= paginate @issues, remote: true, theme: "gitlab" - .right + .pull-right %span.issue_counter #{@issues.total_count} issues for this filter - else diff --git a/app/views/issues/_show.html.haml b/app/views/issues/_show.html.haml index dcef901c15fe836accb8f1d51e233a2a20351265..fa888618066628be9e63596df8c7e6b766d3a50e 100644 --- a/app/views/issues/_show.html.haml +++ b/app/views/issues/_show.html.haml @@ -2,17 +2,17 @@ - if controller.controller_name == 'issues' .issue_check = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) - .right + .pull-right - if issue.notes.any? - %span.btn.small.disabled.grouped + %span.btn.btn-small.disabled.grouped %i.icon-comment = issue.notes.count - if can? current_user, :modify_issue, issue - if issue.closed - = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small grouped reopen_issue", remote: true + = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true - else - = link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small grouped close_issue", remote: true - = link_to edit_project_issue_path(issue.project, issue), class: "btn small edit-issue-link grouped" do + = link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true + = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do %i.icon-edit Edit diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index d5c29c780ce3423b6c326c8b1645a1de16406e6b..875f29e2600749d8ff788c22fd60dd95a4d1ee58 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -3,16 +3,16 @@ %h3.page_title Issues %span (<span class=issue_counter>#{@issues.total_count}</span>) - .right + .pull-right .span5 - if can? current_user, :write_issue, @project - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "right btn primary", title: "New Issue", id: "new_issue_link" do + = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-primary pull-right", title: "New Issue", id: "new_issue_link" do %i.icon-plus New Issue - = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do + = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: 'pull-right' do = hidden_field_tag :project_id, @project.id, { id: 'project_id' } = hidden_field_tag :status, params[:status] - = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib search-text-input' } + = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 pull-right neib search-text-input' } .clearfix @@ -33,7 +33,7 @@ = select_tag('update[milestone_id]', options_from_collection_for_select(issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone") = hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag :status, params[:status] - = button_tag "Save", class: "btn update_selected_issues btn-small save-btn" + = button_tag "Save", class: "btn update_selected_issues btn-small btn-save" .issues_filters = form_tag project_issues_path(@project), method: :get do = select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), prompt: "Labels") diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 6bf789296997bdd647839839ac9144aec0a16d01..474955cc665c78d6620b25b329a40c52235bb800 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -5,7 +5,7 @@ created at = @issue.created_at.stamp("Aug 21, 2011") - %span.right + %span.pull-right - if can?(current_user, :admin_project, @project) || @issue.author == current_user - if @issue.closed = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped reopen_issue" @@ -16,7 +16,7 @@ %i.icon-edit Edit -.right +.pull-right .span3#votes= render 'votes/votes_block', votable: @issue .back_link @@ -42,7 +42,7 @@ %cite.cgray and attached to milestone %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) - .right + .pull-right - @issue.labels.each do |label| %span.label %i.icon-tag diff --git a/app/views/keys/_form.html.haml b/app/views/keys/_form.html.haml index 26700803e61247fbb76eab513cb2e575feb4cb92..fe26216b1d5acf17eefe9ce465d7aac32fd91fa4 100644 --- a/app/views/keys/_form.html.haml +++ b/app/views/keys/_form.html.haml @@ -1,7 +1,7 @@ %div = form_for @key do |f| -if @key.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @key.errors.full_messages.each do |msg| %li= msg @@ -19,6 +19,6 @@ .actions - = f.submit 'Save', class: "btn save-btn" - = link_to "Cancel", keys_path, class: "btn cancel-btn" + = f.submit 'Save', class: "btn btn-save" + = link_to "Cancel", keys_path, class: "btn btn-cancel" diff --git a/app/views/keys/_show.html.haml b/app/views/keys/_show.html.haml index 9d4485cf9a4bbd4982cbb7815a47c41cd790676b..52bbea6fc7bad85d3f63303fce3934edb3ee6234 100644 --- a/app/views/keys/_show.html.haml +++ b/app/views/keys/_show.html.haml @@ -8,5 +8,5 @@ = time_ago_in_words(key.created_at) ago %td - = link_to 'Remove', key, confirm: 'Are you sure?', method: :delete, class: "btn small danger delete-key right" + = link_to 'Remove', key, confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove delete-key pull-right" diff --git a/app/views/keys/index.html.haml b/app/views/keys/index.html.haml index f5a8283a0ce3738e4e902e4ae3e787a1ac1bf5c1..7730b344a7db039935efaf5806883d907a187547 100644 --- a/app/views/keys/index.html.haml +++ b/app/views/keys/index.html.haml @@ -1,6 +1,6 @@ %h3.page_title SSH Keys - = link_to "Add new", new_key_path, class: "btn right" + = link_to "Add new", new_key_path, class: "btn pull-right" %hr %p.slead diff --git a/app/views/keys/show.html.haml b/app/views/keys/show.html.haml index a8cba6c8f9ecfb1ba76d91aab8c6cadd65825398..059fe5e5806f705ab03b2b819693da699f4972f0 100644 --- a/app/views/keys/show.html.haml +++ b/app/views/keys/show.html.haml @@ -10,5 +10,5 @@ %hr %pre= @key.key -.right - = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn danger delete-key" +.pull-right + = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key" diff --git a/app/views/labels/_label.html.haml b/app/views/labels/_label.html.haml index 6e223e8e61d7f7b641f854da854a118d4d25dd76..027b041d58e91e8ae74c964b221394796f7151c0 100644 --- a/app/views/labels/_label.html.haml +++ b/app/views/labels/_label.html.haml @@ -2,7 +2,7 @@ %strong %i.icon-tag = label.name - .right + .pull-right = link_to project_issues_path(label_name: label.name) do %strong = pluralize(label.count, 'issue') diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index f4b2228a41babd38710f022486a7a406c4b46918..8f4f3d7815f31b7a00876ee6d973e2e0e4c5521c 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -17,7 +17,7 @@ = link_to new_project_path, title: "Create New Project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do %i.icon-plus %li - = link_to profile_path, title: "Your Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do + = link_to profile_path, title: "My Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do %i.icon-user %li.separator %li diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 7ea9079849a0d9c115c57d1cdc073dc7c5afe3a0..c484af04704e8503b6fa4507a985fe0ef75e1afb 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -1,6 +1,8 @@ .search = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f| = text_field_tag "search", nil, placeholder: "Search", class: "search-input" + = hidden_field_tag :group_id, @group.try(:id) + = hidden_field_tag :project_id, @project.try(:id) :javascript $(function(){ diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index a60e7febe76ea70340a58ac16e57e2ac331f973e..a01886cdabf601cf3793784365edb6f1a2462c72 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -7,9 +7,12 @@ .container %ul.main_menu = nav_link(controller: :dashboard, html_options: {class: 'home'}) do - = link_to "Stats", admin_root_path + = link_to admin_root_path, title: "Stats" do + %i.icon-home = nav_link(controller: :projects) do = link_to "Projects", admin_projects_path + = nav_link(controller: :teams) do + = link_to "Teams", admin_teams_path = nav_link(controller: :groups) do = link_to "Groups", admin_groups_path = nav_link(controller: :users) do diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 88da5c98c78e0a5873d5a1d61ba71d2a44cd5595..7ee44238d10bd0e8f0ca4faeb33f9a3a8b2b53a8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -6,14 +6,18 @@ = render "layouts/head_panel", title: "Dashboard" .container %ul.main_menu - = nav_link(path: 'dashboard#index', html_options: {class: 'home'}) do - = link_to "Home", root_path, title: "Home" + = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do + = link_to root_path, title: "Home" do + %i.icon-home + = nav_link(path: 'dashboard#projects') do + = link_to projects_dashboard_path do + Projects = nav_link(path: 'dashboard#issues') do - = link_to dashboard_issues_path do + = link_to issues_dashboard_path do Issues %span.count= current_user.assigned_issues.opened.count = nav_link(path: 'dashboard#merge_requests') do - = link_to dashboard_merge_requests_path do + = link_to merge_requests_dashboard_path do Merge Requests %span.count= current_user.cared_merge_requests.opened.count = nav_link(path: 'search#show') do diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index f47e8b3e9ff882c0fb32392608a1f1c9dad12034..9057ad50ce68620c314925bb9c943eb8291a2c06 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -3,11 +3,12 @@ = render "layouts/head", title: "#{@group.name}" %body{class: "#{app_theme} application"} = render "layouts/flash" - = render "layouts/head_panel", title: "#{@group.name}" + = render "layouts/head_panel", title: "group: #{@group.name}" .container %ul.main_menu = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to "Home", group_path(@group), title: "Home" + = link_to group_path(@group), title: "Home" do + %i.icon-home = nav_link(path: 'groups#issues') do = link_to issues_group_path(@group) do Issues @@ -21,4 +22,10 @@ = nav_link(path: 'groups#people') do = link_to "People", people_group_path(@group) + - if can?(current_user, :manage_group, @group) + = nav_link(path: 'groups#edit') do + = link_to edit_group_path(@group), class: "tab " do + %i.icon-edit + Edit Group + .content= yield diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 7852ed6f0e1380a88df9124abd0d064c56fae1c1..57f250c775bcd3b9d2ccd78c05040c0a6c99fcbc 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -7,7 +7,8 @@ .container %ul.main_menu = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to "Profile", profile_path + = link_to profile_path, title: "Profile" do + %i.icon-home = nav_link(path: 'profiles#account') do = link_to "Account", account_profile_path = nav_link(controller: :keys) do diff --git a/app/views/layouts/project_resource.html.haml b/app/views/layouts/project_resource.html.haml index 14671c5ca70b116eb0066908653211618fbc5d5c..09ccb1d7b6a818027cbdf2f24179782b3b79658e 100644 --- a/app/views/layouts/project_resource.html.haml +++ b/app/views/layouts/project_resource.html.haml @@ -12,7 +12,8 @@ .container %ul.main_menu = nav_link(html_options: {class: "home #{project_tab_class}"}) do - = link_to @project.path, project_path(@project), title: "Project" + = link_to project_path(@project), title: "Project" do + %i.icon-home - if @project.repo_exists? - if can? current_user, :download_code, @project @@ -20,8 +21,8 @@ = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) = nav_link(controller: %w(commit commits compare repositories protected_branches)) do = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) - = nav_link(path: 'projects#graph') do - = link_to "Network", graph_project_path(@project) + = nav_link(controller: %w(graph)) do + = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) - if @project.issues_enabled = nav_link(controller: %w(issues milestones labels)) do diff --git a/app/views/layouts/user_team.html.haml b/app/views/layouts/user_team.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..19bbc373f46378d0575fb384725b53753070b972 --- /dev/null +++ b/app/views/layouts/user_team.html.haml @@ -0,0 +1,39 @@ +!!! 5 +%html{ lang: "en"} + = render "layouts/head", title: "#{@team.name}" + %body{class: "#{app_theme} application"} + = render "layouts/flash" + = render "layouts/head_panel", title: "team: #{@team.name}" + .container + %ul.main_menu + = nav_link(path: 'teams#show', html_options: {class: 'home'}) do + = link_to team_path(@team), title: "Home" do + %i.icon-home + + = nav_link(path: 'teams#issues') do + = link_to issues_team_path(@team) do + Issues + %span.count= Issue.opened.of_user_team(@team).count + + = nav_link(path: 'teams#merge_requests') do + = link_to merge_requests_team_path(@team) do + Merge Requests + %span.count= MergeRequest.opened.of_user_team(@team).count + + = nav_link(controller: [:members]) do + = link_to team_members_path(@team), class: "team-tab tab" do + Members + %span.count= @team.members.count + + - if can? current_user, :admin_user_team, @team + = nav_link(controller: [:projects]) do + = link_to team_projects_path(@team), class: "team-tab tab" do + Projects + %span.count= @team.projects.count + + = nav_link(path: 'teams#edit') do + = link_to edit_team_path(@team), class: "stat-tab tab " do + %i.icon-edit + Edit Team + + .content= yield diff --git a/app/views/merge_requests/_filter.html.haml b/app/views/merge_requests/_filter.html.haml index 86148fbcfee994da62090ded2a4937ffa0866051..4b48306ed0575bc8e8471610d4a71b8819f24fcf 100644 --- a/app/views/merge_requests/_filter.html.haml +++ b/app/views/merge_requests/_filter.html.haml @@ -16,5 +16,5 @@ %fieldset %hr - = link_to "Reset", project_merge_requests_path(@project), class: 'btn right' + = link_to "Reset", project_merge_requests_path(@project), class: 'btn pull-right' diff --git a/app/views/merge_requests/_form.html.haml b/app/views/merge_requests/_form.html.haml index 9a4f0617a3a60210616083a11188263209308882..816c852d24bbffdec761d55d535935c06d8fc4e2 100644 --- a/app/views/merge_requests/_form.html.haml +++ b/app/views/merge_requests/_form.html.haml @@ -1,6 +1,6 @@ = form_for [@project, @merge_request], html: { class: "#{controller.action_name}-merge-request form-horizontal" } do |f| -if @merge_request.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @merge_request.errors.full_messages.each do |msg| %li= msg @@ -51,19 +51,19 @@ .form-actions - if @merge_request.new_record? - = f.submit 'Submit merge request', class: "btn success" + = f.submit 'Submit merge request', class: "btn btn-create" -else - = f.submit 'Save changes', class: "save-btn btn" + = f.submit 'Save changes', class: "btn btn-save" - if @merge_request.new_record? - = link_to project_merge_requests_path(@project), class: "btn cancel-btn" do + = link_to project_merge_requests_path(@project), class: "btn btn-cancel" do Cancel - else - = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do + = link_to project_merge_request_path(@project, @merge_request), class: "btn btn-cancel" do Cancel :javascript $(function(){ - disableButtonIfEmptyField("#merge_request_title", ".save-btn"); + disableButtonIfEmptyField("#merge_request_title", ".btn-save"); var source_branch = $("#merge_request_source_branch") , target_branch = $("#merge_request_target_branch"); diff --git a/app/views/merge_requests/_merge_request.html.haml b/app/views/merge_requests/_merge_request.html.haml index 7369f3dd0619f67baae0e2572cf7b74d512aedcf..09c55d9846523ee6a512a9d0307d9242f58e7c14 100644 --- a/app/views/merge_requests/_merge_request.html.haml +++ b/app/views/merge_requests/_merge_request.html.haml @@ -1,20 +1,20 @@ %li{ class: mr_css_classes(merge_request) } - .right + .pull-right .left - if merge_request.merged? - %span.btn.small.disabled.grouped + %span.btn.btn-small.disabled.grouped %strong %i.icon-ok = "MERGED" - if merge_request.notes.any? - %span.btn.small.disabled.grouped + %span.btn.btn-small.disabled.grouped %i.icon-comment = merge_request.mr_and_commit_notes.count - if merge_request.milestone_id? - %span.btn.small.disabled.grouped + %span.btn.btn-small.disabled.grouped %i.icon-time = merge_request.milestone.title - %span.btn.small.disabled.grouped + %span.btn.btn-small.disabled.grouped = merge_request.source_branch → = merge_request.target_branch diff --git a/app/views/merge_requests/index.html.haml b/app/views/merge_requests/index.html.haml index 61c32b533f6d7ef798f59958a8f883e7372badf7..3073c8f6bab447f57a9ea65320897b54caee26df 100644 --- a/app/views/merge_requests/index.html.haml +++ b/app/views/merge_requests/index.html.haml @@ -1,5 +1,5 @@ - if can? current_user, :write_merge_request, @project - = link_to new_project_merge_request_path(@project), class: "right btn primary", title: "New Merge Request" do + = link_to new_project_merge_request_path(@project), class: "pull-right btn btn-primary", title: "New Merge Request" do %i.icon-plus New Merge Request %h3.page_title @@ -28,8 +28,8 @@ - if @merge_requests.present? %li.bottom .left= paginate @merge_requests, theme: "gitlab" - .right - %span.cgray.right #{@merge_requests.total_count} merge requests for this filter + .pull-right + %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter :javascript $(merge_requestsPage); diff --git a/app/views/merge_requests/show/_mr_accept.html.haml b/app/views/merge_requests/show/_mr_accept.html.haml index 128ffe76782e9b0e01ef45d3705019179d8bd0f6..c2c04b863e7df72c52b5890d0f5517de53f6ed1e 100644 --- a/app/views/merge_requests/show/_mr_accept.html.haml +++ b/app/views/merge_requests/show/_mr_accept.html.haml @@ -1,5 +1,5 @@ - unless can?(current_user, :accept_mr, @project) - .alert-message + .alert %strong Only masters can accept MR @@ -29,14 +29,14 @@ %strong This repository does not have satellite. Ask administrator to fix this issue .automerge_widget.cannot_be_merged{style: "display:none"} - .alert.alert-info + .alert.alert-disabled %span - = link_to "Show how to merge", "#", class: "how_to_merge_link btn small padded", title: "How To Merge" + = link_to "Show how to merge", "#", class: "how_to_merge_link btn btn-small padded", title: "How To Merge" %strong This request can't be merged with GitLab. You should do it manually .automerge_widget.unchecked - .alert-message + .alert %strong %i.icon-refresh Checking for ability to automatically merge… diff --git a/app/views/merge_requests/show/_mr_ci.html.haml b/app/views/merge_requests/show/_mr_ci.html.haml index d46b606ef7f8b4cfa125be781adee638faa212b4..dd1e78a020578711e84077773cdf5bf1f6f7cf91 100644 --- a/app/views/merge_requests/show/_mr_ci.html.haml +++ b/app/views/merge_requests/show/_mr_ci.html.haml @@ -23,7 +23,7 @@ = link_to "Build page", ci_build_details_path(@merge_request) .ci_widget - .alert-message + .alert %strong %i.icon-refresh Checking for CI status for #{@merge_request.last_commit_short_sha} diff --git a/app/views/merge_requests/show/_mr_title.html.haml b/app/views/merge_requests/show/_mr_title.html.haml index c2ffe8e3770f1c89ab60b18f9d9222a64729f801..8119728dcb93588aeb595b127378145269dfdf85 100644 --- a/app/views/merge_requests/show/_mr_title.html.haml +++ b/app/views/merge_requests/show/_mr_title.html.haml @@ -5,7 +5,7 @@ → %span.label_branch= @merge_request.target_branch - %span.right + %span.pull-right - if can?(current_user, :modify_merge_request, @merge_request) - if @merge_request.open? .left.btn-group @@ -17,13 +17,13 @@ %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close merge request" + = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped btn-close", title: "Close merge request" = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do %i.icon-edit Edit -.right +.pull-right .span3#votes= render 'votes/votes_block', votable: @merge_request .back_link diff --git a/app/views/milestones/_form.html.haml b/app/views/milestones/_form.html.haml index 1c496a93e54137a29e7d2748629cd95a634d825e..fbaf64a305cdb559eb9420f865fcf5c1a94eb953 100644 --- a/app/views/milestones/_form.html.haml +++ b/app/views/milestones/_form.html.haml @@ -7,7 +7,7 @@ = form_for [@project, @milestone], html: {class: "new_milestone form-horizontal"} do |f| -if @milestone.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @milestone.errors.full_messages.each do |msg| %li= msg @@ -32,16 +32,16 @@ .form-actions - if @milestone.new_record? - = f.submit 'Create milestone', class: "save-btn btn" - = link_to "Cancel", project_milestones_path(@project), class: "btn cancel-btn" + = f.submit 'Create milestone', class: "btn-save btn" + = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel" -else - = f.submit 'Save changes', class: "save-btn btn" - = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn cancel-btn" + = f.submit 'Save changes', class: "btn-save btn" + = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel" :javascript $(function() { - disableButtonIfEmptyField("#milestone_title", ".save-btn"); + disableButtonIfEmptyField("#milestone_title", ".btn-save"); $( ".datepicker" ).datepicker({ dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } diff --git a/app/views/milestones/_milestone.html.haml b/app/views/milestones/_milestone.html.haml index 3864792f7e81995db1246d4a3964208dd1f21839..00e20117f36de3b6df691d7abcc32eb844e75278 100644 --- a/app/views/milestones/_milestone.html.haml +++ b/app/views/milestones/_milestone.html.haml @@ -1,7 +1,7 @@ %li{class: "milestone milestone-#{milestone.closed ? 'closed' : 'open'}", id: dom_id(milestone) } - .right + .pull-right - if can?(current_user, :admin_milestone, milestone.project) and milestone.open? - = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" do + = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do %i.icon-edit Edit %h4 diff --git a/app/views/milestones/index.html.haml b/app/views/milestones/index.html.haml index 3089595fe0b004298305882a4350f2d91a21fd12..b78f17053fd91e794ae7909ff3fede251d7c7436 100644 --- a/app/views/milestones/index.html.haml +++ b/app/views/milestones/index.html.haml @@ -3,7 +3,7 @@ %h3.page_title Milestones - if can? current_user, :admin_milestone, @project - = link_to "New Milestone", new_project_milestone_path(@project), class: "right btn small", title: "New Milestone" + = link_to "New Milestone", new_project_milestone_path(@project), class: "pull-right btn btn-small", title: "New Milestone" %br %div.ui-box .title diff --git a/app/views/milestones/show.html.haml b/app/views/milestones/show.html.haml index fc7ae51f184328b41a87bb7e1bd008fe5a16e772..43d82a54dd64c654e9c608a761ec4c841e203b91 100644 --- a/app/views/milestones/show.html.haml +++ b/app/views/milestones/show.html.haml @@ -8,14 +8,14 @@ = link_to project_milestones_path(@project) do ← To milestones list .span6 - .right + .pull-right - unless @milestone.closed - = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn small grouped", title: "New Issue" do + = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do %i.icon-plus New Issue = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link small grouped" - if can?(current_user, :admin_milestone, @project) - = link_to edit_project_milestone_path(@project, @milestone), class: "btn small grouped" do + = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-small grouped" do %i.icon-edit Edit @@ -25,7 +25,7 @@ %hr %p %span All issues for this milestone are closed. You may close milestone now. - = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn small danger" + = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn btn-small btn-remove" .ui-box.ui-box-show .ui-box-head @@ -43,7 +43,7 @@ #{@milestone.closed_items_count} closed – #{@milestone.open_items_count} open - %span.right= @milestone.expires_at + %span.pull-right= @milestone.expires_at .progress.progress-info .bar{style: "width: #{@milestone.percent_complete}%;"} diff --git a/app/views/notes/_discussion.html.haml b/app/views/notes/_discussion.html.haml index a9a11fc2d7ebd303e0215af9e361754ba6c7a7f0..24cb422817445f186124a86743460a5d15c1ec09 100644 --- a/app/views/notes/_discussion.html.haml +++ b/app/views/notes/_discussion.html.haml @@ -38,7 +38,7 @@ - if note.for_diff_line? - if note.diff .content - .diff_file= render "notes/discussion_diff", discussion_notes: discussion_notes, note: note + .file= render "notes/discussion_diff", discussion_notes: discussion_notes, note: note - else = link_to 'show outdated discussion', '#', class: 'js-show-outdated-discussion' %div.hide.outdated-discussion diff --git a/app/views/notes/_discussion_diff.html.haml b/app/views/notes/_discussion_diff.html.haml index 93ab59c72c501749bcf932e3d7f24992fd816973..790b77333d5c6b6eca9a878f22735b68e30ce61f 100644 --- a/app/views/notes/_discussion_diff.html.haml +++ b/app/views/notes/_discussion_diff.html.haml @@ -1,5 +1,5 @@ - diff = note.diff -.diff_file_header +.header - if diff.deleted_file %span= diff.old_path - else @@ -7,7 +7,7 @@ - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" %br/ -.diff_file_content +.content %table - each_diff_line(diff, note.diff_file_index) do |line, type, line_code, line_new, line_old| %tr.line_holder{ id: line_code } diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml index d094119a9da5ea209974de1c4b2c247de258466e..a154c31e5abeedbc565a621c316e941206d05c08 100644 --- a/app/views/notes/_form.html.haml +++ b/app/views/notes/_form.html.haml @@ -15,30 +15,30 @@ = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' .note_preview.js-note-preview.turn-off - .buttons - = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button" - %a.btn.grouped.js-close-discussion-note-form Cancel .hint - .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + .pull-right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. .clearfix - .note_options - .attachment - %h6 Attachment: - .file_name.js-attachment-filename File name... - %a.choose-btn.btn.small.js-choose-note-attachment-button Choose File ... - .hint Any file up to 10 MB + .note-form-actions + .buttons + = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button" + %a.btn.grouped.js-close-discussion-note-form Cancel - = f.file_field :attachment, class: "js-note-attachment-input" - - .notify_options - %h6 Notify via email: + .note-form-option = label_tag :notify do = check_box_tag :notify, 1, !@note.for_commit? - Project team + %span.light Notify team via email .js-notify-commit-author = label_tag :notify_author do = check_box_tag :notify_author, 1 , @note.for_commit? - Commit author - .clearfix + %span.light Notify commit author + .note-form-option + %a.choose-btn.btn.btn-small.js-choose-note-attachment-button + %i.icon-paper-clip + %span Choose File ... + + %span.file_name.js-attachment-filename File name... + = f.file_field :attachment, class: "js-note-attachment-input hide" + + .clearfix diff --git a/app/views/notes/_note.html.haml b/app/views/notes/_note.html.haml index 9efeb563e0a276bb49a2dfa2d4d317f4e466f516..4d3007a0ed1ddc8406bb5abeab33c054862b9afe 100644 --- a/app/views/notes/_note.html.haml +++ b/app/views/notes/_note.html.haml @@ -30,8 +30,8 @@ - if note.attachment.url - if note.attachment.image? = image_tag note.attachment.url, class: 'note-image-attach' - .attachment.right + .attachment.pull-right = link_to note.attachment.url, target: "_blank" do - %i.icon-attachment + %i.icon-paper-clip = note.attachment_identifier .clear diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml index 522e45e637a390a25a7a58e3d64c0291799b50be..2ad000b815bd86cf36e85478b511e87c98adaa00 100644 --- a/app/views/profiles/account.html.haml +++ b/app/views/profiles/account.html.haml @@ -12,7 +12,7 @@ %fieldset %legend Private token - %span.cred.right + %span.cred.pull-right keep it secret! .padded = form_for @user, url: reset_private_token_profile_path, method: :put do |f| @@ -24,7 +24,7 @@ %p.cgray - if current_user.private_token = text_field_tag "token", current_user.private_token, class: "xxlarge large_text" - = f.submit 'Reset', confirm: "Are you sure?", class: "btn primary btn-build-token" + = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" - else %span You don`t have one yet. Click generate to fix it. = f.submit 'Generate', class: "btn success btn-build-token" @@ -35,7 +35,7 @@ .padded %p.slead After successful password update you will be redirected to login page where you should login with new password -if @user.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @user.errors.full_messages.each do |msg| %li= msg @@ -49,14 +49,14 @@ = f.password_field :password_confirmation, required: true .clearfix .input - = f.submit 'Save password', class: "btn save-btn" + = f.submit 'Save password', class: "btn btn-save" %fieldset.update-username %legend Username - %small.cred.right + %small.cred.pull-right Changing your username can have unintended side effects! = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| .padded @@ -75,6 +75,6 @@ %li It will change web url for personal projects. %li It will change the git path to repositories for personal projects. .input - = f.submit 'Save username', class: "btn save-btn" + = f.submit 'Save username', class: "btn btn-save" diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 687463b672cb731c8136803abb4198f72c044bdc..3cf6330cc3ccd9dcc97998111f48351009edbb8c 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -6,7 +6,7 @@ %small = @user.email - .right + .pull-right = link_to destroy_user_session_path, class: "logout", method: :delete do %small %i.icon-signout @@ -15,7 +15,7 @@ = form_for @user, url: profile_path, method: :put, html: { class: "edit_user form-horizontal" } do |f| -if @user.errors.any? - %div.alert-message.block-message.error + %div.alert.alert-error %ul - @user.errors.full_messages.each do |msg| %li= msg @@ -31,8 +31,22 @@ .controls = f.text_field :email, class: "input-xlarge", required: true %span.help-block We also use email for avatar detection. + .control-group + = f.label :skype, class: "control-label" + .controls= f.text_field :skype, class: "input-xlarge" + .control-group + = f.label :linkedin, class: "control-label" + .controls= f.text_field :linkedin, class: "input-xlarge" + .control-group + = f.label :twitter, class: "control-label" + .controls= f.text_field :twitter, class: "input-xlarge" + .control-group + = f.label :bio, class: "control-label" + .controls + = f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250 + %span.help-block Tell us about yourself in fewer than 250 characters. - .span5.right + .span5.pull-right %fieldset.tips %legend Tips: %ul @@ -47,28 +61,22 @@ %p You can login through #{@user.provider.titleize}! = link_to "click here to change", account_profile_path - - .row - .span7 - .control-group - = f.label :skype, class: "control-label" - .controls= f.text_field :skype, class: "input-xlarge" - .control-group - = f.label :linkedin, class: "control-label" - .controls= f.text_field :linkedin, class: "input-xlarge" - .control-group - = f.label :twitter, class: "control-label" - .controls= f.text_field :twitter, class: "input-xlarge" - .control-group - = f.label :bio, class: "control-label" - .controls - = f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250 - %span.help-block Tell us about yourself in fewer than 250 characters. - .span5.right + - if current_user.can_create_group? + %li + %p + Need a group for several dependent projects? + = link_to new_group_path, class: "btn btn-tiny" do + Create a group + - if current_user.can_create_team? + %li + %p + Want to share a team between projects? + = link_to new_team_path, class: "btn btn-tiny" do + Create a team %fieldset %legend Personal projects: - %small.right + %small.pull-right %span= current_user.personal_projects.count of %span= current_user.projects_limit @@ -79,9 +87,10 @@ %fieldset %legend SSH public keys: - %strong.right= link_to current_user.keys.count, keys_path + %span.pull-right + = link_to pluralize(current_user.keys.count, 'key'), keys_path .padded - = link_to "Add Public Key", new_key_path, class: "btn small" + = link_to "Add Public Key", new_key_path, class: "btn btn-small" .form-actions - = f.submit 'Save', class: "btn save-btn" + = f.submit 'Save', class: "btn btn-save" diff --git a/app/views/projects/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml index 2962ad980b3bfd69ffc450f6afe8a4a02277cf81..e52df19be965aff530be022186c3071fcd52d35b 100644 --- a/app/views/projects/_clone_panel.html.haml +++ b/app/views/projects/_clone_panel.html.haml @@ -2,8 +2,8 @@ .row .span7 .form-horizontal= render "shared/clone_panel" - .span4.right - .right + .span4.pull-right + .pull-right - unless @project.empty_repo? - if can? current_user, :download_code, @project = link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index b582adc97a2ce84306cf2dbdd98b03bc0f0014e8..0336654dc698d488cea77bd2f209eab6c9143dbc 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -1,6 +1,6 @@ = form_for(@project, remote: true) do |f| - if @project.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @project.errors.full_messages.each do |msg| %li= msg @@ -42,7 +42,7 @@ = f.check_box :wiki_enabled %span.descr Pages for project documentation - - if can? current_user, :change_public_mode, @project + - if can?(current_user, :change_public_mode, @project) %fieldset.features %legend %i.icon-share @@ -77,9 +77,9 @@ %br .actions - = f.submit 'Save', class: "btn save-btn" + = f.submit 'Save', class: "btn btn-save" = link_to 'Cancel', @project, class: "btn" - unless @project.new_record? - if can?(current_user, :remove_project, @project) - .right - = link_to 'Remove Project', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn danger" + .pull-right + = link_to 'Remove Project', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn btn-remove" diff --git a/app/views/projects/_new_form.html.haml b/app/views/projects/_new_form.html.haml index 131a4de42184218ede8675bc1a764c3b5288ae40..185164955fcd3271882bfec0b16501e48d60bd4c 100644 --- a/app/views/projects/_new_form.html.haml +++ b/app/views/projects/_new_form.html.haml @@ -1,13 +1,13 @@ = form_for(@project, remote: true) do |f| - if @project.errors.any? - .alert-message.block-message.error + .alert.alert-error %span= @project.errors.full_messages.first .clearfix.project_name_holder = f.label :name do Project name is .input = f.text_field :name, placeholder: "Example Project", class: "xxlarge" - = f.submit 'Create project', class: "btn success project-submit" + = f.submit 'Create project', class: "btn btn-create project-submit" - if current_user.can_select_namespace? .clearfix @@ -15,6 +15,20 @@ %span Namespace .input = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'chosen'} - %hr + %p.padded - All created project are private. You choose who can see project and commit to repository. + New projects are private by default. You choose who can see the project and commit to repository. + %hr + + - if current_user.can_create_group? + .clearfix + .input.light + Need a group for several dependent projects? + = link_to new_group_path, class: "btn btn-tiny" do + Create a group + - if current_user.can_create_team? + .clearfix + .input.light + Want to share a project between team? + = link_to new_team_path, class: "btn btn-tiny" do + Create a team diff --git a/app/views/projects/_project_head.html.haml b/app/views/projects/_project_head.html.haml index 94052650694b284251343ff63bd5eea4d1815131..b8c88853a620a53fba325044acdf4f088b93f694 100644 --- a/app/views/projects/_project_head.html.haml +++ b/app/views/projects/_project_head.html.haml @@ -3,7 +3,7 @@ = link_to project_path(@project), class: "activities-tab tab" do %i.icon-home Show - = nav_link(controller: :team_members) do + = nav_link(controller: [:team_members, :teams]) do = link_to project_team_index_path(@project), class: "team-tab tab" do %i.icon-user Team @@ -13,19 +13,19 @@ = link_to 'Snippets', project_snippets_path(@project), class: "snippets-tab tab" - if can? current_user, :admin_project, @project - = nav_link(controller: :deploy_keys, html_options: {class: 'right'}) do + = nav_link(controller: :deploy_keys, html_options: {class: 'pull-right'}) do = link_to project_deploy_keys_path(@project) do %span Deploy Keys - = nav_link(controller: :hooks, html_options: {class: 'right'}) do + = nav_link(controller: :hooks, html_options: {class: 'pull-right'}) do = link_to project_hooks_path(@project) do %span Hooks - = nav_link(controller: :services, html_options: {class: 'right'}) do + = nav_link(controller: :services, html_options: {class: 'pull-right'}) do = link_to project_services_path(@project) do %span Services - = nav_link(path: 'projects#edit', html_options: {class: 'right'}) do + = nav_link(path: 'projects#edit', html_options: {class: 'pull-right'}) do = link_to edit_project_path(@project), class: "stat-tab tab " do %i.icon-edit Edit diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 52dff687a3a70a775401632d35011c316e4077c3..9426517876ea8fc9fd5c5b67a1de5bf5907a4e14 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -31,4 +31,4 @@ - if can? current_user, :remove_project, @project .prepend-top-20 - = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right" + = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/teams/available.html.haml b/app/views/projects/teams/available.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..da7823638b9741a7a84d199609437b0f2c6f0adc --- /dev/null +++ b/app/views/projects/teams/available.html.haml @@ -0,0 +1,22 @@ += render "projects/project_head" + +%h3.page_title + = "Assign project to team of users" +%hr +%p.slead + Read more about assign to team of users #{link_to "here", '#', class: 'vlink'}. += form_tag assign_project_teams_path(@project), method: 'post' do + %p.slead Choose Team of users you want to assign: + .padded + = label_tag :team_id, "Team" + .input= select_tag(:team_id, options_from_collection_for_select(@teams, :id, :name), prompt: "Select team", class: "chosen xxlarge", required: true) + %p.slead Choose greatest user acces in team you want to assign: + .padded + = label_tag :team_ids, "Permission" + .input= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" } + + + .actions + = submit_tag 'Assign', class: "btn btn-create" + = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" + diff --git a/app/views/protected_branches/index.html.haml b/app/views/protected_branches/index.html.haml index c1ecceda435a6f69fb80ef878627d277e6bdde45..15644de552f9246f4c1f4d006ae032d7a3bda96b 100644 --- a/app/views/protected_branches/index.html.haml +++ b/app/views/protected_branches/index.html.haml @@ -14,7 +14,7 @@ - if can? current_user, :admin_project, @project = form_for [@project, @protected_branch] do |f| -if @protected_branch.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @protected_branch.errors.full_messages.each do |msg| %li= msg @@ -24,7 +24,7 @@ .span3 = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"}) - = f.submit 'Protect', class: "primary btn" + = f.submit 'Protect', class: "btn-primary btn" - unless @branches.empty? %table @@ -51,4 +51,4 @@ (branch was removed from repository) %td - if can? current_user, :admin_project, @project - = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small" + = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small" diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml index afdd4c5fd95150eab8dc3b9554a7f730a3ee9a0b..21e9d2e6029c93036f3d2a07ff5df988cd8447ab 100644 --- a/app/views/public/projects/index.html.haml +++ b/app/views/public/projects/index.html.haml @@ -9,7 +9,7 @@ %h5 %i.icon-share = project.name_with_namespace - .right + .pull-right %pre.dark.tiny git clone #{project.http_url_to_repo} diff --git a/app/views/repositories/_feed.html.haml b/app/views/repositories/_feed.html.haml index 443801337181bdbd7ea08c38cdd041592c62b842..eaf15ca77d63237da15022f7dfce7c139349131b 100644 --- a/app/views/repositories/_feed.html.haml +++ b/app/views/repositories/_feed.html.haml @@ -15,6 +15,6 @@ = image_tag gravatar_icon(commit.author_email), class: "", width: 16 = gfm escape_once(truncate(commit.title, length: 40)) %td - %span.right.cgray + %span.pull-right.cgray = time_ago_in_words(commit.committed_date) ago diff --git a/app/views/repositories/stats.html.haml b/app/views/repositories/stats.html.haml index bdf047f1e989ee9c690b756cf5f1b45aa5fdd4ad..dde35ea38aa0b1d0f4600ea09165c94973a62358 100644 --- a/app/views/repositories/stats.html.haml +++ b/app/views/repositories/stats.html.haml @@ -23,7 +23,7 @@ = image_tag gravatar_icon(author.email, 16), class: 'avatar s16' = author.name %small.light= author.email - .right + .pull-right = author.commits diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..3fe17dce8574ba8bee305f26fbb7bac86ab3e58f --- /dev/null +++ b/app/views/search/_filter.html.haml @@ -0,0 +1,24 @@ +%fieldset + %legend Groups: + %ul.nav.nav-pills.nav-stacked + %li{class: ("active" if params[:group_id].blank?)} + = link_to search_path(group_id: nil, search: params[:search]) do + Any + - current_user.authorized_groups.each do |group| + %li{class: ("active" if params[:group_id] == group.id.to_s)} + = link_to search_path(group_id: group.id, search: params[:search]) do + = group.name + +%fieldset + %legend Projects: + %ul.nav.nav-pills.nav-stacked + %li{class: ("active" if params[:project_id].blank?)} + = link_to search_path(project_id: nil, search: params[:search]) do + Any + - current_user.authorized_projects.each do |project| + %li{class: ("active" if params[:project_id] == project.id.to_s)} + = link_to search_path(project_id: project.id, search: params[:search]) do + = project.name_with_namespace + += hidden_field_tag :group_id, params[:group_id] += hidden_field_tag :project_id, params[:project_id] diff --git a/app/views/search/_result.html.haml b/app/views/search/_result.html.haml index 79bed4f737cdd9b64ade5ccfbd68ccbba04a0bdc..bfa46075baa3a2154328ba5ca355499ee4513a99 100644 --- a/app/views/search/_result.html.haml +++ b/app/views/search/_result.html.haml @@ -1,83 +1,38 @@ -%br -%h3.page_title - Search results - %span.cgray (#{@projects.count + @merge_requests.count + @issues.count + @wiki_pages.count}) -%hr +%fieldset + %legend + Search results + %span.cgray (#{@projects.count + @merge_requests.count + @issues.count + @wiki_pages.count}) .search_results - .row - .span6 - %table - %thead - %tr - %th Projects - %tbody - - @projects.each do |project| - %tr - %td - = link_to project do - %strong.term= project.name_with_namespace - %small.cgray - last activity at - = project.last_activity_date.stamp("Aug 25, 2011") - - if @projects.blank? - %tr - %td - %h4.nothing_here_message No Projects - %br - %table - %thead - %tr - %th Merge Requests - %tbody - - @merge_requests.each do |merge_request| - %tr - %td - = link_to [merge_request.project, merge_request] do - %span.badge.badge-info ##{merge_request.id} - – - %strong.term= truncate merge_request.title, length: 50 - %strong.right - %span.label= merge_request.project.name - - if @merge_requests.blank? - %tr - %td - %h4.nothing_here_message No Merge Requests - .span6 - %table - %thead - %tr - %th Issues - %tbody - - @issues.each do |issue| - %tr - %td - = link_to [issue.project, issue] do - %span.badge.badge-info ##{issue.id} - – - %strong.term= truncate issue.title, length: 40 - %strong.right - %span.label= issue.project.name - - if @issues.blank? - %tr - %td - %h4.nothing_here_message No Issues - .span6 - %table - %thead - %tr - %th Wiki - %tbody - - @wiki_pages.each do |wiki_page| - %tr - %td - = link_to project_wiki_path(wiki_page.project, wiki_page) do - %strong.term= truncate wiki_page.title, length: 40 - %strong.right - %span.label= wiki_page.project.name - - if @wiki_pages.blank? - %tr - %td - %h4.nothing_here_message No wiki pages + %ul.well-list + - @projects.each do |project| + %li + project: + = link_to project do + %strong.term= project.name_with_namespace + - @merge_requests.each do |merge_request| + %li + merge request: + = link_to [merge_request.project, merge_request] do + %span ##{merge_request.id} + %strong.term + = truncate merge_request.title, length: 50 + %span.light (#{merge_request.project.name_with_namespace}) + - @issues.each do |issue| + %li + issue: + = link_to [issue.project, issue] do + %span ##{issue.id} + %strong.term + = truncate issue.title, length: 50 + %span.light (#{issue.project.name_with_namespace}) + - @wiki_pages.each do |wiki_page| + %li + wiki: + = link_to project_wiki_path(wiki_page.project, wiki_page) do + %strong.term + = truncate wiki_page.title, length: 50 + %span.light (#{wiki_page.project.name_with_namespace}) + :javascript $(function() { $(".search_results .term").highlight("#{escape_javascript(params[:search])}"); diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index aa0d6d700d9cac5fc339cd57cc1ef743d26e8ead..5914c22df6ef951d442f7132b177247a83b82b70 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -1,9 +1,15 @@ = form_tag search_path, method: :get, class: 'form-inline' do |f| - .padded + .search-holder = label_tag :search do - %strong Looking for + %span Looking for .input = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search" - = submit_tag 'Search', class: "btn primary wide" -- if params[:search].present? - = render 'search/result' + = submit_tag 'Search', class: "btn btn-primary wide" + .clearfix + .row + .span3 + = render 'filter', f: f + .span9 + .results + - if params[:search].present? + = render 'search/result' diff --git a/app/views/services/_gitlab_ci.html.haml b/app/views/services/_gitlab_ci.html.haml index 649c5cc4c3c60212e57f7a927b7097c6a38538a9..dfde643849e3c2fffbfda69b55f5e20f20ebd41e 100644 --- a/app/views/services/_gitlab_ci.html.haml +++ b/app/views/services/_gitlab_ci.html.haml @@ -1,7 +1,7 @@ %h3.page_title GitLab CI %small Continuous integration server from GitLab - .right + .pull-right - if @service.active %small.cgreen Enabled - else @@ -16,7 +16,7 @@ %hr = form_for(@service, :as => :service, :url => project_service_path(@project, :gitlab_ci), :method => :put) do |f| - if @service.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @service.errors.full_messages.each do |msg| %li= msg @@ -40,7 +40,7 @@ .form-actions - = f.submit 'Save', class: 'btn save-btn' + = f.submit 'Save', class: 'btn btn-save' - if @service.valid? && @service.active = link_to 'Test settings', test_project_service_path(@project), class: 'btn btn-small' diff --git a/app/views/services/index.html.haml b/app/views/services/index.html.haml index 2c94f965eec1f968a999d6613f2e7ec9ddc858c7..27dbf5025691bfb35ee8f631953048244504b59a 100644 --- a/app/views/services/index.html.haml +++ b/app/views/services/index.html.haml @@ -8,7 +8,7 @@ = link_to edit_project_service_path(@project, :gitlab_ci) do GitLab CI %small Continuous integration server from GitLab - .right + .pull-right - if @gitlab_ci_service.try(:active) %small.cgreen %i.icon-ok @@ -21,11 +21,11 @@ %h4 Jenkins CI %small An extendable open source continuous integration server - .right + .pull-right %small Not implemented yet %li.disabled %h4 Campfire %small Web-based group chat tool - .right + .pull-right %small Not implemented yet diff --git a/app/views/snippets/_blob.html.haml b/app/views/snippets/_blob.html.haml index ed518300ac05615a7464406189c090c843df3101..017a33b34f343fdba99596bf6cdecaff8e8a2ca6 100644 --- a/app/views/snippets/_blob.html.haml +++ b/app/views/snippets/_blob.html.haml @@ -3,7 +3,7 @@ %i.icon-file %strong= @snippet.file_name %span.options - = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn very_small", target: "_blank" + = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-tiny", target: "_blank" .file_content.code - unless @snippet.content.empty? %div{class: user_color_scheme_class} diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml index baef737b56524c8a42da9fa64184f23106f7c1d2..77162cdcde338beb2c54be39ac765e6961b31a15 100644 --- a/app/views/snippets/_form.html.haml +++ b/app/views/snippets/_form.html.haml @@ -4,7 +4,7 @@ .snippet-form-holder = form_for [@project, @snippet] do |f| -if @snippet.errors.any? - .alert-message.block-message.error + .alert.alert-error %ul - @snippet.errors.full_messages.each do |msg| %li= msg @@ -27,10 +27,10 @@ = f.hidden_field :content, class: 'snippet-file-content' .form-actions - = f.submit 'Save', class: "save-btn btn" + = f.submit 'Save', class: "btn-save btn" = link_to "Cancel", project_snippets_path(@project), class: " btn" - unless @snippet.new_record? - .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" + .pull-right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" :javascript diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index 7b8f94de7dd5d3a25b79e8bb53444b51722ab8c4..28a533d238f3024428368b054419505c2ff8bd96 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -5,7 +5,7 @@ %small share code pastes with others out of git repository - if can? current_user, :write_snippet, @project - = link_to new_project_snippet_path(@project), class: "btn small add_new right", title: "New Snippet" do + = link_to new_project_snippet_path(@project), class: "btn btn-small add_new pull-right", title: "New Snippet" do Add new snippet %br %table diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 02022185f9abded1d05ebb6cbabfc13a76bac008..e6bcd88f830e610f290e7827288076db26860f96 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -4,7 +4,7 @@ = @snippet.title %small= @snippet.file_name - if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user - = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn small right" + = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right" %br %div= render 'blob' diff --git a/app/views/team_members/_form.html.haml b/app/views/team_members/_form.html.haml index a963e462a7868fe990934f135d84463bbdf0641e..05bea2db87e91bbbe399b40882ca0fc1c0860db1 100644 --- a/app/views/team_members/_form.html.haml +++ b/app/views/team_members/_form.html.haml @@ -1,11 +1,11 @@ %h3.page_title = "New Team member(s)" %hr -= form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f| - -if @team_member.errors.any? - .alert-message.block-message.error += form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f| + -if @user_project_relation.errors.any? + .alert.alert-error %ul - - @team_member.errors.full_messages.each do |msg| + - @user_project_relation.errors.full_messages.each do |msg| %li= msg %h6 1. Choose people you want in the team @@ -16,8 +16,8 @@ %h6 2. Set access level for them .clearfix = f.label :project_access, "Project Access" - .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen" + .input= select_tag :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), class: "project-access-select chosen" .actions - = f.submit 'Save', class: "btn save-btn" - = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn" + = f.submit 'Add users', class: "btn btn-create" + = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml index 8082f47fca8b14f1dada1eb81d606f36f3993e97..3df2caed64a0cfbb65a6a1f00625ed918dc2c7b1 100644 --- a/app/views/team_members/_show.html.haml +++ b/app/views/team_members/_show.html.haml @@ -1,28 +1,28 @@ - user = member.user - allow_admin = can? current_user, :admin_project, @project -%li{id: dom_id(member), class: "team_member_row user_#{user.id}"} +%li{id: dom_id(user), class: "team_member_row user_#{user.id}"} .row .span6 - = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do + = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do = image_tag gravatar_icon(user.email, 40), class: "avatar s32" - = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do + = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do %strong= truncate(user.name, lenght: 40) %br %small.cgray= user.email - .span5.right + .span5.pull-right - if allow_admin .left - = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f| + = form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f| = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2" - .right + .pull-right - if current_user == user %span.btn.disabled This is you! - if @project.namespace_owner == user - %span.btn.disabled.success Owner + %span.btn.disabled Owner - elsif user.blocked %span.btn.disabled.blocked Blocked - elsif allow_admin - = link_to project_team_member_path(project_id: @project, id: member.id), confirm: remove_from_team_message(@project, member), method: :delete, class: "very_small btn danger" do + = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do %i.icon-minus.icon-white diff --git a/app/views/team_members/_show_team.html.haml b/app/views/team_members/_show_team.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..f1555f0b87b050a3a8070b5d4110f81f3545324c --- /dev/null +++ b/app/views/team_members/_show_team.html.haml @@ -0,0 +1,15 @@ +- team = team_rel.user_team +- allow_admin = can? current_user, :admin_team_member, @project +%li{id: dom_id(team), class: "user_team_row team_#{team.id}"} + .row + .span6 + %strong= link_to team.name, team_path(team), title: team.name, class: "dark" + %br + %small.cgray Members: #{team.members.count} + + .span5.pull-right + .pull-right + - if allow_admin + .left + = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove small" do + %i.icon-minus.icon-white diff --git a/app/views/team_members/_teams.html.haml b/app/views/team_members/_teams.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..156fdd1befa13df95e66b40e15e2991e94e3fff8 --- /dev/null +++ b/app/views/team_members/_teams.html.haml @@ -0,0 +1,16 @@ +- grouper_project_teams(@project).each do |access, teams| + .ui-box + %h5.title + = UserTeam.access_roles.key(access).pluralize + %small= teams.size + %ul.well-list + - teams.sort_by(&:team_name).each do |tofr| + = render(partial: 'team_members/show_team', locals: {team_rel: tofr}) + + +:javascript + $(function(){ + $('.repo-access-select, .project-access-select').live("change", function() { + $(this.form).submit(); + }); + }) diff --git a/app/views/team_members/create.js.haml b/app/views/team_members/create.js.haml index d5ae5d0cc435582e285a7a0a6c1c050ac3d2079a..b7dff35a269a39287d6b8af50156bda82e2ce21b 100644 --- a/app/views/team_members/create.js.haml +++ b/app/views/team_members/create.js.haml @@ -1,4 +1,4 @@ -- if @team_member.valid? +- if @user_project_relation.valid? :plain $("#new_team_member").hide("slide", { direction: "right" }, 150, function(){ $("#team-table").show("slide", { direction: "left" }, 150, function() { diff --git a/app/views/team_members/import.html.haml b/app/views/team_members/import.html.haml index de82f4162481fa52af1923817a4225995f8392b2..d6c81befd08865726d99e1e997bf21e0c84e6fa1 100644 --- a/app/views/team_members/import.html.haml +++ b/app/views/team_members/import.html.haml @@ -4,7 +4,7 @@ = "Import team from another project" %hr %p.slead - Read more about team import #{link_to "here", '#', class: 'vlink'}. + Read more about project team import #{link_to "here", '#', class: 'vlink'}. = form_tag apply_import_project_team_members_path(@project), method: 'post' do %p.slead Choose project you want to use as team source: .padded @@ -12,6 +12,6 @@ .input= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "chosen xxlarge", required: true) .actions - = submit_tag 'Import', class: "btn save-btn" - = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn" + = submit_tag 'Import', class: "btn btn-save" + = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" diff --git a/app/views/team_members/index.html.haml b/app/views/team_members/index.html.haml index e413c81bb6c2fe2ee25bf8ba94c520319c5914d2..3264f58cb32d0bf1817924fcc39fb3bb19e2ac53 100644 --- a/app/views/team_members/index.html.haml +++ b/app/views/team_members/index.html.haml @@ -1,20 +1,33 @@ = render "projects/project_head" %h3.page_title Team Members - (#{@project.users_projects.count}) + (#{@project.users.count}) %small Read more about project permissions %strong= link_to "here", help_permissions_path, class: "vlink" - if can? current_user, :admin_team_member, @project - %span.right - = link_to import_project_team_members_path(@project), class: "btn small grouped", title: "Import team from another project" do + %span.pull-right + = link_to import_project_team_members_path(@project), class: "btn btn-small grouped", title: "Import team from another project" do Import team from another project - = link_to new_project_team_member_path(@project), class: "btn success small grouped", title: "New Team Member" do + = link_to available_project_teams_path(@project), class: "btn btn-small grouped", title: "Assign project to team of users" do + Assign project to Team of users + = link_to new_project_team_member_path(@project), class: "btn btn-primary small grouped", title: "New Team Member" do New Team Member -%hr +%hr .clearfix %div.team-table = render partial: "team_members/team", locals: {project: @project} + + +%h3.page_title + Assigned teams + (#{@project.user_teams.count}) + +%hr + +.clearfix +%div.team-table + = render partial: "team_members/teams", locals: {project: @project} diff --git a/app/views/team_members/show.html.haml b/app/views/team_members/show.html.haml index 4008e8bd23e6c17bbf2351be96ea5b1966fbe5ba..192948eff7da0134ff4aae79d9596f7458f03312 100644 --- a/app/views/team_members/show.html.haml +++ b/app/views/team_members/show.html.haml @@ -1,14 +1,13 @@ - allow_admin = can? current_user, :admin_project, @project -- user = @team_member.user .team_member_show - if can? current_user, :admin_project, @project - = link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn danger" + = link_to 'Remove from team', project_team_member_path(@project, @member), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove pull-right" .profile_avatar_holder - = image_tag gravatar_icon(user.email, 60), class: "borders" + = image_tag gravatar_icon(@member.email, 60), class: "borders" %h3.page_title - = user.name - %small (@#{user.username}) + = @member.name + %small (@#{@member.username}) %hr .back_link @@ -21,34 +20,34 @@ %table.lite %tr %td Email - %td= mail_to user.email + %td= mail_to @member.email %tr %td Skype - %td= user.skype - - unless user.linkedin.blank? + %td= @member.skype + - unless @member.linkedin.blank? %tr %td LinkedIn - %td= user.linkedin - - unless user.twitter.blank? + %td= @member.linkedin + - unless @member.twitter.blank? %tr %td Twitter - %td= user.twitter - - unless user.bio.blank? + %td= @member.twitter + - unless @member.bio.blank? %tr %td Bio - %td= user.bio + %td= @member.bio .span6 %table.lite %tr %td Member since - %td= @team_member.created_at.stamp("Aug 21, 2011") + %td= @user_project_relation.created_at.stamp("Aug 21, 2011") %tr %td Project Access: %small (#{link_to "read more", help_permissions_path, class: "vlink"}) %td - = form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f| - = f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin + = form_for(@user_project_relation, as: :team_member, url: project_team_member_path(@project, @member)) do |f| + = f.select :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), {}, class: "project-access-select", disabled: !allow_admin %hr = render @events :javascript diff --git a/app/views/team_members/update.js.haml b/app/views/team_members/update.js.haml index 6d7f88160deefa8e2a0c1c078f65fab9ab59112d..c68fe9574a2526a4fb136c22bd2d1428919aad4b 100644 --- a/app/views/team_members/update.js.haml +++ b/app/views/team_members/update.js.haml @@ -1,6 +1,6 @@ -- if @team_member.valid? +- if @user_project_relation.valid? :plain - $("##{dom_id(@team_member)}").effect("highlight", {color: "#529214"}, 1000);; + $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#529214"}, 1000);; - else :plain - $("##{dom_id(@team_member)}").effect("highlight", {color: "#D12F19"}, 1000);; + $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#D12F19"}, 1000);; diff --git a/app/views/teams/_filter.html.haml b/app/views/teams/_filter.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..f461fcad42e41dbb00faea920ef584103946f486 --- /dev/null +++ b/app/views/teams/_filter.html.haml @@ -0,0 +1,33 @@ += form_tag team_filter_path(entity), method: 'get' do + %fieldset.dashboard-search-filter + = search_field_tag "search", params[:search], { placeholder: 'Search', class: 'search-text-input' } + = button_tag type: 'submit', class: 'btn' do + %i.icon-search + + %fieldset + %legend Status: + %ul.nav.nav-pills.nav-stacked + %li{class: ("active" if !params[:status])} + = link_to team_filter_path(entity, status: nil) do + Open + %li{class: ("active" if params[:status] == 'closed')} + = link_to team_filter_path(entity, status: 'closed') do + Closed + %li{class: ("active" if params[:status] == 'all')} + = link_to team_filter_path(entity, status: 'all') do + All + + %fieldset + %legend Projects: + %ul.nav.nav-pills.nav-stacked + - @projects.each do |project| + - unless entities_per_project(project, entity).zero? + %li{class: ("active" if params[:project_id] == project.id.to_s)} + = link_to team_filter_path(entity, project_id: project.id) do + = project.name_with_namespace + %small.pull-right= entities_per_project(project, entity) + + %fieldset + %hr + = link_to "Reset", team_filter_path(entity), class: 'btn pull-right' + diff --git a/app/views/teams/_projects.html.haml b/app/views/teams/_projects.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5677255b15be9b602671e7a0c862032481c096e1 --- /dev/null +++ b/app/views/teams/_projects.html.haml @@ -0,0 +1,22 @@ +.projects_box + %h5.title + Projects + %small + (#{projects.count}) + - if can? current_user, :manage_user_team, @team + %span.pull-right + = link_to new_team_project_path(@team), class: "btn btn-tiny info" do + %i.icon-plus + Assign Project + %ul.well-list + - if projects.blank? + %p.nothing_here_message This team has no projects yet + - projects.each do |project| + %li + = link_to project_path(project), class: dom_class(project) do + %strong.well-title= truncate(project.name, length: 25) + %span.arrow + → + %span.last_activity + %strong Last activity: + %span= project_last_activity(project) diff --git a/app/views/teams/edit.html.haml b/app/views/teams/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..3435583600d2ccbe5f29f93e9f3f4a6e985b1a18 --- /dev/null +++ b/app/views/teams/edit.html.haml @@ -0,0 +1,20 @@ +%h3.page_title= "Edit Team #{@team.name}" +%hr += form_for @team, url: team_path(@team) do |f| + - if @team.errors.any? + .alert.alert-error + %span= @team.errors.full_messages.first + .clearfix + = f.label :name do + Team name is + .input + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" + + .clearfix + = f.label :path do + Team path is + .input + = f.text_field :path, placeholder: "opensource", class: "xxlarge left" + .form-actions + = f.submit 'Save team changes', class: "btn btn-primary" + = link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn btn-remove pull-right" diff --git a/app/views/teams/issues.html.haml b/app/views/teams/issues.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..c6a68c37b9c9edeca02b441ae1019cf819030e47 --- /dev/null +++ b/app/views/teams/issues.html.haml @@ -0,0 +1,23 @@ +%h3.page_title + Issues + %small (in Team projects assigned to Team members) + %small.pull-right #{@issues.total_count} issues + +%hr +.row + .span3 + = render 'filter', entity: 'issue' + .span9 + - if @issues.any? + - @issues.group_by(&:project).each do |group| + %div.ui-box + - @project = group[0] + %h5.title + = link_to_project @project + %ul.well-list.issues_table + - group[1].each do |issue| + = render(partial: 'issues/show', locals: {issue: issue}) + %hr + = paginate @issues, theme: "gitlab" + - else + %p.nothing_here_message Nothing to show here diff --git a/app/views/teams/members/_form.html.haml b/app/views/teams/members/_form.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..c22ee78305fab97f9ce5c48269d863269c85aa83 --- /dev/null +++ b/app/views/teams/members/_form.html.haml @@ -0,0 +1,20 @@ += form_tag admin_team_member_path(@team, @member), method: :put do + -if @member.errors.any? + .alert.alert-error + %ul + - @member.errors.full_messages.each do |msg| + %li= msg + + .clearfix + %label Default access for Team projects: + .input + = select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3" + .clearfix + %label Team admin? + .input + = check_box_tag :group_admin, true, @team.admin?(@member) + + %br + .actions + = submit_tag 'Save', class: "btn btn-save" + = link_to 'Cancel', :back, class: "btn" diff --git a/app/views/teams/members/_show.html.haml b/app/views/teams/members/_show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..6cddb8e482663c34f9368bf0fcf1904954c0d74e --- /dev/null +++ b/app/views/teams/members/_show.html.haml @@ -0,0 +1,31 @@ +- user = member.user +- allow_admin = can? current_user, :manage_user_team, @team +%li{id: dom_id(member), class: "team_member_row user_#{user.id}"} + .row + .span5 + = link_to user_path(user.username), title: user.name, class: "dark" do + = image_tag gravatar_icon(user.email, 40), class: "avatar s32" + = link_to user_path(user.username), title: user.name, class: "dark" do + %strong= truncate(user.name, lenght: 40) + %br + %small.cgray= user.email + + .span6.pull-right + - if allow_admin + .left.span2 + = form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f| + = f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium project-access-select span2" + .left.span2 + %span + = check_box_tag :group_admin, true, @team.admin?(user) + Admin access + .pull-right + - if current_user == user + %span.btn.disabled This is you! + - if @team.owner == user + %span.btn.disabled.btn-success Owner + - elsif user.blocked + %span.btn.disabled.blocked Blocked + - elsif allow_admin + = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do + %i.icon-minus.icon-white diff --git a/app/views/teams/members/_team.html.haml b/app/views/teams/members/_team.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d8afc1fa3711e87b0d9400c561dbf20b06f69676 --- /dev/null +++ b/app/views/teams/members/_team.html.haml @@ -0,0 +1,16 @@ +- grouped_user_team_members(@team).each do |access, members| + .ui-box + %h5.title + = Project.access_options.key(access).pluralize + %small= members.size + %ul.well-list + - members.sort_by(&:user_name).each do |up| + = render(partial: 'teams/members/show', locals: {member: up}) + + +:javascript + $(function(){ + $('.repo-access-select, .project-access-select').live("change", function() { + $(this.form).submit(); + }); + }) diff --git a/app/views/teams/members/edit.html.haml b/app/views/teams/members/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..375880496abfaceb019f6b9b8908d6e1e0051a67 --- /dev/null +++ b/app/views/teams/members/edit.html.haml @@ -0,0 +1,16 @@ +%h3.page_title + Edit access #{@member.name} in #{@team.name} team + +%hr +%table.zebra-striped + %tr + %td User: + %td= @member.name + %tr + %td Team: + %td= @team.name + %tr + %td Since: + %td= member_since(@team, @member).stamp("Nov 11, 2010") + += render 'form' diff --git a/app/views/teams/members/index.html.haml b/app/views/teams/members/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..87438266cfb75264bf268c3d7d7013a5f9bef5ae --- /dev/null +++ b/app/views/teams/members/index.html.haml @@ -0,0 +1,17 @@ +%h3.page_title + Team Members + (#{@members.count}) + %small + Read more about project permissions + %strong= link_to "here", help_permissions_path, class: "vlink" + + - if can? current_user, :manage_user_team, @team + %span.pull-right + = link_to new_team_member_path(@team), class: "btn btn-primary small grouped", title: "New Team Member" do + New Team Member +%hr + + +.clearfix +%div.team-table + = render partial: "teams/members/team", locals: {project: @team} diff --git a/app/views/teams/members/new.html.haml b/app/views/teams/members/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..083e137e0ae0b6104188bac142f2a9d7487649ce --- /dev/null +++ b/app/views/teams/members/new.html.haml @@ -0,0 +1,28 @@ +%h3.page_title + Team: #{@team.name} + +%fieldset + %legend Members (#{@team.members.count}) + = form_tag team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do + %table#members_list + %thead + %tr + %th User name + %th Default project access + %th Team access + %th + - @team.members.each do |member| + %tr.member + %td + = member.name + %small= "(#{member.email})" + %td= @team.human_default_projects_access(member) + %td= @team.admin?(member) ? "Admin" : "Member" + %td + %tr + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_email), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' + %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } + %td + %span= check_box_tag :group_admin + %span Admin? + %td= submit_tag 'Add User', class: "btn btn-create", id: :add_members_to_team diff --git a/app/views/teams/members/show.html.haml b/app/views/teams/members/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..f760c2dae3a2c405376acc73e29c5cb1d21450e8 --- /dev/null +++ b/app/views/teams/members/show.html.haml @@ -0,0 +1,60 @@ +- allow_admin = can? current_user, :admin_project, @project +- user = @team_member.user + +.team_member_show + - if can? current_user, :admin_project, @project + = link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "pull-right btn btn-remove" + .profile_avatar_holder + = image_tag gravatar_icon(user.email, 60), class: "borders" + %h3.page_title + = user.name + %small (@#{user.username}) + + %hr + .back_link + %br + = link_to project_team_index_path(@project), class: "" do + ← To team list + %br + .row + .span6 + %table.lite + %tr + %td Email + %td= mail_to user.email + %tr + %td Skype + %td= user.skype + - unless user.linkedin.blank? + %tr + %td LinkedIn + %td= user.linkedin + - unless user.twitter.blank? + %tr + %td Twitter + %td= user.twitter + - unless user.bio.blank? + %tr + %td Bio + %td= user.bio + .span6 + %table.lite + %tr + %td Member since + %td= @team_member.created_at.stamp("Aug 21, 2011") + %tr + %td + Project Access: + %small (#{link_to "read more", help_permissions_path, class: "vlink"}) + %td + = form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f| + = f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin + %hr + = render @events +:javascript + $(function(){ + $('.repo-access-select, .project-access-select').live("change", function() { + $(this.form).submit(); + }); + }) + diff --git a/app/views/teams/merge_requests.html.haml b/app/views/teams/merge_requests.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..417d1aa604029e3d0e052f5bdb7902c55d0394b7 --- /dev/null +++ b/app/views/teams/merge_requests.html.haml @@ -0,0 +1,24 @@ +%h3.page_title + Merge Requests + %small (authored by or assigned to Team members) + %small.pull-right #{@merge_requests.total_count} merge requests + +%hr +.row + .span3 + = render 'filter', entity: 'merge_request' + .span9 + - if @merge_requests.any? + - @merge_requests.group_by(&:project).each do |group| + .ui-box + - @project = group[0] + %h5.title + = link_to_project @project + %ul.well-list + - group[1].each do |merge_request| + = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request}) + %hr + = paginate @merge_requests, theme: "gitlab" + + - else + %h3.nothing_here_message Nothing to show here diff --git a/app/views/teams/new.html.haml b/app/views/teams/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..38f61c11c0cac08711fec3e092d980062e09fd58 --- /dev/null +++ b/app/views/teams/new.html.haml @@ -0,0 +1,19 @@ +%h3.page_title New Team +%hr += form_for @team, url: teams_path do |f| + - if @team.errors.any? + .alert.alert-error + %span= @team.errors.full_messages.first + .clearfix + = f.label :name do + Team name is + .input + = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left" + + = f.submit 'Create team', class: "btn btn-create" + %hr + .padded + %ul + %li All created teams are public (users can view who enter into team and which project are assigned for this team) + %li People within a team see only projects they have access to + %li You will be able to assign existing projects for team diff --git a/app/views/teams/projects/_form.html.haml b/app/views/teams/projects/_form.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d2c89b0c36bce601e58a7c535600acf5e5dbbe47 --- /dev/null +++ b/app/views/teams/projects/_form.html.haml @@ -0,0 +1,16 @@ += form_tag team_project_path(@team, @project), method: :put do + -if @project.errors.any? + .alert.alert-error + %ul + - @project.errors.full_messages.each do |msg| + %li= msg + + .clearfix + %label Max access for Team members: + .input + = select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3" + + %br + .actions + = submit_tag 'Save', class: "btn btn-save" + = link_to 'Cancel', :back, class: "btn btn-cancel" diff --git a/app/views/teams/projects/edit.html.haml b/app/views/teams/projects/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..82c7d734815d84de50dc50b37da0dbfa923af78b --- /dev/null +++ b/app/views/teams/projects/edit.html.haml @@ -0,0 +1,6 @@ +%h3.page_title + Edit max access in #{link_to @project.name_with_namespace, @project} for #{link_to(@team.name, team_path(@team))} team + +%hr + += render 'form' diff --git a/app/views/teams/projects/index.html.haml b/app/views/teams/projects/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..696ee29c7786d7fed3ba43aeba2745916ad7bf93 --- /dev/null +++ b/app/views/teams/projects/index.html.haml @@ -0,0 +1,36 @@ +%h3.page_title + Assigned projects (#{@team.projects.count}) + %small + Read more about project permissions + %strong= link_to "here", help_permissions_path, class: "vlink" + + - if current_user.can?(:manage_user_team, @team) && @avaliable_projects.any? + %span.pull-right + = link_to new_team_project_path(@team), class: "btn btn-primary small grouped", title: "New Team Member" do + Assign project to Team + +%hr + +- if @team.projects.present? + %table.projects-table + %thead + %tr + %th Project name + %th Max access + - if current_user.can?(:admin_user_team, @team) + %th.span3 + + - @team.projects.each do |project| + %tr.project + %td + = link_to project.name_with_namespace, project_path(project) + %td + %span= @team.human_max_project_access(project) + + - if current_user.can?(:admin_user_team, @team) + %td.bgred + = link_to 'Edit max access', edit_team_project_path(@team, project), class: "btn btn-small" + = link_to 'Relegate', team_project_path(@team, project), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small" + +- else + %p.nothing_here_message This team has no projects yet diff --git a/app/views/teams/projects/new.html.haml b/app/views/teams/projects/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..3f3671aa0a47859c3376c0ad00397e472fffd9eb --- /dev/null +++ b/app/views/teams/projects/new.html.haml @@ -0,0 +1,23 @@ +%h3.page_title + Team: #{@team.name} + +%fieldset + %legend Projects (#{@team.projects.count}) + = form_tag team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do + %table#projects_list + %thead + %tr + %th Project name + %th Max access + %th + - @team.projects.each do |project| + %tr.project + %td + = link_to project.name_with_namespace, team_project_path(@team, project) + %td + %span= @team.human_max_project_access(project) + %td + %tr + %td= select_tag :project_ids, options_from_collection_for_select(@avaliable_projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' + %td= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" } + %td= submit_tag 'Add Project', class: "btn btn-create", id: :assign_projects_to_team diff --git a/app/views/teams/show.html.haml b/app/views/teams/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d6e80e2a51e23e82e3e53e1a6876ec793fa5d364 --- /dev/null +++ b/app/views/teams/show.html.haml @@ -0,0 +1,28 @@ +.projects + .activities.span8 + = link_to dashboard_path, class: 'btn btn-tiny' do + ← To dashboard + + %span.cgray Events and projects are filtered in scope of team + %hr + - if @events.any? + .content_list + - else + %p.nothing_here_message Projects activity will be displayed here + .loading.hide + .side.span4 + = render "projects", projects: @projects + %div + %span.rss-icon + = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do + = image_tag "rss_ui.png", title: "feed" + %strong News Feed + + %hr + .gitlab-promo + = link_to "Homepage", "http://gitlabhq.com" + = link_to "Blog", "http://blog.gitlabhq.com" + = link_to "@gitlabhq", "https://twitter.com/gitlabhq" + +:javascript + $(function(){ Pager.init(20, true); }); diff --git a/app/views/tree/_blob_actions.html.haml b/app/views/tree/_blob_actions.html.haml index 21334ea1f1652177d038209722ca404d8a45a2dd..0bde968d0e69610e41c3d9cb1bf7739b89a2b222 100644 --- a/app/views/tree/_blob_actions.html.haml +++ b/app/views/tree/_blob_actions.html.haml @@ -1,12 +1,12 @@ .btn-group.tree-btn-group -# only show edit link for text files - if @tree.text? - = link_to "edit", edit_project_tree_path(@project, @id), class: "btn very_small", disabled: !allowed_tree_edit? - = link_to "raw", project_blob_path(@project, @id), class: "btn very_small", target: "_blank" + = link_to "edit", edit_project_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit? + = link_to "raw", project_blob_path(@project, @id), class: "btn btn-tiny", target: "_blank" -# only show normal/blame view links for text files - if @tree.text? - if current_page? project_blame_path(@project, @id) - = link_to "normal view", project_tree_path(@project, @id), class: "btn very_small" + = link_to "normal view", project_tree_path(@project, @id), class: "btn btn-tiny" - else - = link_to "blame", project_blame_path(@project, @id), class: "btn very_small" - = link_to "history", project_commits_path(@project, @id), class: "btn very_small" + = link_to "blame", project_blame_path(@project, @id), class: "btn btn-tiny" + = link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny" diff --git a/app/views/tree/_head.html.haml b/app/views/tree/_head.html.haml index f14526cf23a4dbb440c8cb2aa2dd32ea90952b5d..32c3882400eed8d0858d46be2e1c71c38bb6fee7 100644 --- a/app/views/tree/_head.html.haml +++ b/app/views/tree/_head.html.haml @@ -3,5 +3,5 @@ = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: @path} = nav_link(controller: :tree) do = link_to 'Source', project_tree_path(@project, @ref) - %li.right + %li.pull-right = render "shared/clone_panel" diff --git a/app/views/tree/_tree.html.haml b/app/views/tree/_tree.html.haml index c284295951023e892ee3e69a3db1db2da3eeee56..29a2ed02d31a1398204205eef0db676148188f30 100644 --- a/app/views/tree/_tree.html.haml +++ b/app/views/tree/_tree.html.haml @@ -24,7 +24,7 @@ %th Name %th Last Update %th Last Commit - %th= link_to "history", project_commits_path(@project, @id), class: "btn very_small right" + %th= link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny pull-right" - if tree.up_dir? %tr.tree-item diff --git a/app/views/tree/edit.html.haml b/app/views/tree/edit.html.haml index adee68a00f8f8955b1f5b9c676714eb133308cb1..81918e509b83c96d1e7215c2d71c56768fdb2102 100644 --- a/app/views/tree/edit.html.haml +++ b/app/views/tree/edit.html.haml @@ -10,7 +10,7 @@ %strong= @ref %span.options .btn-group.tree-btn-group - = link_to "Cancel", project_tree_path(@project, @id), class: "btn very_small cancel-btn", confirm: "Are you sure?" + = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-tiny btn-cancel", confirm: "Are you sure?" .file_content.code %pre#editor= @tree.data @@ -27,7 +27,7 @@ .message to branch %strong= @ref - = link_to "Cancel", project_tree_path(@project, @id), class: "btn cancel-btn", confirm: "Are you sure?" + = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", confirm: "Are you sure?" :javascript var ace_mode = "#{@tree.language.try(:ace_mode)}"; diff --git a/app/views/users/_profile.html.haml b/app/views/users/_profile.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..4981aaba0ac39ac0e9c5963606d08cabd5b5432f --- /dev/null +++ b/app/views/users/_profile.html.haml @@ -0,0 +1,23 @@ +.ui-box + %h5.title + Profile + %ul.well-list + %li + %strong Email + %span.pull-right= mail_to @user.email + - unless @user.skype.blank? + %li + %strong Skype + %span.pull-right= @user.skype + - unless @user.linkedin.blank? + %li + %strong LinkedIn + %span.pull-right= @user.linkedin + - unless @user.twitter.blank? + %li + %strong Twitter + %span.pull-right= @user.twitter + - unless @user.bio.blank? + %li + %strong Bio + %span.pull-right= @user.bio diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..73f635f3a088f8beff8ec8895db70edee2b8d554 --- /dev/null +++ b/app/views/users/_projects.html.haml @@ -0,0 +1,20 @@ +.ui-box + %h5.title Projects + %ul.well-list + - @projects.each do |project| + %li + = link_to project_path(project), class: dom_class(project) do + - if project.namespace + = project.namespace.human_name + \/ + %strong.well-title + = truncate(project.name, length: 45) + %span.pull-right.light + - if project.owner == @user + %i.icon-wrench + - tm = project.team.get_tm(@user.id) + - if tm + = tm.project_access_human +%p.light + %i.icon-wrench + – user is a project owner diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..969fed9ce537f02d127f8838651ed82d8b7941f4 --- /dev/null +++ b/app/views/users/show.html.haml @@ -0,0 +1,21 @@ +.row + .span8 + %h3.page_title + = image_tag gravatar_icon(@user.email, 90), class: "avatar s90" + = @user.name + - if @user == current_user + .pull-right + = link_to profile_path, class: 'btn btn-small' do + %i.icon-edit + Edit Profile + %br + %small @#{@user.username} + %br + %small member since #{@user.created_at.stamp("Nov 12, 2031")} + .clearfix + %hr + %h5 Recent events + = render @events + .span4 + = render 'profile' + = render 'projects' diff --git a/app/views/wikis/_form.html.haml b/app/views/wikis/_form.html.haml index 9eb2a571fe5c0bef1cc77913c25c534adf3e58b9..7758b129b07aec99ac6a9b88e11f1ae487adb360 100644 --- a/app/views/wikis/_form.html.haml +++ b/app/views/wikis/_form.html.haml @@ -23,5 +23,5 @@ = f.label :content .input= f.text_area :content, class: 'span8 js-gfm-input' .actions - = f.submit 'Save', class: "save-btn btn" - = link_to "Cancel", project_wiki_path(@project, :index), class: "btn cancel-btn" + = f.submit 'Save', class: "btn-save btn" + = link_to "Cancel", project_wiki_path(@project, :index), class: "btn btn-cancel" diff --git a/app/views/wikis/edit.html.haml b/app/views/wikis/edit.html.haml index 8f6b457f22a833f6716c148aaf357e03f1ceda95..9e221aba47dfcbb24c1ce8e2f8160c7126221cbe 100644 --- a/app/views/wikis/edit.html.haml +++ b/app/views/wikis/edit.html.haml @@ -2,7 +2,7 @@ %hr = render 'form' -.right +.pull-right - if can? current_user, :admin_wiki, @project - = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn small danger" do + = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn btn-small btn-remove" do Delete this page \ No newline at end of file diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml index d3bd58bbeec2f46363627f95018b1bb5286fc429..7ff8b5cc01ec9e94faaee58d9f74bec4db998542 100644 --- a/app/views/wikis/show.html.haml +++ b/app/views/wikis/show.html.haml @@ -1,12 +1,12 @@ %h3.page_title = @wiki.title - %span.right - = link_to pages_project_wikis_path(@project), class: "btn small grouped" do + %span.pull-right + = link_to pages_project_wikis_path(@project), class: "btn btn-small grouped" do Pages - if can? current_user, :write_wiki, @project - = link_to history_project_wiki_path(@project, @wiki), class: "btn small grouped" do + = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do History - = link_to edit_project_wiki_path(@project, @wiki), class: "btn small grouped" do + = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do %i.icon-edit Edit %br diff --git a/app/workers/gitolite_worker.rb b/app/workers/gitolite_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..bff7a8c6a6f257a342112f43e9f75a8629617555 --- /dev/null +++ b/app/workers/gitolite_worker.rb @@ -0,0 +1,10 @@ +class GitoliteWorker + include Sidekiq::Worker + include Gitolited + + sidekiq_options queue: :gitolite + + def perform(action, *arg) + gitolite.send(action, *arg) + end +end diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index e74379a65dd367dea54d9e52ad4cee9ba0082c9a..6e2d0e7aba22b485c1f6402919a0044b9fdbdc52 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -4,23 +4,38 @@ class PostReceive sidekiq_options queue: :post_receive def perform(repo_path, oldrev, newrev, ref, identifier) - repo_path.gsub!(Gitlab.config.gitolite.repos_path.to_s, "") + + if repo_path.start_with?(Gitlab.config.gitolite.repos_path.to_s) + repo_path.gsub!(Gitlab.config.gitolite.repos_path.to_s, "") + else + Gitlab::GitLogger.error("POST-RECEIVE: Check gitlab.yml config for correct gitolite.repos_path variable. \"#{Gitlab.config.gitolite.repos_path}\" does not match \"#{repo_path}\"") + end + repo_path.gsub!(/.git$/, "") repo_path.gsub!(/^\//, "") project = Project.find_with_namespace(repo_path) - return false if project.nil? + + if project.nil? + Gitlab::GitLogger.error("POST-RECEIVE: Triggered hook for non-existing project with full path \"#{repo_path} \"") + return false + end # Ignore push from non-gitlab users user = if identifier.eql? Gitlab.config.gitolite.admin_key - email = project.repository.commit(newrev).author.email rescue nil - User.find_by_email(email) if email - elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier) - User.find_by_email(identifier) - else - Key.find_by_identifier(identifier).try(:user) + email = project.repository.commit(newrev).author.email rescue nil + User.find_by_email(email) if email + elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier) + User.find_by_email(identifier) + elsif identifier =~ /key/ + key_id = identifier.gsub("key-", "") + Key.find_by_id(key_id).try(:user) + end + + unless user + Gitlab::GitLogger.error("POST-RECEIVE: Triggered hook for non-existing user \"#{identifier} \"") + return false end - return false unless user project.trigger_post_receive(oldrev, newrev, ref, user) end diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..9f9b9b1df5f672249af8397439d2c3eccc2f25c2 --- /dev/null +++ b/app/workers/project_web_hook_worker.rb @@ -0,0 +1,9 @@ +class ProjectWebHookWorker + include Sidekiq::Worker + + sidekiq_options queue: :project_web_hook + + def perform(hook_id, data) + WebHook.find(hook_id).execute data + end +end diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql index 0e873d2b8fb45ca5713caa5680f160d2dfd8d166..2bc0884f099beb374a2c20f280636bf093428fd3 100644 --- a/config/database.yml.postgresql +++ b/config/database.yml.postgresql @@ -6,7 +6,7 @@ production: encoding: unicode database: gitlabhq_production pool: 5 - username: postgres + username: gitlab password: # host: localhost # port: 5432 diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index b2dccbe3d4c9a4062300d26a3b0ddd9aa3332d07..02118cbd9501ea2d8f59ca81c8799e379fc86a09 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -29,7 +29,7 @@ gitlab: # Email address used in the "From" field in mails sent by GitLab email_from: gitlab@localhost - # Email address of your support contanct (default: same as email_from) + # Email address of your support contact (default: same as email_from) support_email: support@localhost ## Project settings @@ -96,7 +96,7 @@ omniauth: # GitLab Satellites satellites: # Relative paths are relative to Rails.root (default: tmp/repo_satellites/) - path: /home/gitlab/gitlab-satellites/ + path: /home/git/gitlab-satellites/ ## Backup settings backup: @@ -105,8 +105,7 @@ backup: ## Gitolite settings gitolite: - admin_uri: git@localhost:gitolite-admin - # repos_path must not be a symlink + # REPOS_PATH MUST NOT BE A SYMLINK!!! repos_path: /home/git/repositories/ hooks_path: /home/git/.gitolite/hooks/ admin_key: gitlab diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index a1afa5b22c4f434b5fd1ff65ace51c387f7fa307..ebcbbf91636fa74221f409352f708cd68e3d69ad 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -51,7 +51,7 @@ Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" Settings.gitlab['support_email'] ||= Settings.gitlab.email_from Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) -Settings.gitlab['user'] ||= 'gitlab' +Settings.gitlab['user'] ||= 'git' Settings.gitlab['signup_enabled'] ||= false Settings['gravatar'] ||= Settingslogic.new({}) @@ -63,10 +63,10 @@ Settings['gitolite'] ||= Settingslogic.new({}) Settings.gitolite['admin_key'] ||= 'gitlab' Settings.gitolite['admin_uri'] ||= 'git@localhost:gitolite-admin' Settings.gitolite['config_file'] ||= 'gitolite.conf' -Settings.gitolite['hooks_path'] ||= '/home/git/share/gitolite/hooks/' +Settings.gitolite['hooks_path'] = File.expand_path(Settings.gitolite['hooks_path'] || '/home/git/share/gitolite/hooks/', Rails.root) Settings.gitolite['receive_pack'] = true if Settings.gitolite['receive_pack'].nil? Settings.gitolite['upload_pack'] = true if Settings.gitolite['upload_pack'].nil? -Settings.gitolite['repos_path'] ||= '/home/git/repositories/' +Settings.gitolite['repos_path'] = File.expand_path(Settings.gitolite['repos_path'] || '/home/git/repositories/', Rails.root) Settings.gitolite['ssh_host'] ||= (Settings.gitlab.host || 'localhost') Settings.gitolite['ssh_port'] ||= 22 Settings.gitolite['ssh_user'] ||= 'git' diff --git a/config/initializers/5_backend.rb b/config/initializers/5_backend.rb index 85f747ac334033ad1dd464dfc2cfbb60e3da1739..73436608c933aa0b82558f4d2c69d01513571740 100644 --- a/config/initializers/5_backend.rb +++ b/config/initializers/5_backend.rb @@ -1,5 +1,5 @@ # GIT over HTTP require Rails.root.join("lib", "gitlab", "backend", "grack_auth") -# GITOLITE backend -require Rails.root.join("lib", "gitlab", "backend", "gitolite") +# GIT over SSH +require Rails.root.join("lib", "gitlab", "backend", "shell") diff --git a/config/routes.rb b/config/routes.rb index 00ff3f637521a792ab603588a3cc85ae545a080f..d6432b86007593f4c64199ea63b0bbe134c9474e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,7 +21,7 @@ Gitlab::Application.routes.draw do project_root: Gitlab.config.gitolite.repos_path, upload_pack: Gitlab.config.gitolite.upload_pack, receive_pack: Gitlab.config.gitolite.receive_pack - }), at: '/', constraints: lambda { |request| /[-\/\w\.-]+\.git\//.match(request.path_info) } + }), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) } # # Help @@ -49,13 +49,14 @@ Gitlab::Application.routes.draw do # Admin Area # namespace :admin do - resources :users do + resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do member do put :team_update put :block put :unblock end end + resources :groups, constraints: { id: /[^\/]+/ } do member do put :project_update @@ -63,18 +64,31 @@ Gitlab::Application.routes.draw do delete :remove_project end end - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, except: [:new, :create] do - member do - get :team - put :team_update + + resources :teams, constraints: { id: /[^\/]+/ } do + scope module: :teams do + resources :members, only: [:edit, :update, :destroy, :new, :create] + resources :projects, only: [:edit, :update, :destroy, :new, :create], constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } end end - resources :team_members, only: [:edit, :update, :destroy] + resources :hooks, only: [:index, :create, :destroy] do get :test end + resource :logs, only: [:show] resource :resque, controller: 'resque', only: [:show] + + resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, except: [:new, :create] do + member do + get :team + put :team_update + end + scope module: :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do + resources :members, only: [:edit, :update, :destroy] + end + end + root to: "dashboard#index" end @@ -97,19 +111,25 @@ Gitlab::Application.routes.draw do end resources :keys + match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ } + + # # Dashboard Area # - get "dashboard" => "dashboard#index" - get "dashboard/issues" => "dashboard#issues" - get "dashboard/merge_requests" => "dashboard#merge_requests" - + resource :dashboard, controller: "dashboard" do + member do + get :projects + get :issues + get :merge_requests + end + end # # Groups Area # - resources :groups, constraints: { id: /[^\/]+/ }, only: [:show] do + resources :groups, constraints: { id: /[^\/]+/ } do member do get :issues get :merge_requests @@ -119,6 +139,20 @@ Gitlab::Application.routes.draw do end end + # + # Teams Area + # + resources :teams, constraints: { id: /[^\/]+/ } do + member do + get :issues + get :merge_requests + end + scope module: :teams do + resources :members, only: [:index, :new, :create, :edit, :update, :destroy] + resources :projects, only: [:index, :new, :create, :edit, :update, :destroy], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ } + end + end + resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create] devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations } @@ -129,7 +163,6 @@ Gitlab::Application.routes.draw do resources :projects, constraints: { id: /[a-zA-Z.0-9_\-\/]+/ }, except: [:new, :create, :index], path: "/" do member do get "wall" - get "graph" get "files" end @@ -139,6 +172,7 @@ Gitlab::Application.routes.draw do resources :compare, only: [:index, :create] resources :blame, only: [:show], constraints: {id: /.+/} resources :blob, only: [:show], constraints: {id: /.+/} + resources :graph, only: [:show], constraints: {id: /.+/} match "/compare/:from...:to" => "compare#show", as: "compare", :via => [:get, :post], constraints: {from: /.+/, to: /.+/} @@ -235,6 +269,18 @@ Gitlab::Application.routes.draw do end end + scope module: :projects do + resources :teams, only: [] do + collection do + get :available + post :assign + end + member do + delete :resign + end + end + end + resources :notes, only: [:index, :create, :destroy] do collection do post :preview @@ -242,5 +288,5 @@ Gitlab::Application.routes.draw do end end - root to: "dashboard#index" + root to: "dashboard#show" end diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index 4852cd65daaddad1b2a8021e994656651bda3a2b..29b7146c7dff736bcf1e16498b4d9815194999c0 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -2,7 +2,7 @@ # note that config/gitlab.yml web path should also be changed # ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab" -app_dir = "/home/gitlab/gitlab/" +app_dir = "/home/git/gitlab/" worker_processes 2 working_directory app_dir diff --git a/db/migrate/20121219183753_create_user_teams.rb b/db/migrate/20121219183753_create_user_teams.rb new file mode 100644 index 0000000000000000000000000000000000000000..65c4d053982cadce45061357bc18b95c89dbf541 --- /dev/null +++ b/db/migrate/20121219183753_create_user_teams.rb @@ -0,0 +1,11 @@ +class CreateUserTeams < ActiveRecord::Migration + def change + create_table :user_teams do |t| + t.string :name + t.string :path + t.integer :owner_id + + t.timestamps + end + end +end diff --git a/db/migrate/20121220064104_create_user_team_project_relationships.rb b/db/migrate/20121220064104_create_user_team_project_relationships.rb new file mode 100644 index 0000000000000000000000000000000000000000..8eb654c8728ba6d1a06308f2b3253d8b4df7a6c8 --- /dev/null +++ b/db/migrate/20121220064104_create_user_team_project_relationships.rb @@ -0,0 +1,11 @@ +class CreateUserTeamProjectRelationships < ActiveRecord::Migration + def change + create_table :user_team_project_relationships do |t| + t.integer :project_id + t.integer :user_team_id + t.integer :greatest_access + + t.timestamps + end + end +end diff --git a/db/migrate/20121220064453_create_user_team_user_relationships.rb b/db/migrate/20121220064453_create_user_team_user_relationships.rb new file mode 100644 index 0000000000000000000000000000000000000000..7783b0ae4328ff8d1b1d7958f3708d0b6749a29b --- /dev/null +++ b/db/migrate/20121220064453_create_user_team_user_relationships.rb @@ -0,0 +1,12 @@ +class CreateUserTeamUserRelationships < ActiveRecord::Migration + def change + create_table :user_team_user_relationships do |t| + t.integer :user_id + t.integer :user_team_id + t.boolean :group_admin + t.integer :permission + + t.timestamps + end + end +end diff --git a/db/migrate/20130125090214_add_user_permissions.rb b/db/migrate/20130125090214_add_user_permissions.rb new file mode 100644 index 0000000000000000000000000000000000000000..38b5f439a2d22687348aacc87ad919c51440ca6f --- /dev/null +++ b/db/migrate/20130125090214_add_user_permissions.rb @@ -0,0 +1,11 @@ +class AddUserPermissions < ActiveRecord::Migration + def up + add_column :users, :can_create_group, :boolean, default: true, null: false + add_column :users, :can_create_team, :boolean, default: true, null: false + end + + def down + remove_column :users, :can_create_group + remove_column :users, :can_create_team + end +end diff --git a/db/migrate/20130131070232_remove_private_flag_from_project.rb b/db/migrate/20130131070232_remove_private_flag_from_project.rb new file mode 100644 index 0000000000000000000000000000000000000000..5754db115589d6442a1c5a1bc9991b68c2b8937c --- /dev/null +++ b/db/migrate/20130131070232_remove_private_flag_from_project.rb @@ -0,0 +1,9 @@ +class RemovePrivateFlagFromProject < ActiveRecord::Migration + def up + remove_column :projects, :private_flag + end + + def down + add_column :projects, :private_flag, :boolean, default: true, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 4b3a224360948f006c9ba83bda8cceda11171bb1..0f07d2bc8c54061a309b3b9cdf0a07016a6f62c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130110172407) do +ActiveRecord::Schema.define(:version => 20130131070232) do create_table "events", :force => true do |t| t.string "target_type" @@ -147,7 +147,6 @@ ActiveRecord::Schema.define(:version => 20130110172407) do t.text "description" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false - t.boolean "private_flag", :default => true, :null => false t.integer "creator_id" t.string "default_branch" t.boolean "issues_enabled", :default => true, :null => false @@ -213,6 +212,31 @@ ActiveRecord::Schema.define(:version => 20130110172407) do t.string "name" end + create_table "user_team_project_relationships", :force => true do |t| + t.integer "project_id" + t.integer "user_team_id" + t.integer "greatest_access" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "user_team_user_relationships", :force => true do |t| + t.integer "user_id" + t.integer "user_team_id" + t.boolean "group_admin" + t.integer "permission" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "user_teams", :force => true do |t| + t.string "name" + t.string "path" + t.integer "owner_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "users", :force => true do |t| t.string "email", :default => "", :null => false t.string "encrypted_password", :default => "", :null => false @@ -242,6 +266,8 @@ ActiveRecord::Schema.define(:version => 20130110172407) do t.string "extern_uid" t.string "provider" t.string "username" + t.boolean "can_create_group", :default => true, :null => false + t.boolean "can_create_team", :default => true, :null => false end add_index "users", ["admin"], :name => "index_users_on_admin" diff --git a/doc/api/README.md b/doc/api/README.md index 477429c9fa09a64c8e1d9ef8cd3eddd9c4e139ad..0618db7e369b6e5c11428fce6672d696b05526f1 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -10,7 +10,7 @@ If no, or an invalid, `private_token` is provided then an error message will be } ``` -API requests should be prefixed with `api` and the API version. The API version is equal to the GitLab major version number, which is defined in `lib/api.rb`. +API requests should be prefixed with `api` and the API version. The API version is defined in `lib/api.rb`. Example of a valid API request: @@ -32,6 +32,7 @@ When listing resources you can pass the following parameters: + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) + [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md) + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) ++ [Groups](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/groups.md) + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) + [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md) + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) diff --git a/doc/api/groups.md b/doc/api/groups.md new file mode 100644 index 0000000000000000000000000000000000000000..00a7387c76fb29b9030a22b413f996c207bccdf8 --- /dev/null +++ b/doc/api/groups.md @@ -0,0 +1,45 @@ +## List project groups + +Get a list of groups. (As user: my groups, as admin: all groups) + +``` +GET /groups +``` + +```json +[ + { + "id": 1, + "name": "Foobar Group", + "path": "foo-bar", + "owner_id": 18 + } +] +``` + +## Details of group + +Get all details of a group. + +``` +GET /groups/:id +``` + +Parameters: + ++ `id` (required) - The ID of a group + +## New group + +Create a new project group. Available only for admin + +``` +POST /groups +``` + +Parameters: ++ `name` (required) - Email ++ `path` - Password + +Will return created group with status `201 Created` on success, or `404 Not found` on fail. + diff --git a/doc/api/notes.md b/doc/api/notes.md index bb33efb8c25dfdbb0f93384a2d4d34f27af5608b..a4ba282607643d6f324c9f8767cf337776c50705 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -30,6 +30,19 @@ Parameters: + `id` (required) - The ID of a project +### List merge request notes + +Get a list of merge request notes. + +``` +GET /projects/:id/merge_requests/:merge_request_id/notes +``` + +Parameters: + ++ `id` (required) - The ID of a project ++ `merge_request_id` (required) - The ID of an merge request + ### List issue notes Get a list of issue notes. diff --git a/doc/api/projects.md b/doc/api/projects.md index 411286750f856b9dfa19425bf4bc0f6432d7e7af..82bb0c0d561068393849c36dcc7fc777f045fabc 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -22,6 +22,8 @@ GET /projects "created_at": "2012-05-23T08:00:58Z" }, "private": true, + "path": "rails", + "path_with_namespace": "rails/rails", "issues_enabled": false, "merge_requests_enabled": false, "wall_enabled": true, @@ -42,6 +44,8 @@ GET /projects "created_at": "2012-05-23T08:00:58Z" }, "private": true, + "path": "gitlab", + "path_with_namespace": "randx/gitlab", "issues_enabled": true, "merge_requests_enabled": true, "wall_enabled": true, @@ -78,6 +82,8 @@ Parameters: "created_at": "2012-05-23T08:00:58Z" }, "private": true, + "path": "gitlab", + "path_with_namespace": "randx/gitlab", "issues_enabled": true, "merge_requests_enabled": true, "wall_enabled": true, diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 685797ad78d7a71098f2a6c75b611c71f945c5ce..bc6ca70aa50064045cdd836797a07e1a7abbb50c 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -33,7 +33,8 @@ Parameters: }, "authored_date": "2012-06-27T05:51:39-07:00", "committed_date": "2012-06-28T03:44:20-07:00" - } + }, + "protected": true } ] ``` @@ -73,7 +74,88 @@ Parameters: }, "authored_date": "2012-06-27T05:51:39-07:00", "committed_date": "2012-06-28T03:44:20-07:00" - } + }, + "protected": true +} +``` + +## Protect a project repository branch + +Protect a single project repository branch. + +``` +PUT /projects/:id/repository/branches/:branch/protect +``` + +Parameters: + ++ `id` (required) - The ID of a project ++ `branch` (required) - The name of the branch + +```json +{ + "name": "master", + "commit": { + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "parents": [ + { + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + } + ], + "tree": "46e82de44b1061621357f24c05515327f2795a95", + "message": "add projects API", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "John Smith", + "email": "john@example.com" + }, + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00" + }, + "protected": true +} +``` + +## Unprotect a project repository branch + +Unprotect a single project repository branch. + +``` +PUT /projects/:id/repository/branches/:branch/unprotect +``` + +Parameters: + ++ `id` (required) - The ID of a project ++ `branch` (required) - The name of the branch + +```json +{ + "name": "master", + "commit": { + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "parents": [ + { + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + } + ], + "tree": "46e82de44b1061621357f24c05515327f2795a95", + "message": "add projects API", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "John Smith", + "email": "john@example.com" + }, + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00" + }, + "protected": false } ``` @@ -110,7 +192,8 @@ Parameters: }, "authored_date": "2012-05-28T04:42:42-07:00", "committed_date": "2012-05-28T04:42:42-07:00" - } + }, + "protected": null } ] ``` diff --git a/doc/api/users.md b/doc/api/users.md index 200c0e06e0469cbccf76c19341be138619a75a07..b94d7c0f789d56a926b6cdb3f1d35b23af3539be 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -20,6 +20,8 @@ GET /users "linkedin": "", "twitter": "", "dark_scheme": false, + "extern_uid": "john.smith", + "provider": "provider_name", "theme_id": 1 }, { @@ -34,6 +36,8 @@ GET /users "linkedin": "", "twitter": "", "dark_scheme": true, + "extern_uid": "jack.smith", + "provider": "provider_name", "theme_id": 1 } ] @@ -64,6 +68,8 @@ Parameters: "linkedin": "", "twitter": "", "dark_scheme": false, + "extern_uid": "john.smith", + "provider": "provider_name", "theme_id": 1 } ``` @@ -84,10 +90,47 @@ Parameters: + `linkedin` - Linkedin + `twitter` - Twitter account + `projects_limit` - Number of projects user can create ++ `extern_uid` - External UID ++ `provider` - External provider name ++ `bio` - User's bio Will return created user with status `201 Created` on success, or `404 Not found` on fail. +## User modification +Modify user. Available only for admin + +``` +PUT /users/:id +``` + +Parameters: ++ `email` - Email ++ `username` - Username ++ `name` - Name ++ `password` - Password ++ `skype` - Skype ID ++ `linkedin` - Linkedin ++ `twitter` - Twitter account ++ `projects_limit` - Limit projects wich user can create ++ `extern_uid` - External UID ++ `provider` - External provider name ++ `bio` - User's bio + + +Will return created user with status `200 OK` on success, or `404 Not +found` on fail. + +## User deletion +Delete user. Available only for admin + +``` +DELETE /users/:id +``` + +Will return deleted user with status `200 OK` on success, or `404 Not +found` on fail. + ## Current user Get currently authenticated user. diff --git a/doc/install/installation.md b/doc/install/installation.md index 27c87ec825f737466cca9fe935229b6c7ab39dce..4e9818bcb9ee835204c5381af59e9d6c00917206 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -90,134 +90,75 @@ Install the Bundler Gem: # 3. System Users -Create a user for Git and Gitolite: +Create a `git` user for Gitlab: - sudo adduser \ - --system \ - --shell /bin/sh \ - --gecos 'Git Version Control' \ - --group \ - --disabled-password \ - --home /home/git \ - git + sudo adduser --disabled-login --gecos 'GitLab' git -Create a user for GitLab: +# 4. GitLab shell - sudo adduser --disabled-login --gecos 'GitLab' gitlab - - # Add it to the git group - sudo usermod -a -G git gitlab - - # Generate the SSH key - sudo -u gitlab -H ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa - - -# 4. Gitolite - -Clone GitLab's fork of the Gitolite source code: + # login as git + sudo su git + # go to home directory cd /home/git - sudo -u git -H git clone -b gl-v320 https://github.com/gitlabhq/gitolite.git /home/git/gitolite - -Setup Gitolite with GitLab as its admin: - -**Important Note:** -GitLab assumes *full and unshared* control over this Gitolite installation. - - # Add Gitolite scripts to $PATH - sudo -u git -H mkdir /home/git/bin - sudo -u git -H sh -c 'printf "%b\n%b\n" "PATH=\$PATH:/home/git/bin" "export PATH" >> /home/git/.profile' - sudo -u git -H sh -c 'gitolite/install -ln /home/git/bin' - - # Copy the gitlab user's (public) SSH key ... - sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub - sudo chmod 0444 /home/git/gitlab.pub - - # ... and use it as the admin key for the Gitolite setup - sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gitolite setup -pk /home/git/gitlab.pub" - -Fix the directory permissions for the configuration directory: - - # Make sure the Gitolite config dir is owned by git - sudo chmod 750 /home/git/.gitolite/ - sudo chown -R git:git /home/git/.gitolite/ -Fix the directory permissions for the repositories: + # clone gitlab shell + git clone https://dzaporozhets@dev.gitlab.org/gitlab/gitlab-shell.git - # Make sure the repositories dir is owned by git and it stays that way - sudo chmod -R ug+rwXs,o-rwx /home/git/repositories/ - sudo chown -R git:git /home/git/repositories/ + # setup + cd gitlab-shell + cp config.yml.example config.yml + ./bin/install -## Add domains to list to the list of known hosts - - sudo -u gitlab -H ssh git@localhost - sudo -u gitlab -H ssh git@YOUR_DOMAIN_NAME - sudo -u gitlab -H ssh git@YOUR_GITOLITE_DOMAIN_NAME - - -## Test if everything works so far - - # Clone the admin repo so SSH adds localhost to known_hosts ... - # ... and to be sure your users have access to Gitolite - sudo -u gitlab -H git clone git@localhost:gitolite-admin.git /tmp/gitolite-admin - - # If it succeeded without errors you can remove the cloned repo - sudo rm -rf /tmp/gitolite-admin - -**Important Note:** -If you can't clone the `gitolite-admin` repository: **DO NOT PROCEED WITH INSTALLATION**! -Check the [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) -and make sure you have followed all of the above steps carefully. - # 5. Database -See `doc/install/databases.md` +To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md) . # 6. GitLab - # We'll install GitLab into home directory of the user "gitlab" - cd /home/gitlab + # We'll install GitLab into home directory of the user "git" + cd /home/git ## Clone the Source # Clone GitLab repository - sudo -u gitlab -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab + sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab # Go to gitlab dir - cd /home/gitlab/gitlab + cd /home/git/gitlab # Checkout to stable release - sudo -u gitlab -H git checkout 4-0-stable + sudo -u git -H git checkout 5-0-stable **Note:** -You can change `4-0-stable` to `master` if you want the *bleeding edge* version, but +You can change `5-0-stable` to `master` if you want the *bleeding edge* version, but do so with caution! ## Configure it - cd /home/gitlab/gitlab + cd /home/git/gitlab # Copy the example GitLab config - sudo -u gitlab -H cp config/gitlab.yml.example config/gitlab.yml + sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml # Make sure to change "localhost" to the fully-qualified domain name of your # host serving GitLab where necessary - sudo -u gitlab -H vim config/gitlab.yml + sudo -u git -H vim config/gitlab.yml # Make sure GitLab can write to the log/ and tmp/ directories - sudo chown -R gitlab log/ - sudo chown -R gitlab tmp/ + sudo chown -R git log/ + sudo chown -R git tmp/ sudo chmod -R u+rwX log/ sudo chmod -R u+rwX tmp/ # Make directory for satellites - sudo -u gitlab -H mkdir /home/gitlab/gitlab-satellites + sudo -u git -H mkdir /home/git/gitlab-satellites # Copy the example Unicorn config - sudo -u gitlab -H cp config/unicorn.rb.example config/unicorn.rb + sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb **Important Note:** Make sure to edit both files to match your setup. @@ -225,42 +166,29 @@ Make sure to edit both files to match your setup. ## Configure GitLab DB settings # Mysql - sudo -u gitlab cp config/database.yml.mysql config/database.yml + sudo -u git cp config/database.yml.mysql config/database.yml # PostgreSQL - sudo -u gitlab cp config/database.yml.postgresql config/database.yml + sudo -u git cp config/database.yml.postgresql config/database.yml Make sure to update username/password in config/database.yml. ## Install Gems - cd /home/gitlab/gitlab + cd /home/git/gitlab sudo gem install charlock_holmes --version '0.6.9' # For MySQL (note, the option says "without") - sudo -u gitlab -H bundle install --deployment --without development test postgres + sudo -u git -H bundle install --deployment --without development test postgres # Or for PostgreSQL - sudo -u gitlab -H bundle install --deployment --without development test mysql - -## Configure Git - -GitLab needs to be able to commit and push changes to Gitolite. In order to do -that Git requires a username and email. (We recommend using the same address -used for the `email.from` setting in `config/gitlab.yml`) - - sudo -u gitlab -H git config --global user.name "GitLab" - sudo -u gitlab -H git config --global user.email "gitlab@localhost" - -## Setup GitLab Hooks + sudo -u git -H bundle install --deployment --without development test mysql - sudo cp ./lib/hooks/post-receive /home/git/.gitolite/hooks/common/post-receive - sudo chown git:git /home/git/.gitolite/hooks/common/post-receive ## Initialise Database and Activate Advanced Features - sudo -u gitlab -H bundle exec rake gitlab:setup RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production ## Install Init Script @@ -279,11 +207,11 @@ Make GitLab start on boot: Check if GitLab and its environment is configured correctly: - sudo -u gitlab -H bundle exec rake gitlab:env:info RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production To make sure you didn't miss anything run a more thorough check with: - sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production If all items are green, then congratulations on successfully installing GitLab! However there are still a few steps left. @@ -356,7 +284,7 @@ a different host, you can configure its connection string via the If you are running SSH on a non-standard port, you must change the gitlab user'S SSH config. - # Add to /home/gitlab/.ssh/config + # Add to /home/git/.ssh/config host localhost # Give your setup a name (here: override localhost) user git # Your remote git user port 2222 # Your port number diff --git a/doc/install/structure.md b/doc/install/structure.md index a67e12cc217d9aafb92e223b5452145731d8c985..f580ea159a2a461d5b7b852229caee786215c1ea 100644 --- a/doc/install/structure.md +++ b/doc/install/structure.md @@ -3,37 +3,23 @@ This is the directory structure you will end up with following the instructions in the Installation Guide. |-- home - | |-- gitlab + | |-- git | |-- .ssh | |-- gitlab | |-- gitlab-satellites - | |-- git - | |-- .gitolite - | |-- .ssh - | |-- bin - | |-- gitolite + | |-- gitlab-shell | |-- repositories -**/home/gitlab/.ssh** - Contains the Gitolite admin key GitLab uses to configure Gitolite. +**/home/git/.ssh** -**/home/gitlab/gitlab** +**/home/git/gitlab** This is where GitLab lives. -**/home/gitlab/gitlab-satellites** +**/home/git/gitlab-satellites** Contains a copy of all repositories with a working tree. It's used for merge requests, editing files, etc. -**/home/git/.ssh** - Contains the SSH access configuration managed by Gitolite. - -**/home/git/bin** - Contains Gitolite executables. - -**/home/git/gitolite** - This is where Gitolite lives. - **/home/git/repositories** Holds all your repositories in bare format. This is the place Git uses when you pull/push to your projects. diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md index ee5a8a3b5fba3fcba50df087526202c4d2f96fca..110dbd161f7b63aa07e3349ad6fdf14452a63e4e 100644 --- a/doc/raketasks/maintenance.md +++ b/doc/raketasks/maintenance.md @@ -81,7 +81,7 @@ Config directory owned by git:git? ... yes Config directory access is drwxr-x---? ... yes Repo base directory exists? ... yes Repo base owned by git:git? ... yes -Repo base access is drwsrws---? ... yes +Repo base access is drwxrws---? ... yes Can clone gitolite-admin? ... yes Can commit to gitolite-admin? ... yes post-receive hook exists? ... yes diff --git a/features/admin/teams.feature b/features/admin/teams.feature new file mode 100644 index 0000000000000000000000000000000000000000..6a15fddcdcc88641b22076cfb041fb54eeeed983 --- /dev/null +++ b/features/admin/teams.feature @@ -0,0 +1,70 @@ +Feature: Admin Teams + Background: + Given I sign in as an admin + And Create gitlab user "John" + + Scenario: Create a team + When I visit admin teams page + And I click new team link + And submit form with new team info + Then I should be redirected to team page + And I should see newly created team + + Scenario: Add user to team + When I visit admin teams page + When I have clean "HardCoders" team + And I visit "HardCoders" team page + When I click to "Add members" link + When I select user "John" from user list as "Developer" + And submit form with new team member info + Then I should see "John" in teams members list as "Developer" + + Scenario: Assign team to existing project + When I visit admin teams page + When I have "HardCoders" team with "John" member with "Developer" role + When I have "Shop" project + And I visit "HardCoders" team page + Then I should see empty projects table + When I click to "Add projects" link + When I select project "Shop" with max access "Reporter" + And submit form with new team project info + Then I should see "Shop" project in projects list + When I visit "Shop" project admin page + Then I should see "John" user with role "Reporter" in team table + + Scenario: Add user to team with ptojects + When I visit admin teams page + When I have "HardCoders" team with "John" member with "Developer" role + And "HardCoders" team assigned to "Shop" project with "Developer" max role access + When I have gitlab user "Jimm" + And I visit "HardCoders" team page + Then I should see members table without "Jimm" member + When I click to "Add members" link + When I select user "Jimm" ub team members list as "Master" + And submit form with new team member info + Then I should see "Jimm" in teams members list as "Master" + + Scenario: Remove member from team + Given I have users team "HardCoders" + And gitlab user "John" is a member "HardCoders" team + And gitlab user "Jimm" is a member "HardCoders" team + And "HardCoders" team is assigned to "Shop" project + When I visit admin teams page + When I visit "HardCoders" team admin page + Then I shoould see "John" in members list + And I should see "Jimm" in members list + And I should see "Shop" in projects list + When I click on remove "Jimm" user link + Then I should be redirected to "HardCoders" team admin page + And I should not to see "Jimm" user in members list + + Scenario: Remove project from team + Given I have users team "HardCoders" + And gitlab user "John" is a member "HardCoders" team + And gitlab user "Jimm" is a member "HardCoders" team + And "HardCoders" team is assigned to "Shop" project + When I visit admin teams page + When I visit "HardCoders" team admin page + Then I should see "Shop" project in projects list + When I click on "Relegate" link on "Shop" project + Then I should see projects liston team page without "Shop" project diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature index 759843692b88402d0c3223a73d67e5eff94bd6bb..695148b5cdf4a65db0493df92ffa710db762fb4c 100644 --- a/features/dashboard/dashboard.feature +++ b/features/dashboard/dashboard.feature @@ -16,12 +16,6 @@ Feature: Dashboard And I visit dashboard page Then I should see groups list - Scenario: I should see correct projects count - Given I have group with projects - And group has a projects that does not belongs to me - When I visit dashboard page - Then I should see 1 project at group list - Scenario: I should see last push widget Then I should see last push widget And I click "Create Merge Request" link diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature new file mode 100644 index 0000000000000000000000000000000000000000..17022dab54fa58cddb341358265226f4557db854 --- /dev/null +++ b/features/dashboard/projects.feature @@ -0,0 +1,8 @@ +Feature: Dashboard + Background: + Given I sign in as a user + And I own project "Shop" + And I visit dashboard projects page + + Scenario: I should see issues list + Then I should see projects list diff --git a/features/group/create_group.feature b/features/group/create_group.feature new file mode 100644 index 0000000000000000000000000000000000000000..b77f3599e6a20134bfbc0ff77c86f4287462011b --- /dev/null +++ b/features/group/create_group.feature @@ -0,0 +1,11 @@ +Feature: Groups + Background: + Given I sign in as a user + + Scenario: Create a group from dasboard + Given I have group with projects + And I visit dashboard page + When I click new group link + And submit form with new group info + Then I should be redirected to group page + And I should see newly created group diff --git a/features/group/group.feature b/features/group/group.feature index a4a55a7fc26e5f336ed9de887b99f3b5bff83d41..a48affe8e02ae94c27fe8bb27fc6b68017f370dc 100644 --- a/features/group/group.feature +++ b/features/group/group.feature @@ -24,3 +24,9 @@ Feature: Groups When I visit group people page And I select user "John" from list with role "Reporter" Then I should see user "John" in team list + + Scenario: I should see edit group page + When I visit group settings page + And I change group name + Then I should see new group name + diff --git a/features/steps/admin/admin_active_tab.rb b/features/steps/admin/admin_active_tab.rb index 48ec7bac0d648a9846f8283e1b447086878d3a0f..f14c5f396bedbb433e31452be5e6ded96f7e7941 100644 --- a/features/steps/admin/admin_active_tab.rb +++ b/features/steps/admin/admin_active_tab.rb @@ -4,7 +4,7 @@ class AdminActiveTab < Spinach::FeatureSteps include SharedActiveTab Then 'the active main tab should be Home' do - ensure_active_main_tab('Stats') + ensure_active_main_tab('Home') end Then 'the active main tab should be Projects' do diff --git a/features/steps/admin/admin_teams.rb b/features/steps/admin/admin_teams.rb new file mode 100644 index 0000000000000000000000000000000000000000..5c66b24bccf6c39776a1d94a56539b28a8ed61b0 --- /dev/null +++ b/features/steps/admin/admin_teams.rb @@ -0,0 +1,234 @@ +class AdminTeams < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedActiveTab + include SharedAdmin + + And 'I have own project' do + create :project + end + + And 'Create gitlab user "John"' do + @user = create(:user, :name => "John") + end + + And 'I click new team link' do + click_link "New Team" + end + + And 'submit form with new team info' do + fill_in 'user_team_name', with: 'gitlab' + click_button 'Create team' + end + + Then 'I should be redirected to team page' do + current_path.should == admin_team_path(UserTeam.last) + end + + And 'I should see newly created team' do + page.should have_content "Team: gitlab" + end + + When 'I visit admin teams page' do + visit admin_teams_path + end + + When 'I have clean "HardCoders" team' do + @team = create :user_team, name: "HardCoders", owner: current_user + end + + And 'I visit "HardCoders" team page' do + visit admin_team_path(UserTeam.find_by_name("HardCoders")) + end + + Then 'I should see only me in members table' do + members_list = find("#members_list .member") + members_list.should have_content(current_user.name) + members_list.should have_content(current_user.email) + end + + When 'I select user "John" from user list as "Developer"' do + @user ||= User.find_by_name("John") + within "#team_members" do + select @user.name, :from => "user_ids" + select "Developer", :from => "default_project_access" + end + end + + And 'submit form with new team member info' do + click_button 'add_members_to_team' + end + + Then 'I should see "John" in teams members list as "Developer"' do + @user ||= User.find_by_name("John") + find_in_list("#members_list .member", @user).must_equal true + end + + When 'I visit "John" user admin page' do + pending 'step not implemented' + end + + Then 'I should see "HardCoders" team in teams table' do + pending 'step not implemented' + end + + When 'I have "HardCoders" team with "John" member with "Developer" role' do + @team = create :user_team, name: "HardCoders", owner: current_user + @user ||= User.find_by_name("John") + @team.add_member(@user, UserTeam.access_roles["Developer"], group_admin: false) + end + + When 'I have "Shop" project' do + @project = create :project, name: "Shop" + end + + Then 'I should see empty projects table' do + page.has_no_css?("#projects_list").must_equal true + end + + When 'I select project "Shop" with max access "Reporter"' do + @project ||= Project.find_by_name("Shop") + within "#assign_projects" do + select @project.name, :from => "project_ids" + select "Reporter", :from => "greatest_project_access" + end + + end + + And 'submit form with new team project info' do + click_button 'assign_projects_to_team' + end + + Then 'I should see "Shop" project in projects list' do + project = Project.find_by_name("Shop") + find_in_list("#projects_list .project", project).must_equal true + end + + When 'I visit "Shop" project admin page' do + project = Project.find_by_name("Shop") + visit admin_project_path(project) + end + + And '"HardCoders" team assigned to "Shop" project with "Developer" max role access' do + @team = UserTeam.find_by_name("HardCoders") + @project = create :project, name: "Shop" + @team.assign_to_project(@project, UserTeam.access_roles["Developer"]) + end + + When 'I have gitlab user "Jimm"' do + create :user, name: "Jimm" + end + + Then 'I should see members table without "Jimm" member' do + user = User.find_by_name("Jimm") + find_in_list("#members_list .member", user).must_equal false + end + + When 'I select user "Jimm" ub team members list as "Master"' do + user = User.find_by_name("Jimm") + within "#team_members" do + select user.name, :from => "user_ids" + select "Developer", :from => "default_project_access" + end + end + + Then 'I should see "Jimm" in teams members list as "Master"' do + user = User.find_by_name("Jimm") + find_in_list("#members_list .member", user).must_equal true + end + + Given 'I have users team "HardCoders"' do + @team = create :user_team, name: "HardCoders" + end + + And 'gitlab user "John" is a member "HardCoders" team' do + @team = UserTeam.find_by_name("HardCoders") + @user = User.find_by_name("John") + @user = create :user, name: "John" unless @user + @team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false) + end + + And 'gitlab user "Jimm" is a member "HardCoders" team' do + @team = UserTeam.find_by_name("HardCoders") + @user = User.find_by_name("Jimm") + @user = create :user, name: "Jimm" unless @user + @team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false) + end + + And '"HardCoders" team is assigned to "Shop" project' do + @team = UserTeam.find_by_name("HardCoders") + @project = create :project, name: "Shop" + @team.assign_to_project(@project, UserTeam.access_roles["Developer"]) + end + + When 'I visit "HardCoders" team admin page' do + visit admin_team_path(UserTeam.find_by_name("HardCoders")) + end + + Then 'I shoould see "John" in members list' do + user = User.find_by_name("John") + find_in_list("#members_list .member", user).must_equal true + end + + And 'I should see "Jimm" in members list' do + user = User.find_by_name("Jimm") + find_in_list("#members_list .member", user).must_equal true + end + + And 'I should see "Shop" in projects list' do + project = Project.find_by_name("Shop") + find_in_list("#projects_list .project", project).must_equal true + end + + When 'I click on remove "Jimm" user link' do + user = User.find_by_name("Jimm") + click_link "remove_member_#{user.id}" + end + + Then 'I should be redirected to "HardCoders" team admin page' do + current_path.should == admin_team_path(UserTeam.find_by_name("HardCoders")) + end + + And 'I should not to see "Jimm" user in members list' do + user = User.find_by_name("Jimm") + find_in_list("#members_list .member", user).must_equal false + end + + When 'I click on "Relegate" link on "Shop" project' do + project = Project.find_by_name("Shop") + click_link "relegate_project_#{project.id}" + end + + Then 'I should see projects liston team page without "Shop" project' do + project = Project.find_by_name("Shop") + find_in_list("#projects_list .project", project).must_equal false + end + + Then 'I should see "John" user with role "Reporter" in team table' do + user = User.find_by_name("John") + find_in_list(".team_members", user).must_equal true + end + + When 'I click to "Add members" link' do + click_link "Add members" + end + + When 'I click to "Add projects" link' do + click_link "Add projects" + end + + protected + + def current_team + @team ||= Team.first + end + + def find_in_list(selector, item) + members_list = all(selector) + entered = false + members_list.each do |member_item| + entered = true if member_item.has_content?(item.name) + end + entered + end +end diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 4bcefba76de71b925defd90fdff8f3314f167974..8c13ad0e151b57366c2cc7e144384a3d63cd5639 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -63,6 +63,12 @@ class Dashboard < Spinach::FeatureSteps @project.team << [current_user, :master] end + Then 'I should see projects list' do + @user.authorized_projects.all.each do |project| + page.should have_link project.name_with_namespace + end + end + Then 'I should see groups list' do Group.all.each do |group| page.should have_link group.name diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb index 04d8c874b3ecd59c08fd539f253fb8c75de1ca3c..5cfa4756ac35e4002176a70a3050853b9ae41283 100644 --- a/features/steps/group/group.rb +++ b/features/steps/group/group.rb @@ -64,6 +64,35 @@ class Groups < Spinach::FeatureSteps author: current_user end + When 'I click new group link' do + click_link "New Group" + end + + And 'submit form with new group info' do + fill_in 'group_name', :with => 'Samurai' + click_button "Create group" + end + + Then 'I should see newly created group' do + page.should have_content "Samurai" + page.should have_content "You will only see events from projects in this group" + end + + Then 'I should be redirected to group page' do + current_path.should == group_path(Group.last) + end + + And 'I change group name' do + fill_in 'group_name', :with => 'new-name' + click_button "Save group" + end + + Then 'I should see new group name' do + within ".navbar-gitlab" do + page.should have_content "group: new-name" + end + end + protected def current_group diff --git a/features/steps/profile/profile_active_tab.rb b/features/steps/profile/profile_active_tab.rb index 1924a6fa785bb24b3bd289423d598517fecc79e2..ee9f5f201cfca9509192c15dab16adf906f2baf7 100644 --- a/features/steps/profile/profile_active_tab.rb +++ b/features/steps/profile/profile_active_tab.rb @@ -4,7 +4,7 @@ class ProfileActiveTab < Spinach::FeatureSteps include SharedActiveTab Then 'the active main tab should be Home' do - ensure_active_main_tab('Profile') + ensure_active_main_tab('Home') end Then 'the active main tab should be Account' do diff --git a/features/steps/project/project_active_tab.rb b/features/steps/project/project_active_tab.rb index a5c80353862a7f313641f209fcf87b40148b9a6b..bce67c8296233634f54df21951998ebfc4c6d472 100644 --- a/features/steps/project/project_active_tab.rb +++ b/features/steps/project/project_active_tab.rb @@ -7,7 +7,7 @@ class ProjectActiveTab < Spinach::FeatureSteps # Main Tabs Then 'the active main tab should be Home' do - ensure_active_main_tab(@project.name) + ensure_active_main_tab('Home') end Then 'the active main tab should be Files' do diff --git a/features/steps/project/project_network_graph.rb b/features/steps/project/project_network_graph.rb index 77149bfe2c32231324a5f2cac7e8768b68f0565e..f26deff9367821611cc01f19cc68434495aa970e 100644 --- a/features/steps/project/project_network_graph.rb +++ b/features/steps/project/project_network_graph.rb @@ -14,6 +14,6 @@ class ProjectNetworkGraph < Spinach::FeatureSteps Gitlab::Graph::JsonBuilder.stub(max_count: 10) project = Project.find_by_name("Shop") - visit graph_project_path(project) + visit project_graph_path(project, "master") end end diff --git a/features/steps/project/project_team_management.rb b/features/steps/project/project_team_management.rb index 91b3ffeee9a85cd3391c740a846f1169e2d36c53..19352fe0ab8224018fe5856ccc2103cc2a81f6ef 100644 --- a/features/steps/project/project_team_management.rb +++ b/features/steps/project/project_team_management.rb @@ -24,7 +24,7 @@ class ProjectTeamManagement < Spinach::FeatureSteps select user.name, :from => "user_ids" select "Reporter", :from => "project_access" end - click_button "Save" + click_button "Add users" end Then 'I should see "Mike" in team list as "Reporter"' do diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index 884f2d5fb71b1c8b4cda68b6d589d2947bc11f97..446e3b9a8b3e6589dff7ab5f05e2d5c0e621d749 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -2,7 +2,11 @@ module SharedActiveTab include Spinach::DSL def ensure_active_main_tab(content) - page.find('ul.main_menu li.active').should have_content(content) + if content == "Home" + page.find('ul.main_menu li.active').should have_css('i.icon-home') + else + page.find('ul.main_menu li.active').should have_content(content) + end end def ensure_active_sub_tab(content) diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index 2ae0f1241b8e823b42f9b0f32f657bba44860e07..04862338026cb609c80ac426b1a52fb7fba58b92 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -2,27 +2,27 @@ module SharedDiffNote include Spinach::DSL Given 'I cancel the diff comment' do - within(".diff_file") do + within(".file") do find(".js-close-discussion-note-form").trigger("click") end end Given 'I delete a diff comment' do sleep 1 - within(".diff_file") do + within(".file") do first(".js-note-delete").trigger("click") end end Given 'I haven\'t written any diff comment text' do - within(".diff_file") do + within(".file") do fill_in "note[note]", with: "" end end Given 'I leave a diff comment like "Typo, please fix"' do find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") - within(".diff_file") do + within(".file") do fill_in "note[note]", with: "Typo, please fix" #click_button("Add Comment") find(".js-comment-button").trigger("click") @@ -32,7 +32,7 @@ module SharedDiffNote Given 'I preview a diff comment text like "Should fix it :smile:"' do find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") - within(".diff_file") do + within(".file") do fill_in "note[note]", with: "Should fix it :smile:" find(".js-note-preview-button").trigger("click") end @@ -40,7 +40,7 @@ module SharedDiffNote Given 'I preview another diff comment text like "DRY this up"' do find("#586fb7c4e1add2d4d24e27566ed7064680098646_57_41.line_holder .js-add-diff-note-button").trigger("click") - within(".diff_file") do + within(".file") do fill_in "note[note]", with: "DRY this up" find(".js-note-preview-button").trigger("click") end @@ -55,13 +55,13 @@ module SharedDiffNote end Given 'I write a diff comment like ":-1: I don\'t like this"' do - within(".diff_file") do + within(".file") do fill_in "note[note]", with: ":-1: I don\'t like this" end end Given 'I submit the diff comment' do - within(".diff_file") do + within(".file") do click_button("Add Comment") end end @@ -69,49 +69,49 @@ module SharedDiffNote Then 'I should not see the diff comment form' do - within(".diff_file") do + within(".file") do page.should_not have_css("form.new_note") end end Then 'I should not see the diff comment preview button' do - within(".diff_file") do + within(".file") do page.should have_css(".js-note-preview-button", visible: false) end end Then 'I should not see the diff comment text field' do - within(".diff_file") do + within(".file") do page.should have_css(".js-note-text", visible: false) end end Then 'I should only see one diff form' do - within(".diff_file") do + within(".file") do page.should have_css("form.new_note", count: 1) end end Then 'I should see a diff comment form with ":-1: I don\'t like this"' do - within(".diff_file") do + within(".file") do page.should have_field("note[note]", with: ":-1: I don\'t like this") end end Then 'I should see a diff comment saying "Typo, please fix"' do - within(".diff_file .note") do + within(".file .note") do page.should have_content("Typo, please fix") end end Then 'I should see a discussion reply button' do - within(".diff_file") do + within(".file") do page.should have_link("Reply") end end Then 'I should see a temporary diff comment form' do - within(".diff_file") do + within(".file") do page.should have_css(".js-temp-notes-holder form.new_note") end end @@ -121,37 +121,37 @@ module SharedDiffNote end Then 'I should see an empty diff comment form' do - within(".diff_file") do + within(".file") do page.should have_field("note[note]", with: "") end end Then 'I should see the cancel comment button' do - within(".diff_file form") do + within(".file form") do page.should have_css(".js-close-discussion-note-form", text: "Cancel") end end Then 'I should see the diff comment preview' do - within(".diff_file form") do + within(".file form") do page.should have_css(".js-note-preview", visible: false) end end Then 'I should see the diff comment edit button' do - within(".diff_file") do + within(".file") do page.should have_css(".js-note-edit-button", visible: true) end end Then 'I should see the diff comment preview button' do - within(".diff_file") do + within(".file") do page.should have_css(".js-note-preview-button", visible: true) end end Then 'I should see two separate previews' do - within(".diff_file") do + within(".file") do page.should have_css(".js-note-preview", visible: true, count: 2) page.should have_content("Should fix it") page.should have_content("DRY this up") diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index c046c4e63e68f3100b0636bc351029026972f9d1..a8e68012d05adb10fe7ef83e43fdb8581403d8bd 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -25,6 +25,10 @@ module SharedPaths visit people_group_path(current_group) end + When 'I visit group settings page' do + visit edit_group_path(current_group) + end + # ---------------------------------------- # Dashboard # ---------------------------------------- @@ -33,12 +37,16 @@ module SharedPaths visit dashboard_path end + Given 'I visit dashboard projects page' do + visit projects_dashboard_path + end + Given 'I visit dashboard issues page' do - visit dashboard_issues_path + visit issues_dashboard_path end Given 'I visit dashboard merge requests page' do - visit dashboard_merge_requests_path + visit merge_requests_dashboard_path end Given 'I visit dashboard search page' do @@ -105,6 +113,10 @@ module SharedPaths visit admin_groups_path end + When 'I visit admin teams page' do + visit admin_teams_path + end + # ---------------------------------------- # Generic Project # ---------------------------------------- @@ -133,7 +145,7 @@ module SharedPaths # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650) Gitlab::Graph::JsonBuilder.stub(max_count: 10) - visit graph_project_path(@project) + visit project_graph_path(@project, root_ref) end Given "I visit my project's issues page" do diff --git a/features/steps/userteams/userteams.rb b/features/steps/userteams/userteams.rb new file mode 100644 index 0000000000000000000000000000000000000000..be83b4bac05ad9c9bfd0990cd6f385ee8cfa6cea --- /dev/null +++ b/features/steps/userteams/userteams.rb @@ -0,0 +1,254 @@ +class Userteams < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + + When 'I do not have teams with me' do + UserTeam.with_member(current_user).destroy_all + end + + Then 'I should see dashboard page without teams info block' do + page.has_no_css?(".teams-box").must_equal true + end + + When 'I have teams with my membership' do + team = create :user_team, owner: current_user + team.add_member(current_user, UserTeam.access_roles["Master"], true) + end + + Then 'I should see dashboard page with teams information block' do + page.should have_css(".teams-box") + end + + When 'exist user teams' do + team = create :user_team + team.add_member(current_user, UserTeam.access_roles["Master"], true) + end + + And 'I click on "All teams" link' do + click_link("All Teams") + end + + Then 'I should see "All teams" page' do + current_path.should == teams_path + end + + And 'I should see exist teams in teams list' do + team = UserTeam.last + find_in_list(".teams_list tr", team).must_equal true + end + + When 'I click to "New team" link' do + click_link("New Team") + end + + And 'I submit form with new team info' do + fill_in 'name', with: 'gitlab' + click_button 'Create team' + end + + Then 'I should be redirected to new team page' do + team = UserTeam.last + current_path.should == team_path(team) + end + + When 'I have teams with projects and members' do + team = create :user_team, owner: current_user + @project = create :project + team.add_member(current_user, UserTeam.access_roles["Master"], true) + team.assign_to_project(@project, UserTeam.access_roles["Master"]) + @event = create(:closed_issue_event, project: @project) + end + + When 'I visit team page' do + visit team_path(UserTeam.last) + end + + Then 'I should see projects list' do + page.should have_css(".projects_box") + projects_box = find(".projects_box") + projects_box.should have_content(@project.name) + end + + And 'project from team has issues assigned to me' do + team = UserTeam.last + team.projects.each do |project| + project.issues << create(:issue, assignee: current_user) + end + end + + When 'I visit team issues page' do + team = UserTeam.last + visit issues_team_path(team) + end + + Then 'I should see issues from this team assigned to me' do + team = UserTeam.last + team.projects.each do |project| + project.issues.assigned(current_user).each do |issue| + page.should have_content issue.title + end + end + end + + Given 'I have team with projects and members' do + team = create :user_team, owner: current_user + project = create :project + user = create :user + team.add_member(current_user, UserTeam.access_roles["Master"], true) + team.add_member(user, UserTeam.access_roles["Developer"], false) + team.assign_to_project(project, UserTeam.access_roles["Master"]) + end + + Given 'project from team has issues assigned to teams members' do + team = UserTeam.last + team.projects.each do |project| + team.members.each do |member| + project.issues << create(:issue, assignee: member) + end + end + end + + Then 'I should see issues from this team assigned to teams members' do + team = UserTeam.last + team.projects.each do |project| + team.members.each do |member| + project.issues.assigned(member).each do |issue| + page.should have_content issue.title + end + end + end + end + + Given 'project from team has merge requests assigned to me' do + team = UserTeam.last + team.projects.each do |project| + team.members.each do |member| + 3.times { project.merge_requests << create(:merge_request, assignee: member) } + end + end + end + + When 'I visit team merge requests page' do + team = UserTeam.last + visit merge_requests_team_path(team) + end + + Then 'I should see merge requests from this team assigned to me' do + team = UserTeam.last + team.projects.each do |project| + team.members.each do |member| + project.issues.assigned(member).each do |merge_request| + page.should have_content merge_request.title + end + end + end + end + + Given 'project from team has merge requests assigned to team members' do + team = UserTeam.last + team.projects.each do |project| + team.members.each do |member| + 3.times { project.merge_requests << create(:merge_request, assignee: member) } + end + end + end + + Then 'I should see merge requests from this team assigned to me' do + team = UserTeam.last + team.projects.each do |project| + team.members.each do |member| + project.issues.assigned(member).each do |merge_request| + page.should have_content merge_request.title + end + end + end + end + + Given 'I have new user "John"' do + create :user, name: "John" + end + + When 'I visit team people page' do + team = UserTeam.last + visit team_members_path(team) + end + + And 'I select user "John" from list with role "Reporter"' do + user = User.find_by_name("John") + within "#team_members" do + select user.name, :from => "user_ids" + select "Reporter", :from => "default_project_access" + end + click_button "Add" + end + + Then 'I should see user "John" in team list' do + user = User.find_by_name("John") + team_members_list = find(".team-table") + team_members_list.should have_content user.name + end + + And 'I have my own project without teams' do + @project = create :project, namespace: current_user.namespace + end + + And 'I visit my team page' do + team = UserTeam.where(owner_id: current_user.id).last + visit team_path(team) + end + + When 'I click on link "Projects"' do + click_link "Projects" + end + + And 'I click link "Assign project to Team"' do + click_link "Assign project to Team" + end + + Then 'I should see form with my own project in avaliable projects list' do + projects_select = find("#project_ids") + projects_select.should have_content(@project.name) + end + + When 'I submit form with selected project and max access' do + within "#assign_projects" do + select @project.name_with_namespace, :from => "project_ids" + select "Reporter", :from => "greatest_project_access" + end + click_button "Add" + end + + Then 'I should see my own project in team projects list' do + projects = find(".projects-table") + projects.should have_content(@project.name) + end + + When 'I click link "New Team Member"' do + click_link "New Team Member" + end + + protected + + def current_team + @user_team ||= UserTeam.first + end + + def project + current_team.projects.first + end + + def assigned_to_user key, user + project.send(key).where(assignee_id: user) + end + + def find_in_list(selector, item) + members_list = all(selector) + entered = false + members_list.each do |member_item| + entered = true if member_item.has_content?(item.name) + end + entered + end + +end diff --git a/features/support/env.rb b/features/support/env.rb index be10ad1b8b9806882fd607ea1384ee23b4f5fa25..c19ca3088cbb0496753710804bc61f96db9ba7c9 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -9,19 +9,13 @@ require 'spinach/capybara' require 'sidekiq/testing/inline' -%w(gitolite_stub stubbed_repository valid_commit).each do |f| +%w(stubbed_repository valid_commit).each do |f| require Rails.root.join('spec', 'support', f) end Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file} -# -# Stub gitolite -# -include GitoliteStub - WebMock.allow_net_connect! - # # JS driver # @@ -50,6 +44,4 @@ Spinach.hooks.before_run do RSpec::Mocks::setup self include FactoryGirl::Syntax::Methods - - stub_gitolite! end diff --git a/features/teams/team.feature b/features/teams/team.feature new file mode 100644 index 0000000000000000000000000000000000000000..9255e0daadb42ebcd260776dcdc06dd4c2479317 --- /dev/null +++ b/features/teams/team.feature @@ -0,0 +1,69 @@ +Feature: UserTeams + Background: + Given I sign in as a user + And I own project "Shop" + And project "Shop" has push event + + Scenario: No teams, no dashboard info block + When I do not have teams with me + And I visit dashboard page + Then I should see dashboard page without teams info block + + Scenario: I should see teams info block + When I have teams with my membership + And I visit dashboard page + Then I should see dashboard page with teams information block + + Scenario: I should can create new team + When I have teams with my membership + And I visit dashboard page + When I click to "New team" link + And I submit form with new team info + Then I should be redirected to new team page + + Scenario: I should see team dashboard list + When I have teams with projects and members + When I visit team page + Then I should see projects list + + Scenario: I should see team issues list + Given I have team with projects and members + And project from team has issues assigned to me + When I visit team issues page + Then I should see issues from this team assigned to me + + Scenario: I should see teams members issues list + Given I have team with projects and members + Given project from team has issues assigned to teams members + When I visit team issues page + Then I should see issues from this team assigned to teams members + + Scenario: I should see team merge requests list + Given I have team with projects and members + Given project from team has merge requests assigned to me + When I visit team merge requests page + Then I should see merge requests from this team assigned to me + + Scenario: I should see teams members merge requests list + Given I have team with projects and members + Given project from team has merge requests assigned to team members + When I visit team merge requests page + Then I should see merge requests from this team assigned to me + + Scenario: I should add user to projects in Team + Given I have team with projects and members + Given I have new user "John" + When I visit team people page + When I click link "New Team Member" + And I select user "John" from list with role "Reporter" + Then I should see user "John" in team list + + Scenario: I should assign my team to my own project + Given I have team with projects and members + And I have my own project without teams + And I visit my team page + When I click on link "Projects" + And I click link "Assign project to Team" + Then I should see form with my own project in avaliable projects list + When I submit form with selected project and max access + Then I should see my own project in team projects list diff --git a/lib/api.rb b/lib/api.rb index f58b82ff98ebb99cfba84e027e573937ca3c4ca6..d9dce7c70ccb648f153e6db30cbe87681f11af3a 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -11,7 +11,8 @@ module Gitlab format :json error_format :json helpers APIHelpers - + + mount Groups mount Users mount Projects mount Issues @@ -19,5 +20,6 @@ module Gitlab mount Session mount MergeRequests mount Notes + mount Internal end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 80e2954a3448b5d34e8eda720f3fff7a1d88c9d5..c1873d87b55fc9216e6542c21133620fb8a9c65c 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -2,7 +2,7 @@ module Gitlab module Entities class User < Grape::Entity expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter, - :dark_scheme, :theme_id, :blocked, :created_at + :dark_scheme, :theme_id, :blocked, :created_at, :extern_uid, :provider end class UserBasic < Grape::Entity @@ -21,6 +21,7 @@ module Gitlab expose :id, :name, :description, :default_branch expose :owner, using: Entities::UserBasic expose :private_flag, as: :private + expose :path, :path_with_namespace expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at expose :namespace end @@ -31,8 +32,22 @@ module Gitlab end end + class Group < Grape::Entity + expose :id, :name, :path, :owner_id + end + + class GroupDetail < Group + expose :projects, using: Entities::Project + end + + class RepoObject < Grape::Entity expose :name, :commit + expose :protected do |repo, options| + if options[:project] + options[:project].protected_branch? repo.name + end + end end class RepoCommit < Grape::Entity diff --git a/lib/api/groups.rb b/lib/api/groups.rb new file mode 100644 index 0000000000000000000000000000000000000000..a67caef0bc56f7b348fc7af1e3f5238af49cfbfa --- /dev/null +++ b/lib/api/groups.rb @@ -0,0 +1,56 @@ +module Gitlab + # groups API + class Groups < Grape::API + before { authenticate! } + + resource :groups do + # Get a groups list + # + # Example Request: + # GET /groups + get do + if current_user.admin + @groups = paginate Group + else + @groups = paginate current_user.groups + end + present @groups, with: Entities::Group + end + + # Create group. Available only for admin + # + # Parameters: + # name (required) - Name + # path (required) - Path + # Example Request: + # POST /groups + post do + authenticated_as_admin! + attrs = attributes_for_keys [:name, :path] + @group = Group.new(attrs) + @group.owner = current_user + + if @group.save + present @group, with: Entities::Group + else + not_found! + end + end + + # Get a single group, with containing projects + # + # Parameters: + # id (required) - The ID of a group + # Example Request: + # GET /groups/:id + get ":id" do + @group = Group.find(params[:id]) + if current_user.admin or current_user.groups.include? @group + present @group, with: Entities::GroupDetail + else + not_found! + end + end + end + end +end diff --git a/lib/api/internal.rb b/lib/api/internal.rb new file mode 100644 index 0000000000000000000000000000000000000000..3e5e3a478ba1d34176ac197ecd2007f73820b5ee --- /dev/null +++ b/lib/api/internal.rb @@ -0,0 +1,49 @@ +module Gitlab + # Internal access API + class Internal < Grape::API + namespace 'internal' do + # + # Check if ssh key has access to project code + # + get "/allowed" do + key = Key.find(params[:key_id]) + project = Project.find_with_namespace(params[:project]) + git_cmd = params[:action] + + if key.is_deploy_key + project == key.project && git_cmd == 'git-upload-pack' + else + user = key.user + action = case git_cmd + when 'git-upload-pack' + then :download_code + when 'git-receive-pack' + then + if project.protected_branch?(params[:ref]) + :push_code_to_protected_branches + else + :push_code + end + end + + user.can?(action, project) + end + end + + # + # Discover user by ssh key + # + get "/discover" do + key = Key.find(params[:key_id]) + present key.user, with: Entities::User + end + + get "/check" do + { + api_version: '3' + } + end + end + end +end + diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 4613db54578ad8de224297566c2b27bdd099a2ec..70344d6e3813a2098f14f9f30dd6428375491de8 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -3,7 +3,7 @@ module Gitlab class Notes < Grape::API before { authenticate! } - NOTEABLE_TYPES = [Issue, Snippet] + NOTEABLE_TYPES = [Issue, MergeRequest, Snippet] resource :projects do # Get a list of project wall notes diff --git a/lib/api/projects.rb b/lib/api/projects.rb index cbef1ed3b5071719134279616d0ef14ba3b0b602..a16243aa8228d34cbb69ad0ab9789b446e4ffb0d 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -218,7 +218,7 @@ module Gitlab # Example Request: # GET /projects/:id/repository/branches get ":id/repository/branches" do - present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject + present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project end # Get a single branch @@ -230,7 +230,43 @@ module Gitlab # GET /projects/:id/repository/branches/:branch get ":id/repository/branches/:branch" do @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } - present @branch, with: Entities::RepoObject + present @branch, with: Entities::RepoObject, project: user_project + end + + # Protect a single branch + # + # Parameters: + # id (required) - The ID of a project + # branch (required) - The name of the branch + # Example Request: + # PUT /projects/:id/repository/branches/:branch/protect + put ":id/repository/branches/:branch/protect" do + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } + protected = user_project.protected_branches.find_by_name(@branch.name) + + unless protected + user_project.protected_branches.create(:name => @branch.name) + end + + present @branch, with: Entities::RepoObject, project: user_project + end + + # Unprotect a single branch + # + # Parameters: + # id (required) - The ID of a project + # branch (required) - The name of the branch + # Example Request: + # PUT /projects/:id/repository/branches/:branch/unprotect + put ":id/repository/branches/:branch/unprotect" do + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } + protected = user_project.protected_branches.find_by_name(@branch.name) + + if protected + protected.destroy + end + + present @branch, with: Entities::RepoObject, project: user_project end # Get a project repository tags diff --git a/lib/api/users.rb b/lib/api/users.rb index 140c20f6bd21cb8e6cb54e3d5852b8d409074aac..7ea90c75e9ee6732321c1e13b60e847edf98e211 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -34,11 +34,14 @@ module Gitlab # linkedin - Linkedin # twitter - Twitter account # projects_limit - Number of projects user can create + # extern_uid - External authentication provider UID + # provider - External provider + # bio - Bio # Example Request: # POST /users post do authenticated_as_admin! - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username] + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] user = User.new attrs, as: :admin if user.save present user, with: Entities::User @@ -46,6 +49,48 @@ module Gitlab not_found! end end + + # Update user. Available only for admin + # + # Parameters: + # email - Email + # name - Name + # password - Password + # skype - Skype ID + # linkedin - Linkedin + # twitter - Twitter account + # projects_limit - Limit projects wich user can create + # extern_uid - External authentication provider UID + # provider - External provider + # bio - Bio + # Example Request: + # PUT /users/:id + put ":id" do + authenticated_as_admin! + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] + user = User.find_by_id(params[:id]) + + if user && user.update_attributes(attrs) + present user, with: Entities::User + else + not_found! + end + end + + # Delete user. Available only for admin + # + # Example Request: + # DELETE /users/:id + delete ":id" do + authenticated_as_admin! + user = User.find_by_id(params[:id]) + + if user + user.destroy + else + not_found! + end + end end resource :user do diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 270a0aaa87ac260df33655d76394af802a47948e..976ac01820429b285a215200e42e7e7641bfeae7 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -50,11 +50,14 @@ module ExtractsPath return pair unless @project + # Remove relative_url_root from path + input.gsub!(/^#{Gitlab.config.gitlab.relative_url_root}/, "") # Remove project, actions and all other staff from path input.gsub!(/^\/#{Regexp.escape(@project.path_with_namespace)}/, "") - input.gsub!(/^\/(tree|commits|blame|blob|refs)\//, "") # remove actions + input.gsub!(/^\/(tree|commits|blame|blob|refs|graph)\//, "") # remove actions input.gsub!(/\?.*$/, "") # remove stamps suffix input.gsub!(/.atom$/, "") # remove rss feed + input.gsub!(/.json$/, "") # remove json suffix input.gsub!(/\/edit$/, "") # remove edit route part if input.match(/^([[:alnum:]]{40})(.+)/) diff --git a/lib/gitlab/backend/gitolite.rb b/lib/gitlab/backend/gitolite.rb deleted file mode 100644 index 3b8a2090f737a072d1fe9c984e609a842bdaa836..0000000000000000000000000000000000000000 --- a/lib/gitlab/backend/gitolite.rb +++ /dev/null @@ -1,56 +0,0 @@ -require_relative 'gitolite_config' - -module Gitlab - class Gitolite - class AccessDenied < StandardError; end - - def config - Gitlab::GitoliteConfig.new - end - - def set_key key_id, key_content, projects - config.apply do |config| - config.write_key(key_id, key_content) - config.update_projects(projects) - end - end - - def remove_key key_id, projects - config.apply do |config| - config.rm_key(key_id) - config.update_projects(projects) - end - end - - def update_repository project - config.update_project!(project) - end - - def move_repository(old_repo, project) - config.apply do |config| - config.clean_repo(old_repo) - config.update_project(project) - end - end - - def remove_repository project - config.destroy_project!(project) - end - - def url_to_repo path - Gitlab.config.gitolite.ssh_path_prefix + "#{path}.git" - end - - def enable_automerge - config.admin_all_repo! - end - - def update_repositories projects - config.apply do |config| - config.update_projects(projects) - end - end - - alias_method :create_repository, :update_repository - end -end diff --git a/lib/gitlab/backend/gitolite_config.rb b/lib/gitlab/backend/gitolite_config.rb deleted file mode 100644 index 7d59ddae0c85bc74697866437d2c0f60b7082ff9..0000000000000000000000000000000000000000 --- a/lib/gitlab/backend/gitolite_config.rb +++ /dev/null @@ -1,242 +0,0 @@ -require 'gitolite' -require 'timeout' -require 'fileutils' - -module Gitlab - class GitoliteConfig - class PullError < StandardError; end - class PushError < StandardError; end - class BrokenGitolite < StandardError; end - - attr_reader :config_tmp_dir, :ga_repo, :conf - - def config_tmp_dir - @config_tmp_dir ||= Rails.root.join('tmp',"gitlabhq-gitolite-#{Time.now.to_i}") - end - - def ga_repo - @ga_repo ||= ::Gitolite::GitoliteAdmin.new( - File.join(config_tmp_dir,'gitolite'), - conf: Gitlab.config.gitolite.config_file - ) - end - - def apply - Timeout::timeout(30) do - File.open(Rails.root.join('tmp', "gitlabhq-gitolite.lock"), "w+") do |f| - begin - # Set exclusive lock - # to prevent race condition - f.flock(File::LOCK_EX) - - # Pull gitolite-admin repo - # in tmp dir before do any changes - pull(config_tmp_dir) - - # Build ga_repo object and @conf - # to access gitolite-admin configuration - @conf = ga_repo.config - - # Do any changes - # in gitolite-admin - # config here - yield(self) - - # Save changes in - # gitolite-admin repo - # before push it - ga_repo.save - - # Push gitolite-admin repo - # to apply all changes - push(config_tmp_dir) - ensure - # Remove tmp dir - # removing the gitolite folder first is important to avoid - # NFS issues. - FileUtils.rm_rf(File.join(config_tmp_dir, 'gitolite')) - - # Remove parent tmp dir - FileUtils.rm_rf(config_tmp_dir) - - # Unlock so other task can access - # gitolite configuration - f.flock(File::LOCK_UN) - end - end - end - rescue PullError => ex - log("Pull error -> " + ex.message) - raise Gitolite::AccessDenied, ex.message - - rescue PushError => ex - log("Push error -> " + " " + ex.message) - raise Gitolite::AccessDenied, ex.message - - rescue BrokenGitolite => ex - log("Gitolite error -> " + " " + ex.message) - raise Gitolite::AccessDenied, ex.message - - rescue Exception => ex - log(ex.class.name + " " + ex.message) - raise Gitolite::AccessDenied.new("gitolite timeout") - end - - def log message - Gitlab::GitLogger.error(message) - end - - def destroy_project(project) - FileUtils.rm_rf(project.repository.path_to_repo) - conf.rm_repo(project.path_with_namespace) - end - - def clean_repo repo_name - conf.rm_repo(repo_name) - end - - def destroy_project!(project) - apply do |config| - config.destroy_project(project) - end - end - - def write_key(id, key) - File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f| - f.write(key.gsub(/\n/,'')) - end - end - - def rm_key(user) - key_path = File.join(config_tmp_dir, 'gitolite/keydir', "#{user}.pub") - ga_key = ::Gitolite::SSHKey.from_file(key_path) - ga_repo.rm_key(ga_key) - end - - # update or create - def update_project(project) - repo = update_project_config(project, conf) - conf.add_repo(repo, true) - end - - def update_project!( project) - apply do |config| - config.update_project(project) - end - end - - # Updates many projects and uses project.path_with_namespace as the repo path - # An order of magnitude faster than update_project - def update_projects(projects) - projects.each do |project| - repo = update_project_config(project, conf) - conf.add_repo(repo, true) - end - end - - def update_project_config(project, conf) - repo_name = project.path_with_namespace - - repo = if conf.has_repo?(repo_name) - conf.get_repo(repo_name) - else - ::Gitolite::Config::Repo.new(repo_name) - end - - name_readers = project.team.repository_readers - name_writers = project.team.repository_writers - name_masters = project.team.repository_masters - - pr_br = project.protected_branches.map(&:name).join("$ ") - - repo.clean_permissions - - # Deny access to protected branches for writers - unless name_writers.blank? || pr_br.blank? - repo.add_permission("-", pr_br.strip + "$ ", name_writers) - end - - # Add read permissions - repo.add_permission("R", "", name_readers) unless name_readers.blank? - - # Add write permissions - repo.add_permission("RW+", "", name_writers) unless name_writers.blank? - repo.add_permission("RW+", "", name_masters) unless name_masters.blank? - - # Add sharedRepository config - repo.set_git_config("core.sharedRepository", "0660") - - repo - end - - # Enable access to all repos for gitolite admin. - # We use it for accept merge request feature - def admin_all_repo - owner_name = Gitlab.config.gitolite.admin_key - - # @ALL repos premission for gitolite owner - repo_name = "@all" - repo = if conf.has_repo?(repo_name) - conf.get_repo(repo_name) - else - ::Gitolite::Config::Repo.new(repo_name) - end - - repo.add_permission("RW+", "", owner_name) - conf.add_repo(repo, true) - end - - def admin_all_repo! - apply { |config| config.admin_all_repo } - end - - private - - def pull tmp_dir - Dir.mkdir tmp_dir - `git clone #{Gitlab.config.gitolite.admin_uri} #{tmp_dir}/gitolite` - - unless File.exists?(File.join(tmp_dir, 'gitolite', 'conf', 'gitolite.conf')) - raise PullError, "unable to clone gitolite-admin repo" - end - end - - def push tmp_dir - output, status = popen('git add -A') - raise "Git add failed." unless status.zero? - - # git commit returns 0 on success, and 1 if there is nothing to commit - output, status = popen('git commit -m "GitLab"') - raise "Git add failed." unless [0,1].include?(status) - - output, status = popen('git push') - - if output =~ /remote\: FATAL/ - raise BrokenGitolite, output - end - - if status.zero? || output =~ /Everything up\-to\-date/ - return true - else - raise PushError, "unable to push gitolite-admin repo" - end - end - - def popen(cmd) - path = File.join(config_tmp_dir,'gitolite') - vars = { "PWD" => path } - options = { :chdir => path } - - @cmd_output = "" - @cmd_status = 0 - Open3.popen3(vars, cmd, options) do |stdin, stdout, stderr, wait_thr| - @cmd_status = wait_thr.value.exitstatus - @cmd_output << stdout.read - @cmd_output << stderr.read - end - - return @cmd_output, @cmd_status - end - end -end - diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb new file mode 100644 index 0000000000000000000000000000000000000000..50ebfc5b07c0c96c5b7769c149d7c5a84c077db0 --- /dev/null +++ b/lib/gitlab/backend/shell.rb @@ -0,0 +1,50 @@ +module Gitlab + class Shell + class AccessDenied < StandardError; end + + # Init new repository + # + # name - project path with namespace + # + # Ex. + # add_repository("gitlab/gitlab-ci") + # + def add_repository(name) + system("/home/git/gitlab-shell/bin/gitlab-projects add-project #{name}.git") + end + + # Remove repository from file system + # + # name - project path with namespace + # + # Ex. + # remove_repository("gitlab/gitlab-ci") + # + def remove_repository(name) + system("/home/git/gitlab-shell/bin/gitlab-projects rm-project #{name}.git") + end + + # Add new key to gitlab-shell + # + # Ex. + # add_key("key-42", "sha-rsa ...") + # + def add_key(key_id, key_content) + system("/home/git/gitlab-shell/bin/gitlab-keys add-key #{key_id} \"#{key_content}\"") + end + + # Remove ssh key from gitlab shell + # + # Ex. + # remove_key("key-342", "sha-rsa ...") + # + def remove_key(key_id, key_content) + system("/home/git/gitlab-shell/bin/gitlab-keys rm-key #{key_id} \"#{key_content}\"") + end + + + def url_to_repo path + Gitlab.config.gitolite.ssh_path_prefix + "#{path}.git" + end + end +end diff --git a/lib/gitlab/graph/commit.rb b/lib/gitlab/graph/commit.rb index a6bf23a2381899c396895c7ee101bd4eb948aa49..13c8ebc9952b32b1f7f708f8490639e0be9ce3a8 100644 --- a/lib/gitlab/graph/commit.rb +++ b/lib/gitlab/graph/commit.rb @@ -5,12 +5,13 @@ module Gitlab class Commit include ActionView::Helpers::TagHelper - attr_accessor :time, :space, :refs + attr_accessor :time, :space, :refs, :parent_spaces def initialize(commit) @_commit = commit @time = -1 @space = 0 + @parent_spaces = [] end def method_missing(m, *args, &block) @@ -28,6 +29,7 @@ module Gitlab } h[:time] = time h[:space] = space + h[:parent_spaces] = parent_spaces h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? h[:id] = sha h[:date] = date diff --git a/lib/gitlab/graph/json_builder.rb b/lib/gitlab/graph/json_builder.rb index 4a48b3b5675d4ce91fc8e90be88d50558088c649..4b3687e06c36f48abe9718c6daab736629544865 100644 --- a/lib/gitlab/graph/json_builder.rb +++ b/lib/gitlab/graph/json_builder.rb @@ -9,8 +9,9 @@ module Gitlab @max_count ||= 650 end - def initialize project + def initialize project, ref @project = project + @ref = ref @repo = project.repo @ref_cache = {} @@ -52,7 +53,7 @@ module Gitlab # # @return [Array<TimeDate>] list of commit dates corelated with time on commits def index_commits - days, heads = [], [] + days, heads, times = [], [], [] map = {} commits.reverse.each_with_index do |c,i| @@ -60,14 +61,15 @@ module Gitlab days[i] = c.committed_date map[c.id] = c heads += c.refs unless c.refs.nil? + times[i] = c end heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} # sort heads so the master is top and current branches are closer heads.sort! do |a,b| - if a.name == "master" + if a.name == @ref -1 - elsif b.name == "master" + elsif b.name == @ref 1 else b.commit.committed_date <=> a.commit.committed_date @@ -85,9 +87,62 @@ module Gitlab end end + # find parent spaces for not overlap lines + times.each do |c| + c.parent_spaces.concat(find_free_parent_spaces(c, map, times)) + end + days end + def find_free_parent_spaces(commit, map, times) + spaces = [] + + commit.parents.each do |p| + if map.include?(p.id) then + parent = map[p.id] + + range = if commit.time < parent.time then + commit.time..parent.time + else + parent.time..commit.time + end + + space = if commit.space >= parent.space then + find_free_parent_space(range, parent.space, 1, commit.space, times) + else + find_free_parent_space(range, parent.space, -1, parent.space, times) + end + + mark_reserved(range, space) + spaces << space + end + end + + spaces + end + + def find_free_parent_space(range, space_base, space_step, space_default, times) + if is_overlap?(range, times, space_default) then + find_free_space(range, space_base, space_step) + else + space_default + end + end + + def is_overlap?(range, times, overlap_space) + range.each do |i| + if i != range.first && + i != range.last && + times[i].space == overlap_space then + + return true; + end + end + + false + end + # Add space mark on commit and its parents # # @param [Graph::Commit] the commit object. @@ -97,10 +152,9 @@ module Gitlab if leaves.empty? return end - space = find_free_space(leaves, map) - leaves.each{|l| l.space = space} # and mark it as reserved min_time = leaves.last.time + max_space = 1 parents = leaves.last.parents.collect parents.each do |p| if map.include? p.id @@ -108,6 +162,9 @@ module Gitlab if parent.time < min_time min_time = parent.time end + if max_space < parent.space then + max_space = parent.space + end end end if parent_time.nil? @@ -115,6 +172,11 @@ module Gitlab else max_time = parent_time - 1 end + + time_range = leaves.last.time..leaves.first.time + space = find_free_space(time_range, max_space, 2) + leaves.each{|l| l.space = space} + mark_reserved(min_time..max_time, space) # Visit branching chains @@ -132,27 +194,23 @@ module Gitlab end end - def find_free_space(leaves, map) - time_range = leaves.last.time..leaves.first.time + def find_free_space(time_range, space_base, space_step) reserved = [] for day in time_range reserved += @_reserved[day] end - space = base_space(leaves, map) - while reserved.include? space do - space += 1 - end - space - end - - def base_space(leaves, map) - parents = [] - leaves.each do |l| - parents.concat l.parents.collect.select{|p| map.include? p.id and map[p.id].space.nonzero?} + reserved.uniq! + + space = space_base + while reserved.include?(space) do + space += space_step + if space <= 0 then + space_step *= -1 + space = space_base + space_step + end end - space = parents.map{|p| map[p.id].space}.max || 0 - space += 1 + space end # Takes most left subtree branch of commits diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb new file mode 100644 index 0000000000000000000000000000000000000000..f2cfd8073e3c8b602d056330073fdef37b963542 --- /dev/null +++ b/lib/gitlab/popen.rb @@ -0,0 +1,18 @@ +module Gitlab + module Popen + def popen(cmd, path) + vars = { "PWD" => path } + options = { :chdir => path } + + @cmd_output = "" + @cmd_status = 0 + Open3.popen3(vars, cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_status = wait_thr.value.exitstatus + @cmd_output << stdout.read + @cmd_output << stderr.read + end + + return @cmd_output, @cmd_status + end + end +end diff --git a/lib/gitlab/satellite/logger.rb b/lib/gitlab/satellite/logger.rb new file mode 100644 index 0000000000000000000000000000000000000000..6f3f8255aca11e5b9c77bf98bd968a299e3c9756 --- /dev/null +++ b/lib/gitlab/satellite/logger.rb @@ -0,0 +1,13 @@ +module Gitlab + module Satellite + class Logger < Gitlab::Logger + def self.file_name + 'satellites.log' + end + + def format_message(severity, timestamp, progname, msg) + "#{timestamp.to_s(:long)}: #{msg}\n" + end + end + end +end diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index 164af55d895842a3337aa3d96dcef3aa25b329aa..e7f7a7673b5410310350dc01675abfc75ddc1382 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -3,6 +3,8 @@ module Gitlab module Satellite class Satellite + include Gitlab::Popen + PARKING_BRANCH = "__parking_branch" attr_accessor :project @@ -11,6 +13,10 @@ module Gitlab @project = project end + def log message + Gitlab::Satellite::Logger.error(message) + end + def raise_no_satellite raise SatelliteNotExistError.new("Satellite doesn't exist") end @@ -24,11 +30,16 @@ module Gitlab end def create - create_cmd = "git clone #{project.url_to_repo} #{path}" - if system(create_cmd) + output, status = popen("git clone #{project.repository.path_to_repo} #{path}", + Gitlab.config.satellites.path) + + log("PID: #{project.id}: git clone #{project.repository.path_to_repo} #{path}") + log("PID: #{project.id}: -> #{output}") + + if status.zero? true else - Gitlab::GitLogger.error("Failed to create satellite for #{project.name_with_namespace}") + log("Failed to create satellite for #{project.name_with_namespace}") false end end @@ -66,6 +77,10 @@ module Gitlab @repo ||= Grit::Repo.new(path) end + def destroy + FileUtils.rm_rf(path) + end + private # Clear the working directory diff --git a/lib/gitlab/user_team_manager.rb b/lib/gitlab/user_team_manager.rb new file mode 100644 index 0000000000000000000000000000000000000000..a8ff4a3d94df296b23d0daed479c5d486fad45b1 --- /dev/null +++ b/lib/gitlab/user_team_manager.rb @@ -0,0 +1,135 @@ +# UserTeamManager class +# +# Used for manage User teams with project repositories +module Gitlab + class UserTeamManager + class << self + def assign(team, project, access) + project = Project.find(project) unless project.is_a? Project + searched_project = team.user_team_project_relationships.find_by_project_id(project.id) + + unless searched_project.present? + team.user_team_project_relationships.create(project_id: project.id, greatest_access: access) + update_team_users_access_in_project(team, project) + end + end + + def resign(team, project) + project = Project.find(project) unless project.is_a? Project + + team.user_team_project_relationships.with_project(project).destroy_all + + update_team_users_access_in_project(team, project) + end + + def update_team_user_membership(team, member, options) + updates = {} + + if options[:default_projects_access] && options[:default_projects_access] != team.default_projects_access(member) + updates[:permission] = options[:default_projects_access] + end + + if options[:group_admin].to_s != team.admin?(member).to_s + updates[:group_admin] = options[:group_admin].present? + end + + unless updates.blank? + user_team_relationship = team.user_team_user_relationships.find_by_user_id(member) + if user_team_relationship.update_attributes(updates) + if updates[:permission] + rebuild_project_permissions_to_member(team, member) + end + true + else + false + end + else + true + end + end + + def update_project_greates_access(team, project, permission) + project_relation = team.user_team_project_relationships.find_by_project_id(project) + if permission != team.max_project_access(project) + if project_relation.update_attributes(greatest_access: permission) + update_team_users_access_in_project(team, project) + true + else + false + end + else + true + end + end + + def rebuild_project_permissions_to_member(team, member) + team.projects.each do |project| + update_team_user_access_in_project(team, member, project) + end + end + + def update_team_users_access_in_project(team, project) + members = team.members + members.each do |member| + update_team_user_access_in_project(team, member, project) + end + end + + def update_team_user_access_in_project(team, user, project) + granted_access = max_teams_member_permission_in_project(user, project) + + project_team_user = UsersProject.find_by_user_id_and_project_id(user.id, project.id) + project_team_user.destroy if project_team_user.present? + + # project_team_user.project_access != granted_access + project.team << [user, granted_access] if granted_access > 0 + end + + def max_teams_member_permission_in_project(user, project, teams = nil) + result_access = 0 + + user_teams = project.user_teams.with_member(user) + + teams ||= user_teams + + if teams.any? + teams.each do |team| + granted_access = max_team_member_permission_in_project(team, user, project) + result_access = [granted_access, result_access].max + end + end + result_access + end + + def max_team_member_permission_in_project(team, user, project) + member_access = team.default_projects_access(user) + team_access = team.user_team_project_relationships.find_by_project_id(project.id).greatest_access + + [team_access, member_access].min + end + + def add_member_into_team(team, user, access, admin) + user = User.find(user) unless user.is_a? User + + team.user_team_user_relationships.create(user_id: user.id, permission: access, group_admin: admin) + team.projects.each do |project| + update_team_user_access_in_project(team, user, project) + end + end + + def remove_member_from_team(team, user) + user = User.find(user) unless user.is_a? User + + team.user_team_user_relationships.with_user(user).destroy_all + other_teams = [] + team.projects.each do |project| + other_teams << project.user_teams.with_member(user) + end + other_teams.uniq + unless other_teams.any? + UsersProject.in_projects(team.projects).with_user(user).destroy_all + end + end + end + end +end diff --git a/lib/gitolited.rb b/lib/gitolited.rb index 68b9b6255253b0e4faeeac16045ee3d8235061b9..4911a473f05e1d42b0127cd1d98372775897df1c 100644 --- a/lib/gitolited.rb +++ b/lib/gitolited.rb @@ -6,6 +6,6 @@ # module Gitolited def gitolite - Gitlab::Gitolite.new + Gitlab::Shell.new end end diff --git a/lib/hooks/post-receive b/lib/hooks/post-receive deleted file mode 100755 index 6944d3e3f72fddbc1ca6839ed0c331fab787dcc6..0000000000000000000000000000000000000000 --- a/lib/hooks/post-receive +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# Version 4.1 -# This file was placed here by GitLab. It makes sure that your pushed commits -# will be processed properly. - -while read oldrev newrev ref -do - # For every branch or tag that was pushed, create a Resque job in redis. - repo_path=`pwd` - env -i redis-cli rpush "resque:gitlab:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$repo_path\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1 -done diff --git a/lib/support/rewrite-hooks.sh b/lib/support/rewrite-hooks.sh deleted file mode 100755 index b8fd36b9a1e5e61ebc0b4620d33235cbbe27548f..0000000000000000000000000000000000000000 --- a/lib/support/rewrite-hooks.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -src="/home/git/repositories" - -for dir in `ls "$src/"` -do - if [ -d "$src/$dir" ]; then - - if [ "$dir" = "gitolite-admin.git" ] - then - continue - fi - - if [[ "$dir" =~ ^.*.git$ ]] - then - project_hook="$src/$dir/hooks/post-receive" - gitolite_hook="/home/git/.gitolite/hooks/common/post-receive" - - ln -s -f $gitolite_hook $project_hook - else - for subdir in `ls "$src/$dir/"` - do - if [ -d "$src/$dir/$subdir" ] && [[ "$subdir" =~ ^.*.git$ ]]; then - project_hook="$src/$dir/$subdir/hooks/post-receive" - gitolite_hook="/home/git/.gitolite/hooks/common/post-receive" - - ln -s -f $gitolite_hook $project_hook - fi - done - fi - fi -done diff --git a/lib/support/truncate_repositories.sh b/lib/support/truncate_repositories.sh deleted file mode 100755 index 3b14e2ee362fb858b7ffa61d8da672866493e382..0000000000000000000000000000000000000000 --- a/lib/support/truncate_repositories.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -echo "Danger!!! Data Loss" -while true; do - read -p "Do you wish to all directories except gitolite-admin.git from /home/git/repositories/ (y/n) ?: " yn - case $yn in - [Yy]* ) sh -c "find /home/git/repositories/. -maxdepth 1 -not -name 'gitolite-admin.git' -not -name '.' | xargs sudo rm -rf"; break;; - [Nn]* ) exit;; - * ) echo "Please answer yes or no.";; - esac -done diff --git a/lib/tasks/gitlab/bulk_add_permission.rake b/lib/tasks/gitlab/bulk_add_permission.rake index 36c51d060bc735a12bb203a1f189b943cd13d70c..eb1a7559dbda102f60cd9993df7305cc48e4b485 100644 --- a/lib/tasks/gitlab/bulk_add_permission.rake +++ b/lib/tasks/gitlab/bulk_add_permission.rake @@ -4,21 +4,21 @@ namespace :gitlab do task :all_users_to_all_projects => :environment do |t, args| user_ids = User.where(:admin => false).pluck(:id) admin_ids = User.where(:admin => true).pluck(:id) + projects_ids = Project.pluck(:id) - Project.find_each do |project| - puts "Importing #{user_ids.size} users into #{project.code}" - UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER) - puts "Importing #{admin_ids.size} admins into #{project.code}" - UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER) - end + puts "Importing #{user_ids.size} users into #{projects_ids.size} projects" + UsersProject.add_users_into_projects(projects_ids, user_ids, UsersProject::DEVELOPER) + + puts "Importing #{admin_ids.size} admins into #{projects_ids.size} projects" + UsersProject.add_users_into_projects(projects_ids, admin_ids, UsersProject::MASTER) end desc "GITLAB | Add a specific user to all projects (as a developer)" task :user_to_projects, [:email] => :environment do |t, args| user = User.find_by_email args.email project_ids = Project.pluck(:id) - - UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER) + puts "Importing #{user.email} users into #{project_ids.size} projects" + UsersProject.add_users_into_projects(project_ids, Array.wrap(user.id), UsersProject::DEVELOPER) end end end \ No newline at end of file diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 826b78ec5b1bc8ff175efa78704215aab5f773ba..b54e63acfbc6b31a7679c61e227ed2594064ceea 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -169,7 +169,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("bundle exec rake db:migrate") + sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production") ) fix_and_rerun end @@ -194,7 +194,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("bundle exec rake gitlab:satellites:create"), + sudo_gitlab("bundle exec rake gitlab:satellites:create RAILS_ENV=production"), "If necessary, remove the tmp/repo_satellites directory ...", "... and rerun the above command" ) @@ -716,7 +716,7 @@ namespace :gitlab do end def check_repo_base_permissions - print "Repo base access is drwsrws---? ... " + print "Repo base access is drwxrws---? ... " repo_base_path = Gitlab.config.gitolite.repos_path unless File.exists?(repo_base_path) @@ -724,12 +724,14 @@ namespace :gitlab do return end - if File.stat(repo_base_path).mode.to_s(8).ends_with?("6770") + if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770") puts "yes".green else puts "no".red try_fixing_it( - "sudo chmod -R ug+rwXs,o-rwx #{repo_base_path}" + "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}", + "sudo chmod -R ug-s #{repo_base_path}", + "find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s" ) for_more_information( see_installation_guide_section "Gitolite" @@ -780,21 +782,25 @@ namespace :gitlab do Project.find_each(batch_size: 100) do |project| print "#{project.name_with_namespace.yellow} ... " - correct_options = options.map do |name, value| - run("git --git-dir=\"#{project.repository.path_to_repo}\" config --get #{name}").try(:chomp) == value - end - - if correct_options.all? - puts "ok".green + if project.empty_repo? + puts "repository is empty".magenta else - puts "wrong or missing".red - try_fixing_it( - sudo_gitlab("bundle exec rake gitlab:gitolite:update_repos") - ) - for_more_information( - "doc/raketasks/maintenance.md" - ) - fix_and_rerun + correct_options = options.map do |name, value| + run("git --git-dir=\"#{project.repository.path_to_repo}\" config --get #{name}").try(:chomp) == value + end + + if correct_options.all? + puts "ok".green + else + puts "wrong or missing".red + try_fixing_it( + sudo_gitlab("bundle exec rake gitlab:gitolite:update_repos RAILS_ENV=production") + ) + for_more_information( + "doc/raketasks/maintenance.md" + ) + fix_and_rerun + end end end end @@ -820,32 +826,37 @@ namespace :gitlab do Project.find_each(batch_size: 100) do |project| print "#{project.name_with_namespace.yellow} ... " - project_hook_file = File.join(project.repository.path_to_repo, "hooks", hook_file) - unless File.exists?(project_hook_file) - puts "missing".red - try_fixing_it( - "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}" - ) - for_more_information( - "lib/support/rewrite-hooks.sh" - ) - fix_and_rerun - next - end - - if File.lstat(project_hook_file).symlink? && - File.realpath(project_hook_file) == File.realpath(gitolite_hook_file) - puts "ok".green + if project.empty_repo? + puts "repository is empty".magenta else - puts "not a link to Gitolite's hook".red - try_fixing_it( - "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}" - ) - for_more_information( - "lib/support/rewrite-hooks.sh" - ) - fix_and_rerun + project_hook_file = File.join(project.repository.path_to_repo, "hooks", hook_file) + + unless File.exists?(project_hook_file) + puts "missing".red + try_fixing_it( + "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}" + ) + for_more_information( + "lib/support/rewrite-hooks.sh" + ) + fix_and_rerun + next + end + + if File.lstat(project_hook_file).symlink? && + File.realpath(project_hook_file) == File.realpath(gitolite_hook_file) + puts "ok".green + else + puts "not a link to Gitolite's hook".red + try_fixing_it( + "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}" + ) + for_more_information( + "lib/support/rewrite-hooks.sh" + ) + fix_and_rerun + end end end end @@ -895,7 +906,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("bundle exec rake sidekiq:start") + sudo_gitlab("bundle exec rake sidekiq:start RAILS_ENV=production") ) for_more_information( see_installation_guide_section("Install Init Script"), diff --git a/lib/tasks/gitlab/enable_automerge.rake b/lib/tasks/gitlab/enable_automerge.rake index e92da81021f06a1f3a19daad21994e9fb3740332..a89c6eaa5c41291f2aae66ecb3a1cac289c2d1e4 100644 --- a/lib/tasks/gitlab/enable_automerge.rake +++ b/lib/tasks/gitlab/enable_automerge.rake @@ -3,11 +3,6 @@ namespace :gitlab do task :enable_automerge => :environment do warn_user_is_not_gitlab - puts "Updating repo permissions ..." - Gitlab::Gitolite.new.enable_automerge - puts "... #{"done".green}" - puts "" - print "Creating satellites for ..." unless Project.count > 0 puts "skipping, because you have no projects".magenta diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index 4bf9110508ed41a12ba25d6517364a45daa5f681..0ca652faa747949184370ee92fb9d317643a8276 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -44,7 +44,7 @@ namespace :gitlab do :name => path, } - project = Project.create_by_user(project_params, user) + project = Projects::CreateContext.new(user, project_params).execute if project.valid? puts " * Created #{project.name} (#{repo_name})".green diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake new file mode 100644 index 0000000000000000000000000000000000000000..25713482ed81e0dff32cfdaadbb22fc2bfa38c8f --- /dev/null +++ b/lib/tasks/gitlab/shell.rake @@ -0,0 +1,32 @@ +namespace :gitlab do + namespace :shell do + desc "GITLAB | Setup gitlab-shell" + task :setup => :environment do + setup + end + end + + def setup + warn_user_is_not_gitlab + + puts "This will rebuild an authorized_keys file." + puts "You will lose any data stored in /home/git/.ssh/authorized_keys." + ask_to_continue + puts "" + + system("echo '# Managed by gitlab-shell' > /home/git/.ssh/authorized_keys") + + Key.find_each(:batch_size => 1000) do |key| + if Gitlab::Shell.new.add_key(key.shell_id, key.key) + print '.' + else + print 'F' + end + end + + rescue Gitlab::TaskAbortedByUserError + puts "Quitting...".red + exit 1 + end +end + diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake index 01da919d7f8ab22f97ddf5b0709b8761114c5659..e4eb0e6776f9637eff3980073d77ab057a73f515 100644 --- a/lib/tasks/sidekiq.rake +++ b/lib/tasks/sidekiq.rake @@ -6,7 +6,7 @@ namespace :sidekiq do desc "GITLAB | Start sidekiq" task :start do - run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &" + run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitolite,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &" end def pidfile diff --git a/public/deploy.html b/public/deploy.html new file mode 100644 index 0000000000000000000000000000000000000000..d8c287809ea0ae0ed485aa81f57bca3e753065d0 --- /dev/null +++ b/public/deploy.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <title>Deploy in progress. Please try again in few minutes</title> + <link href="/static.css" media="screen" rel="stylesheet" type="text/css" /> + </head> + <body> + <h1>Deploy in progress</h1> + <h3>Please try again in few minutes or contact your administrator.</h3> + </body> +</html> diff --git a/spec/factories.rb b/spec/factories.rb index 593b8350fdaf8d229fc90b9d3989d9be7107f36a..0e0c04f98751ef17db30e5dd1d88b8a64f6b400b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -12,7 +12,7 @@ FactoryGirl.define do factory :user, aliases: [:author, :assignee, :owner, :creator] do email { Faker::Internet.email } name - username { Faker::Internet.user_name } + sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" } password "123456" password_confirmation { password } diff --git a/spec/factories/user_team_project_relationships.rb b/spec/factories/user_team_project_relationships.rb new file mode 100644 index 0000000000000000000000000000000000000000..e900d86c2e40f6cc8e5fa3c1678b01f153d3aa8f --- /dev/null +++ b/spec/factories/user_team_project_relationships.rb @@ -0,0 +1,21 @@ +# == Schema Information +# +# Table name: user_team_project_relationships +# +# id :integer not null, primary key +# project_id :integer +# user_team_id :integer +# greatest_access :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :user_team_project_relationship do + project + user_team + greatest_access { UsersProject::MASTER } + end +end diff --git a/spec/factories/user_team_user_relationships.rb b/spec/factories/user_team_user_relationships.rb new file mode 100644 index 0000000000000000000000000000000000000000..8c729dd875105524d5dea6874849e8948bc6b4d4 --- /dev/null +++ b/spec/factories/user_team_user_relationships.rb @@ -0,0 +1,23 @@ +# == Schema Information +# +# Table name: user_team_user_relationships +# +# id :integer not null, primary key +# user_id :integer +# user_team_id :integer +# group_admin :boolean +# permission :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :user_team_user_relationship do + user + user_team + group_admin false + permission { UsersProject::MASTER } + end +end diff --git a/spec/factories/user_teams.rb b/spec/factories/user_teams.rb new file mode 100644 index 0000000000000000000000000000000000000000..1a9ae8e885c3cf924219bef7ba49d893884d52cc --- /dev/null +++ b/spec/factories/user_teams.rb @@ -0,0 +1,21 @@ +# == Schema Information +# +# Table name: user_teams +# +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# owner_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :user_team do + sequence(:name) { |n| "team#{n}" } + path { name.downcase.gsub(/\s/, '_') } + owner + end +end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index deb6499e00803707ed537f044ad2f3f9696dba07..ee20ae79809d4af3338416e2677a60ef96a818e2 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -73,5 +73,28 @@ describe ExtractsPath do extract_ref('/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG?_=12354435').should == ['v2.0.0', 'CHANGELOG'] end end + + context "with a fullpath and a relative_url_root" do + before do + Gitlab.config.gitlab.stub(relative_url_root: '/relative') + end + + it "extracts a valid branch with relative_url_root" do + extract_ref('/relative/gitlab/gitlab-ci/tree/foo/bar/baz/CHANGELOG').should == ['foo/bar/baz', 'CHANGELOG'] + end + + it "extracts a valid tag" do + extract_ref('/relative/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG').should == ['v2.0.0', 'CHANGELOG'] + end + + it "extracts a valid commit SHA" do + extract_ref('/relative/gitlab/gitlab-ci/tree/f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG').should == + ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG'] + end + + it "extracts a timestamp" do + extract_ref('/relative/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG?_=12354435').should == ['v2.0.0', 'CHANGELOG'] + end + end end end diff --git a/spec/lib/gitolite_config_spec.rb b/spec/lib/gitolite_config_spec.rb deleted file mode 100644 index c3ce0db569aef71093eefac45a2acb61bc07b093..0000000000000000000000000000000000000000 --- a/spec/lib/gitolite_config_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe Gitlab::GitoliteConfig do - let(:gitolite) { Gitlab::GitoliteConfig.new } - - it { should respond_to :write_key } - it { should respond_to :rm_key } - it { should respond_to :update_project } - it { should respond_to :update_project! } - it { should respond_to :update_projects } - it { should respond_to :destroy_project } - it { should respond_to :destroy_project! } - it { should respond_to :apply } - it { should respond_to :admin_all_repo } - it { should respond_to :admin_all_repo! } -end diff --git a/spec/lib/gitolite_spec.rb b/spec/lib/gitolite_spec.rb deleted file mode 100644 index 8075b99ed99037ce637bcb510fbd66ec660f7a4d..0000000000000000000000000000000000000000 --- a/spec/lib/gitolite_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Gitolite do - let(:project) { double('Project', path: 'diaspora') } - let(:gitolite_config) { double('Gitlab::GitoliteConfig') } - let(:gitolite) { Gitlab::Gitolite.new } - - before do - gitolite.stub(config: gitolite_config) - end - - it { should respond_to :set_key } - it { should respond_to :remove_key } - - it { should respond_to :update_repository } - it { should respond_to :create_repository } - it { should respond_to :remove_repository } - - it { gitolite.url_to_repo('diaspora').should == Gitlab.config.gitolite.ssh_path_prefix + "diaspora.git" } - - it "should call config update" do - gitolite_config.should_receive(:update_project!) - gitolite.update_repository project - end -end diff --git a/spec/lib/shell_spec.rb b/spec/lib/shell_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1c546e59235a7d1fcfc65c9a4c1057891dea5c2d --- /dev/null +++ b/spec/lib/shell_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Shell do + let(:project) { double('Project', id: 7, path: 'diaspora') } + let(:gitolite) { Gitlab::Shell.new } + + before do + Project.stub(find: project) + end + + it { should respond_to :add_key } + it { should respond_to :remove_key } + it { should respond_to :add_repository } + it { should respond_to :remove_repository } + + it { gitolite.url_to_repo('diaspora').should == Gitlab.config.gitolite.ssh_path_prefix + "diaspora.git" } +end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index d947f0e28822b0ca7cb158c5f11372c8518c5ed9..befc10594dba7aa152e17687967d92dcffed840a 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -266,7 +266,7 @@ describe Notify do before(:each) { note.stub(:noteable).and_return(commit) } - subject { Notify.note_commit_email(recipient.email, note.id) } + subject { Notify.note_commit_email(recipient.id, note.id) } it_behaves_like 'a note email' diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 0816b3eed5a30dc0370b504787d0721f7de61455..10db53e07450862d9da1b66488c3ad8ed9b5ba89 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -28,10 +28,6 @@ describe Issue do it { should_not allow_mass_assignment_of(:project_id) } end - describe "Validation" do - it { should ensure_length_of(:description).is_within(0..10000) } - end - describe 'modules' do it { should include_module(Issuable) } end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 6d2310df5c025782cef8da8ecc472635b25a69e1..94b952cf932f58d0d26b4f21dcdbc6a247562a9e 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -46,9 +46,9 @@ describe Key do key.should_not be_valid end - it "does accept the same key for another project" do + it "does not accept the same key for another project" do key = build(:key, project_id: 0) - key.should be_valid + key.should_not be_valid end end diff --git a/spec/models/project_hooks_spec.rb b/spec/models/project_hooks_spec.rb index 60457e20c51425e8bc43d6befc5f6cfab2c8cce3..3559c72f6f0f785d07ef5685a233bd32e6757027 100644 --- a/spec/models/project_hooks_spec.rb +++ b/spec/models/project_hooks_spec.rb @@ -38,11 +38,14 @@ describe Project, "Hooks" do @project_hook = create(:project_hook) @project_hook_2 = create(:project_hook) project.hooks << [@project_hook, @project_hook_2] + + stub_request(:post, @project_hook.url) + stub_request(:post, @project_hook_2.url) end it "executes multiple web hook" do - @project_hook.should_receive(:execute).once - @project_hook_2.should_receive(:execute).once + @project_hook.should_receive(:async_execute).once + @project_hook_2.should_receive(:async_execute).once project.trigger_post_receive('oldrev', 'newrev', 'refs/heads/master', @user) end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 17bc988bf059b49c56488084da8fcc844d97ed6d..3dccb482375c9d4d230a1ddd85e04da4931b11ea 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -8,7 +8,6 @@ # description :text # created_at :datetime not null # updated_at :datetime not null -# private_flag :boolean default(TRUE), not null # creator_id :integer # default_branch :string(255) # issues_enabled :boolean default(TRUE), not null @@ -16,6 +15,7 @@ # merge_requests_enabled :boolean default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null # namespace_id :integer +# public :boolean default(FALSE), not null # require 'spec_helper' @@ -42,7 +42,6 @@ describe Project do describe "Mass assignment" do it { should_not allow_mass_assignment_of(:namespace_id) } it { should_not allow_mass_assignment_of(:creator_id) } - it { should_not allow_mass_assignment_of(:private_flag) } end describe "Validation" do @@ -78,8 +77,6 @@ describe Project do it { should respond_to(:url_to_repo) } it { should respond_to(:repo_exists?) } it { should respond_to(:satellite) } - it { should respond_to(:update_repository) } - it { should respond_to(:destroy_repository) } it { should respond_to(:observe_push) } it { should respond_to(:update_merge_requests) } it { should respond_to(:execute_hooks) } diff --git a/spec/models/team_spec.rb b/spec/models/project_team_spec.rb similarity index 94% rename from spec/models/team_spec.rb rename to spec/models/project_team_spec.rb index 65ffe13b490c651f75d792e4fc483718a23afbcd..7803811f395a9778659ae7f910a1e202404fe5b6 100644 --- a/spec/models/team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Team do +describe ProjectTeam do let(:team) { create(:project).team } describe "Respond to" do diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index c4d2e2f49f515856ef3f2276f096fc0121adf863..6e830393e32e632fb4d57a10ab1824331c58c718 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -24,19 +24,4 @@ describe ProtectedBranch do it { should validate_presence_of(:project) } it { should validate_presence_of(:name) } end - - describe 'Callbacks' do - let(:branch) { build(:protected_branch) } - - it 'call update_repository after save' do - branch.should_receive(:update_repository) - branch.save - end - - it 'call update_repository after destroy' do - branch.save - branch.should_receive(:update_repository) - branch.destroy - end - end end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index b474d88c9e27e070181a3fe78e5bee7be0a0fc85..e4d1934829f663f30462cfd5b52445fdd3d80eee 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -38,6 +38,5 @@ describe Snippet do it { should ensure_length_of(:title).is_within(0..255) } it { should validate_presence_of(:content) } - it { should ensure_length_of(:content).is_within(0..10_000) } end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index f19c40b5fa6b9a760dc5bf643de6906faef72c13..8ab0a0343bbf385eb3b1b4cda9d1d285a10dff00 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -31,6 +31,8 @@ # extern_uid :string(255) # provider :string(255) # username :string(255) +# can_create_group :boolean default(TRUE), not null +# can_create_team :boolean default(TRUE), not null # require 'spec_helper' @@ -189,7 +191,7 @@ describe User do it { user.is_admin?.should be_false } it { user.require_ssh_key?.should be_true } - it { user.can_create_group?.should be_false } + it { user.can_create_group?.should be_true } it { user.can_create_project?.should be_true } it { user.first_name.should == 'John' } end diff --git a/spec/models/user_team_project_relationship_spec.rb b/spec/models/user_team_project_relationship_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..86150cf305f31f0a5251ec5bf0892992f2f8973d --- /dev/null +++ b/spec/models/user_team_project_relationship_spec.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: user_team_project_relationships +# +# id :integer not null, primary key +# project_id :integer +# user_team_id :integer +# greatest_access :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +require 'spec_helper' + +describe UserTeamProjectRelationship do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/user_team_spec.rb b/spec/models/user_team_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..76d47f41498ca53f2be784b3ac9d7d8c7e52c9c3 --- /dev/null +++ b/spec/models/user_team_spec.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: user_teams +# +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# owner_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +require 'spec_helper' + +describe UserTeam do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/user_team_user_relationship_spec.rb b/spec/models/user_team_user_relationship_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..981ad1e887313725922352f82ecdbbf64c8f5275 --- /dev/null +++ b/spec/models/user_team_user_relationship_spec.rb @@ -0,0 +1,18 @@ +# == Schema Information +# +# Table name: user_team_user_relationships +# +# id :integer not null, primary key +# user_id :integer +# user_team_id :integer +# group_admin :boolean +# permission :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +require 'spec_helper' + +describe UserTeamUserRelationship do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/observers/key_observer_spec.rb b/spec/observers/key_observer_spec.rb index ae7b0f73e57ed413702807b49737a38d19a58073..0a886a57afdd79b030a3ee8be98bd161a2c4ba55 100644 --- a/spec/observers/key_observer_spec.rb +++ b/spec/observers/key_observer_spec.rb @@ -3,31 +3,25 @@ require 'spec_helper' describe KeyObserver do before do @key = double('Key', - identifier: 'admin_654654', + shell_id: 'key-32', key: '== a vaild ssh key', projects: [], is_deploy_key: false ) - @gitolite = double('Gitlab::Gitolite', - set_key: true, - remove_key: true - ) - @observer = KeyObserver.instance - @observer.stub(gitolite: @gitolite) end context :after_save do it do - @gitolite.should_receive(:set_key).with(@key.identifier, @key.key, @key.projects) + GitoliteWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key) @observer.after_save(@key) end end context :after_destroy do it do - @gitolite.should_receive(:remove_key).with(@key.identifier, @key.projects) + GitoliteWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key) @observer.after_destroy(@key) end end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..c39a4228408e6108d30084ca02846bd1349d2e12 --- /dev/null +++ b/spec/requests/api/groups_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe Gitlab::API do + include ApiHelpers + + let(:user1) { create(:user) } + let(:user2) { create(:user) } + let(:admin) { create(:admin) } + let!(:group1) { create(:group, owner: user1) } + let!(:group2) { create(:group, owner: user2) } + + describe "GET /groups" do + context "when unauthenticated" do + it "should return authentication error" do + get api("/groups") + response.status.should == 401 + end + end + + context "when authenticated as user" do + it "normal user: should return an array of groups of user1" do + get api("/groups", user1) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 1 + json_response.first['name'].should == group1.name + end + end + + context "when authenticated as admin" do + it "admin: should return an array of all groups" do + get api("/groups", admin) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 2 + end + end + end + + describe "GET /groups/:id" do + context "when authenticated as user" do + it "should return one of user1's groups" do + get api("/groups/#{group1.id}", user1) + response.status.should == 200 + json_response['name'] == group1.name + end + + it "should not return a non existing group" do + get api("/groups/1328", user1) + response.status.should == 404 + end + + it "should not return a group not attached to user1" do + get api("/groups/#{group2.id}", user1) + response.status.should == 404 + end + end + + context "when authenticated as admin" do + it "should return any existing group" do + get api("/groups/#{group2.id}", admin) + response.status.should == 200 + json_response['name'] == group2.name + end + + it "should not return a non existing group" do + get api("/groups/1328", admin) + response.status.should == 404 + end + end + end + + describe "POST /groups" do + context "when authenticated as user" do + it "should not create group" do + post api("/groups", user1), attributes_for(:group) + response.status.should == 403 + end + end + + context "when authenticated as admin" do + it "should create group" do + post api("/groups", admin), attributes_for(:group) + response.status.should == 201 + end + + it "should not create group, duplicate" do + post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path} + response.status.should == 404 + end + end + end +end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index ae4fc111f636edea2174453b8601d43a171eb6c0..ee99d85df4d721aeb412ba84fc9af5f921e0b5ea 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -6,8 +6,10 @@ describe Gitlab::API do let(:user) { create(:user) } let!(:project) { create(:project, namespace: user.namespace ) } let!(:issue) { create(:issue, project: project, author: user) } + let!(:merge_request) { create(:merge_request, project: project, author: user) } let!(:snippet) { create(:snippet, project: project, author: user) } let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } + let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) } let!(:wall_note) { create(:note, project: project, author: user) } before { project.team << [user, :reporter] } @@ -64,6 +66,15 @@ describe Gitlab::API do json_response.first['body'].should == snippet_note.note end end + + context "when noteable is a Merge Request" do + it "should return an array of merge_requests notes" do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) + response.status.should == 200 + json_response.should be_an Array + json_response.first['body'].should == merge_request_note.note + end + end end describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index c2244210bcf29da26c977e2a06dda8b91516d989..d932fd9e74dc001ebe6f12955ff97cdd422844a5 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -107,6 +107,29 @@ describe Gitlab::API do json_response['name'].should == 'new_design' json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' + json_response['protected'].should == false + end + end + + describe "PUT /projects/:id/repository/branches/:branch/protect" do + it "should protect a single branch" do + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) + response.status.should == 200 + + json_response['name'].should == 'new_design' + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' + json_response['protected'].should == true + end + end + + describe "PUT /projects/:id/repository/branches/:branch/unprotect" do + it "should unprotect a single branch" do + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) + response.status.should == 200 + + json_response['name'].should == 'new_design' + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' + json_response['protected'].should == false end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index ee5f510aac5083d5496e36d2b9cdcd8540150e0d..1645117e231c01d62b55cbba90ff92ff92a682e9 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -83,6 +83,54 @@ describe Gitlab::API do end end + describe "PUT /users/:id" do + before { admin } + + it "should update user" do + put api("/users/#{user.id}", admin), {bio: 'new test bio'} + response.status.should == 200 + json_response['bio'].should == 'new test bio' + user.reload.bio.should == 'new test bio' + end + + it "should not allow invalid update" do + put api("/users/#{user.id}", admin), {email: 'invalid email'} + response.status.should == 404 + user.reload.email.should_not == 'invalid email' + end + + it "shouldn't available for non admin users" do + put api("/users/#{user.id}", user), attributes_for(:user) + response.status.should == 403 + end + + it "should return 404 for non-existing user" do + put api("/users/999999", admin), {bio: 'update should fail'} + response.status.should == 404 + end + end + + describe "DELETE /users/:id" do + before { admin } + + it "should delete user" do + delete api("/users/#{user.id}", admin) + response.status.should == 200 + expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound + json_response['email'].should == user.email + end + + it "shouldn't available for non admin users" do + delete api("/users/#{user.id}", user) + response.status.should == 403 + end + + it "should return 404 for non-existing user" do + delete api("/users/999999", admin) + response.status.should == 404 + end + end + describe "GET /user" do it "should return current user" do get api("/user", user) diff --git a/spec/requests/atom/dashboard_issues_spec.rb b/spec/requests/atom/dashboard_issues_spec.rb index 8ce64cd4c14a825d75fca86662932176e278fe64..6f5d51d16b6acb373f2eb0c1428fb115b2cc318a 100644 --- a/spec/requests/atom/dashboard_issues_spec.rb +++ b/spec/requests/atom/dashboard_issues_spec.rb @@ -10,7 +10,7 @@ describe "Dashboard Issues Feed" do describe "atom feed" do it "should render atom feed via private token" do - visit dashboard_issues_path(:atom, private_token: user.private_token) + visit issues_dashboard_path(:atom, private_token: user.private_token) page.response_headers['Content-Type'].should have_content("application/atom+xml") page.body.should have_selector("title", text: "#{user.name} issues") diff --git a/spec/requests/notes_on_merge_requests_spec.rb b/spec/requests/notes_on_merge_requests_spec.rb index 2b670359a49d7914069b6bb4658f787bf4202475..0111cf42ac7a7063acd6e776ef6d05b02d97694b 100644 --- a/spec/requests/notes_on_merge_requests_spec.rb +++ b/spec/requests/notes_on_merge_requests_spec.rb @@ -22,9 +22,9 @@ describe "On a merge request", js: true do it { within(".js-main-target-form") { should_not have_link("Cancel") } } # notifiactions - it { within(".js-main-target-form") { should have_checked_field("Project team") } } - it { within(".js-main-target-form") { should_not have_checked_field("Commit author") } } - it { within(".js-main-target-form") { should_not have_unchecked_field("Commit author") } } + it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } } + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } } + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } } describe "without text" do it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } } @@ -125,7 +125,7 @@ describe "On a merge request diff", js: true, focus: true do it { should have_css(".js-close-discussion-note-form", text: "Cancel") } # notification options - it { should have_checked_field("Project team") } + it { should have_checked_field("Notify team via email") } it "shouldn't add a second form for same row" do find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click") diff --git a/spec/requests/notes_on_wall_spec.rb b/spec/requests/notes_on_wall_spec.rb index 01673f997e935709dbd95a8799e3fb2d44d1c4d1..4adcf74e0b6a4cca6911b6364a5218dec8ef1644 100644 --- a/spec/requests/notes_on_wall_spec.rb +++ b/spec/requests/notes_on_wall_spec.rb @@ -21,9 +21,9 @@ describe "On the project wall", js: true do it { within(".js-main-target-form") { should_not have_link("Cancel") } } # notifiactions - it { within(".js-main-target-form") { should have_checked_field("Project team") } } - it { within(".js-main-target-form") { should_not have_checked_field("Commit author") } } - it { within(".js-main-target-form") { should_not have_unchecked_field("Commit author") } } + it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } } + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } } + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } } describe "without text" do it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } } diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index fb26bf98d0ff3631c7587deb01dd9f2c94565e63..3e0e4bb38838c4ab036c4cc57165a79e8af7a33c 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -95,20 +95,20 @@ describe Admin::ProjectsController, "routing" do end end -# edit_admin_team_member GET /admin/team_members/:id/edit(.:format) admin/team_members#edit -# admin_team_member PUT /admin/team_members/:id(.:format) admin/team_members#update -# DELETE /admin/team_members/:id(.:format) admin/team_members#destroy -describe Admin::TeamMembersController, "routing" do +# edit_admin_project_member GET /admin/projects/:project_id/members/:id/edit(.:format) admin/projects/members#edit {:id=>/[^\/]+/, :project_id=>/[^\/]+/} +# admin_project_member PUT /admin/projects/:project_id/members/:id(.:format) admin/projects/members#update {:id=>/[^\/]+/, :project_id=>/[^\/]+/} +# DELETE /admin/projects/:project_id/members/:id(.:format) admin/projects/members#destroy {:id=>/[^\/]+/, :project_id=>/[^\/]+/} +describe Admin::Projects::MembersController, "routing" do it "to #edit" do - get("/admin/team_members/1/edit").should route_to('admin/team_members#edit', id: '1') + get("/admin/projects/test/members/1/edit").should route_to('admin/projects/members#edit', project_id: 'test', id: '1') end it "to #update" do - put("/admin/team_members/1").should route_to('admin/team_members#update', id: '1') + put("/admin/projects/test/members/1").should route_to('admin/projects/members#update', project_id: 'test', id: '1') end it "to #destroy" do - delete("/admin/team_members/1").should route_to('admin/team_members#destroy', id: '1') + delete("/admin/projects/test/members/1").should route_to('admin/projects/members#destroy', project_id: 'test', id: '1') end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 6171141648c48ac9fb4477497f893f58adb0ad36..f94bedc79a1d1cb36edd6402cf32686c25da13e0 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -76,7 +76,7 @@ describe ProjectsController, "routing" do end it "to #graph" do - get("/gitlabhq/graph").should route_to('projects#graph', id: 'gitlabhq') + get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master') end it "to #files" do diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 57fd70e7497cd912e774b7a011c617f7ca7d5faa..5ad8165ecce1de76442b5f4c19394f13a7136634 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -146,14 +146,14 @@ describe KeysController, "routing" do end end -# dashboard GET /dashboard(.:format) dashboard#index +# dashboard GET /dashboard(.:format) dashboard#show # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests -# root / dashboard#index +# root / dashboard#show describe DashboardController, "routing" do it "to #index" do - get("/dashboard").should route_to('dashboard#index') - get("/").should route_to('dashboard#index') + get("/dashboard").should route_to('dashboard#show') + get("/").should route_to('dashboard#show') end it "to #issues" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dbac3c549011f393de88c8d7e914e96d9d76e2f6..bb314e60eb7d47c5e822a721ce7e396905af2dda 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,6 @@ RSpec.configure do |config| config.mock_with :rspec config.include LoginHelpers, type: :request - config.include GitoliteStub config.include FactoryGirl::Syntax::Methods config.include Devise::TestHelpers, type: :controller @@ -34,8 +33,6 @@ RSpec.configure do |config| config.use_transactional_fixtures = false config.before do - stub_gitolite! - # Use tmp dir for FS manipulations temp_repos_path = Rails.root.join('tmp', 'test-git-base-path') Gitlab.config.gitolite.stub(repos_path: temp_repos_path) diff --git a/spec/support/gitolite_stub.rb b/spec/support/gitolite_stub.rb deleted file mode 100644 index 574bb5a12a3f9e52d2f84be600523d0560fcd866..0000000000000000000000000000000000000000 --- a/spec/support/gitolite_stub.rb +++ /dev/null @@ -1,21 +0,0 @@ -module GitoliteStub - def stub_gitolite! - stub_gitlab_gitolite - stub_gitolite_admin - end - - def stub_gitolite_admin - gitolite_admin = double('Gitolite::GitoliteAdmin') - gitolite_admin.as_null_object - - Gitolite::GitoliteAdmin.stub(new: gitolite_admin) - end - - def stub_gitlab_gitolite - gitolite_config = double('Gitlab::GitoliteConfig') - gitolite_config.stub(apply: ->() { yield(self) }) - gitolite_config.as_null_object - - Gitlab::GitoliteConfig.stub(new: gitolite_config) - end -end diff --git a/spec/support/stubbed_repository.rb b/spec/support/stubbed_repository.rb index e6e194d70f556b49933093795165988f455cb962..434cab6516e7ad539bb5817b2de45de2ae68a9b0 100644 --- a/spec/support/stubbed_repository.rb +++ b/spec/support/stubbed_repository.rb @@ -1,5 +1,6 @@ require "repository" require "project" +require "shell" # Stubs out all Git repository access done by models so that specs can run # against fake repositories without Grit complaining that they don't exist. @@ -21,6 +22,10 @@ class Project true end + def destroy + true + end + def create true end @@ -32,3 +37,23 @@ class GitLabTestRepo < Repository @repo ||= Grit::Repo.new(Rails.root.join('tmp', 'repositories', 'gitlabhq')) end end + +module Gitlab + class Shell + def add_repository name + true + end + + def remove_repository name + true + end + + def add_key id, key + true + end + + def remove_key id, key + true + end + end +end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index f408c89afddf6c487516b8f3e14dbcbed8d746cb..f1a69b1b2e4c7ac5999c620fedb695f6c0090ce3 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -11,7 +11,7 @@ describe PostReceive do context "web hook" do let(:project) { create(:project) } let(:key) { create(:key, user: project.owner) } - let(:key_id) { key.identifier } + let(:key_id) { key.shell_id } it "fetches the correct project" do Project.should_receive(:find_with_namespace).with(project.path_with_namespace).and_return(project) @@ -19,7 +19,7 @@ describe PostReceive do end it "does not run if the author is not in the project" do - Key.stub(find_by_identifier: nil) + Key.stub(find_by_id: nil) project.should_not_receive(:observe_push) project.should_not_receive(:execute_hooks) diff --git a/vendor/assets/javascripts/branch-graph.js b/vendor/assets/javascripts/branch-graph.js index 805423bc70d93bd02f2ae306d19f91db822a1b29..7929d3b2a14c867e607d71547883a25dfb452004 100644 --- a/vendor/assets/javascripts/branch-graph.js +++ b/vendor/assets/javascripts/branch-graph.js @@ -65,15 +65,16 @@ BranchGraph.prototype.buildGraph = function(){ var graphWidth = $(this.element).width() - , ch = this.mspace * 20 + 20 - , cw = Math.max(graphWidth, this.mtime * 20 + 20) + , ch = this.mspace * 20 + 100 + , cw = Math.max(graphWidth, this.mtime * 20 + 260) , r = Raphael(this.element.get(0), cw, ch) , top = r.set() , cuday = 0 , cumonth = "" , offsetX = 20 , offsetY = 60 - , barWidth = Math.max(graphWidth, this.dayCount * 20 + 80); + , barWidth = Math.max(graphWidth, this.dayCount * 20 + 320) + , scrollLeft = cw; this.raphael = r; @@ -103,8 +104,9 @@ for (i = 0; i < this.commitCount; i++) { var x = offsetX + 20 * this.commits[i].time - , y = offsetY + 20 * this.commits[i].space - , c; + , y = offsetY + 10 * this.commits[i].space + , c + , ps; // Draw dot r.circle(x, y, 3).attr({ @@ -115,26 +117,40 @@ // Draw lines for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) { c = this.preparedCommits[this.commits[i].parents[j][0]]; + ps = this.commits[i].parent_spaces[j]; if (c) { var cx = offsetX + 20 * c.time - , cy = offsetY + 20 * c.space; - if (c.space == this.commits[i].space) { + , cy = offsetY + 10 * c.space + , psy = offsetY + 10 * ps; + if (c.space == this.commits[i].space && c.space == ps) { r.path([ "M", x, y, - "L", x - 20 * (c.time + 1), y + "L", cx, cy ]).attr({ stroke: this.colors[c.space], "stroke-width": 2 }); } else if (c.space < this.commits[i].space) { - r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C", x - 5, y, x - 17, y + 2, x - 20, y - 5, "L", cx, y - 5, cx, cy]) + r.path([ + "M", x - 5, y, + "l-5-2,0,4,5,-2", + "L", x - 10, y, + "L", x - 15, psy, + "L", cx + 5, psy, + "L", cx, cy]) .attr({ stroke: this.colors[this.commits[i].space], "stroke-width": 2 }); } else { - r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5L", x - 10, y + 20, "L", x - 10, cy, cx, cy]) + r.path([ + "M", x - 3, y + 6, + "l-4,3,4,2,0,-5", + "L", x - 5, y + 10, + "L", x - 10, psy, + "L", cx + 5, psy, + "L", cx, cy]) .attr({ stroke: this.colors[c.space], "stroke-width": 2 @@ -145,12 +161,18 @@ if (this.commits[i].refs) { this.appendLabel(x, y, this.commits[i].refs); + + // The main branch is displayed in the center. + re = new RegExp('(^| )' + this.options.ref + '( |$)'); + if (this.commits[i].refs.match(re)) { + scrollLeft = x - graphWidth / 2; + } } this.appendAnchor(top, this.commits[i], x, y); } top.toFront(); - this.element.scrollLeft(cw); + this.element.scrollLeft(scrollLeft); this.bindEvents(); }; @@ -260,7 +282,7 @@ cursor: "pointer" }) .click(function(){ - window.location = options.commit_url.replace('%s', commit.id); + window.open(options.commit_url.replace('%s', commit.id), '_blank'); }) .hover(function(){ this.tooltip = r.commitTooltip(x, y + 5, commit); @@ -351,4 +373,4 @@ function textWrap(t, width) { t.attr({ "y": b.y + h }); -} \ No newline at end of file +}