diff --git a/CHANGELOG b/CHANGELOG
index 26cef6c6c1e..520a25d8aba 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,10 +3,10 @@ Note: The upcoming release contains empty lines to reduce the number of merge co
v 7.8.0
- Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
- Make project search case insensitive (Hannes Rosenögger)
- -
+ - Include issue/mr participants in list of recipients for reassign/close/reopen emails
- Expose description in groups API
- -
- -
+ - Better UI for project services page
+ - Cleaner UI for web editor
- Add diff syntax highlighting in email-on-push service notifications (Hannes Rosenögger)
-
-
@@ -17,8 +17,10 @@ v 7.8.0
- Show tags in commit view (Hannes Rosenögger)
- Only count a user's vote once on a merge request or issue (Michael Clarke)
-
+ - Increate font size when browse source files and diffs
+ - Create new file in empty repository using GitLab UI
-
- -
+ - Ability to clone project using oauth2 token
-
- Upgrade Sidekiq gem to version 3.3.0
- Stop git zombie creation during force push check
@@ -29,11 +31,13 @@ v 7.8.0
-
-
- Allow configuring protection of the default branch upon first push (Marco Wessel)
+ -
+ -
+ - Add a commit calendar to the user profile (Hannes Rosenögger)
-
-
-
- -
- -
+ - Fix long broadcast message cut-off on left sidebar (Visay Keo)
- Add Project Avatars (Steven Thonus and Hannes Rosenögger)
-
-
@@ -59,6 +63,7 @@ v 7.8.0
-
-
-
+ - Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov)
v 7.7.1
- Improve mention autocomplete performance
diff --git a/Gemfile b/Gemfile
index 96a1097d6d8..a0f5b70de32 100644
--- a/Gemfile
+++ b/Gemfile
@@ -154,6 +154,9 @@ gem "slack-notifier", "~> 1.0.0"
# d3
gem "d3_rails", "~> 3.1.4"
+#cal-heatmap
+gem "cal-heatmap-rails", "~> 0.0.1"
+
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
@@ -170,7 +173,7 @@ gem 'ace-rails-ap'
gem 'mousetrap-rails'
# Semantic UI Sass for Sidebar
-gem 'semantic-ui-sass', '~> 0.16.1.0'
+gem 'semantic-ui-sass', '~> 1.8.0'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
diff --git a/Gemfile.lock b/Gemfile.lock
index 18fae9b7001..4b5b718c87e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -52,6 +52,7 @@ GEM
sass (~> 3.2)
browser (0.7.2)
builder (3.2.2)
+ cal-heatmap-rails (0.0.1)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@@ -490,7 +491,7 @@ GEM
activesupport (>= 3.1, < 4.2)
select2-rails (3.5.2)
thor (~> 0.14)
- semantic-ui-sass (0.16.1.0)
+ semantic-ui-sass (1.8.0.0)
sass (~> 3.2)
settingslogic (2.0.9)
sexp_processor (4.4.0)
@@ -627,6 +628,7 @@ DEPENDENCIES
binding_of_caller
bootstrap-sass (~> 3.0)
browser
+ cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
carrierwave
coffee-rails
@@ -715,7 +717,7 @@ DEPENDENCIES
sdoc
seed-fu
select2-rails
- semantic-ui-sass (~> 0.16.1.0)
+ semantic-ui-sass (~> 1.8.0)
settingslogic
shoulda-matchers (~> 2.1.0)
sidekiq (~> 3.3)
diff --git a/app/assets/images/no_project_icon.png b/app/assets/images/no_project_icon.png
deleted file mode 100644
index 8e9529c67ec..00000000000
Binary files a/app/assets/images/no_project_icon.png and /dev/null differ
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 337170605dc..4912c534b0e 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -39,6 +39,7 @@
#= require shortcuts_dashboard_navigation
#= require shortcuts_issueable
#= require shortcuts_network
+#= require cal-heatmap
#= require_tree .
window.slugify = (text) ->
diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob/blob.js.coffee
similarity index 100%
rename from app/assets/javascripts/blob.js.coffee
rename to app/assets/javascripts/blob/blob.js.coffee
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
new file mode 100644
index 00000000000..6914ca759f6
--- /dev/null
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -0,0 +1,44 @@
+class @EditBlob
+ constructor: (assets_path, mode)->
+ ace.config.set "modePath", assets_path + '/ace'
+ ace.config.loadModule "ace/ext/searchbox"
+ if mode
+ ace_mode = mode
+ editor = ace.edit("editor")
+ editor.focus()
+ @editor = editor
+
+ if ace_mode
+ editor.getSession().setMode "ace/mode/" + ace_mode
+
+ disableButtonIfEmptyField "#commit_message", ".js-commit-button"
+ $(".js-commit-button").click ->
+ $("#file-content").val editor.getValue()
+ $(".file-editor form").submit()
+ return
+
+ editModePanes = $(".js-edit-mode-pane")
+ editModeLinks = $(".js-edit-mode a")
+ editModeLinks.click (event) ->
+ event.preventDefault()
+ currentLink = $(this)
+ paneId = currentLink.attr("href")
+ currentPane = editModePanes.filter(paneId)
+ editModeLinks.parent().removeClass "active hover"
+ currentLink.parent().addClass "active hover"
+ editModePanes.hide()
+ if paneId is "#preview"
+ currentPane.fadeIn 200
+ $.post currentLink.data("preview-url"),
+ content: editor.getValue()
+ , (response) ->
+ currentPane.empty().append response
+ return
+
+ else
+ currentPane.fadeIn 200
+ editor.focus()
+ return
+
+ editor: ->
+ return @editor
diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee
new file mode 100644
index 00000000000..a6e27116b40
--- /dev/null
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -0,0 +1,21 @@
+class @NewBlob
+ constructor: (assets_path, mode)->
+ ace.config.set "modePath", assets_path + '/ace'
+ ace.config.loadModule "ace/ext/searchbox"
+ if mode
+ ace_mode = mode
+ editor = ace.edit("editor")
+ editor.focus()
+ @editor = editor
+
+ if ace_mode
+ editor.getSession().setMode "ace/mode/" + ace_mode
+
+ disableButtonIfEmptyField "#commit_message", ".js-commit-button"
+ $(".js-commit-button").click ->
+ $("#file-content").val editor.getValue()
+ $(".file-editor form").submit()
+ return
+
+ editor: ->
+ return @editor
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
new file mode 100644
index 00000000000..6a0d5e43567
--- /dev/null
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -0,0 +1,35 @@
+class @calendar
+ options =
+ month: "short"
+ day: "numeric"
+ year: "numeric"
+
+ constructor: (timestamps, starting_year, starting_month) ->
+ cal = new CalHeatMap()
+ cal.init
+ itemName: ["commit"]
+ data: timestamps
+ domain: "year"
+ subDomain: "month"
+ start: new Date(starting_year, starting_month)
+ domainLabelFormat: "%b"
+ id: "cal-heatmap"
+ domain: "month"
+ subDomain: "day"
+ range: 12
+ tooltip: true
+ domainDynamicDimension: false
+ colLimit: 4
+ label:
+ position: "top"
+ domainMargin: 1
+ legend: [
+ 0
+ 1
+ 4
+ 7
+ ]
+ legendCellPadding: 3
+ onClick: (date, count) ->
+ return
+ return
diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee
index f4a2ca813d2..836269c44f9 100644
--- a/app/assets/javascripts/project_new.js.coffee
+++ b/app/assets/javascripts/project_new.js.coffee
@@ -9,17 +9,3 @@ class @ProjectNew
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
-
- $('#project_issues_enabled').change ->
- if ($(this).is(':checked') == true)
- $('#project_issues_tracker').removeAttr('disabled')
- else
- $('#project_issues_tracker').attr('disabled', 'disabled')
-
- $('#project_issues_tracker').change()
-
- $('#project_issues_tracker').change ->
- if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
- $('#project_issues_tracker_id').attr('disabled', 'disabled')
- else
- $('#project_issues_tracker_id').removeAttr('disabled')
diff --git a/app/assets/javascripts/project_show.js.coffee b/app/assets/javascripts/project_show.js.coffee
index 02a7d7b731d..d0eaaad92b8 100644
--- a/app/assets/javascripts/project_show.js.coffee
+++ b/app/assets/javascripts/project_show.js.coffee
@@ -6,7 +6,7 @@ class @ProjectShow
new Flash('Star toggle failed. Try again later.', 'alert')
$("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
- $.cookie "default_view", $(e.target).attr("href")
+ $.cookie "default_view", $(e.target).attr("href"), { expires: 30 }
defaultView = $.cookie("default_view")
if defaultView
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 3cf08782c3c..8f63a7fee64 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -8,6 +8,7 @@
*= require select2
*= require_self
*= require dropzone/basic
+ *= require cal-heatmap
*/
@import "main/*";
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index d098f1ecaa2..3b360275065 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -173,6 +173,11 @@
margin-right: 0px;
}
}
+
+ &.btn-lg {
+ font-size: 15px;
+ line-height: 1.4;
+ }
}
.btn-block {
diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/generic/calendar.scss
new file mode 100644
index 00000000000..9483b26164e
--- /dev/null
+++ b/app/assets/stylesheets/generic/calendar.scss
@@ -0,0 +1,95 @@
+.calendar_onclick_placeholder {
+ padding: 0 0 2px 0;
+}
+
+.calendar_commit_activity {
+ padding: 5px 0 0;
+}
+
+.calendar_onclick_second {
+ font-size: 14px;
+ display: block;
+}
+
+.calendar_onclick_hr {
+ padding: 0;
+ margin: 10px 0;
+}
+
+.calendar_commit_date {
+ color: #999;
+}
+
+.calendar_activity_summary {
+ font-size: 14px;
+}
+
+/**
+* This overwrites the default values of the cal-heatmap gem
+*/
+.calendar {
+ .qi {
+ background-color: #999;
+ fill: #fff;
+ }
+
+ .q1 {
+ background-color: #dae289;
+ fill: #ededed;
+ }
+
+ .q2 {
+ background-color: #cedb9c;
+ fill: #ACD5F2;
+ }
+
+ .q3 {
+ background-color: #b5cf6b;
+ fill: #7FA8D1;
+ }
+
+ .q4 {
+ background-color: #637939;
+ fill: #49729B;
+ }
+
+ .q5 {
+ background-color: #3b6427;
+ fill: #254E77;
+ }
+
+ .domain-background {
+ fill: none;
+ shape-rendering: crispedges;
+ }
+
+ .ch-tooltip {
+ position: absolute;
+ display: none;
+ margin-top: 22px;
+ margin-left: 1px;
+ font-size: 13px;
+ padding: 3px;
+ font-weight: 550;
+ background-color: #222;
+ span {
+ position: absolute;
+ width: 200px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 10px;
+ &:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0;
+ height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/generic/highlight.scss
index 83dc7ab491a..e1ca86af816 100644
--- a/app/assets/stylesheets/generic/highlight.scss
+++ b/app/assets/stylesheets/generic/highlight.scss
@@ -10,8 +10,8 @@
border: none;
border-radius: 0;
font-family: $monospace_font;
- font-size: 12px !important;
- line-height: 16px !important;
+ font-size: $code_font_size !important;
+ line-height: $code_line_height !important;
margin: 0;
overflow: auto;
overflow-y: hidden;
@@ -38,8 +38,8 @@
a {
font-family: $monospace_font;
display: block;
- font-size: 12px !important;
- line-height: 16px !important;
+ font-size: $code_font_size !important;
+ line-height: $code_line_height !important;
white-space: nowrap;
i {
diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/gl_bootstrap.scss
index 2a68d922bb7..6efa56544a5 100644
--- a/app/assets/stylesheets/gl_bootstrap.scss
+++ b/app/assets/stylesheets/gl_bootstrap.scss
@@ -1,9 +1,6 @@
/*
* Twitter bootstrap with GitLab customizations/additions
*
- * Some unused bootstrap compontents like panels are not included.
- * Other components like tabs are modified to GitLab style.
- *
*/
$font-size-base: 13px !default;
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index 8435d1dae79..e54482d14c3 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -139,7 +139,7 @@
}
@mixin panel-colored {
- border: none;
+ border: 1px solid #EEE;
background: $box_bg;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss
index 6bbce70a782..acbf5be94a3 100644
--- a/app/assets/stylesheets/main/variables.scss
+++ b/app/assets/stylesheets/main/variables.scss
@@ -59,3 +59,5 @@ $list-font-size: 15px;
$sidebar_width: 230px;
$avatar_radius: 50%;
+$code_font_size: 13px;
+$code_line_height: 1.5;
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss
index 00795f990bf..90010781af0 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/sections/dashboard.scss
@@ -112,3 +112,7 @@
color: #FFF;
}
}
+
+.dash-list .str-truncated {
+ max-width: 72%;
+}
diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss
index 758f15c8013..da50dbe4715 100644
--- a/app/assets/stylesheets/sections/diff.scss
+++ b/app/assets/stylesheets/sections/diff.scss
@@ -37,7 +37,7 @@
overflow-y: hidden;
background: #FFF;
color: #333;
- font-size: 12px;
+ font-size: $code_font_size;
.old {
span.idiff {
background-color: #F99;
@@ -64,8 +64,8 @@
margin: 0px;
padding: 0px;
td {
- line-height: 18px;
- font-size: 12px;
+ line-height: $code_line_height;
+ font-size: $code_font_size;
}
}
diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/sections/editor.scss
index f62f46ee168..88aa256e56e 100644
--- a/app/assets/stylesheets/sections/editor.scss
+++ b/app/assets/stylesheets/sections/editor.scss
@@ -31,4 +31,26 @@
margin: 5px 8px 0 8px;
}
}
+
+ .file-title {
+ @extend .monospace;
+ font-size: 14px;
+ padding: 5px;
+ }
+
+ .editor-ref {
+ background: #f5f5f5;
+ padding: 11px 15px;
+ border-right: 1px solid #CCC;
+ display: inline-block;
+ margin: -5px -5px;
+ margin-right: 10px;
+ }
+
+ .editor-file-name {
+ .new-file-name {
+ display: inline-block;
+ width: 200px;
+ }
+ }
}
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index 047617e54ba..e255cbcada8 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -138,9 +138,10 @@ header {
top: -1px;
padding-right: 0px !important;
img {
- width: 26px;
- height: 26px;
- @include border-radius($avatar_radius);
+ width: 50px;
+ height: 50px;
+ margin: -15px;
+ margin-left: 5px;
}
}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index fbfd9c8cd9b..7a9d3334d96 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -163,8 +163,9 @@ form.edit-issue {
}
}
-.issue-title {
+h3.issue-title {
margin-top: 0;
+ font-size: 2em;
}
.context .select2-container {
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss
index 8bd32f41e2c..0e27c389387 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/sections/merge_requests.scss
@@ -122,6 +122,7 @@
background: $box_bg;
margin-bottom: 20px;
color: #666;
+ border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget {
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss
index 93c0c2bc518..0a7671e3feb 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/sections/projects.scss
@@ -16,6 +16,8 @@
.project-home-panel {
margin-bottom: 15px;
+ position: relative;
+ padding-left: 85px;
&.empty-project {
border-bottom: 0px;
@@ -23,6 +25,22 @@
margin-bottom: 0px;
}
+ .project-identicon-holder {
+ position: absolute;
+ left: 0;
+
+ .avatar {
+ width: 70px;
+ height: 70px;
+ @include border-radius(0px);
+ }
+
+ .identicon {
+ font-size: 45px;
+ line-height: 1.6;
+ }
+ }
+
.project-home-dropdown {
margin-left: 10px;
float: right;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ad13a0ac3e4..36e13706768 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -181,7 +181,7 @@ class ApplicationController < ActionController::Base
end
def add_gon_variables
- gon.default_issues_tracker = Project.issues_tracker.default_value
+ gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
gon.api_version = API::API.version
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
diff --git a/app/controllers/projects/base_tree_controller.rb b/app/controllers/projects/base_tree_controller.rb
deleted file mode 100644
index a7b1b7b40e8..00000000000
--- a/app/controllers/projects/base_tree_controller.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Projects::BaseTreeController < Projects::ApplicationController
- include ExtractsPath
-
- before_filter :authorize_download_code!
- before_filter :require_non_empty_project
-end
-
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index 367d1295f34..106f21b83e6 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -2,7 +2,7 @@
class Projects::BlameController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 2412800c493..b471d57f698 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -2,16 +2,70 @@
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
- # Authorize
- before_filter :authorize_download_code!
- before_filter :require_non_empty_project
- before_filter :authorize_push_code!, only: [:destroy]
+ # Raised when given an invalid file path
+ class InvalidPathError < StandardError; end
- before_filter :blob
+ before_filter :authorize_download_code!
+ before_filter :require_non_empty_project, except: [:new, :create]
+ before_filter :authorize_push_code!, only: [:destroy]
+ before_filter :assign_blob_vars
+ before_filter :commit, except: [:new, :create]
+ before_filter :blob, except: [:new, :create]
+ before_filter :from_merge_request, only: [:edit, :update]
+ before_filter :after_edit_path, only: [:edit, :update]
+ before_filter :require_branch_head, only: [:edit, :update]
+
+ def new
+ commit unless @repository.empty?
+ end
+
+ def create
+ file_path = File.join(@path, File.basename(params[:file_name]))
+ result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+ redirect_to project_blob_path(@project, File.join(@ref, file_path))
+ else
+ flash[:alert] = result[:message]
+ render :new
+ end
+ end
def show
end
+ def edit
+ @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
+ end
+
+ def update
+ result = Files::UpdateService.
+ new(@project, current_user, params, @ref, @path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+
+ if from_merge_request
+ from_merge_request.reload_code
+ end
+
+ redirect_to after_edit_path
+ else
+ flash[:alert] = result[:message]
+ render :edit
+ end
+ end
+
+ def preview
+ @content = params[:content]
+ diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
+ include_diff_info: true)
+ @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
+
+ render layout: false
+ end
+
def destroy
result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute
@@ -46,10 +100,44 @@ class Projects::BlobController < Projects::ApplicationController
if @blob
@blob
- elsif tree.entries.any?
- redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
else
+ if tree = @repository.tree(@commit.id, @path)
+ if tree.entries.any?
+ redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
+ end
+ end
+
return not_found!
end
end
+
+ def commit
+ @commit = @repository.commit(@ref)
+
+ return not_found! unless @commit
+ end
+
+ def assign_blob_vars
+ @id = params[:id]
+ @ref, @path = extract_ref(@id)
+
+
+ rescue InvalidPathError
+ not_found!
+ end
+
+ def after_edit_path
+ @after_edit_path ||=
+ if from_merge_request
+ diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
+ "#file-path-#{hexdigest(@path)}"
+ else
+ project_blob_path(@project, @id)
+ end
+ end
+
+ def from_merge_request
+ # If blob edit was initiated from merge request page
+ @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
+ end
end
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 9476b6c0284..0a85c36a758 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -3,7 +3,7 @@ require "base64"
class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
deleted file mode 100644
index 65661c80410..00000000000
--- a/app/controllers/projects/edit_tree_controller.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-class Projects::EditTreeController < Projects::BaseTreeController
- before_filter :require_branch_head
- before_filter :blob
- before_filter :authorize_push_code!
- before_filter :from_merge_request
- before_filter :after_edit_path
-
- def show
- @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
- end
-
- def update
- result = Files::UpdateService.
- new(@project, current_user, params, @ref, @path).execute
-
- if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
-
- if from_merge_request
- from_merge_request.reload_code
- end
-
- redirect_to after_edit_path
- else
- flash[:alert] = result[:message]
- render :show
- end
- end
-
- def preview
- @content = params[:content]
-
- diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
- include_diff_info: true)
- @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
-
- render layout: false
- end
-
- private
-
- def blob
- @blob ||= @repository.blob_at(@commit.id, @path)
- end
-
- def after_edit_path
- @after_edit_path ||=
- if from_merge_request
- diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
- "#file-path-#{hexdigest(@path)}"
- else
- project_blob_path(@project, @id)
- end
- end
-
- def from_merge_request
- # If blob edit was initiated from merge request page
- @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
- end
-end
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index ada1aed0df7..59f2a745367 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -2,7 +2,7 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/new_tree_controller.rb b/app/controllers/projects/new_tree_controller.rb
deleted file mode 100644
index ffba706b2f6..00000000000
--- a/app/controllers/projects/new_tree_controller.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class Projects::NewTreeController < Projects::BaseTreeController
- before_filter :require_branch_head
- before_filter :authorize_push_code!
-
- def show
- end
-
- def update
- file_path = File.join(@path, File.basename(params[:file_name]))
- result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute
-
- if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
- redirect_to project_blob_path(@project, File.join(@ref, file_path))
- else
- flash[:alert] = result[:message]
- render :show
- end
- end
-end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index fdbc4c5a098..84888265dc1 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -2,7 +2,7 @@
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 67665f5f601..cede0ebe0ae 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -1,7 +1,7 @@
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 5ac6947c5de..5b35cc90413 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -9,7 +9,7 @@ class Projects::ServicesController < Projects::ApplicationController
def index
@project.build_missing_services
- @services = @project.services.reload
+ @services = @project.services.visible.reload
end
def edit
@@ -17,7 +17,8 @@ class Projects::ServicesController < Projects::ApplicationController
def update
if @service.update_attributes(service_params)
- redirect_to edit_project_service_path(@project, @service.to_param)
+ redirect_to edit_project_service_path(@project, @service.to_param),
+ notice: 'Successfully updated.'
else
render 'edit'
end
@@ -45,7 +46,8 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
- :build_key, :server, :teamcity_url, :build_type
+ :build_key, :server, :teamcity_url, :build_type,
+ :description, :issues_url, :new_issue_url
)
end
end
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 4d033b36848..5b52640a4e1 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,7 +1,12 @@
# Controller for viewing a repository's file structure
-class Projects::TreeController < Projects::BaseTreeController
- def show
+class Projects::TreeController < Projects::ApplicationController
+ include ExtractsPath
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
+ before_filter :require_non_empty_project, except: [:new, :create]
+
+ def show
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
redirect_to project_blob_path(@project, File.join(@ref, @path)) and return
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 67af1801bda..ff5e31067fb 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,16 +1,12 @@
class UsersController < ApplicationController
- skip_before_filter :authenticate_user!, only: [:show]
+ skip_before_filter :authenticate_user!
+ before_filter :set_user
layout :determine_layout
def show
- @user = User.find_by_username!(params[:username])
-
- unless current_user || @user.public_profile?
- return authenticate_user!
- end
-
# Projects user can view
- authorized_projects_ids = ProjectsFinder.new.execute(current_user).pluck(:id)
+ visible_projects = ProjectsFinder.new.execute(current_user)
+ authorized_projects_ids = visible_projects.pluck(:id)
@projects = @user.personal_projects.
where(id: authorized_projects_ids)
@@ -30,6 +26,19 @@ class UsersController < ApplicationController
end
end
+ def calendar
+ visible_projects = ProjectsFinder.new.execute(current_user)
+
+ # Get user repositories and collect timestamps for commits
+ user_repositories = visible_projects.map(&:repository)
+ calendar = Gitlab::CommitsCalendar.new(user_repositories, @user)
+ @timestamps = calendar.timestamps
+ @starting_year = (Time.now - 1.year).strftime("%Y")
+ @starting_month = Date.today.strftime("%m").to_i
+
+ render 'calendar', layout: false
+ end
+
def determine_layout
if current_user
'navless'
@@ -37,4 +46,14 @@ class UsersController < ApplicationController
'public_users'
end
end
+
+ private
+
+ def set_user
+ @user = User.find_by_username!(params[:username])
+
+ unless current_user || @user.public_profile?
+ return authenticate_user!
+ end
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f253ae91306..104ae517a08 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -56,8 +56,6 @@ module ApplicationHelper
image_tag project.avatar.url, options
elsif project.avatar_in_git
image_tag project_avatar_path(project), options
- elsif options[:only_uploaded]
- image_tag '/assets/no_project_icon.png', options
else # generated icon
project_identicon(project, options)
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 3a282803963..e75eebd2da9 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -19,4 +19,42 @@ module BlobHelper
def no_highlight_files
%w(credits changelog copying copyright license authors)
end
+
+ def edit_blob_link(project, ref, path, options = {})
+ blob =
+ begin
+ project.repository.blob_at(ref, path)
+ rescue
+ nil
+ end
+
+ if blob && blob.text?
+ text = 'Edit'
+ after = options[:after] || ''
+ from_mr = options[:from_merge_request_id]
+ link_opts = {}
+ link_opts[:from_merge_request_id] = from_mr if from_mr
+ cls = 'btn btn-small'
+ if allowed_tree_edit?(project, ref)
+ link_to text, project_edit_blob_path(project, tree_join(ref, path),
+ link_opts), class: cls
+ else
+ content_tag :span, text, class: cls + ' disabled'
+ end + after.html_safe
+ else
+ ''
+ end
+ end
+
+ def leave_edit_message
+ "Leave edit mode?\nAll unsaved changes will be lost."
+ end
+
+ def editing_preview_title(filename)
+ if Gitlab::MarkdownHelper.previewable?(filename)
+ 'Preview'
+ else
+ 'Preview changes'
+ end
+ end
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index bcf108c5c48..9fe183e6e2f 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -16,45 +16,25 @@ module IssuesHelper
def url_for_project_issues(project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- project_issues_path(project)
- else
- url = Gitlab.config.issues_tracker[project.issues_tracker]['project_url']
- url.gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.project_url
end
def url_for_new_issue(project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- url = new_project_issue_path project_id: project
- else
- issues_tracker = Gitlab.config.issues_tracker[project.issues_tracker]
- url = issues_tracker['new_issue_url']
- url.gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.new_issue_url
end
def url_for_issue(issue_iid, project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- url = project_issue_url project_id: project, id: issue_iid
- else
- url = Gitlab.config.issues_tracker[project.issues_tracker]['issues_url']
- url.gsub(':id', issue_iid.to_s).
- gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.issue_url(issue_iid)
end
def title_for_issue(issue_iid, project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker?
+ if project.default_issues_tracker?
issue = project.issues.where(iid: issue_iid).first
return issue.title if issue
end
@@ -77,11 +57,6 @@ module IssuesHelper
ts.html_safe
end
- # Checks if issues_tracker setting exists in gitlab.yml
- def external_issues_tracker_enabled?
- Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any?
- end
-
def bulk_update_milestone_options
options_for_select(['None (backlog)']) +
options_from_collection_for_select(project_active_milestones, 'id',
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index de232ab4e25..351641e19af 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -72,18 +72,6 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC")
end
- def project_issues_trackers(current_tracker = nil)
- values = Project.issues_tracker.values.map do |tracker_key|
- if tracker_key.to_sym == :gitlab
- ['GitLab', tracker_key]
- else
- [Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key]
- end
- end
-
- options_for_select(values, current_tracker)
- end
-
def link_to_toggle_star(title, starred, signed_in)
cls = 'star-btn'
cls << ' disabled' unless signed_in
@@ -187,7 +175,13 @@ module ProjectsHelper
"Issues - " + title
end
elsif current_controller?(:blob)
- "#{@project.path}\/#{@blob.path} at #{@ref} - " + title
+ if current_action?(:new) || current_action?(:create)
+ "New file at #{@ref}"
+ elsif current_action?(:show)
+ "#{@blob.path} at #{@ref}"
+ elsif @blob
+ "Edit file #{@blob.path} at #{@ref}"
+ end
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
@@ -257,7 +251,7 @@ module ProjectsHelper
end
def github_import_enabled?
- Gitlab.config.omniauth.enabled && enabled_oauth_providers.include?(:github)
+ enabled_oauth_providers.include?(:github)
end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 1d987a6ffc0..727ec3fb231 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -64,32 +64,6 @@ module TreeHelper
::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
end
- def edit_blob_link(project, ref, path, options = {})
- blob =
- begin
- project.repository.blob_at(ref, path)
- rescue
- nil
- end
-
- if blob && blob.text?
- text = 'Edit'
- after = options[:after] || ''
- from_mr = options[:from_merge_request_id]
- link_opts = {}
- link_opts[:from_merge_request_id] = from_mr if from_mr
- cls = 'btn btn-small'
- if allowed_tree_edit?(project, ref)
- link_to text, project_edit_tree_path(project, tree_join(ref, path),
- link_opts), class: cls
- else
- content_tag :span, text, class: cls + ' disabled'
- end + after.html_safe
- else
- ''
- end
- end
-
def tree_breadcrumbs(tree, max_links = 2)
if @path.present?
part_path = ""
@@ -121,16 +95,4 @@ module TreeHelper
return tree.name
end
end
-
- def leave_edit_message
- "Leave edit mode?\nAll unsaved changes will be lost."
- end
-
- def editing_preview_title(filename)
- if Gitlab::MarkdownHelper.previewable?(filename)
- 'Preview'
- else
- 'Diff'
- end
- end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 733afa2fc07..e098dfb3cdf 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -25,6 +25,9 @@ class Group < Namespace
mount_uploader :avatar, AttachmentUploader
+ after_create :post_create_hook
+ after_destroy :post_destroy_hook
+
def human_name
name
end
@@ -74,6 +77,18 @@ class Group < Namespace
projects.public_only.any?
end
+ def post_create_hook
+ system_hook_service.execute_hooks_for(self, :create)
+ end
+
+ def post_destroy_hook
+ system_hook_service.execute_hooks_for(self, :destroy)
+ end
+
+ def system_hook_service
+ SystemHooksService.new
+ end
+
class << self
def search(query)
where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%")
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index b7f296b13fb..28d0b4483b4 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -27,8 +27,9 @@ class GroupMember < Member
scope :with_group, ->(group) { where(source_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
- after_create :notify_create
+ after_create :post_create_hook
after_update :notify_update
+ after_destroy :post_destroy_hook
def self.access_level_roles
Gitlab::Access.options_with_owner
@@ -42,8 +43,9 @@ class GroupMember < Member
access_level
end
- def notify_create
+ def post_create_hook
notification_service.new_group_member(self)
+ system_hook_service.execute_hooks_for(self, :create)
end
def notify_update
@@ -52,6 +54,14 @@ class GroupMember < Member
end
end
+ def post_destroy_hook
+ system_hook_service.execute_hooks_for(self, :destroy)
+ end
+
+ def system_hook_service
+ SystemHooksService.new
+ end
+
def notification_service
NotificationService.new
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 97f23227484..b26c697a7b7 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -74,7 +74,13 @@ class Project < ActiveRecord::Base
has_one :bamboo_service, dependent: :destroy
has_one :teamcity_service, dependent: :destroy
has_one :pushover_service, dependent: :destroy
- has_one :forked_project_link, dependent: :destroy, foreign_key: 'forked_to_project_id'
+ has_one :jira_service, dependent: :destroy
+ has_one :redmine_service, dependent: :destroy
+ has_one :custom_issue_tracker_service, dependent: :destroy
+ has_one :gitlab_issue_tracker_service, dependent: :destroy
+
+ has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
+
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
@@ -144,8 +150,6 @@ class Project < ActiveRecord::Base
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) }
- enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
-
state_machine :import_status, initial: :none do
event :import_start do
transition [:none, :finished] => :started
@@ -305,19 +309,43 @@ class Project < ActiveRecord::Base
end
def issue_exists?(issue_id)
- if used_default_issues_tracker?
+ if default_issues_tracker?
self.issues.where(iid: issue_id).first.present?
else
true
end
end
- def used_default_issues_tracker?
- self.issues_tracker == Project.issues_tracker.default_value
+ def default_issue_tracker
+ gitlab_issue_tracker_service ||= create_gitlab_issue_tracker_service
+ end
+
+ def issues_tracker
+ if external_issue_tracker
+ external_issue_tracker
+ else
+ default_issue_tracker
+ end
+ end
+
+ def default_issues_tracker?
+ if external_issue_tracker
+ false
+ else
+ true
+ end
+ end
+
+ def external_issues_trackers
+ services.select(&:issue_tracker?).reject(&:default?)
+ end
+
+ def external_issue_tracker
+ @external_issues_tracker ||= external_issues_trackers.select(&:activated?).first
end
def can_have_issues_tracker_id?
- self.issues_enabled && !self.used_default_issues_tracker?
+ self.issues_enabled && !self.default_issues_tracker?
end
def build_missing_services
@@ -332,7 +360,7 @@ class Project < ActiveRecord::Base
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla
- emails_on_push gemnasium slack pushover buildbox bamboo teamcity)
+ emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
end
def gitlab_ci?
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
new file mode 100644
index 00000000000..2476b62da89
--- /dev/null
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -0,0 +1,38 @@
+class CustomIssueTrackerService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Custom Issue Tracker'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Custom issue tracker'
+ end
+ end
+
+ def to_param
+ 'custom_issue_tracker'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'title', placeholder: title },
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url'},
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url'}
+ ]
+ end
+
+ def initialize_properties
+ self.properties = {} if properties.nil?
+ end
+end
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
new file mode 100644
index 00000000000..25f5f23bdf9
--- /dev/null
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -0,0 +1,25 @@
+class GitlabIssueTrackerService < IssueTrackerService
+ include Rails.application.routes.url_helpers
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+
+ def default?
+ true
+ end
+
+ def to_param
+ 'gitlab'
+ end
+
+ def project_url
+ project_issues_path(project)
+ end
+
+ def new_issue_url
+ new_project_issue_path project_id: project
+ end
+
+ def issue_url(iid)
+ "#{Gitlab.config.gitlab.url}#{project_issue_path(project_id: project, id: iid)}"
+ end
+end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
new file mode 100644
index 00000000000..632f053d17b
--- /dev/null
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -0,0 +1,74 @@
+class IssueTrackerService < Service
+
+ validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
+
+ def category
+ :issue_tracker
+ end
+
+ def default?
+ false
+ end
+
+ def project_url
+ # implement inside child
+ end
+
+ def issues_url
+ # implement inside child
+ end
+
+ def new_issue_url
+ # implement inside child
+ end
+
+ def issue_url(iid)
+ self.issues_url.gsub(':id', iid.to_s)
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url'},
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url'}
+ ]
+ end
+
+ def initialize_properties
+ if properties.nil?
+ if enabled_in_gitlab_config
+ self.properties = {
+ title: issues_tracker['title'],
+ project_url: set_project_url,
+ issues_url: issues_tracker['issues_url'],
+ new_issue_url: issues_tracker['new_issue_url']
+ }
+ else
+ self.properties = {}
+ end
+ end
+ end
+
+ private
+
+ def enabled_in_gitlab_config
+ Gitlab.config.issues_tracker &&
+ Gitlab.config.issues_tracker.values.any? &&
+ issues_tracker
+ end
+
+ def issues_tracker
+ Gitlab.config.issues_tracker[to_param]
+ end
+
+ def set_project_url
+ id = self.project.issues_tracker_id
+
+ if id
+ issues_tracker['project_url'].gsub(":issues_tracker_id", id)
+ else
+ issues_tracker['project_url']
+ end
+ end
+end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
new file mode 100644
index 00000000000..b0d668948d0
--- /dev/null
+++ b/app/models/project_services/jira_service.rb
@@ -0,0 +1,24 @@
+class JiraService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'JIRA'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Jira issue tracker'
+ end
+ end
+
+ def to_param
+ 'jira'
+ end
+end
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
new file mode 100644
index 00000000000..11cce3e0561
--- /dev/null
+++ b/app/models/project_services/redmine_service.rb
@@ -0,0 +1,24 @@
+class RedmineService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Redmine'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Redmine issue tracker'
+ end
+ end
+
+ def to_param
+ 'redmine'
+ end
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e93c76790c7..f6400f7aff1 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -139,8 +139,10 @@ class Repository
def graph_log
Rails.cache.fetch(cache_key(:graph_log)) do
- commits = raw_repository.log(limit: 6000, skip_merges: true,
+ commits = raw_repository.log(limit: 6000,
+ skip_merges: true,
ref: root_ref)
+
commits.map do |rugged_commit|
commit = Gitlab::Git::Commit.new(rugged_commit)
@@ -148,12 +150,32 @@ class Repository
author_name: commit.author_name.force_encoding('UTF-8'),
author_email: commit.author_email.force_encoding('UTF-8'),
additions: commit.stats.additions,
- deletions: commit.stats.deletions
+ deletions: commit.stats.deletions,
}
end
end
end
+ def timestamps_by_user_log(user)
+ args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short)
+ dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
+
+ if dates.present?
+ dates
+ else
+ []
+ end
+ end
+
+ def commits_per_day_for_user(user)
+ timestamps_by_user_log(user).
+ group_by { |commit_date| commit_date }.
+ inject({}) do |hash, (timestamp_date, commits)|
+ hash[timestamp_date] = commits.count
+ hash
+ end
+ end
+
def cache_key(type)
"#{type}:#{path_with_namespace}"
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 71c8aa39e45..15948e63e41 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -26,6 +26,8 @@ class Service < ActiveRecord::Base
validates :project_id, presence: true
+ scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
+
def activated?
active
end
@@ -86,4 +88,12 @@ class Service < ActiveRecord::Base
def async_execute(data)
Sidekiq::Client.enqueue(ProjectServiceWorker, id, data)
end
+
+ def issue_tracker?
+ self.category == :issue_tracker
+ end
+
+ def self.issue_tracker_service_list
+ Service.select(&:issue_tracker?).map{ |s| s.to_param }
+ end
end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index b90adeef00a..2c457ef2cef 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -9,10 +9,6 @@ module Files
return error("You are not allowed to create file in this branch")
end
- unless repository.branch_names.include?(ref)
- return error("You can only create files if you are on top of a branch")
- end
-
file_name = File.basename(path)
file_path = path
@@ -23,12 +19,21 @@ module Files
)
end
- blob = repository.blob_at_branch(ref, file_path)
+ if project.empty_repo?
+ # everything is ok because repo does not have a commits yet
+ else
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
- if blob
- return error("Your changes could not be committed, because file with such name exists")
+ blob = repository.blob_at_branch(ref, file_path)
+
+ if blob
+ return error("Your changes could not be committed, because file with such name exists")
+ end
end
+
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
created_successfully = new_file_action.commit!(
params[:content],
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index ffed13a12e1..f670019cc63 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -2,9 +2,9 @@ module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
if issue.close
- notification_service.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue, commit)
+ notification_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 0ee9635ed99..83e413d7248 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -23,8 +23,8 @@ module Issues
end
if issue.previous_changes.include?('assignee_id')
- notification_service.reassigned_issue(issue, current_user)
create_assignee_note(issue)
+ notification_service.reassigned_issue(issue, current_user)
end
issue.notice_added_references(issue.project, current_user)
diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb
index b5d90a74e15..378b39bb9d6 100644
--- a/app/services/merge_requests/auto_merge_service.rb
+++ b/app/services/merge_requests/auto_merge_service.rb
@@ -11,9 +11,9 @@ module MergeRequests
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
- notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
create_note(merge_request)
+ notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request)
true
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index 4249a84f382..47454f9f0c2 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -7,8 +7,8 @@ module MergeRequests
if merge_request.close
event_service.close_mr(merge_request, current_user)
- notification_service.close_mr(merge_request, current_user)
create_note(merge_request)
+ notification_service.close_mr(merge_request, current_user)
execute_hooks(merge_request, 'close')
end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 1e1614028f7..327ead4ff3f 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -9,9 +9,9 @@ module MergeRequests
def execute(merge_request, commit_message)
merge_request.merge
- notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
create_note(merge_request)
+ notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
true
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index a2a9c933f63..8279ad2001b 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -3,8 +3,8 @@ module MergeRequests
def execute(merge_request)
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
- notification_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
+ notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
merge_request.reload_code
merge_request.mark_as_unchecked
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 56c8510e0ae..10c401756eb 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -33,8 +33,8 @@ module MergeRequests
end
if merge_request.previous_changes.include?('assignee_id')
- notification_service.reassigned_merge_request(merge_request, current_user)
create_assignee_note(merge_request)
+ notification_service.reassigned_merge_request(merge_request, current_user)
end
merge_request.notice_added_references(merge_request.project, current_user)
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 72c9149378e..2fc63b9f4b7 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -314,15 +314,7 @@ class NotificationService
end
def new_resource_email(target, project, method)
- if target.respond_to?(:participants)
- recipients = target.participants
- else
- recipients = []
- end
-
- recipients = reject_muted_users(recipients, project)
- recipients = reject_mention_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(target.author)
recipients.each do |recipient|
@@ -331,9 +323,7 @@ class NotificationService
end
def close_resource_email(target, project, current_user, method)
- recipients = reject_muted_users([target.author, target.assignee], project)
- recipients = reject_mention_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -343,17 +333,7 @@ class NotificationService
def reassign_resource_email(target, project, current_user, method)
assignee_id_was = previous_record(target, "assignee_id")
-
- recipients = User.where(id: [target.assignee_id, assignee_id_was])
-
- # Add watchers to email list
- recipients = recipients.concat(project_watchers(project))
-
- # reject users with disabled notifications
- recipients = reject_muted_users(recipients, project)
- recipients = reject_mention_users(recipients, project)
-
- # Reject me from recipients if I reassign an item
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -362,9 +342,7 @@ class NotificationService
end
def reopen_resource_email(target, project, current_user, method, status)
- recipients = reject_muted_users([target.author, target.assignee], project)
- recipients = reject_mention_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -372,6 +350,20 @@ class NotificationService
end
end
+ def build_recipients(target, project)
+ recipients =
+ if target.respond_to?(:participants)
+ target.participants
+ else
+ [target.author, target.assignee]
+ end
+
+ recipients = reject_muted_users(recipients, project)
+ recipients = reject_mention_users(recipients, project)
+ recipients = recipients.concat(project_watchers(project)).uniq
+ recipients
+ end
+
def mailer
Notify.delay
end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 44e494525b3..46f6e91e808 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -60,6 +60,26 @@ class SystemHooksService
access_level: model.human_access,
project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
})
+ when Group
+ owner = model.owner
+
+ data.merge!(
+ name: model.name,
+ path: model.path,
+ group_id: model.id,
+ owner_name: owner.respond_to?(:name) ? owner.name : nil,
+ owner_email: owner.respond_to?(:email) ? owner.email : nil,
+ )
+ when GroupMember
+ data.merge!(
+ group_name: model.group.name,
+ group_path: model.group.path,
+ group_id: model.group.id,
+ user_name: model.user.name,
+ user_email: model.user.email,
+ user_id: model.user.id,
+ group_access: model.human_access,
+ )
end
end
@@ -68,6 +88,9 @@ class SystemHooksService
when ProjectMember
return "user_add_to_team" if event == :create
return "user_remove_from_team" if event == :destroy
+ when GroupMember
+ return 'user_add_to_group' if event == :create
+ return 'user_remove_from_group' if event == :destroy
else
"#{model.class.name.downcase}_#{event.to_s}"
end
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index dd95af426c4..32e0e4a6848 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -32,7 +32,7 @@
%span.light.pull-right
= Milestone.count
%p
- Active users last 30 days
+ Users who signed in during last 30 days
%span.light.pull-right
= User.where("current_sign_in_at > ?", 30.days.ago).count
.col-md-4
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 61383315373..c7976ba564f 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -11,5 +11,4 @@
- elsif event.note?
= render "events/event/note", event: event
- else
- = render "events/event/common", event: event
-
+ = render "events/event/common", event: event
\ No newline at end of file
diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml
index e590ddbf931..ed00153de7e 100644
--- a/app/views/groups/_new_group_member.html.haml
+++ b/app/views/groups/_new_group_member.html.haml
@@ -5,7 +5,11 @@
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
+ .col-sm-10
+ = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
.form-actions
= f.submit 'Add users into group', class: "btn btn-create"
diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml
index 35180792a0d..e6aee22e529 100644
--- a/app/views/groups/_settings_nav.html.haml
+++ b/app/views/groups/_settings_nav.html.haml
@@ -1,11 +1,11 @@
%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group) do
+ = link_to edit_group_path(@group), title: 'Group' do
%i.fa.fa-pencil-square-o
%span
Group
= nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group) do
+ = link_to projects_group_path(@group), title: 'Projects' do
%i.fa.fa-folder
%span
Projects
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index bdf27562c26..77bfe4f996e 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -43,6 +43,6 @@
%i.fa.fa-sign-out
%li.hidden-xs
= link_to current_user, class: "profile-pic", id: 'profile-pic' do
- = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
+ = image_tag avatar_icon(current_user.email, 60), alt: 'User activity'
= render 'shared/outdated_browser'
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 621365fa6aa..1263f44eca9 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,5 +1,6 @@
- if defined?(sidebar)
.page-with-sidebar
+ = render "layouts/broadcast"
.sidebar-wrapper
= render(sidebar)
.content-wrapper
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index fb62d5fea0a..dc8652cb145 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} #{theme_type} admin", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/head_panel", title: "Admin area"
= render 'layouts/page', sidebar: 'layouts/nav/admin'
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index d40c9753b10..e5420a13605 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page }
- = render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard"
= render 'layouts/page', sidebar: 'layouts/nav/dashboard'
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index 72b0d03908d..98edcf3a140 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/head_panel", title: @group.name
= render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index d9c6670d1bc..4813a4f16f5 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -5,49 +5,50 @@
%span
Overview
= nav_link(controller: :projects) do
- = link_to admin_projects_path do
+ = link_to admin_projects_path, title: 'Projects' do
%i.fa.fa-cube
%span
Projects
= nav_link(controller: :users) do
- = link_to admin_users_path do
+ = link_to admin_users_path, title: 'Users' do
%i.fa.fa-user
%span
Users
= nav_link(controller: :groups) do
- = link_to admin_groups_path do
+ = link_to admin_groups_path, title: 'Groups' do
%i.fa.fa-group
%span
Groups
= nav_link(controller: :logs) do
- = link_to admin_logs_path do
+ = link_to admin_logs_path, title: 'Logs' do
%i.fa.fa-file-text
%span
Logs
= nav_link(controller: :broadcast_messages) do
- = link_to admin_broadcast_messages_path do
+ = link_to admin_broadcast_messages_path, title: 'Broadcast Messages' do
%i.fa.fa-bullhorn
%span
Messages
= nav_link(controller: :hooks) do
- = link_to admin_hooks_path do
+ = link_to admin_hooks_path, title: 'Hooks' do
%i.fa.fa-external-link
%span
Hooks
= nav_link(controller: :background_jobs) do
- = link_to admin_background_jobs_path do
+ = link_to admin_background_jobs_path, title: 'Background Jobs' do
%i.fa.fa-cog
%span
Background Jobs
- = nav_link(controller: :application_settings) do
- = link_to admin_application_settings_path do
+ = nav_link(controller: :applications) do
+ = link_to admin_applications_path, title: 'Applications' do
+ %i.fa.fa-cloud
+ %span
+ Applications
+
+ = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
+ = link_to admin_application_settings_path, title: 'Settings' do
%i.fa.fa-cogs
%span
Settings
- = nav_link(controller: :applications) do
- = link_to admin_applications_path do
- %i.fa.fa-cloud
- %span
- Applications
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index a2eaa2d83c5..48c7c999427 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -5,24 +5,24 @@
%span
Activity
= nav_link(path: 'dashboard#projects') do
- = link_to projects_dashboard_path, class: 'shortcuts-projects' do
+ = link_to projects_dashboard_path, title: 'Projects', class: 'shortcuts-projects' do
%i.fa.fa-cube
%span
Projects
= nav_link(path: 'dashboard#issues') do
- = link_to assigned_issues_dashboard_path, class: 'shortcuts-issues' do
+ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
- = link_to assigned_mrs_dashboard_path, class: 'shortcuts-merge_requests' do
+ = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do
- = link_to help_path do
+ = link_to help_path, title: 'Help' do
%i.fa.fa-question-circle
%span
Help
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 54468d077ab..ddd3df19eec 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -6,33 +6,33 @@
Activity
- if current_user
= nav_link(controller: [:group, :milestones]) do
- = link_to group_milestones_path(@group) do
+ = link_to group_milestones_path(@group), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
= nav_link(path: 'groups#issues') do
- = link_to issues_group_path(@group) do
+ = link_to issues_group_path(@group), title: 'Issues' do
%i.fa.fa-exclamation-circle
%span
Issues
- if current_user
%span.count= Issue.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
- = link_to merge_requests_group_path(@group) do
+ = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%i.fa.fa-tasks
%span
Merge Requests
- if current_user
%span.count= MergeRequest.opened.of_group(@group).count
= nav_link(path: 'groups#members') do
- = link_to members_group_path(@group) do
+ = link_to members_group_path(@group), title: 'Members' do
%i.fa.fa-users
%span
Members
- if can?(current_user, :manage_group, @group)
= nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
- = link_to edit_group_path(@group), class: "tab no-highlight" do
+ = link_to edit_group_path(@group), title: 'Settings', class: "tab no-highlight" do
%i.fa.fa-cogs
%span
Settings
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index cc50b9b570a..0914d2a167a 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -5,52 +5,51 @@
%span
Profile
= nav_link(controller: :accounts) do
- = link_to profile_account_path do
+ = link_to profile_account_path, title: 'Account' do
%i.fa.fa-gear
%span
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
- = link_to applications_profile_path do
+ = link_to applications_profile_path, title: 'Applications' do
%i.fa.fa-cloud
%span
Applications
= nav_link(controller: :emails) do
- = link_to profile_emails_path do
+ = link_to profile_emails_path, title: 'Emails' do
%i.fa.fa-envelope-o
%span
Emails
%span.count= current_user.emails.count + 1
- unless current_user.ldap_user?
= nav_link(controller: :passwords) do
- = link_to edit_profile_password_path do
+ = link_to edit_profile_password_path, title: 'Password' do
%i.fa.fa-lock
%span
Password
= nav_link(controller: :notifications) do
- = link_to profile_notifications_path do
+ = link_to profile_notifications_path, title: 'Notifications' do
%i.fa.fa-inbox
%span
Notifications
= nav_link(controller: :keys) do
- = link_to profile_keys_path do
+ = link_to profile_keys_path, title: 'SSH Keys' do
%i.fa.fa-key
%span
SSH Keys
%span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
- = link_to design_profile_path do
+ = link_to design_profile_path, title: 'Design' do
%i.fa.fa-image
%span
Design
= nav_link(controller: :groups) do
- = link_to profile_groups_path do
+ = link_to profile_groups_path, title: 'Groups' do
%i.fa.fa-group
%span
Groups
= nav_link(path: 'profiles#history') do
- = link_to history_profile_path do
+ = link_to history_profile_path, title: 'History' do
%i.fa.fa-history
%span
History
-
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 94cee0bd50f..6c2d5966cbe 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -6,45 +6,44 @@
Project
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
- = link_to project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' do
+ = link_to project_tree_path(@project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do
%i.fa.fa-files-o
%span
Files
-
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
- = link_to project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' do
+ = link_to project_commits_path(@project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do
%i.fa.fa-history
%span
Commits
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
- = link_to project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' do
+ = link_to project_network_path(@project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do
%i.fa.fa-code-fork
%span
Network
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
- = link_to project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' do
+ = link_to project_graph_path(@project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do
%i.fa.fa-area-chart
%span
Graphs
- if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do
- = link_to url_for_project_issues, class: 'shortcuts-issues' do
+ = link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
- - if @project.used_default_issues_tracker?
+ - if @project.default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
- = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do
+ = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests
@@ -52,21 +51,21 @@
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
- = link_to project_wiki_path(@project, :home), class: 'shortcuts-wiki' do
+ = link_to project_wiki_path(@project, :home), title: 'Wiki', class: 'shortcuts-wiki' do
%i.fa.fa-book
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
- = link_to project_snippets_path(@project), class: 'shortcuts-snippets' do
+ = link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
%i.fa.fa-file-text-o
%span
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
- = link_to edit_project_path(@project), class: "stat-tab tab no-highlight" do
+ = link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do
%i.fa.fa-cogs
%span
Settings
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 941084cc4ad..89d816061e2 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} #{theme_type} profile", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/head_panel", title: "Profile"
= render 'layouts/page', sidebar: 'layouts/nav/profile'
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 0f20bf38bfd..d2c9c2a991c 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -2,7 +2,6 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
- = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- @project_settings_nav = true
diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml
index d4ee53db55c..c44a40c9c12 100644
--- a/app/views/layouts/projects.html.haml
+++ b/app/views/layouts/projects.html.haml
@@ -2,7 +2,6 @@
%html{ lang: "en"}
= render "layouts/head", title: project_head_title
%body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
- = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
= render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_group.html.haml b/app/views/layouts/public_group.html.haml
index 64794104ac5..ae3d2bd8a89 100644
--- a/app/views/layouts/public_group.html.haml
+++ b/app/views/layouts/public_group.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/public_head_panel", title: "group: #{@group.name}"
= render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml
index 5964a29d522..027e9a53139 100644
--- a/app/views/layouts/public_projects.html.haml
+++ b/app/views/layouts/public_projects.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project)
= render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_users.html.haml b/app/views/layouts/public_users.html.haml
index 0510ce34a7f..37767df33d2 100644
--- a/app/views/layouts/public_users.html.haml
+++ b/app/views/layouts/public_users.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: @title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/public_head_panel", title: @title
= render 'layouts/page'
diff --git a/app/views/projects/_blob_editor.html.haml b/app/views/projects/_blob_editor.html.haml
deleted file mode 100644
index 1fb74b55c41..00000000000
--- a/app/views/projects/_blob_editor.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-.file-holder.file
- .file-title
- %i.icon-file
- %span.file_name
- %span.monospace.light #{ref}
- - if local_assigns[:path]
- = ': ' + local_assigns[:path]
- .file-content.code
- %pre.js-edit-mode-pane#editor
- = params[:content] || local_assigns[:blob_data]
- - if local_assigns[:path]
- .js-edit-mode-pane#preview.hide
- .center
- %h2
- %i.icon-spinner.icon-spin
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 05910c6038c..2ed49f83a7a 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,8 +1,9 @@
- empty_repo = @project.empty_repo?
.project-home-panel{:class => ("empty-project" if empty_repo)}
+ .project-identicon-holder
+ = project_icon(@project.to_param, alt: '', class: 'avatar')
.project-home-row
.project-home-desc
- = project_icon(@project.to_param, alt: '', class: 'avatar s32')
- if @project.description.present?
= escaped_autolink(@project.description)
- if can?(current_user, :admin_project, @project)
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
index 64eda0bf286..646e48a1e1d 100644
--- a/app/views/projects/_settings_nav.html.haml
+++ b/app/views/projects/_settings_nav.html.haml
@@ -1,31 +1,31 @@
%ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do
- = link_to edit_project_path(@project), class: "stat-tab tab " do
+ = link_to edit_project_path(@project), title: 'Project', class: "stat-tab tab " do
%i.fa.fa-pencil-square-o
%span
Project
= nav_link(controller: [:team_members, :teams]) do
- = link_to project_team_index_path(@project), class: "team-tab tab" do
+ = link_to project_team_index_path(@project), title: 'Members', class: "team-tab tab" do
%i.fa.fa-users
%span
Members
= nav_link(controller: :deploy_keys) do
- = link_to project_deploy_keys_path(@project) do
+ = link_to project_deploy_keys_path(@project), title: 'Deploy Keys' do
%i.fa.fa-key
%span
Deploy Keys
= nav_link(controller: :hooks) do
- = link_to project_hooks_path(@project) do
+ = link_to project_hooks_path(@project), title: 'Web Hooks' do
%i.fa.fa-link
%span
Web Hooks
= nav_link(controller: :services) do
- = link_to project_services_path(@project) do
+ = link_to project_services_path(@project), title: 'Services' do
%i.fa.fa-cogs
%span
Services
= nav_link(controller: :protected_branches) do
- = link_to project_protected_branches_path(@project) do
+ = link_to project_protected_branches_path(@project), title: 'Protected Branches' do
%i.fa.fa-lock
%span
Protected branches
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
new file mode 100644
index 00000000000..96f188e4aa7
--- /dev/null
+++ b/app/views/projects/blob/_editor.html.haml
@@ -0,0 +1,25 @@
+.file-holder.file
+ .file-title
+ .editor-ref
+ %i.fa.fa-code-fork
+ = ref
+ %span.editor-file-name
+ - if @path
+ %span.monospace
+ = @path
+
+ - if current_action?(:new) || current_action?(:create)
+ \/
+ = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
+ required: true, class: 'form-control new-file-name'
+ .pull-right
+ = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
+
+ .file-content.code
+ %pre.js-edit-mode-pane#editor
+ = params[:content] || local_assigns[:blob_data]
+ - if local_assigns[:path]
+ .js-edit-mode-pane#preview.hide
+ .center
+ %h2
+ %i.icon-spinner.icon-spin
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
new file mode 100644
index 00000000000..b150b639888
--- /dev/null
+++ b/app/views/projects/blob/edit.html.haml
@@ -0,0 +1,24 @@
+.file-editor
+ %ul.nav.nav-tabs.js-edit-mode
+ %li.active
+ = link_to '#editor' do
+ %i.fa.fa-edit
+ Edit file
+
+ %li
+ = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do
+ %i.fa.fa-eye
+ = editing_preview_title(@blob.name)
+
+ = form_tag(project_update_blob_path(@project, @id), method: :put, class: "form-horizontal") do
+ = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
+ = render 'shared/commit_message_container', params: params,
+ placeholder: "Update #{@blob.name}"
+ = hidden_field_tag 'last_commit', @last_commit
+ = hidden_field_tag 'content', '', id: "file-content"
+ = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
+ = render 'projects/commit_button', ref: @ref,
+ cancel_path: @after_edit_path
+
+:javascript
+ blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
new file mode 100644
index 00000000000..df6aedbe17d
--- /dev/null
+++ b/app/views/projects/blob/new.html.haml
@@ -0,0 +1,12 @@
+%h3.page-title New file
+.file-editor
+ = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'form-horizontal form-new-file') do
+ = render 'projects/blob/editor', ref: @ref
+ = render 'shared/commit_message_container', params: params,
+ placeholder: 'Add new file'
+ = hidden_field_tag 'content', '', id: 'file-content'
+ = render 'projects/commit_button', ref: @ref,
+ cancel_path: project_tree_path(@project, @id)
+
+:javascript
+ blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)
diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/blob/preview.html.haml
similarity index 100%
rename from app/views/projects/edit_tree/preview.html.haml
rename to app/views/projects/blob/preview.html.haml
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 28de1a778a7..31bdbb562a1 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -51,15 +51,6 @@
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
- - if Project.issues_tracker.values.count > 1
- .form-group
- = f.label :issues_tracker, "Issues tracker", class: 'control-label'
- .col-sm-10= f.select(:issues_tracker, project_issues_trackers(@project.issues_tracker), {}, { disabled: !@project.issues_enabled })
-
- .form-group
- = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
- .col-sm-10= f.text_field :issues_tracker_id, disabled: !@project.can_have_issues_tracker_id?, class: 'form-control'
-
.form-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
.col-sm-10
@@ -89,8 +80,6 @@
.col-sm-10
- if @project.avatar?
= project_icon(@project.to_param, alt: '', class: 'avatar s160')
- - else
- = project_icon(@project.to_param, alt: '', class: 'avatar s160', only_uploaded: true)
%p.light
- if @project.avatar_in_git
Project avatar in repository: #{ @project.avatar_in_git }
diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml
deleted file mode 100644
index 7e0789853af..00000000000
--- a/app/views/projects/edit_tree/show.html.haml
+++ /dev/null
@@ -1,57 +0,0 @@
-.file-editor
- %ul.nav.nav-tabs.js-edit-mode
- %li.active
- = link_to 'Edit', '#editor'
- %li
- = link_to editing_preview_title(@blob.name), '#preview', 'data-preview-url' => preview_project_edit_tree_path(@project, @id)
-
- = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do
- = render 'projects/blob_editor', ref: @ref, path: @path, blob_data: @blob.data
- = render 'shared/commit_message_container', params: params,
- placeholder: "Update #{@blob.name}"
- = hidden_field_tag 'last_commit', @last_commit
- = hidden_field_tag 'content', '', id: "file-content"
- = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
- = render 'projects/commit_button', ref: @ref,
- cancel_path: @after_edit_path
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace")
- ace.config.loadModule("ace/ext/searchbox");
- var ace_mode = "#{@blob.language.try(:ace_mode)}";
- var editor = ace.edit("editor");
- if (ace_mode) {
- editor.getSession().setMode('ace/mode/' + ace_mode);
- }
-
- disableButtonIfEmptyField("#commit_message", ".js-commit-button");
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
-
- var editModePanes = $('.js-edit-mode-pane'),
- editModeLinks = $('.js-edit-mode a');
-
- editModeLinks.click(function(event) {
- event.preventDefault();
-
- var currentLink = $(this),
- paneId = currentLink.attr('href'),
- currentPane = editModePanes.filter(paneId);
-
- editModeLinks.parent().removeClass('active hover');
- currentLink.parent().addClass('active hover');
- editModePanes.hide();
-
- if (paneId == '#preview') {
- currentPane.fadeIn(200);
- $.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) {
- currentPane.empty().append(response);
- })
- } else {
- currentPane.fadeIn(200);
- editor.focus()
- }
- })
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 2e46de6bfe0..36628195b4e 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -3,6 +3,17 @@
= render "home_panel"
+.center.well
+ %h3
+ The repository for this project is empty
+ %h4
+ You can
+ = link_to project_new_blob_path(@project, 'master'), class: 'btn btn-new btn-lg' do
+ add a file
+ or push it via command line.
+
+%h4
+ %strong Command line instructions
%div.git-empty
%fieldset
%legend Git global setup
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 54f2cef023b..959d5f08d47 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,5 +1,6 @@
%h3.page-title Fork project
-%p.lead Select namespace where to fork this project
+%p.lead
+ Click to fork the project to a user or group
%hr
.fork-namespaces
diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml
index 11a111e5faa..f8ee6973637 100644
--- a/app/views/projects/merge_requests/show/_mr_accept.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml
@@ -45,10 +45,17 @@
.automerge_widget.cannot_be_merged.hide
%h4
This request can't be merged with GitLab.
- %p
You should do it manually with
%strong
- = link_to "command line", "#modal_merge_info", class: "how_to_merge_link", title: "How To Merge", "data-toggle" => "modal"
+ = link_to "#modal_merge_info", class: "underlined-link how_to_merge_link", title: "How To Merge", "data-toggle" => "modal" do
+ command line
+
+ %p
+ %button.btn.disabled
+ %i.fa.fa-warning
+ Accept Merge Request
+
+ This usually happens when git can not resolve conflicts between branches automatically.
.automerge_widget.unchecked
%p
diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml
deleted file mode 100644
index cf7b768694f..00000000000
--- a/app/views/projects/new_tree/show.html.haml
+++ /dev/null
@@ -1,38 +0,0 @@
-%h3.page-title New file
-%hr
-.file-editor
- = form_tag(project_new_tree_path(@project, @id), method: :put, class: 'form-horizontal form-new-file') do
- .form-group.commit_message-group
- = label_tag 'file_name', class: 'control-label' do
- File name
- .col-sm-10
- .input-group
- %span.input-group-addon
- = @path[-1] == "/" ? @path : @path + "/"
- = text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true, class: 'form-control'
- %span.input-group-addon
- on
- %span= @ref
-
- .form-group.commit_message-group
- = label_tag :encoding, class: "control-label" do
- Encoding
- .col-sm-10
- = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
- = render 'projects/blob_editor', ref: @ref
- = render 'shared/commit_message_container', params: params,
- placeholder: 'Add new file'
- = hidden_field_tag 'content', '', id: 'file-content'
- = render 'projects/commit_button', ref: @ref,
- cancel_path: project_tree_path(@project, @id)
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
- var editor = ace.edit("editor");
-
- disableButtonIfAnyEmptyField($('.form-new-file'), '.form-control', '.btn-create')
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml
index 2bf61fa12bb..ddf8cb76f78 100644
--- a/app/views/projects/team_members/_form.html.haml
+++ b/app/views/projects/team_members/_form.html.haml
@@ -17,7 +17,12 @@
%p 2. Set access level for them
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
+ .col-sm-10
+ = select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
+
.form-actions
= f.submit 'Add users', class: "btn btn-create"
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 68ccd4d61bb..f902440b3f1 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -10,7 +10,7 @@
= link_to title, '#'
- if current_user && can_push_branch?(@project, @ref)
%li
- = link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
+ = link_to project_new_blob_path(@project, @id), title: 'New file', id: 'new-file-link' do
%small
%i.fa.fa-plus
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
new file mode 100644
index 00000000000..727faf23679
--- /dev/null
+++ b/app/views/users/calendar.html.haml
@@ -0,0 +1,8 @@
+%h4 Calendar:
+#cal-heatmap.calendar
+ :javascript
+ new calendar(
+ #{@timestamps.to_json},
+ #{@starting_year},
+ #{@starting_month}
+ );
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 54f2666ce5d..445f43cd500 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -18,6 +18,11 @@
%h4 Groups:
= render 'groups', groups: @groups
%hr
+
+ .user-calendar
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ %hr
%h4
User Activity:
@@ -32,3 +37,8 @@
= render 'profile', user: @user
- if @projects.present?
= render 'projects', projects: @projects
+
+
+:coffeescript
+ $ ->
+ $(".user-calendar").load("#{user_calendar_path}")
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index e5780cabb63..59af49c0180 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -153,9 +153,9 @@ production: &base
label: 'LDAP'
host: '_your_ldap_server'
- port: 636
+ port: 389
uid: 'sAMAccountName'
- method: 'ssl' # "tls" or "ssl" or "plain"
+ method: 'plain' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
diff --git a/config/routes.rb b/config/routes.rb
index 8c3eef23260..e122777314a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -157,6 +157,9 @@ Gitlab::Application.routes.draw do
end
end
+ get 'u/:username/calendar' => 'users#calendar', as: :user_calendar,
+ constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
+
get '/u/:username' => 'users#show', as: :user,
constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
@@ -211,17 +214,20 @@ Gitlab::Application.routes.draw do
end
scope module: :projects do
+ # Blob routes:
+ get '/new/:id', to: 'blob#new', constraints: {id: /.+/}, as: 'new_blob'
+ post '/create/:id', to: 'blob#create', constraints: {id: /.+/}, as: 'create_blob'
+ get '/edit/:id', to: 'blob#edit', constraints: {id: /.+/}, as: 'edit_blob'
+ put '/update/:id', to: 'blob#update', constraints: {id: /.+/}, as: 'update_blob'
+ post '/preview/:id', to: 'blob#preview', constraints: {id: /.+/}, as: 'preview_blob'
+
resources :blob, only: [:show, :destroy], constraints: { id: /.+/, format: false } do
get :diff, on: :member
end
+
resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
- resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
- # Cannot be GET to differentiate from GET paths that end in preview.
- post :preview, on: :member
- end
resource :avatar, only: [:show, :destroy]
- resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create]
diff --git a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
new file mode 100644
index 00000000000..c28ba3197ac
--- /dev/null
+++ b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
@@ -0,0 +1,5 @@
+class AddGitlabAccessTokenToUser < ActiveRecord::Migration
+ def change
+ add_column :users, :gitlab_access_token, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 32e49ff7a74..0e4af3df7c2 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -435,6 +435,7 @@ ActiveRecord::Schema.define(version: 20150125163100) do
t.boolean "hide_no_ssh_key", default: false
t.string "website_url", default: "", null: false
t.string "github_access_token"
+ t.string "gitlab_access_token"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/api/users.md b/doc/api/users.md
index b30a31deccc..71fa62bdd65 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -322,6 +322,31 @@ Parameters:
- `title` (required) - new SSH Key's title
- `key` (required) - new SSH key
+```json
+{
+ "created_at": "2015-01-21T17:44:33.512Z",
+ "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAMLrhYgI3atfrSD6KDas1b/3n6R/HP+bLaHHX6oh+L1vg31mdUqK0Ac/NjZoQunavoyzqdPYhFz9zzOezCrZKjuJDS3NRK9rspvjgM0xYR4d47oNZbdZbwkI4cTv/gcMlquRy0OvpfIvJtjtaJWMwTLtM5VhRusRuUlpH99UUVeXAAAAFQCVyX+92hBEjInEKL0v13c/egDCTQAAAIEAvFdWGq0ccOPbw4f/F8LpZqvWDydAcpXHV3thwb7WkFfppvm4SZte0zds1FJ+Hr8Xzzc5zMHe6J4Nlay/rP4ewmIW7iFKNBEYb/yWa+ceLrs+TfR672TaAgO6o7iSRofEq5YLdwgrwkMmIawa21FrZ2D9SPao/IwvENzk/xcHu7YAAACAQFXQH6HQnxOrw4dqf0NqeKy1tfIPxYYUZhPJfo9O0AmBW2S36pD2l14kS89fvz6Y1g8gN/FwFnRncMzlLY/hX70FSc/3hKBSbH6C6j8hwlgFKfizav21eS358JJz93leOakJZnGb8XlWvz1UJbwCsnR2VEY8Dz90uIk1l/UqHkA= loic@call",
+ "title": "ABC",
+ "id": 4
+}
+```
+
+Will return created key with status `201 Created` on success. If an
+error occurs a `400 Bad Request` is returned with a message explaining the error:
+
+```json
+{
+ "message": {
+ "fingerprint": [
+ "has already been taken"
+ ],
+ "key": [
+ "has already been taken"
+ ]
+ }
+}
+```
+
## Add SSH key for user
Create new key owned by specified user. Available only for admin
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 56b0d826adb..125ce31b521 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -29,9 +29,9 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
label: 'LDAP'
host: '_your_ldap_server'
- port: 636
+ port: 389
uid: 'sAMAccountName'
- method: 'ssl' # "tls" or "ssl" or "plain"
+ method: 'plain' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
@@ -76,6 +76,9 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
EOS
```
+If you are getting 'Connection Refused' errors when trying to connect to the LDAP server please double-check the LDAP `port` and `method` settings used by GitLab.
+Common combinations are `method: 'plain'` and `port: 389`, OR `method: 'ssl'` and `port: 636`.
+
If you are using a GitLab installation from source you can find the LDAP settings in `/home/git/gitlab/config/gitlab.yml`:
```
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 54e6e3a9e3f..41c2732ef77 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -1,6 +1,6 @@
# System hooks
-Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create` and `key_destroy`.
+Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`.
System hooks can be used, e.g. for logging or changing information in a LDAP server.
@@ -50,6 +50,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"project_path": "storecloud",
"user_email": "johnsmith@gmail.com",
"user_name": "John Smith",
+ "user_id": 41,
"project_visibility": "private",
}
```
@@ -66,6 +67,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"project_path": "storecloud",
"user_email": "johnsmith@gmail.com",
"user_name": "John Smith",
+ "user_id": 41,
"project_visibility": "private",
}
```
@@ -117,3 +119,62 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"id": 4
}
```
+
+**Group created:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:54Z",
+ "event_name": "group_create",
+ "name": "StormCloud",
+ "owner_email": "johnsmith@gmail.com",
+ "owner_name": "John Smith",
+ "path": "stormcloud",
+ "group_id": 78
+}
+```
+
+**Group removed:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:54Z",
+ "event_name": "group_destroy",
+ "name": "StoreCloud",
+ "owner_email": "johnsmith@gmail.com",
+ "owner_name": "John Smith",
+ "path": "storecloud",
+ "group_id": 78
+}
+```
+
+**New Group Member:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:56Z",
+ "event_name": "user_add_to_group",
+ "group_access": "Master",
+ "group_id": 78,
+ "group_name": "StoreCloud",
+ "group_path": "storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_id": 41
+}
+```
+**Group Member Removed:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:56Z",
+ "event_name": "user_remove_from_group",
+ "group_access": "Master",
+ "group_id": 78,
+ "group_name": "StoreCloud",
+ "group_path": "storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_id": 41
+}
+```
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 445fdd6d063..70d6c721f1c 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -11,7 +11,7 @@ RUN apt-get update -q \
# If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \
- wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.6.2-omnibus.5.3.0.ci.1-1_amd64.deb \
+ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.7.1-omnibus.5.4.1.ci-1_amd64.deb \
&& dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE
diff --git a/features/project/edit_issuetracker.feature b/features/project/edit_issuetracker.feature
deleted file mode 100644
index cc0de07ca69..00000000000
--- a/features/project/edit_issuetracker.feature
+++ /dev/null
@@ -1,18 +0,0 @@
-Feature: Project Issue Tracker
- Background:
- Given I sign in as a user
- And I own project "Shop"
- And project "Shop" has issues enabled
- And I visit project "Shop" page
-
- Scenario: I set the issue tracker to "GitLab"
- When I visit edit project "Shop" page
- And change the issue tracker to "GitLab"
- And I save project
- Then I the project should have "GitLab" as issue tracker
-
- Scenario: I set the issue tracker to "Redmine"
- When I visit edit project "Shop" page
- And change the issue tracker to "Redmine"
- And I save project
- Then I the project should have "Redmine" as issue tracker
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index 6ea64f70092..ccb29293a89 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -34,6 +34,19 @@ Feature: Project Source Browse Files
Then I am redirected to the new file
And I should see its new content
+ @javascript
+ Scenario: I can create file in empty repo
+ Given I own an empty project
+ And I visit my empty project page
+ And I create bare repo
+ When I click on "add a file" link
+ And I edit code
+ And I fill the new file name
+ And I fill the commit message
+ And I click on "Commit Changes"
+ Then I am redirected to the new file
+ And I should see its new content
+
@javascript
Scenario: If I enter an illegal file name I see an error message
Given I click on "new file" link in repo
diff --git a/features/steps/project/issue_tracker.rb b/features/steps/project/issue_tracker.rb
deleted file mode 100644
index e1700292701..00000000000
--- a/features/steps/project/issue_tracker.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-class Spinach::Features::ProjectIssueTracker < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedProject
- include SharedPaths
-
- step 'project "Shop" has issues enabled' do
- @project = Project.find_by(name: "Shop")
- @project ||= create(:project, name: "Shop", namespace: @user.namespace)
- @project.issues_enabled = true
- end
-
- step 'change the issue tracker to "GitLab"' do
- select 'GitLab', from: 'project_issues_tracker'
- end
-
- step 'I the project should have "GitLab" as issue tracker' do
- find_field('project_issues_tracker').value.should == 'gitlab'
- end
-
- step 'change the issue tracker to "Redmine"' do
- select 'Redmine', from: 'project_issues_tracker'
- end
-
- step 'I the project should have "Redmine" as issue tracker' do
- find_field('project_issues_tracker').value.should == 'redmine'
- end
-
- step 'I save project' do
- click_button 'Save changes'
- end
-end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 071ef75dc62..6f421de1aba 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -173,7 +173,9 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
merge!: true,
)
- click_button "Accept Merge Request"
+ within '.can_be_merged' do
+ click_button "Accept Merge Request"
+ end
end
step 'I should see merged request' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 805e6ff0eac..770e8162497 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -58,7 +58,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I can edit code' do
set_new_content
- evaluate_script('editor.getValue()').should == new_gitignore_content
+ evaluate_script('blob.editor.getValue()').should == new_gitignore_content
end
step 'I edit code' do
@@ -78,7 +78,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I click link "Diff"' do
- click_link 'Diff'
+ click_link 'Preview changes'
end
step 'I click on "Commit Changes"' do
@@ -103,7 +103,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I can see new file page' do
page.should have_content "New file"
- page.should have_content "File name"
page.should have_content "Commit message"
end
@@ -167,10 +166,21 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).to have_content('Your changes could not be committed')
end
+ step 'I create bare repo' do
+ click_link 'Create empty bare repository'
+ end
+
+ step 'I click on "add a file" link' do
+ click_link 'add a file'
+
+ # Remove pre-receive hook so we can push without auth
+ FileUtils.rm(File.join(Project.last.repository.path, 'hooks', 'pre-receive'))
+ end
+
private
def set_new_content
- execute_script("editor.setValue('#{new_gitignore_content}')")
+ execute_script("blob.editor.setValue('#{new_gitignore_content}')")
end
# Content of the gitignore file on the seed repository.
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 33ef6ccacf1..cef48c179b2 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -284,11 +284,11 @@ module SharedPaths
end
step 'I am on the new file page' do
- current_path.should eq(project_new_tree_path(@project, root_ref))
+ current_path.should eq(project_create_blob_path(@project, root_ref))
end
step 'I am on the ".gitignore" edit file page' do
- current_path.should eq(project_edit_tree_path(
+ current_path.should eq(project_edit_blob_path(
@project, File.join(root_ref, '.gitignore')))
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 0bd5653538c..cf0be256231 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -28,6 +28,10 @@ module SharedProject
@project.team << [@user, :master]
end
+ step 'I visit my empty project page' do
+ visit project_path(Project.find_by(name: 'Empty Project'))
+ end
+
step 'project "Shop" has push event' do
@project = Project.find_by(name: "Shop")
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 03a556a2c55..b259914a01c 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -58,11 +58,13 @@ module API
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# Example Request:
# GET /projects/:id/repository/tree
- get ":id/repository/tree" do
+ get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil
commit = user_project.repository.commit(ref)
+ not_found!('Tree') unless commit
+
tree = user_project.repository.tree(commit.id, path)
present tree.sorted_entries, with: Entities::RepoTreeObject
@@ -100,14 +102,18 @@ module API
# sha (required) - The blob's sha
# Example Request:
# GET /projects/:id/repository/raw_blobs/:sha
- get ":id/repository/raw_blobs/:sha" do
+ get ':id/repository/raw_blobs/:sha' do
ref = params[:sha]
repo = user_project.repository
- blob = Gitlab::Git::Blob.raw(repo, ref)
+ begin
+ blob = Gitlab::Git::Blob.raw(repo, ref)
+ rescue
+ not_found! 'Blob'
+ end
- not_found! "Blob" unless blob
+ not_found! 'Blob' unless blob
env['api.format'] = :txt
@@ -122,13 +128,23 @@ module API
# sha (optional) - the commit sha to download defaults to the tip of the default branch
# Example Request:
# GET /projects/:id/repository/archive
- get ":id/repository/archive", requirements: { format: Gitlab::Regex.archive_formats_regex } do
+ get ':id/repository/archive',
+ requirements: { format: Gitlab::Regex.archive_formats_regex } do
authorize! :download_code, user_project
- file_path = ArchiveRepositoryService.new.execute(user_project, params[:sha], params[:format])
+
+ begin
+ file_path = ArchiveRepositoryService.new.execute(
+ user_project,
+ params[:sha],
+ params[:format])
+ rescue
+ not_found!('File')
+ end
if file_path && File.exists?(file_path)
data = File.open(file_path, 'rb').read
- header["Content-Disposition"] = "attachment; filename=\"#{File.basename(file_path)}\""
+ basename = File.basename(file_path)
+ header['Content-Disposition'] = "attachment; filename=\"#{basename}\""
content_type MIME::Types.type_for(file_path).first.content_type
env['api.format'] = :binary
present data
@@ -161,7 +177,12 @@ module API
get ':id/repository/contributors' do
authorize! :download_code, user_project
- present user_project.repository.contributors, with: Entities::Contributor
+ begin
+ present user_project.repository.contributors,
+ with: Entities::Contributor
+ rescue
+ not_found!
+ end
end
end
end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index e51cb30bdd9..19215cfb7e6 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -1,17 +1,9 @@
# Module providing methods for dealing with separating a tree-ish string and a
# file path string when combined in a request parameter
module ExtractsPath
- extend ActiveSupport::Concern
-
# Raised when given an invalid file path
class InvalidPathError < StandardError; end
- included do
- if respond_to?(:before_filter)
- before_filter :assign_ref_vars
- end
- end
-
# Given a string containing both a Git tree-ish, such as a branch or tag, and
# a filesystem path joined by forward slashes, attempts to separate the two.
#
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 1f71906bc8e..2e393f753e8 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -34,7 +34,7 @@ module Grack
def auth!
if @auth.provided?
return bad_request unless @auth.basic?
-
+
# Authentication with username and password
login, password = @auth.credentials
@@ -71,8 +71,20 @@ module Grack
false
end
+ def oauth_access_token_check(login, password)
+ if login == "oauth2" && git_cmd == 'git-upload-pack' && password.present?
+ token = Doorkeeper::AccessToken.by_token(password)
+ token && token.accessible? && User.find_by(id: token.resource_owner_id)
+ end
+ end
+
def authenticate_user(login, password)
user = Gitlab::Auth.new.find(login, password)
+
+ unless user
+ user = oauth_access_token_check(login, password)
+ end
+
return user if user.present?
# At this point, we know the credentials were wrong. We let Rack::Attack
diff --git a/lib/gitlab/commits_calendar.rb b/lib/gitlab/commits_calendar.rb
new file mode 100644
index 00000000000..ccc80d080af
--- /dev/null
+++ b/lib/gitlab/commits_calendar.rb
@@ -0,0 +1,25 @@
+module Gitlab
+ class CommitsCalendar
+ attr_reader :timestamps
+
+ def initialize(repositories, user)
+ @timestamps = {}
+ date_timestamps = []
+
+ repositories.select(&:exists?).reject(&:empty?).each do |raw_repository|
+ commits_log = raw_repository.commits_per_day_for_user(user)
+ date_timestamps << commits_log
+ end
+
+ date_timestamps = date_timestamps.inject do |collection, date|
+ collection.merge(date) { |k, old_v, new_v| old_v + new_v }
+ end
+
+ date_timestamps ||= []
+ date_timestamps.each do |date, commits|
+ timestamp = Date.parse(date).to_time.to_i.to_s rescue nil
+ @timestamps[timestamp] = commits if timestamp
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index c7bf2efc628..ea96d04c5ab 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -73,7 +73,7 @@ module Gitlab
changes = changes.lines if changes.kind_of?(String)
# Iterate over all changes to find if user allowed all of them to be applied
- changes.each do |change|
+ changes.map(&:strip).reject(&:blank?).each do |change|
status = change_access_check(user, project, change)
unless status.allowed?
# If user does not have access to make at least one change - cancel all push
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 3ef494ba137..cfa8692659d 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -40,12 +40,16 @@ module Gitlab
def update_user_attributes
gl_user.email = auth_hash.email
- gl_user.identities.build(provider: auth_hash.provider, extern_uid: auth_hash.uid)
+
+ # Build new identity only if we dont have have same one
+ gl_user.identities.find_or_initialize_by(provider: auth_hash.provider,
+ extern_uid: auth_hash.uid)
+
gl_user
end
def changed?
- gl_user.changed?
+ gl_user.changed? || gl_user.identities.any?(&:changed?)
end
def needs_blocking?
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 068c342398b..c0e83fb3078 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -208,7 +208,7 @@ module Gitlab
end
def reference_issue(identifier, project = @project, prefix_text = nil)
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
+ if project.default_issues_tracker?
if project.issue_exists? identifier
url = url_for_issue(identifier, project)
title = title_for_issue(identifier, project)
@@ -220,10 +220,8 @@ module Gitlab
link_to("#{prefix_text}##{identifier}", url, options)
end
else
- config = Gitlab.config
- external_issue_tracker = config.issues_tracker[project.issues_tracker]
- if external_issue_tracker.present?
- reference_external_issue(identifier, external_issue_tracker, project,
+ if project.external_issue_tracker.present?
+ reference_external_issue(identifier, project,
prefix_text)
end
end
@@ -267,10 +265,10 @@ module Gitlab
end
end
- def reference_external_issue(identifier, issue_tracker, project = @project,
+ def reference_external_issue(identifier, project = @project,
prefix_text = nil)
url = url_for_issue(identifier, project)
- title = issue_tracker['title']
+ title = project.external_issue_tracker.title
options = html_options.merge(
title: "Issue in #{title}",
diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb
index 15e9b7a6f77..5b657c7aba2 100644
--- a/lib/gitlab/satellite/files/new_file_action.rb
+++ b/lib/gitlab/satellite/files/new_file_action.rb
@@ -14,7 +14,14 @@ module Gitlab
prepare_satellite!(repo)
# create target branch in satellite at the corresponding commit from bare repo
- repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+ current_ref =
+ if @project.empty_repo?
+ # skip this step if we want to add first file to empty repo
+ Satellite::PARKING_BRANCH
+ else
+ repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+ ref
+ end
file_path_in_satellite = File.join(repo.working_dir, file_path)
dir_name_in_satellite = File.dirname(file_path_in_satellite)
@@ -38,10 +45,9 @@ module Gitlab
# will raise CommandFailed when commit fails
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
-
# push commit back to bare repo
# will raise CommandFailed when push fails
- repo.git.push({raise: true, timeout: true}, :origin, ref)
+ repo.git.push({raise: true, timeout: true}, :origin, "#{current_ref}:#{ref}")
# everything worked
true
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
new file mode 100644
index 00000000000..44225c054f2
--- /dev/null
+++ b/spec/controllers/users_controller_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe UsersController do
+ let(:user) { create(:user, username: "user1", name: "User 1", email: "user1@gitlab.com") }
+
+ before do
+ sign_in(user)
+ end
+
+ describe "GET #show" do
+ render_views
+
+ it "renders the show template" do
+ get :show, username: user.username
+ expect(response.status).to eq(200)
+ expect(response).to render_template("show")
+ end
+ end
+
+ describe "GET #calendar" do
+ it "renders calendar" do
+ get :calendar, username: user.username
+ expect(response).to render_template("calendar")
+ end
+ end
+end
+
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 1738b20fab2..5ae57718c1a 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -76,7 +76,19 @@ FactoryGirl.define do
end
factory :redmine_project, parent: :project do
- issues_tracker { "redmine" }
- issues_tracker_id { "project_name_in_redmine" }
+ after :create do |project|
+ project.create_redmine_service(
+ active: true,
+ properties: {
+ 'project_url' => 'http://redmine/projects/project_name_in_redmine',
+ 'issues_url' => "http://redmine/#{project.id}/project_name_in_redmine/:id",
+ 'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
+ }
+ )
+ end
+ after :create do |project|
+ project.issues_tracker = 'redmine'
+ project.issues_tracker_id = 'project_name_in_redmine'
+ end
end
end
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index a7f87906b2d..52ade3e2d31 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe "Dashboard Feed", feature: true do
describe "GET /" do
- let!(:user) { create(:user) }
+ let!(:user) { create(:user, name: "Jonh") }
context "projects atom feed via private token" do
it "should render projects atom feed" do
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 86ba801ce07..d633287b2a9 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -23,6 +23,7 @@ describe GitlabMarkdownHelper do
@project = project
@ref = 'markdown'
@repository = project.repository
+ @request.host = Gitlab.config.gitlab.host
end
describe "#gfm" do
@@ -296,10 +297,13 @@ describe GitlabMarkdownHelper do
let(:reference) { "JIRA-#{issue.iid}" }
before do
- issue_tracker_config = { "jira" => { "title" => "JIRA tracker", "issues_url" => "http://jira.example/browse/:id" } }
- Gitlab.config.stub(:issues_tracker).and_return(issue_tracker_config)
- @project.stub(:issues_tracker).and_return("jira")
- @project.stub(:issues_tracker_id).and_return("JIRA")
+ jira = @project.create_jira_service if @project.jira_service.nil?
+ properties = {"title"=>"JIRA tracker", "project_url"=>"http://jira.example/issues/?jql=project=A", "issues_url"=>"http://jira.example/browse/:id", "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa"}
+ jira.update_attributes(properties: properties, active: true)
+ end
+
+ after do
+ @project.jira_service.destroy! unless @project.jira_service.nil?
end
it "should link using a valid id" do
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 9c95bc044f3..c82729a52e2 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -24,7 +24,7 @@ describe IssuesHelper do
end
describe :url_for_project_issues do
- let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
+ let(:project_url) { ext_project.external_issue_tracker.project_url }
let(:ext_expected) do
project_url.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
@@ -54,17 +54,16 @@ describe IssuesHelper do
Gitlab.config.stub(:issues_tracker).and_return(nil)
end
- it "should return path to internal tracker" do
- url_for_project_issues.should match(polymorphic_path([@project]))
+ it "should return path to external tracker" do
+ url_for_project_issues.should match(ext_expected)
end
end
end
describe :url_for_issue do
- let(:issue_id) { 3 }
- let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
+ let(:issues_url) { ext_project.external_issue_tracker.issues_url}
let(:ext_expected) do
- issues_url.gsub(':id', issue_id.to_s)
+ issues_url.gsub(':id', issue.iid.to_s)
.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
end
@@ -78,7 +77,7 @@ describe IssuesHelper do
it "should return path to external tracker" do
@project = ext_project
- url_for_issue(issue_id).should match(ext_expected)
+ url_for_issue(issue.iid).should match(ext_expected)
end
it "should return empty string if project nil" do
@@ -93,14 +92,14 @@ describe IssuesHelper do
Gitlab.config.stub(:issues_tracker).and_return(nil)
end
- it "should return internal path" do
- url_for_issue(issue.iid).should match(polymorphic_path([@project, issue]))
+ it "should return external path" do
+ url_for_issue(issue.iid).should match(ext_expected)
end
end
end
describe :url_for_new_issue do
- let(:issues_url) { Gitlab.config.issues_tracker.redmine.new_issue_url}
+ let(:issues_url) { ext_project.external_issue_tracker.new_issue_url }
let(:ext_expected) do
issues_url.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
@@ -131,7 +130,7 @@ describe IssuesHelper do
end
it "should return internal path" do
- url_for_new_issue.should match(new_project_issue_path(@project))
+ url_for_new_issue.should match(ext_expected)
end
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 2146b0b1383..281d4862199 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1,32 +1,11 @@
require 'spec_helper'
describe ProjectsHelper do
- describe '#project_issues_trackers' do
- it "returns the correct issues trackers available" do
- project_issues_trackers.should ==
- "\n" \
- ""
- end
-
- it "returns the correct issues trackers available with current tracker 'gitlab' selected" do
- project_issues_trackers('gitlab').should ==
- "\n" \
- ""
- end
-
- it "returns the correct issues trackers available with current tracker 'redmine' selected" do
- project_issues_trackers('redmine').should ==
- "\n" \
- ""
- end
- end
-
describe "#project_status_css_class" do
it "returns appropriate class" do
project_status_css_class("started").should == "active"
project_status_css_class("failed").should == "danger"
project_status_css_class("finished").should == "success"
end
-
end
end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index f73884e6441..63ffc21ba3b 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -13,6 +13,23 @@ describe Gitlab::LDAP::User do
double(uid: 'my-uid', provider: 'ldapmain', info: double(info))
end
+ describe :changed? do
+ it "marks existing ldap user as changed" do
+ existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
+ expect(gl_user.changed?).to be_true
+ end
+
+ it "marks existing non-ldap user if the email matches as changed" do
+ existing_user = create(:user, email: 'john@example.com')
+ expect(gl_user.changed?).to be_true
+ end
+
+ it "dont marks existing ldap user as changed" do
+ existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
+ expect(gl_user.changed?).to be_false
+ end
+ end
+
describe :find_or_create do
it "finds the user if already existing" do
existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 23867df39dd..5f45df4e8c3 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -12,7 +12,6 @@ describe Gitlab::ReferenceExtractor do
end
it 'extracts JIRA issue references' do
- Gitlab.config.gitlab.stub(:issues_tracker).and_return('jira')
subject.analyze('this one talks about issue JIRA-1234', nil)
subject.issues.should == [{ project: nil, id: 'JIRA-1234' }]
end
diff --git a/spec/models/jira_service_spec.rb b/spec/models/jira_service_spec.rb
new file mode 100644
index 00000000000..0c73a68c924
--- /dev/null
+++ b/spec/models/jira_service_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe JiraService do
+ describe "Associations" do
+ it { should belong_to :project }
+ it { should have_one :service_hook }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { should validate_presence_of :project_url }
+ it { should validate_presence_of :issues_url }
+ it { should validate_presence_of :new_issue_url }
+ end
+ end
+
+ describe 'description and title' do
+ let(:project) { create(:project) }
+
+ context 'when it is not set' do
+ before do
+ @service = project.create_jira_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should be initialized' do
+ expect(@service.title).to eq('JIRA')
+ expect(@service.description).to eq("Jira issue tracker")
+ end
+ end
+
+ context 'when it is set' do
+ before do
+ properties = { 'title' => 'Jira One', 'description' => 'Jira One issue tracker' }
+ @service = project.create_jira_service(active: true, properties: properties)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it "should be correct" do
+ expect(@service.title).to eq('Jira One')
+ expect(@service.description).to eq('Jira One issue tracker')
+ end
+ end
+ end
+
+ describe 'project and issue urls' do
+ let(:project) { create(:project) }
+
+ context 'when gitlab.yml was initialized' do
+ before do
+ settings = { "jira" => {
+ "title" => "Jira",
+ "project_url" => "http://jira.sample/projects/project_a",
+ "issues_url" => "http://jira.sample/issues/:id",
+ "new_issue_url" => "http://jira.sample/projects/project_a/issues/new"
+ }
+ }
+ Gitlab.config.stub(:issues_tracker).and_return(settings)
+ @service = project.create_jira_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should be prepopulated with the settings' do
+ expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a')
+ expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id")
+ expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new")
+ end
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c9bdeb43f6f..092c02d552e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -198,16 +198,16 @@ describe Project do
end
end
- describe :used_default_issues_tracker? do
+ describe :default_issues_tracker? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
- it 'should be true if used internal tracker' do
- project.used_default_issues_tracker?.should be_true
+ it "should be true if used internal tracker" do
+ project.default_issues_tracker?.should be_true
end
- it 'should be false if used other tracker' do
- ext_project.used_default_issues_tracker?.should be_false
+ it "should be false if used other tracker" do
+ ext_project.default_issues_tracker?.should be_false
end
end
diff --git a/spec/models/system_hook_spec.rb b/spec/models/system_hook_spec.rb
index 4ab5261dc9d..8deb732de9c 100644
--- a/spec/models/system_hook_spec.rb
+++ b/spec/models/system_hook_spec.rb
@@ -61,5 +61,40 @@ describe SystemHook do
project.project_members.destroy_all
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once
end
+
+ it 'group create hook' do
+ create(:group)
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /group_create/
+ ).once
+ end
+
+ it 'group destroy hook' do
+ group = create(:group)
+ group.destroy
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /group_destroy/
+ ).once
+ end
+
+ it 'group member create hook' do
+ group = create(:group)
+ user = create(:user)
+ group.add_user(user, Gitlab::Access::MASTER)
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /user_add_to_group/
+ ).once
+ end
+
+ it 'group member destroy hook' do
+ group = create(:group)
+ user = create(:user)
+ group.add_user(user, Gitlab::Access::MASTER)
+ group.group_members.destroy_all
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /user_remove_from_group/
+ ).once
+ end
+
end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index beae71c02d9..5518d2df566 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -101,6 +101,14 @@ describe API::API, api: true do
json_response.first['type'].should == 'tree'
json_response.first['mode'].should == '040000'
end
+
+ it 'should return a 404 for unknown ref' do
+ get api("/projects/#{project.id}/repository/tree?ref_name=foo", user)
+ response.status.should == 404
+
+ json_response.should be_an Object
+ json_response['message'] == '404 Tree Not Found'
+ end
end
context "unauthorized user" do
@@ -145,6 +153,14 @@ describe API::API, api: true do
get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user)
response.status.should == 200
end
+
+ it 'should return a 404 for unknown blob' do
+ get api("/projects/#{project.id}/repository/raw_blobs/123456", user)
+ response.status.should == 404
+
+ json_response.should be_an Object
+ json_response['message'] == '404 Blob Not Found'
+ end
end
describe "GET /projects/:id/repository/archive(.:format)?:sha" do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 8191d1fb9c4..e36b266a1ff 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -430,21 +430,17 @@ describe Projects::TreeController, 'routing' do
end
end
-describe Projects::EditTreeController, 'routing' do
- it 'to #show' do
+describe Projects::BlobController, 'routing' do
+ it 'to #edit' do
get('/gitlab/gitlabhq/edit/master/app/models/project.rb').should(
- route_to('projects/edit_tree#show',
+ route_to('projects/blob#edit',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb'))
- get('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should(
- route_to('projects/edit_tree#show',
- project_id: 'gitlab/gitlabhq',
- id: 'master/app/models/project.rb/preview'))
end
it 'to #preview' do
- post('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should(
- route_to('projects/edit_tree#preview',
+ post('/gitlab/gitlabhq/preview/master/app/models/project.rb').should(
+ route_to('projects/blob#preview',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb'))
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 347560414e7..36030577835 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -22,6 +22,7 @@ describe Issues::UpdateService do
}
@issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+ @issue.reload
end
it { @issue.should be_valid }
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index c8f40f48bab..0e60baae2c4 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -21,12 +21,14 @@ describe MergeRequests::UpdateService do
state_event: 'close'
}
end
+
let(:service) { MergeRequests::UpdateService.new(project, user, opts) }
before do
service.stub(:execute_hooks)
@merge_request = service.execute(merge_request)
+ @merge_request.reload
end
it { @merge_request.should be_valid }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index e305536f7ee..2ba1e3372b9 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -187,7 +187,7 @@ describe NotificationService do
end
describe 'Issues' do
- let(:issue) { create :issue, assignee: create(:user) }
+ let(:issue) { create :issue, assignee: create(:user), description: 'cc @participant' }
before do
build_team(issue.project)
@@ -197,6 +197,7 @@ describe NotificationService do
it do
should_email(issue.assignee_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -222,6 +223,7 @@ describe NotificationService do
it 'should email new assignee' do
should_email(issue.assignee_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -242,6 +244,7 @@ describe NotificationService do
should_email(issue.assignee_id)
should_email(issue.author_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -262,6 +265,7 @@ describe NotificationService do
should_email(issue.assignee_id)
should_email(issue.author_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -404,6 +408,7 @@ describe NotificationService do
def build_team(project)
@u_watcher = create(:user, notification_level: Notification::N_WATCH)
@u_participating = create(:user, notification_level: Notification::N_PARTICIPATING)
+ @u_participant_mentioned = create(:user, username: 'participant', notification_level: Notification::N_PARTICIPATING)
@u_disabled = create(:user, notification_level: Notification::N_DISABLED)
@u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_MENTION)
@u_committer = create(:user, username: 'committer')
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 573446d3a19..a45e9d0575c 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -5,6 +5,8 @@ describe SystemHooksService do
let (:project) { create :project }
let (:project_member) { create :project_member }
let (:key) { create(:key, user: user) }
+ let (:group) { create(:group) }
+ let (:group_member) { create(:group_member) }
context 'event data' do
it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) }
@@ -15,6 +17,31 @@ describe SystemHooksService do
it { event_data(project_member, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
it { event_data(key, :create).should include(:username, :key, :id) }
it { event_data(key, :destroy).should include(:username, :key, :id) }
+
+ it do
+ event_data(group, :create).should include(
+ :event_name, :name, :created_at, :path, :group_id, :owner_name,
+ :owner_email
+ )
+ end
+ it do
+ event_data(group, :destroy).should include(
+ :event_name, :name, :created_at, :path, :group_id, :owner_name,
+ :owner_email
+ )
+ end
+ it do
+ event_data(group_member, :create).should include(
+ :event_name, :created_at, :group_name, :group_path, :group_id, :user_id,
+ :user_name, :user_email, :group_access
+ )
+ end
+ it do
+ event_data(group_member, :destroy).should include(
+ :event_name, :created_at, :group_name, :group_path, :group_id, :user_id,
+ :user_name, :user_email, :group_access
+ )
+ end
end
context 'event names' do
@@ -26,6 +53,10 @@ describe SystemHooksService do
it { event_name(project_member, :destroy).should eq "user_remove_from_team" }
it { event_name(key, :create).should eq 'key_create' }
it { event_name(key, :destroy).should eq 'key_destroy' }
+ it { event_name(group, :create).should eq 'group_create' }
+ it { event_name(group, :destroy).should eq 'group_destroy' }
+ it { event_name(group_member, :create).should eq 'user_add_to_group' }
+ it { event_name(group_member, :destroy).should eq 'user_remove_from_group' }
end
def event_data(*args)