summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2015-12-07 12:03:34 +0100
committerDouwe Maan <douwe@gitlab.com>2015-12-07 12:03:34 +0100
commit0bca65b28344cfb4f4a6577c32b448268e7d9465 (patch)
tree682ed6bb606f2a45c9a333e921ad68b387852b62
parentdbbd2b863b402e460ac1dc90f852fcae617a2351 (diff)
parent234f4bf20fb338f2164976fd39203fbc671afd29 (diff)
downloadgitlab-ce-gsmethells/gitlab-ce-sort-by-due-date.tar.gz
Merge branch 'master' into gsmethells/gitlab-ce-sort-by-due-dategsmethells/gitlab-ce-sort-by-due-date
-rw-r--r--.ruby-version2
-rw-r--r--CHANGELOG11
-rw-r--r--Procfile2
-rw-r--r--app/assets/javascripts/application.js.coffee26
-rw-r--r--app/assets/javascripts/issues.js.coffee2
-rw-r--r--app/assets/javascripts/merge_request_widget.js.coffee9
-rw-r--r--app/assets/javascripts/sidebar.js.coffee1
-rw-r--r--app/assets/javascripts/user.js.coffee6
-rw-r--r--app/assets/javascripts/users_select.js.coffee13
-rw-r--r--app/assets/stylesheets/framework/blocks.scss5
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/framework/forms.scss8
-rw-r--r--app/assets/stylesheets/framework/header.scss8
-rw-r--r--app/assets/stylesheets/framework/layout.scss8
-rw-r--r--app/assets/stylesheets/framework/selects.scss60
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss186
-rw-r--r--app/assets/stylesheets/pages/issuable.scss11
-rw-r--r--app/assets/stylesheets/pages/issues.scss11
-rw-r--r--app/assets/stylesheets/pages/login.scss3
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss15
-rw-r--r--app/assets/stylesheets/pages/projects.scss4
-rw-r--r--app/assets/stylesheets/pages/wiki.scss5
-rw-r--r--app/controllers/profiles_controller.rb1
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/helpers/gitlab_routing_helper.rb2
-rw-r--r--app/helpers/icons_helper.rb22
-rw-r--r--app/helpers/issues_helper.rb19
-rw-r--r--app/helpers/merge_requests_helper.rb17
-rw-r--r--app/helpers/namespaces_helper.rb6
-rw-r--r--app/helpers/nav_helper.rb8
-rw-r--r--app/helpers/projects_helper.rb5
-rw-r--r--app/helpers/selects_helper.rb14
-rw-r--r--app/helpers/visibility_level_helper.rb44
-rw-r--r--app/models/application_setting.rb12
-rw-r--r--app/models/ci/application_setting.rb11
-rw-r--r--app/models/commit.rb16
-rw-r--r--app/models/commit_range.rb110
-rw-r--r--app/models/concerns/mentionable.rb11
-rw-r--r--app/models/concerns/referable.rb23
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/issue.rb4
-rw-r--r--app/models/label.rb2
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/milestone.rb6
-rw-r--r--app/models/snippet.rb4
-rw-r--r--app/models/users_star_project.rb2
-rw-r--r--app/services/notification_service.rb1
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--app/views/admin/labels/_form.html.haml2
-rw-r--r--app/views/admin/labels/edit.html.haml8
-rw-r--r--app/views/admin/labels/new.html.haml6
-rw-r--r--app/views/admin/users/edit.html.haml3
-rw-r--r--app/views/dashboard/_projects_head.html.haml3
-rw-r--r--app/views/dashboard/milestones/show.html.haml52
-rw-r--r--app/views/dashboard/projects/index.html.haml2
-rw-r--r--app/views/dashboard/projects/starred.html.haml2
-rw-r--r--app/views/explore/projects/index.html.haml2
-rw-r--r--app/views/explore/projects/starred.html.haml2
-rw-r--r--app/views/explore/projects/trending.html.haml2
-rw-r--r--app/views/groups/edit.html.haml6
-rw-r--r--app/views/groups/milestones/new.html.haml3
-rw-r--r--app/views/groups/milestones/show.html.haml61
-rw-r--r--app/views/groups/new.html.haml8
-rw-r--r--app/views/layouts/_page.html.haml10
-rw-r--r--app/views/layouts/admin.html.haml4
-rw-r--r--app/views/layouts/ci/_page.html.haml10
-rw-r--r--app/views/layouts/devise.html.haml4
-rw-r--r--app/views/layouts/errors.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml12
-rw-r--r--app/views/layouts/nav/_admin.html.haml28
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml18
-rw-r--r--app/views/layouts/nav/_explore.html.haml8
-rw-r--r--app/views/layouts/nav/_group.html.haml14
-rw-r--r--app/views/layouts/nav/_group_settings.html.haml6
-rw-r--r--app/views/layouts/nav/_profile.html.haml20
-rw-r--r--app/views/layouts/nav/_project.html.haml34
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml24
-rw-r--r--app/views/profiles/accounts/show.html.haml22
-rw-r--r--app/views/profiles/keys/_form.html.haml7
-rw-r--r--app/views/profiles/preferences/show.html.haml2
-rw-r--r--app/views/profiles/show.html.haml10
-rw-r--r--app/views/projects/_commit_button.html.haml4
-rw-r--r--app/views/projects/_home_panel.html.haml15
-rw-r--r--app/views/projects/blob/_new_dir.html.haml14
-rw-r--r--app/views/projects/blob/_upload.html.haml7
-rw-r--r--app/views/projects/blob/new.html.haml5
-rw-r--r--app/views/projects/branches/new.html.haml16
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml9
-rw-r--r--app/views/projects/deploy_keys/new.html.haml2
-rw-r--r--app/views/projects/edit.html.haml34
-rw-r--r--app/views/projects/issues/_closed_by_box.html.haml2
-rw-r--r--app/views/projects/issues/_discussion.html.haml7
-rw-r--r--app/views/projects/issues/_form.html.haml8
-rw-r--r--app/views/projects/issues/_issue.html.haml26
-rw-r--r--app/views/projects/issues/edit.html.haml6
-rw-r--r--app/views/projects/issues/new.html.haml4
-rw-r--r--app/views/projects/issues/show.html.haml3
-rw-r--r--app/views/projects/labels/_form.html.haml10
-rw-r--r--app/views/projects/labels/edit.html.haml8
-rw-r--r--app/views/projects/labels/new.html.haml6
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml7
-rw-r--r--app/views/projects/merge_requests/_form.html.haml3
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml40
-rw-r--r--app/views/projects/merge_requests/_new_compare.html.haml11
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml13
-rw-r--r--app/views/projects/merge_requests/_show.html.haml10
-rw-r--r--app/views/projects/merge_requests/edit.html.haml2
-rw-r--r--app/views/projects/merge_requests/merge.js.haml2
-rw-r--r--app/views/projects/merge_requests/new.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/_merged.html.haml8
-rw-r--r--app/views/projects/milestones/_form.html.haml10
-rw-r--r--app/views/projects/milestones/edit.html.haml6
-rw-r--r--app/views/projects/milestones/new.html.haml6
-rw-r--r--app/views/projects/milestones/show.html.haml113
-rw-r--r--app/views/projects/new.html.haml43
-rw-r--r--app/views/projects/notes/_edit_form.html.haml5
-rw-r--r--app/views/projects/notes/_form.html.haml9
-rw-r--r--app/views/projects/protected_branches/index.html.haml3
-rw-r--r--app/views/projects/runners/edit.html.haml2
-rw-r--r--app/views/projects/services/_form.html.haml7
-rw-r--r--app/views/projects/snippets/edit.html.haml2
-rw-r--r--app/views/projects/snippets/new.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml23
-rw-r--r--app/views/projects/wikis/_form.html.haml16
-rw-r--r--app/views/projects/wikis/_main_links.html.haml11
-rw-r--r--app/views/projects/wikis/_nav.html.haml25
-rw-r--r--app/views/projects/wikis/_new.html.haml4
-rw-r--r--app/views/projects/wikis/edit.html.haml24
-rw-r--r--app/views/projects/wikis/git_access.html.haml2
-rw-r--r--app/views/projects/wikis/pages.html.haml7
-rw-r--r--app/views/projects/wikis/show.html.haml12
-rw-r--r--app/views/shared/_confirm_modal.html.haml5
-rw-r--r--app/views/shared/_group_form.html.haml2
-rw-r--r--app/views/shared/_issues.html.haml7
-rw-r--r--app/views/shared/_merge_requests.html.haml7
-rw-r--r--app/views/shared/_new_commit_form.html.haml5
-rw-r--r--app/views/shared/_project_limit.html.haml8
-rw-r--r--app/views/shared/issuable/_context.html.haml21
-rw-r--r--app/views/shared/issuable/_filter.html.haml16
-rw-r--r--app/views/shared/issuable/_form.html.haml49
-rw-r--r--app/views/shared/snippets/_form.html.haml7
-rw-r--r--app/views/shared/snippets/_header.html.haml4
-rw-r--r--app/views/snippets/edit.html.haml2
-rw-r--r--app/views/snippets/new.html.haml2
-rwxr-xr-xbin/background_jobs2
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--db/migrate/20151203162133_add_hide_project_limit_to_users.rb5
-rw-r--r--db/schema.rb3
-rw-r--r--doc/api/merge_requests.md39
-rw-r--r--doc/api/notes.md15
-rw-r--r--doc/api/projects.md71
-rw-r--r--doc/ci/docker/using_docker_images.md4
-rw-r--r--doc/ssh/README.md5
-rw-r--r--features/project/merge_requests/accept.feature17
-rw-r--r--features/steps/admin/labels.rb8
-rw-r--r--features/steps/project/forked_merge_requests.rb9
-rw-r--r--features/steps/project/issues/issues.rb8
-rw-r--r--features/steps/project/issues/labels.rb16
-rw-r--r--features/steps/project/merge_requests.rb2
-rw-r--r--features/steps/project/merge_requests/acceptance.rb35
-rw-r--r--features/steps/project/source/browse_files.rb2
-rw-r--r--features/steps/project/source/markdown_render.rb6
-rw-r--r--features/steps/project/wiki.rb14
-rw-r--r--lib/api/entities.rb3
-rw-r--r--lib/api/merge_requests.rb16
-rw-r--r--lib/gitlab/closing_issue_extractor.rb16
-rw-r--r--lib/gitlab/markdown.rb3
-rw-r--r--lib/gitlab/markdown/abstract_reference_filter.rb85
-rw-r--r--lib/gitlab/markdown/commit_range_reference_filter.rb74
-rw-r--r--lib/gitlab/markdown/commit_reference_filter.rb72
-rw-r--r--lib/gitlab/markdown/external_issue_reference_filter.rb10
-rw-r--r--lib/gitlab/markdown/external_link_filter.rb4
-rw-r--r--lib/gitlab/markdown/label_reference_filter.rb20
-rw-r--r--lib/gitlab/markdown/merge_request_reference_filter.rb10
-rw-r--r--lib/gitlab/markdown/redactor_filter.rb5
-rw-r--r--lib/gitlab/markdown/reference_filter.rb74
-rw-r--r--lib/gitlab/markdown/relative_link_filter.rb3
-rw-r--r--lib/gitlab/markdown/user_reference_filter.rb28
-rw-r--r--lib/gitlab/reference_extractor.rb14
-rwxr-xr-xlib/support/init.d/gitlab2
-rw-r--r--spec/features/issues_spec.rb2
-rw-r--r--spec/fixtures/markdown.md.erb17
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb53
-rw-r--r--spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb85
-rw-r--r--spec/lib/gitlab/markdown/commit_reference_filter_spec.rb60
-rw-r--r--spec/lib/gitlab/markdown/issue_reference_filter_spec.rb102
-rw-r--r--spec/lib/gitlab/markdown/label_reference_filter_spec.rb77
-rw-r--r--spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb54
-rw-r--r--spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb56
-rw-r--r--spec/lib/gitlab/markdown/user_reference_filter_spec.rb51
-rw-r--r--spec/models/commit_range_spec.rb127
-rw-r--r--spec/models/commit_spec.rb21
-rw-r--r--spec/requests/api/merge_requests_spec.rb17
-rw-r--r--spec/requests/api/projects_spec.rb30
-rw-r--r--spec/services/notification_service_spec.rb9
-rw-r--r--spec/support/filter_spec_helper.rb19
-rw-r--r--spec/support/markdown_feature.rb4
-rw-r--r--spec/support/matchers/markdown_matchers.rb14
-rw-r--r--spec/support/mentionable_shared_examples.rb17
201 files changed, 2168 insertions, 1253 deletions
diff --git a/.ruby-version b/.ruby-version
index 399088bf465..04b10b4f150 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.1.6
+2.1.7
diff --git a/CHANGELOG b/CHANGELOG
index aad58af6678..813fd4938c7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,9 +4,17 @@ v 8.3.0 (unreleased)
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
- Fix 500 error when update group member permission
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
+ - Recognize issue/MR/snippet/commit links as references
- Add ignore whitespace change option to commit view
- Fire update hook from GitLab
- Fix: sort milestones by due date once again (Greg Smethells)
+ - Don't show project fork event as "imported"
+ - Add API endpoint to fetch merge request commits list
+ - Expose events API with comment information and author info
+ - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
+
+v 8.2.3
+ - Fix application settings cache not expiring after changes (Stan Hu)
v 8.2.2
- Fix 404 in redirection after removing a project (Stan Hu)
@@ -14,6 +22,9 @@ v 8.2.2
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- Fix: Raw private snippets access workflow
- Prevent "413 Request entity too large" errors when pushing large files with LFS
+ - Fix invalid links within projects dashboard header
+ - Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
+ - Fix: duplicate email notifications on issue comments
v 8.2.1
- Forcefully update builds that didn't want to update with state machine
diff --git a/Procfile b/Procfile
index 2e41485677c..9cfdee7040f 100644
--- a/Procfile
+++ b/Procfile
@@ -3,5 +3,5 @@
# lib/support/init.d, which call scripts in bin/ .
#
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
-worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default
+worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default
# mail_room: bundle exec mail_room -q -c config/mail_room.yml
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 945ffb660e6..1539eba0faa 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -135,17 +135,25 @@ $ ->
), 1
# Initialize tooltips
- $('body').tooltip({
- selector: '.has_tooltip, [data-toggle="tooltip"], .page-sidebar-collapsed .nav-sidebar a'
+ $('body').tooltip(
+ selector: '.has_tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
- if $el.attr('id') == 'js-shortcuts-home'
- # Place the logo tooltip on the right when collapsed, bottom when expanded
- $el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom'
- else
- # Otherwise use the data-placement attribute, or 'bottom' if undefined
- $el.data('placement') or 'bottom'
- })
+ $el.data('placement') || 'bottom'
+ )
+
+ $('.header-logo .home').tooltip(
+ placement: (_, el) ->
+ $el = $(el)
+ if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom'
+ container: 'body'
+ )
+
+ $('.page-with-sidebar').tooltip(
+ selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user'
+ placement: 'right'
+ container: 'body'
+ )
# Form submitter
$('.trigger-submit').on 'change', ->
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index 40bb9e9cb0c..ac9e022e727 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -29,7 +29,7 @@
$('#filter_issue_search').val($('#issue_search').val())
initSelects: ->
- $("select#update_status").select2(width: 'resolve', dropdownAutoWidth: true)
+ $("select#update_state_event").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true)
diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee
index 3176e5a8965..c4b63966fe7 100644
--- a/app/assets/javascripts/merge_request_widget.js.coffee
+++ b/app/assets/javascripts/merge_request_widget.js.coffee
@@ -10,17 +10,20 @@ class @MergeRequestWidget
constructor: (@opts) ->
modal = $('#modal_merge_info').modal(show: false)
- mergeInProgress: ->
+ mergeInProgress: (deleteSourceBranch = false)->
$.ajax
type: 'GET'
url: $('.merge-request').data('url')
success: (data) =>
if data.state == "merged"
- location.reload()
+ urlSuffix = if deleteSourceBranch then '?delete_source=true' else ''
+
+ window.location.href = window.location.href + urlSuffix
else if data.merge_error
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
else
- setTimeout(merge_request_widget.mergeInProgress, 2000)
+ callback = -> merge_request_widget.mergeInProgress(deleteSourceBranch)
+ setTimeout(callback, 2000)
dataType: 'json'
getMergeStatus: ->
diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee
index fb08016fbae..ae59480af9e 100644
--- a/app/assets/javascripts/sidebar.js.coffee
+++ b/app/assets/javascripts/sidebar.js.coffee
@@ -5,6 +5,7 @@ $(document).on("click", '.toggle-nav-collapse', (e) ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
+ $('.sidebar-wrapper').toggleClass("sidebar-collapsed sidebar-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
)
diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee
index d0d81f96921..ec4271b092c 100644
--- a/app/assets/javascripts/user.js.coffee
+++ b/app/assets/javascripts/user.js.coffee
@@ -2,3 +2,9 @@ class @User
constructor: ->
$('.profile-groups-avatars').tooltip("placement": "top")
new ProjectsList()
+
+ $('.hide-project-limit-message').on 'click', (e) ->
+ path = '/'
+ $.cookie('hide_project_limit_message', 'false', { path: path })
+ $(@).parents('.project-limit-message').remove()
+ e.preventDefault()
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index f5db74d84e7..12abf806bfa 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -32,17 +32,15 @@ class @UsersSelect
if showNullUser
nullUser = {
name: 'Unassigned',
- avatar: null,
- username: 'none',
id: 0
}
data.results.unshift(nullUser)
if showAnyUser
+ name = showAnyUser
+ name = 'Any User' if name == true
anyUser = {
- name: 'Any',
- avatar: null,
- username: 'none',
+ name: name,
id: null
}
data.results.unshift(anyUser)
@@ -50,7 +48,6 @@ class @UsersSelect
if showEmailUser && data.results.length == 0 && query.term.match(/^[^@]+@[^@]+$/)
emailUser = {
name: "Invite \"#{query.term}\"",
- avatar: null,
username: query.term,
id: query.term
}
@@ -82,10 +79,10 @@ class @UsersSelect
else
avatar = gon.default_avatar_url
- "<div class='user-result'>
+ "<div class='user-result #{'no-username' unless user.username}'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
- <div class='user-username'>#{user.username}</div>
+ <div class='user-username'>#{user.username || ""}</div>
</div>"
formatSelection: (user) ->
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 8836c8b666b..a62c0f62a4c 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -116,6 +116,11 @@
position: absolute;
top: 10px;
right: 10px;
+
+ &.left {
+ left: 10px;
+ right: auto;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 61ecd58e6c5..d2f491daf78 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -341,10 +341,6 @@ table {
text-align: center;
}
-.task-status {
- margin-left: 10px;
-}
-
#nprogress .spinner {
top: 15px !important;
right: 10px !important;
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index cc92966c458..032d343df44 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -91,9 +91,17 @@ label {
}
.input-group {
+ .select2-container {
+ display: table-cell;
+ width: 200px !important;
+ }
.input-group-addon {
background-color: #f7f8fa;
}
+ .input-group-addon:not(:first-child):not(:last-child) {
+ border-left: 0;
+ border-right: 0;
+ }
}
.help-block {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 02ea91602e8..4dbbb56104b 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -6,15 +6,17 @@ header {
transition-duration: .3s;
&.navbar-empty {
+ height: 58px;
background: #FFF;
border-bottom: 1px solid #EEE;
.center-logo {
- margin: 8px 0;
+ margin: 11px 0;
text-align: center;
- img {
- height: 32px;
+ #tanuki-logo, img {
+ width: 36px;
+ height: 36px;
}
}
}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index a60940a8bee..aa5acb93cc5 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -6,6 +6,10 @@ html {
body {
background-color: #EAEBEC !important;
+
+ &.navless {
+ background-color: white !important;
+ }
}
.container {
@@ -18,8 +22,8 @@ body {
}
.navless-container {
- padding-top: $header-height;
- margin-top: 30px;
+ margin-top: $header-height;
+ padding-top: $gl-padding * 2;
}
.container-limited {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 78fff58d232..c01e1e32e41 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -15,6 +15,16 @@
border-left: none;
padding-top: 5px;
}
+
+ .select2-chosen {
+ color: $gl-text-color;
+ }
+
+ &.select2-default {
+ .select2-chosen {
+ color: #999;
+ }
+ }
}
}
@@ -23,6 +33,7 @@
border: 1px solid #e7e9ed;
}
+
.select2-drop {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include border-radius (0px);
@@ -48,17 +59,38 @@
color: #313236;
}
+.select2-container-multi {
+ .select2-choices {
+ @include border-radius(2px);
+ border-color: $input-border;
+ background: white;
+ padding-left: $gl-padding / 2;
+
+ .select2-search-field input {
+ padding: $gl-padding / 2;
+ font-size: 13px;
+ height: auto;
+ font-family: inherit;
+ font-size: inherit;
+ }
-.select2-container-multi .select2-choices {
- @include border-radius(2px);
- border-color: #CCC;
-}
-
-.select2-container-multi .select2-choices .select2-search-field input {
- padding: 8px 14px;
- font-size: 13px;
- line-height: 18px;
- height: auto;
+ .select2-search-choice {
+ margin: 8px 0 0 8px;
+ background: white;
+ box-shadow: none;
+ border-color: $input-border;
+ color: $gl-text-color;
+ line-height: 15px;
+
+ .select2-search-choice-close {
+ top: 5px;
+ }
+
+ &.select2-search-choice-focus {
+ border-color: $gl-text-color;
+ }
+ }
+ }
}
.select2-drop-active {
@@ -123,10 +155,16 @@
}
.user-result {
+ min-height: 24px;
+
.user-image {
float: left;
}
- .user-name {
+
+ &.no-username {
+ .user-name {
+ line-height: 24px;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 81cda68b94c..458af76cb75 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -1,5 +1,6 @@
.page-with-sidebar {
padding-top: $header-height;
+ transition-duration: .3s;
.sidebar-wrapper {
position: fixed;
@@ -16,7 +17,6 @@
.sidebar-wrapper {
z-index: 99;
background: $background-color;
- transition-duration: .3s;
}
.content-wrapper {
@@ -35,6 +35,83 @@
}
}
+.sidebar-wrapper {
+ .header-logo {
+ border-bottom: 1px solid transparent;
+ float: left;
+ height: $header-height;
+ width: $sidebar_width;
+ position: fixed;
+ z-index: 999;
+ overflow: hidden;
+ transition-duration: .3s;
+
+ a {
+ float: left;
+ height: $header-height;
+ width: 100%;
+ padding: 11px 0 11px 22px;
+ overflow: hidden;
+ outline: none;
+ transition-duration: .3s;
+
+ img {
+ width: 36px;
+ height: 36px;
+ }
+
+ #tanuki-logo, img {
+ float: left;
+ }
+
+ .gitlab-text-container {
+ width: 230px;
+
+ h3 {
+ width: 158px;
+ float: left;
+ margin: 0;
+ margin-left: 14px;
+ font-size: 19px;
+ line-height: 41px;
+ font-weight: normal;
+ }
+ }
+ }
+
+ &:hover {
+ background-color: #EEE;
+ }
+ }
+
+ .sidebar-user {
+ padding: 9px 22px;
+ position: fixed;
+ bottom: 40px;
+ width: $sidebar_width;
+ overflow: hidden;
+ transition-duration: .3s;
+
+ .username {
+ margin-left: 10px;
+ width: $sidebar_width - 2 * 10px;
+ font-size: 16px;
+ line-height: 34px;
+ }
+ }
+}
+
+
+.tanuki-shape {
+ transition: all 0.8s;
+
+ &:hover {
+ fill: rgb(255, 255, 255);
+ transition: all 0.1s;
+ }
+}
+
+
.nav-sidebar {
margin-top: 14 + $header-height;
margin-bottom: 100px;
@@ -61,7 +138,7 @@
color: $gray;
display: block;
text-decoration: none;
- padding-left: 22px;
+ padding-left: 23px;
font-weight: normal;
outline: none;
@@ -85,6 +162,10 @@
padding: 0px 8px;
@include border-radius(6px);
}
+
+ &.back-link i {
+ transition-duration: .3s;
+ }
}
}
}
@@ -100,7 +181,6 @@
@mixin expanded-sidebar {
padding-left: $sidebar_width;
- transition-duration: .3s;
.sidebar-wrapper {
width: $sidebar_width;
@@ -114,16 +194,15 @@
&.back-link {
i {
- visibility: hidden;
+ opacity: 0;
}
}
}
}
}
-@mixin folded-sidebar {
- padding-left: 60px;
- transition-duration: .3s;
+@mixin collapsed-sidebar {
+ padding-left: $sidebar_collapsed_width;
.sidebar-wrapper {
width: $sidebar_collapsed_width;
@@ -132,7 +211,7 @@
width: $sidebar_collapsed_width;
a {
- padding-left: 12px;
+ padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
@@ -143,9 +222,13 @@
.nav-sidebar {
width: $sidebar_collapsed_width;
- li a {
- span {
- display: none;
+ li {
+ width: auto;
+
+ a {
+ span {
+ display: none;
+ }
}
}
}
@@ -155,7 +238,7 @@
}
.sidebar-user {
- padding-left: 12px;
+ padding-left: ($sidebar_collapsed_width - 36) / 2;
width: $sidebar_collapsed_width;
.username {
@@ -186,11 +269,11 @@
@media (max-width: $screen-md-max) {
.page-sidebar-collapsed {
- @include folded-sidebar;
+ @include collapsed-sidebar;
}
.page-sidebar-expanded {
- @include folded-sidebar;
+ @include collapsed-sidebar;
}
.collapse-nav {
@@ -200,83 +283,10 @@
@media(min-width: $screen-md-max) {
.page-sidebar-collapsed {
- @include folded-sidebar;
+ @include collapsed-sidebar;
}
.page-sidebar-expanded {
@include expanded-sidebar;
}
}
-
-.sidebar-user {
- padding: 9px 22px;
- position: fixed;
- bottom: 40px;
- width: $sidebar_width;
- overflow: hidden;
- transition-duration: .3s;
-
- .username {
- margin-left: 10px;
- width: $sidebar_width - 2 * 10px;
- font-size: 16px;
- line-height: 34px;
- }
-}
-
-.sidebar-wrapper {
- .header-logo {
- border-bottom: 1px solid transparent;
- float: left;
- height: $header-height;
- width: $sidebar_width;
- overflow: hidden;
- transition-duration: .3s;
-
- a {
- float: left;
- height: $header-height;
- width: 100%;
- padding: 10px 22px;
- overflow: hidden;
- outline: none;
-
- img {
- width: 36px;
- height: 36px;
- }
-
- #tanuki-logo, img {
- float: left;
- }
-
- .gitlab-text-container {
- width: 230px;
-
- h3 {
- width: 158px;
- float: left;
- margin: 0;
- margin-left: 14px;
- font-size: 19px;
- line-height: 41px;
- font-weight: normal;
- }
- }
- }
-
- &:hover {
- background-color: #EEE;
- }
- }
-}
-
-
-.tanuki-shape {
- transition: all 0.8s;
-
- &:hover {
- fill: rgb(255, 255, 255);
- transition: all 0.1s;
- }
-}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 9313fa4795a..957da5c182e 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -90,6 +90,17 @@
}
}
+.issuable-show-labels {
+ a {
+ margin-right: 5px;
+ margin-bottom: 5px;
+ display: inline-block;
+ .color-label {
+ padding: 6px 10px;
+ }
+ }
+}
+
.cross-project-reference {
text-align: center;
width: 100%;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 41c069f0ad3..f5548c5b88b 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -56,17 +56,6 @@
}
}
-.issue-show-labels {
- a {
- margin-right: 5px;
- margin-bottom: 5px;
- display: inline-block;
- .color-label {
- padding: 6px 10px;
- }
- }
-}
-
form.edit-issue {
margin: 0;
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index edd51705136..f9c6f1b39f9 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -1,7 +1,5 @@
/* Login Page */
.login-page {
- background-color: white;
-
.container {
max-width: 960px;
}
@@ -21,6 +19,7 @@
h1:first-child {
font-weight: normal;
margin-bottom: 30px;
+ margin-top: 0;
}
img {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 017a86bcd9a..f21ad694d06 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -173,27 +173,12 @@
line-height: 1.1;
}
-.merge-request-form-info {
- padding-top: 15px;
-}
-
// hide mr close link for inline diff comment form
.diff-file .close-mr-link,
.diff-file .reopen-mr-link {
display: none;
}
-.merge-request-show-labels {
- a {
- margin-right: 5px;
- margin-bottom: 5px;
- display: inline-block;
- .color-label {
- padding: 6px 10px;
- }
- }
-}
-
.merge-request-form .select2-container {
width: 250px !important;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 9d5b62c75d3..2ded32dba12 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -5,7 +5,7 @@
font-weight: normal;
}
}
-.no-ssh-key-message {
+.no-ssh-key-message, .project-limit-message {
background-color: #f28d35;
margin-bottom: 16px;
}
@@ -84,7 +84,7 @@
@extend .btn-gray;
color: $gray;
- cursor: auto;
+ cursor: default;
i {
color: inherit;
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index dfaeba41cf6..cdf514197cb 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
@@ -4,3 +4,8 @@
margin-right: auto;
padding-right: 7px;
}
+
+.wiki-last-edit-by {
+ font-size: 80%;
+ font-weight: normal;
+}
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 8da7b4d50ea..28803164fcf 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -70,6 +70,7 @@ class ProfilesController < Profiles::ApplicationController
:email,
:hide_no_password,
:hide_no_ssh_key,
+ :hide_project_limit,
:linkedin,
:location,
:name,
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 3ac0a75fa70..3c2849a7601 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -3,7 +3,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
- before_action :authorize_push_code!, only: [:create, :destroy]
+ before_action :authorize_push_code!, only: [:new, :create, :destroy]
def index
@sort = params[:sort] || 'name'
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index cb39c2b8782..280fe12cc7c 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -2,7 +2,7 @@ class Projects::TagsController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
- before_action :authorize_push_code!, only: [:create]
+ before_action :authorize_push_code!, only: [:new, :create]
before_action :authorize_admin_project!, only: [:destroy]
def index
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index b0b536d4649..f3fddef01cb 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -6,7 +6,7 @@
#
# For example instead of this:
#
-# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request)
+# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
#
# We can simply use shortcut:
#
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 1cf5b96481a..5724d3aabec 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -27,16 +27,20 @@ module IconsHelper
end
end
- def public_icon
- icon('globe fw')
- end
-
- def internal_icon
- icon('shield fw')
- end
+ def visibility_level_icon(level, fw: true)
+ name =
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ 'lock'
+ when Gitlab::VisibilityLevel::INTERNAL
+ 'shield'
+ else # Gitlab::VisibilityLevel::PUBLIC
+ 'globe'
+ end
+
+ name << " fw" if fw
- def private_icon
- icon('lock fw')
+ icon(name)
end
def file_type_icon_class(type, mode, name)
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 25befd654d4..e66b9c628c7 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -44,14 +44,17 @@ module IssuesHelper
end
def bulk_update_milestone_options
- options_for_select([['None (backlog)', -1]]) +
- options_from_collection_for_select(project_active_milestones, 'id',
- 'title', params[:milestone_id])
+ milestones = project_active_milestones.to_a
+ milestones.unshift(Milestone::None)
+
+ options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
end
def milestone_options(object)
- options_from_collection_for_select(object.project.milestones.active,
- 'id', 'title', object.milestone_id)
+ milestones = object.project.milestones.active.to_a
+ milestones.unshift(Milestone::None)
+
+ options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end
def issue_box_class(item)
@@ -84,7 +87,11 @@ module IssuesHelper
end
def merge_requests_sentence(merge_requests)
- merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ')
+ # Sorting based on the `!123` or `group/project!123` reference will sort
+ # local merge requests first.
+ merge_requests.map do |merge_request|
+ merge_request.to_reference(@project)
+ end.sort.to_sentence(last_word_connector: ', or ')
end
def url_to_emoji(name)
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index b804d4f4e3b..6c32647594d 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -39,7 +39,11 @@ module MergeRequestsHelper
end
def issues_sentence(issues)
- issues.map(&:to_reference).to_sentence
+ # Sorting based on the `#123` or `group/project#123` reference will sort
+ # local issues first.
+ issues.map do |issue|
+ issue.to_reference(@project)
+ end.sort.to_sentence
end
def mr_change_branches_path(merge_request)
@@ -49,18 +53,21 @@ module MergeRequestsHelper
source_project_id: @merge_request.source_project_id,
target_project_id: @merge_request.target_project_id,
source_branch: @merge_request.source_branch,
- target_branch: nil
- }
+ target_branch: @merge_request.target_branch,
+ },
+ change_branches: true
)
end
def source_branch_with_namespace(merge_request)
+ branch = link_to(merge_request.source_branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch))
+
if merge_request.for_fork?
namespace = link_to(merge_request.source_project_namespace,
project_path(merge_request.source_project))
- namespace + ":#{merge_request.source_branch}"
+ namespace + ":" + branch
else
- merge_request.source_branch
+ branch
end
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index e7f3cb21038..faba418c4db 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -1,10 +1,10 @@
module NamespacesHelper
- def namespaces_options(selected = :current_user, scope = :default)
+ def namespaces_options(selected = :current_user, display_path: false)
groups = current_user.owned_groups + current_user.masters_groups
users = [current_user.namespace]
- group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
- users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
+ group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [display_path ? g.path : g.human_name, g.id]} ]
+ users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [display_path ? u.path : u.human_name, u.id]} ]
options = []
options << group_opts
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 9b1dd8b8e54..e6fb8670e57 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -5,6 +5,14 @@ module NavHelper
def nav_sidebar_class
if nav_menu_collapsed?
+ "sidebar-collapsed"
+ else
+ "sidebar-expanded"
+ end
+ end
+
+ def page_sidebar_class
+ if nav_menu_collapsed?
"page-sidebar-collapsed"
else
"page-sidebar-expanded"
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c0c51aae039..48729e5260e 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -21,7 +21,7 @@ module ProjectsHelper
end
def link_to_member(project, author, opts = {})
- default_opts = { avatar: true, name: true, size: 16, author_class: 'author' }
+ default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
opts = default_opts.merge(opts)
return "(deleted)" unless author
@@ -39,7 +39,8 @@ module ProjectsHelper
if opts[:name]
link_to(author_html, user_path(author), class: "author_link").html_safe
else
- link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe
+ title = opts[:title].sub(":name", sanitize(author.name))
+ link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => title, container: 'body' } ).html_safe
end
end
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index 7e54d4d1b5b..7e175d0de8a 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -15,12 +15,14 @@ module SelectsHelper
html = {
class: css_class,
- 'data-placeholder' => placeholder,
- 'data-null-user' => null_user,
- 'data-any-user' => any_user,
- 'data-email-user' => email_user,
- 'data-first-user' => first_user,
- 'data-current-user' => current_user
+ data: {
+ placeholder: placeholder,
+ null_user: null_user,
+ any_user: any_user,
+ email_user: email_user,
+ first_user: first_user,
+ current_user: current_user
+ }
}
unless opts[:scope] == :all
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index b52cd23aba2..72c65030f94 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -25,48 +25,24 @@ module VisibilityLevelHelper
end
def project_visibility_level_description(level)
- capture_haml do
- haml_tag :span do
- case level
- when Gitlab::VisibilityLevel::PRIVATE
- haml_concat "Project access must be granted explicitly for each user."
- when Gitlab::VisibilityLevel::INTERNAL
- haml_concat "The project can be cloned by"
- haml_concat "any logged in user."
- when Gitlab::VisibilityLevel::PUBLIC
- haml_concat "The project can be cloned"
- haml_concat "without any"
- haml_concat "authentication."
- end
- end
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ "Project access must be granted explicitly for each user."
+ when Gitlab::VisibilityLevel::INTERNAL
+ "The project can be cloned by any logged in user."
+ when Gitlab::VisibilityLevel::PUBLIC
+ "The project can be cloned without any authentication."
end
end
def snippet_visibility_level_description(level)
- capture_haml do
- haml_tag :span do
- case level
- when Gitlab::VisibilityLevel::PRIVATE
- haml_concat "The snippet is visible only for me."
- when Gitlab::VisibilityLevel::INTERNAL
- haml_concat "The snippet is visible for any logged in user."
- when Gitlab::VisibilityLevel::PUBLIC
- haml_concat "The snippet can be accessed"
- haml_concat "without any"
- haml_concat "authentication."
- end
- end
- end
- end
-
- def visibility_level_icon(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
- private_icon
+ "The snippet is visible only for me."
when Gitlab::VisibilityLevel::INTERNAL
- internal_icon
+ "The snippet is visible for any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
- public_icon
+ "The snippet can be accessed without any authentication."
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 3df8135acf1..5ddcf3d9a0b 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -30,6 +30,8 @@
#
class ApplicationSetting < ActiveRecord::Base
+ CACHE_KEY = 'application_setting.last'
+
serialize :restricted_visibility_levels
serialize :import_sources
serialize :restricted_signup_domains, Array
@@ -73,21 +75,17 @@ class ApplicationSetting < ActiveRecord::Base
end
after_commit do
- Rails.cache.write(cache_key, self)
+ Rails.cache.write(CACHE_KEY, self)
end
def self.current
- Rails.cache.fetch(cache_key) do
+ Rails.cache.fetch(CACHE_KEY) do
ApplicationSetting.last
end
end
def self.expire
- Rails.cache.delete(cache_key)
- end
-
- def self.cache_key
- 'application_setting.last'
+ Rails.cache.delete(CACHE_KEY)
end
def self.create_from_defaults
diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb
index 4e512d290ee..7f5df8ce6c4 100644
--- a/app/models/ci/application_setting.rb
+++ b/app/models/ci/application_setting.rb
@@ -12,17 +12,18 @@
module Ci
class ApplicationSetting < ActiveRecord::Base
extend Ci::Model
+ CACHE_KEY = 'ci_application_setting.last'
after_commit do
- Rails.cache.write(cache_key, self)
+ Rails.cache.write(CACHE_KEY, self)
end
def self.expire
- Rails.cache.delete(cache_key)
+ Rails.cache.delete(CACHE_KEY)
end
def self.current
- Rails.cache.fetch(cache_key) do
+ Rails.cache.fetch(CACHE_KEY) do
Ci::ApplicationSetting.last
end
end
@@ -33,9 +34,5 @@ module Ci
add_pusher: Settings.gitlab_ci['add_pusher'],
)
end
-
- def self.cache_key
- 'ci_application_setting.last'
- end
end
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 492f6be1ce3..c0998a45709 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -78,11 +78,23 @@ class Commit
}x
end
+ def self.link_reference_pattern
+ super("commit", /(?<commit>\h{6,40})/)
+ end
+
def to_reference(from_project = nil)
if cross_project_reference?(from_project)
- "#{project.to_reference}@#{id}"
+ project.to_reference + self.class.reference_prefix + self.id
+ else
+ self.id
+ end
+ end
+
+ def reference_link_text(from_project = nil)
+ if cross_project_reference?(from_project)
+ project.to_reference + self.class.reference_prefix + self.short_id
else
- id
+ self.short_id
end
end
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index 86fc9eb01a3..14e7971fa06 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -2,36 +2,38 @@
#
# Examples:
#
-# range = CommitRange.new('f3f85602...e86e1013')
+# range = CommitRange.new('f3f85602...e86e1013', project)
# range.exclude_start? # => false
# range.reference_title # => "Commits f3f85602 through e86e1013"
# range.to_s # => "f3f85602...e86e1013"
#
-# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae')
+# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae', project)
# range.exclude_start? # => true
# range.reference_title # => "Commits f3f85602^ through e86e1013"
# range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"}
# range.to_s # => "f3f85602..e86e1013"
#
-# # Assuming `project` is a Project with a repository containing both commits:
-# range.project = project
+# # Assuming the specified project has a repository containing both commits:
# range.valid_commits? # => true
#
class CommitRange
include ActiveModel::Conversion
include Referable
- attr_reader :sha_from, :notation, :sha_to
+ attr_reader :commit_from, :notation, :commit_to
+ attr_reader :ref_from, :ref_to
# Optional Project model
attr_accessor :project
- # See `exclude_start?`
- attr_reader :exclude_start
-
- # The beginning and ending SHAs can be between 6 and 40 hex characters, and
+ # The beginning and ending refs can be named or SHAs, and
# the range notation can be double- or triple-dot.
- PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
+ REF_PATTERN = /[0-9a-zA-Z][0-9a-zA-Z_.-]*[0-9a-zA-Z\^]/
+ PATTERN = /#{REF_PATTERN}\.{2,3}#{REF_PATTERN}/
+
+ # In text references, the beginning and ending refs can only be SHAs
+ # between 6 and 40 hex characters.
+ STRICT_PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
def self.reference_prefix
'@'
@@ -43,27 +45,40 @@ class CommitRange
def self.reference_pattern
%r{
(?:#{Project.reference_pattern}#{reference_prefix})?
- (?<commit_range>#{PATTERN})
+ (?<commit_range>#{STRICT_PATTERN})
}x
end
+ def self.link_reference_pattern
+ super("compare", /(?<commit_range>#{PATTERN})/)
+ end
+
# Initialize a CommitRange
#
# range_string - The String commit range.
# project - An optional Project model.
#
# Raises ArgumentError if `range_string` does not match `PATTERN`.
- def initialize(range_string, project = nil)
+ def initialize(range_string, project)
+ @project = project
+
range_string.strip!
- unless range_string.match(/\A#{PATTERN}\z/)
+ unless range_string =~ /\A#{PATTERN}\z/
raise ArgumentError, "invalid CommitRange string format: #{range_string}"
end
- @exclude_start = !range_string.include?('...')
- @sha_from, @notation, @sha_to = range_string.split(/(\.{2,3})/, 2)
+ @ref_from, @notation, @ref_to = range_string.split(/(\.{2,3})/, 2)
- @project = project
+ if project.valid_repo?
+ @commit_from = project.commit(@ref_from)
+ @commit_to = project.commit(@ref_to)
+ end
+
+ if valid_commits?
+ @ref_from = Commit.truncate_sha(sha_from) if sha_from.start_with?(@ref_from)
+ @ref_to = Commit.truncate_sha(sha_to) if sha_to.start_with?(@ref_to)
+ end
end
def inspect
@@ -71,15 +86,24 @@ class CommitRange
end
def to_s
- "#{sha_from[0..7]}#{notation}#{sha_to[0..7]}"
+ sha_from + notation + sha_to
end
+ alias_method :id, :to_s
+
def to_reference(from_project = nil)
- # Not using to_s because we want the full SHAs
- reference = sha_from + notation + sha_to
+ if cross_project_reference?(from_project)
+ project.to_reference + self.class.reference_prefix + self.id
+ else
+ self.id
+ end
+ end
+
+ def reference_link_text(from_project = nil)
+ reference = ref_from + notation + ref_to
if cross_project_reference?(from_project)
- reference = project.to_reference + '@' + reference
+ reference = project.to_reference + self.class.reference_prefix + reference
end
reference
@@ -87,46 +111,58 @@ class CommitRange
# Returns a String for use in a link's title attribute
def reference_title
- "Commits #{suffixed_sha_from} through #{sha_to}"
+ "Commits #{sha_start} through #{sha_to}"
end
# Return a Hash of parameters for passing to a URL helper
#
# See `namespace_project_compare_url`
def to_param
- { from: suffixed_sha_from, to: sha_to }
+ { from: sha_start, to: sha_to }
end
def exclude_start?
- exclude_start
+ @notation == '..'
end
# Check if both the starting and ending commit IDs exist in a project's
# repository
- #
- # project - An optional Project to check (default: `project`)
- def valid_commits?(project = project)
- return nil unless project.present?
- return false unless project.valid_repo?
-
- commit_from.present? && commit_to.present?
+ def valid_commits?
+ commit_start.present? && commit_end.present?
end
def persisted?
true
end
- def commit_from
- @commit_from ||= project.repository.commit(suffixed_sha_from)
+ def sha_from
+ return nil unless @commit_from
+
+ @commit_from.id
+ end
+
+ def sha_to
+ return nil unless @commit_to
+
+ @commit_to.id
end
- def commit_to
- @commit_to ||= project.repository.commit(sha_to)
+ def sha_start
+ return nil unless sha_from
+
+ exclude_start? ? sha_from + '^' : sha_from
end
- private
+ def commit_start
+ return nil unless sha_start
- def suffixed_sha_from
- sha_from + (exclude_start? ? '^' : '')
+ if exclude_start?
+ @commit_start ||= project.commit(sha_start)
+ else
+ commit_from
+ end
end
+
+ alias_method :sha_end, :sha_to
+ alias_method :commit_end, :commit_to
end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 193c91f1742..634a8d0f274 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -62,13 +62,18 @@ module Mentionable
return [] if text.blank?
refs = all_references(current_user, text, load_lazy_references: load_lazy_references)
- (refs.issues + refs.merge_requests + refs.commits) - [local_reference]
+ refs = (refs.issues + refs.merge_requests + refs.commits)
+
+ # We're using this method instead of Array diffing because that requires
+ # both of the object's `hash` values to be the same, which may not be the
+ # case for otherwise identical Commit objects.
+ refs.reject { |ref| ref == local_reference }
end
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
def create_cross_references!(author = self.author, without = [], text = self.mentionable_text)
refs = referenced_mentionables(author, text)
-
+
# We're using this method instead of Array diffing because that requires
# both of the object's `hash` values to be the same, which may not be the
# case for otherwise identical Commit objects.
@@ -111,7 +116,7 @@ module Mentionable
# Only include changed fields that are mentionable
source.select { |key, val| mentionable.include?(key) }
end
-
+
# Determine whether or not a cross-reference Note has already been created between this Mentionable and
# the specified target.
def cross_reference_exists?(target)
diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb
index cced66cc1e4..ce064f675ae 100644
--- a/app/models/concerns/referable.rb
+++ b/app/models/concerns/referable.rb
@@ -21,6 +21,10 @@ module Referable
''
end
+ def reference_link_text(from_project = nil)
+ to_reference(from_project)
+ end
+
module ClassMethods
# The character that prefixes the actual reference identifier
#
@@ -44,6 +48,25 @@ module Referable
def reference_pattern
raise NotImplementedError, "#{self} does not implement #{__method__}"
end
+
+ def link_reference_pattern(route, pattern)
+ %r{
+ (?<url>
+ #{Regexp.escape(Gitlab.config.gitlab.url)}
+ \/#{Project.reference_pattern}
+ \/#{Regexp.escape(route)}
+ \/#{pattern}
+ (?<path>
+ (\/[a-z0-9_=-]+)*
+ )?
+ (?<query>
+ \?[a-z0-9_=-]+
+ (&[a-z0-9_=-]+)*
+ )?
+ (?<anchor>\#[a-z0-9_-]+)?
+ )
+ }x
+ end
end
private
diff --git a/app/models/event.rb b/app/models/event.rb
index 9afd223bce5..01d008035a5 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -201,7 +201,7 @@ class Event < ActiveRecord::Base
elsif commented?
"commented on"
elsif created_project?
- if project.import?
+ if project.external_import?
"imported"
else
"created"
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 72183108033..187b6482b6c 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -69,6 +69,10 @@ class Issue < ActiveRecord::Base
}x
end
+ def self.link_reference_pattern
+ super("issues", /(?<issue>\d+)/)
+ end
+
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
diff --git a/app/models/label.rb b/app/models/label.rb
index b306aecbac1..bef6063fe88 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -17,7 +17,7 @@ class Label < ActiveRecord::Base
# Requests that have no label assigned.
LabelStruct = Struct.new(:title, :name)
None = LabelStruct.new('No Label', 'No Label')
- Any = LabelStruct.new('Any', '')
+ Any = LabelStruct.new('Any Label', '')
DEFAULT_COLOR = '#428BCA'
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 1b3d6079d2c..2a4aee7e5d9 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -151,6 +151,10 @@ class MergeRequest < ActiveRecord::Base
}x
end
+ def self.link_reference_pattern
+ super("merge_requests", /(?<merge_request>\d+)/)
+ end
+
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
@@ -316,7 +320,7 @@ class MergeRequest < ActiveRecord::Base
issues = commits.flat_map { |c| c.closes_issues(current_user) }
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(description))
- issues.uniq.sort_by(&:id)
+ issues.uniq
else
[]
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index c2642b75b8a..d8c7536cd31 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -16,9 +16,9 @@
class Milestone < ActiveRecord::Base
# Represents a "No Milestone" state used for filtering Issues and Merge
# Requests that have no milestone assigned.
- MilestoneStruct = Struct.new(:title, :name)
- None = MilestoneStruct.new('No Milestone', 'No Milestone')
- Any = MilestoneStruct.new('Any', '')
+ MilestoneStruct = Struct.new(:title, :name, :id)
+ None = MilestoneStruct.new('No Milestone', 'No Milestone', 0)
+ Any = MilestoneStruct.new('Any Milestone', '', -1)
include InternalId
include Sortable
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index b0831982aa7..f876be7a4c8 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -65,6 +65,10 @@ class Snippet < ActiveRecord::Base
}x
end
+ def self.link_reference_pattern
+ super("snippets", /(?<snippet>\d+)/)
+ end
+
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{id}"
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index 3d49cb05949..413f3f485a8 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -10,7 +10,7 @@
#
class UsersStarProject < ActiveRecord::Base
- belongs_to :project, counter_cache: :star_count
+ belongs_to :project, counter_cache: :star_count, touch: true
belongs_to :user
validates :user, presence: true
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 388a4defb26..bdf7b3ad2bb 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -145,6 +145,7 @@ class NotificationService
recipients = reject_unsubscribed_users(recipients, note.noteable)
recipients.delete(note.author)
+ recipients = recipients.uniq
# build notify method like 'note_commit_email'
notify_method = "note_#{note.noteable_type.underscore}_email".to_sym
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 7e2bc834176..09c159510cd 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -125,7 +125,7 @@ class SystemNoteService
# Returns the created Note object
def self.change_status(noteable, project, author, status, source)
body = "Status changed to #{status}"
- body += " by #{source.gfm_reference}" if source
+ body += " by #{source.gfm_reference(project)}" if source
create_note(noteable: noteable, project: project, author: author, note: body)
end
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
index a5ace4e7a3b..eaa94ed9e36 100644
--- a/app/views/admin/labels/_form.html.haml
+++ b/app/views/admin/labels/_form.html.haml
@@ -12,7 +12,7 @@
.col-sm-10
= f.text_field :title, class: "form-control", required: true
.form-group
- = f.label :color, "Background Color", class: 'control-label'
+ = f.label :color, "Background color", class: 'control-label'
.col-sm-10
.input-group
.input-group-addon.label-color-preview &nbsp;
diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml
index 45c62a76259..309aedceded 100644
--- a/app/views/admin/labels/edit.html.haml
+++ b/app/views/admin/labels/edit.html.haml
@@ -1,9 +1,5 @@
- page_title "Edit", @label.name, "Labels"
-%h3
- Edit label
- %span.light #{@label.name}
-.back-link
- = link_to admin_labels_path do
- &larr; To labels list
+%h3.page-title
+ Edit Label
%hr
= render 'form'
diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml
index 8d298ad20f7..0135ad0723d 100644
--- a/app/views/admin/labels/new.html.haml
+++ b/app/views/admin/labels/new.html.haml
@@ -1,7 +1,5 @@
- page_title "New Label"
-%h3 New label
-.back-link
- = link_to admin_labels_path do
- &larr; To labels list
+%h3.page-title
+ New Label
%hr
= render 'form'
diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml
index a8837d74dd9..3b6fd71500d 100644
--- a/app/views/admin/users/edit.html.haml
+++ b/app/views/admin/users/edit.html.haml
@@ -1,8 +1,5 @@
- page_title "Edit", @user.name, "Users"
%h3.page-title
Edit user: #{@user.name}
-.back-link
- = link_to admin_user_path(@user) do
- &larr; Back to user page
%hr
= render 'form'
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index ed480b8caf8..991e67b1cd3 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -1,3 +1,6 @@
+= content_for :flash_message do
+ = render 'shared/project_limit'
+
%ul.center-top-menu
= nav_link(path: ['projects#index', 'root#index']) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 83077a398bd..3536bbeaf4b 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,19 +1,23 @@
- page_title @milestone.title, "Milestones"
-%h4.page-title
- .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
- - if @milestone.closed?
- Closed
- - else
- Open
- Milestone #{@milestone.title}
+- header_title "Milestones", dashboard_milestones_path
+
+.issuable-details
+ .page-title
+ .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+ - if @milestone.closed?
+ Closed
+ - else
+ Open
+ Milestone #{@milestone.title}
+
+ .gray-content-block.middle-block
+ %h2.issue-title
+ = gfm escape_once(@milestone.title)
-%hr
- if @milestone.complete? && @milestone.active?
- .alert.alert-success
+ .alert.alert-success.prepend-top-default
%span All issues for this milestone are closed. You may close the milestone now.
-.description
-
.table-holder
%table.table
%thead
@@ -44,7 +48,7 @@
#{@milestone.open_items_count} open
= milestone_progress_bar(@milestone)
-%ul.nav.nav-tabs
+%ul.center-top-menu.no-top.no-bottom
%li.active
= link_to '#tab-issues', 'data-toggle' => 'tab' do
Issues
@@ -58,25 +62,39 @@
Participants
%span.badge= @milestone.participants.count
- .pull-right
- = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped"
-
.tab-content
.tab-pane.active#tab-issues
- .row
+ .gray-content-block.middle-block
+ .pull-right
+ = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped"
+
+ .oneline
+ All issues in this milestone
+
+ .row.prepend-top-default
.col-md-6
= render 'issues', title: "Open", issues: @milestone.opened_issues
.col-md-6
= render 'issues', title: "Closed", issues: @milestone.closed_issues
.tab-pane#tab-merge-requests
- .row
+ .gray-content-block.middle-block
+ .pull-right
+ = link_to 'Browse Merge Requests', merge_requests_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped"
+
+ .oneline
+ All merge requests in this milestone
+
+ .row.prepend-top-default
.col-md-6
= render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests
.col-md-6
= render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests
.tab-pane#tab-participants
+ .gray-content-block.middle-block
+ .oneline
+ All participants to this milestone
%ul.bordered-list
- @milestone.participants.each do |user|
%li
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 7a16b811f6b..53abf274bdb 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -3,7 +3,7 @@
= auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
- page_title "Projects"
-- header_title "Projects", root_path
+- header_title "Projects", dashboard_projects_path
= render 'dashboard/projects_head'
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index f75f2e0a32a..70705923d42 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,5 +1,5 @@
- page_title "Starred Projects"
-- header_title "Projects", projects_path
+- header_title "Projects", dashboard_projects_path
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 67e38ca3127..76bdd68fd76 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,5 +1,5 @@
- page_title "Projects"
-- header_title "Projects", root_path
+- header_title "Projects", dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 596cb0a96cd..e30c3633223 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,5 +1,5 @@
- page_title "Projects"
-- header_title "Projects", root_path
+- header_title "Projects", dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 5ea6d81c5b9..1412b19acde 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,5 +1,5 @@
- page_title "Projects"
-- header_title "Projects", root_path
+- header_title "Projects", dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 57308a661c0..8daac585960 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -3,8 +3,7 @@
.panel.panel-default
.panel-heading
- %strong= @group.name
- group settings:
+ Group settings
.panel-body
= form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
- if @group.errors.any?
@@ -45,4 +44,5 @@
%br
%strong Removed group can not be restored!
- = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove"
+ .form-actions
+ = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove"
diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml
index 800bac4ef02..3894a0ece74 100644
--- a/app/views/groups/milestones/new.html.haml
+++ b/app/views/groups/milestones/new.html.haml
@@ -14,8 +14,7 @@
.form-group
= f.label :title, "Title", class: "control-label"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true
- %p.hint Required
+ = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true
.form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index d161259e4aa..3c1d8815013 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,27 +1,29 @@
- page_title @milestone.title, "Milestones"
= render "header_title"
-%h4.page-title
- .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
- - if @milestone.closed?
- Closed
- - else
- Open
- Milestone #{@milestone.title}
- .pull-right
- - if can?(current_user, :admin_milestones, @group)
- - if @milestone.active?
- = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
+.issuable-details
+ .page-title
+ .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+ - if @milestone.closed?
+ Closed
- else
- = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
+ Open
+ Milestone #{@milestone.title}
+ .pull-right
+ - if can?(current_user, :admin_milestones, @group)
+ - if @milestone.active?
+ = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
+ - else
+ = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
+
+ .gray-content-block.middle-block
+ %h2.issue-title
+ = gfm escape_once(@milestone.title)
-%hr
- if @milestone.complete? && @milestone.active?
- .alert.alert-success
+ .alert.alert-success.prepend-top-default
%span All issues for this milestone are closed. You may close the milestone now.
-.description
-
.table-holder
%table.table
%thead
@@ -52,7 +54,7 @@
#{@milestone.open_items_count} open
= milestone_progress_bar(@milestone)
-%ul.nav.nav-tabs
+%ul.center-top-menu.no-top.no-bottom
%li.active
= link_to '#tab-issues', 'data-toggle' => 'tab' do
Issues
@@ -66,25 +68,40 @@
Participants
%span.badge= @milestone.participants.count
- .pull-right
- = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped"
-
.tab-content
.tab-pane.active#tab-issues
- .row
+ .gray-content-block.middle-block
+ .pull-right
+ = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+ .oneline
+ All issues in this milestone
+
+ .row.prepend-top-default
.col-md-6
= render 'issues', title: "Open", issues: @milestone.opened_issues
.col-md-6
= render 'issues', title: "Closed", issues: @milestone.closed_issues
.tab-pane#tab-merge-requests
- .row
+ .gray-content-block.middle-block
+ .pull-right
+ = link_to 'Browse Merge Requests', merge_requests_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+ .oneline
+ All merge requests in this milestone
+
+ .row.prepend-top-default
.col-md-6
= render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests
.col-md-6
= render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests
.tab-pane#tab-participants
+ .gray-content-block.middle-block
+ .oneline
+ All participants to this milestone
+
%ul.bordered-list
- @milestone.participants.each do |user|
%li
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 0665cdf387a..4bc31cabea6 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -1,5 +1,10 @@
- page_title 'New Group'
-- header_title 'New Group'
+- header_title "Groups", dashboard_groups_path
+
+%h3.page-title
+ New Group
+%hr
+
= form_for @group, html: { class: 'group-form form-horizontal' } do |f|
- if @group.errors.any?
.alert.alert-danger
@@ -18,3 +23,4 @@
.form-actions
= f.submit 'Create group', class: "btn btn-create", tabindex: 3
+ = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel'
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 352b8040cf4..ec7cd79bc54 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,8 +1,8 @@
-.page-with-sidebar{ class: nav_sidebar_class }
+.page-with-sidebar{ class: page_sidebar_class }
= render "layouts/broadcast"
- .sidebar-wrapper.nicescroll
+ .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
- = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do
= brand_header_logo
.gitlab-text-container
%h3 GitLab
@@ -17,8 +17,8 @@
.collapse-nav
= render partial: 'layouts/collapse_button'
- if current_user
- = link_to current_user, class: 'sidebar-user' do
- = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36'
+ = link_to current_user, class: 'sidebar-user', title: "Profile" do
+ = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
.username
= current_user.username
.content-wrapper
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 1c738719bd8..6591c52bdbd 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,5 +1,5 @@
-- page_title "Admin area"
-- header_title "Admin area", admin_root_path
+- page_title "Admin Area"
+- header_title "Admin Area", admin_root_path
- sidebar "admin"
= render template: "layouts/application"
diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml
index ab3e29c3f42..7e90af21b21 100644
--- a/app/views/layouts/ci/_page.html.haml
+++ b/app/views/layouts/ci/_page.html.haml
@@ -1,8 +1,8 @@
-.page-with-sidebar{ class: nav_sidebar_class }
+.page-with-sidebar{ class: page_sidebar_class }
= render "layouts/broadcast"
- .sidebar-wrapper.nicescroll
+ .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
- = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do
= brand_header_logo
.gitlab-text-container
%h3 GitLab
@@ -14,8 +14,8 @@
.collapse-nav
= render partial: 'layouts/collapse_button'
- if current_user
- = link_to current_user, class: 'sidebar-user' do
- = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36'
+ = link_to current_user, class: 'sidebar-user', title: "Profile" do
+ = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
.username
= current_user.username
.content-wrapper
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 95e077c339f..f08cb0a5428 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -1,13 +1,13 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head"
- %body.ui_charcoal.login-page.application
+ %body.ui_charcoal.login-page.application.navless
= render "layouts/header/empty"
= render "layouts/broadcast"
.container.navless-container
.content
= render "layouts/flash"
- .row.prepend-top-20
+ .row
.col-sm-5.pull-right
= yield
.col-sm-7.brand-holder.pull-left
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index 2af265a2296..915acc4612e 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head"
- %body{class: "#{user_application_theme} application"}
+ %body{class: "#{user_application_theme} application navless"}
= render "layouts/header/empty"
.container.navless-container
= render "layouts/flash"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 3ca30d3baab..3892ef8eefa 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -11,27 +11,27 @@
%li.hidden-sm.hidden-xs
= render 'layouts/search'
%li.visible-sm.visible-xs
- = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('search')
- if session[:impersonator_id]
%li.impersonation
- = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do
+ = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret fw')
- if current_user.is_admin?
%li
- = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
- if current_user.can_create_project?
%li
- = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('plus fw')
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, title: 'Sherlock Transactions',
- data: {toggle: 'tooltip', placement: 'bottom'} do
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('tachometer fw')
%li
- = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('sign-out')
%h1.title= title
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 2079feeeab6..d04a3d1f227 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -5,78 +5,78 @@
%span
Overview
= nav_link(controller: [:admin, :projects]) do
- = link_to admin_namespaces_projects_path, title: 'Projects', data: {placement: 'right'} do
+ = link_to admin_namespaces_projects_path, title: 'Projects' do
= icon('cube fw')
%span
Projects
= nav_link(controller: :users) do
- = link_to admin_users_path, title: 'Users', data: {placement: 'right'} do
+ = link_to admin_users_path, title: 'Users' do
= icon('user fw')
%span
Users
= nav_link(controller: :groups) do
- = link_to admin_groups_path, title: 'Groups', data: {placement: 'right'} do
+ = link_to admin_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(controller: :deploy_keys) do
- = link_to admin_deploy_keys_path, title: 'Deploy Keys', data: {placement: 'right'} do
+ = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
= icon('key fw')
%span
Deploy Keys
= nav_link do
- = link_to ci_admin_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do
+ = link_to ci_admin_projects_path, title: 'Continuous Integration' do
= icon('building fw')
%span
Continuous Integration
= nav_link(controller: :logs) do
- = link_to admin_logs_path, title: 'Logs', data: {placement: 'right'} do
+ = link_to admin_logs_path, title: 'Logs' do
= icon('file-text fw')
%span
Logs
= nav_link(controller: :broadcast_messages) do
- = link_to admin_broadcast_messages_path, title: 'Broadcast Messages', data: {placement: 'right'} do
+ = link_to admin_broadcast_messages_path, title: 'Messages' do
= icon('bullhorn fw')
%span
Messages
= nav_link(controller: :hooks) do
- = link_to admin_hooks_path, title: 'Hooks', data: {placement: 'right'} do
+ = link_to admin_hooks_path, title: 'Hooks' do
= icon('external-link fw')
%span
Hooks
= nav_link(controller: :background_jobs) do
- = link_to admin_background_jobs_path, title: 'Background Jobs', data: {placement: 'right'} do
+ = link_to admin_background_jobs_path, title: 'Background Jobs' do
= icon('cog fw')
%span
Background Jobs
= nav_link(controller: :applications) do
- = link_to admin_applications_path, title: 'Applications', data: {placement: 'right'} do
+ = link_to admin_applications_path, title: 'Applications' do
= icon('cloud fw')
%span
Applications
= nav_link(controller: :services) do
- = link_to admin_application_settings_services_path, title: 'Service Templates', data: {placement: 'right'} do
+ = link_to admin_application_settings_services_path, title: 'Service Templates' do
= icon('copy fw')
%span
Service Templates
= nav_link(controller: :labels) do
- = link_to admin_labels_path, title: 'Labels', data: {placement: 'right'} do
+ = link_to admin_labels_path, title: 'Labels' do
= icon('tags fw')
%span
Labels
= nav_link(controller: :abuse_reports) do
- = link_to admin_abuse_reports_path, title: "Abuse reports" do
+ = link_to admin_abuse_reports_path, title: "Abuse Reports" do
= icon('exclamation-circle fw')
%span
Abuse Reports
%span.count= AbuseReport.count(:all)
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
- = link_to admin_application_settings_path, title: 'Settings', data: {placement: 'right'} do
+ = link_to admin_application_settings_path, title: 'Settings' do
= icon('cogs fw')
%span
Settings
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index b1a1d531846..da698831300 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,50 +1,50 @@
%ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do
- = link_to dashboard_projects_path, title: 'Projects', data: {placement: 'right'} do
+ = link_to dashboard_projects_path, title: 'Projects' do
= icon('home fw')
%span
Projects
= nav_link(path: 'dashboard#activity') do
- = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity', data: {placement: 'right'} do
+ = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do
= icon('dashboard fw')
%span
Activity
= nav_link(controller: :groups) do
- = link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do
+ = link_to dashboard_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(controller: :milestones) do
- = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do
+ = link_to dashboard_milestones_path, title: 'Milestones' do
= icon('clock-o fw')
%span
Milestones
= nav_link(path: 'dashboard#issues') do
- = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
+ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
= icon('exclamation-circle fw')
%span
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
- = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
+ = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= icon('tasks fw')
%span
Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :snippets) do
- = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do
+ = link_to dashboard_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
- = link_to help_path, title: 'Help', data: {placement: 'right'} do
+ = link_to help_path, title: 'Help' do
= icon('question-circle fw')
%span
Help
%li.separate-item
= nav_link(controller: :profile) do
- = link_to profile_path, title: 'Profile settings', data: {placement: 'bottom'} do
+ = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
= icon('user fw')
%span
Profile Settings
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 21e565972a7..48039ca2918 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,21 +1,21 @@
%ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
- = link_to explore_root_path, title: 'Projects', data: {placement: 'right'} do
+ = link_to explore_root_path, title: 'Projects' do
= icon('home fw')
%span
Projects
= nav_link(controller: :groups) do
- = link_to explore_groups_path, title: 'Groups', data: {placement: 'right'} do
+ = link_to explore_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(controller: :snippets) do
- = link_to explore_snippets_path, title: 'Snippets', data: {placement: 'right'} do
+ = link_to explore_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
- = link_to help_path, title: 'Help', data: {placement: 'right'} do
+ = link_to help_path, title: 'Help' do
= icon('question-circle fw')
%span
Help
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 319352876b4..68da8d5de2a 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,6 +1,6 @@
%ul.nav.nav-sidebar
= nav_link do
- = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
+ = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
@@ -8,39 +8,39 @@
%li.separate-item
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
- = link_to group_path(@group), title: 'Home', data: {placement: 'right'} do
+ = link_to group_path(@group), title: 'Home' do
= icon('dashboard fw')
%span
Group
- if can?(current_user, :read_group, @group)
- if current_user
= nav_link(controller: [:group, :milestones]) do
- = link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do
+ = link_to group_milestones_path(@group), title: 'Milestones' do
= icon('clock-o fw')
%span
Milestones
= nav_link(path: 'groups#issues') do
- = link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do
+ = link_to issues_group_path(@group), title: 'Issues' do
= icon('exclamation-circle fw')
%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), title: 'Merge Requests', data: {placement: 'right'} do
+ = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= icon('tasks fw')
%span
Merge Requests
- if current_user
%span.count= MergeRequest.opened.of_group(@group).count
= nav_link(controller: [:group_members]) do
- = link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do
+ = link_to group_group_members_path(@group), title: 'Members' do
= icon('users fw')
%span
Members
- if can?(current_user, :admin_group, @group)
= nav_link(html_options: { class: "separate-item" }) do
- = link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do
+ = link_to edit_group_path(@group), title: 'Settings' do
= icon ('cogs fw')
%span
Settings
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index c8411521f36..56a92fe9103 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -1,6 +1,6 @@
%ul.nav.nav-sidebar
= nav_link do
- = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
+ = link_to group_path(@group), title: 'Go to group', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to group
@@ -9,12 +9,12 @@
%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group), title: 'Group Settings', data: {placement: 'right'} do
+ = link_to edit_group_path(@group), title: 'Group Settings' do
= icon ('pencil-square-o fw')
%span
Group Settings
= nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
+ = link_to projects_group_path(@group), title: 'Projects' do
= icon('folder fw')
%span
Projects
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 0f3a793e30b..64b30783c05 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,6 +1,6 @@
%ul.nav.nav-sidebar
= nav_link do
- = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
+ = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
@@ -8,52 +8,52 @@
%li.separate-item
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
- = link_to profile_path, title: 'Profile', data: {placement: 'right'} do
+ = link_to profile_path, title: 'Profile Settings' do
= icon('user fw')
%span
Profile Settings
= nav_link(controller: [:accounts, :two_factor_auths]) do
- = link_to profile_account_path, title: 'Account', data: {placement: 'right'} do
+ = link_to profile_account_path, title: 'Account' do
= icon('gear fw')
%span
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new', 'applications#create']) do
- = link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do
+ = link_to applications_profile_path, title: 'Applications' do
= icon('cloud fw')
%span
Applications
= nav_link(controller: :emails) do
- = link_to profile_emails_path, title: 'Emails', data: {placement: 'right'} do
+ = link_to profile_emails_path, title: 'Emails' do
= icon('envelope-o fw')
%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, title: 'Password', data: {placement: 'right'} do
+ = link_to edit_profile_password_path, title: 'Password' do
= icon('lock fw')
%span
Password
= nav_link(controller: :notifications) do
- = link_to profile_notifications_path, title: 'Notifications', data: {placement: 'right'} do
+ = link_to profile_notifications_path, title: 'Notifications' do
= icon('inbox fw')
%span
Notifications
= nav_link(controller: :keys) do
- = link_to profile_keys_path, title: 'SSH Keys', data: {placement: 'right'} do
+ = link_to profile_keys_path, title: 'SSH Keys' do
= icon('key fw')
%span
SSH Keys
%span.count= current_user.keys.count
= nav_link(controller: :preferences) do
- = link_to profile_preferences_path, title: 'Preferences', data: {placement: 'right'} do
+ = link_to profile_preferences_path, title: 'Preferences' do
-# TODO (rspeicher): Better icon?
= icon('image fw')
%span
Preferences
= nav_link(path: 'profiles#audit_log') do
- = link_to audit_log_profile_path, title: 'Audit Log', data: {placement: 'right'} do
+ = link_to audit_log_profile_path, title: 'Audit Log' do
= icon('history fw')
%span
Audit Log
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 2b91d7721f9..87a7707b095 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,13 +1,13 @@
%ul.nav.nav-sidebar
- if @project.group
= nav_link do
- = link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
+ = link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to group
- else
= nav_link do
- = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
+ = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
@@ -15,32 +15,32 @@
%li.separate-item
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
- = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
+ = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= icon('home fw')
%span
Project
= nav_link(path: 'projects#activity') do
- = link_to activity_project_path(@project), title: 'Project Activity', class: 'shortcuts-project-activity', data: {placement: 'right'} do
+ = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
= icon('dashboard fw')
%span
Activity
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
- = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do
+ = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do
= icon('files-o fw')
%span
Files
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches releases)) do
- = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
+ = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
= icon('history fw')
%span
Commits
- if project_nav_tab? :builds
= nav_link(controller: %w(builds)) do
- = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds', data: {placement: 'right'} do
+ = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
= icon('cubes fw')
%span
Builds
@@ -48,28 +48,28 @@
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
- = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
+ = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do
= icon('code-fork fw')
%span
Network
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
- = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do
+ = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do
= icon('area-chart fw')
%span
Graphs
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
- = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
+ = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
= icon('clock-o fw')
%span
Milestones
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
- = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
+ = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
= icon('exclamation-circle fw')
%span
Issues
@@ -78,7 +78,7 @@
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
- = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= icon('tasks fw')
%span
Merge Requests
@@ -86,35 +86,35 @@
- if project_nav_tab? :settings
= nav_link(controller: [:project_members, :teams]) do
- = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
+ = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
= icon('users fw')
%span
Members
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
- = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
+ = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
= icon('tags fw')
%span
Labels
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
- = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
+ = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
= icon('book fw')
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
- = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
+ = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
= icon('clipboard fw')
%span
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
- = link_to edit_project_path(@project), title: 'Settings', data: {placement: 'right'} do
+ = link_to edit_project_path(@project), title: 'Settings' do
= icon('cogs fw')
%span
Settings
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 377a99e719a..f0b3f27b626 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -1,6 +1,6 @@
%ul.nav.nav-sidebar
= nav_link do
- = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do
+ = link_to project_path(@project), title: 'Go to project', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to project
@@ -9,59 +9,59 @@
%ul.sidebar-subnav
= nav_link(path: 'projects#edit') do
- = link_to edit_project_path(@project), title: 'Project Settings', data: {placement: 'right'} do
+ = link_to edit_project_path(@project), title: 'Project Settings' do
= icon('pencil-square-o fw')
%span
Project Settings
= nav_link(controller: :deploy_keys) do
- = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
+ = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
= icon('key fw')
%span
Deploy Keys
= nav_link(controller: :hooks) do
- = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
+ = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do
= icon('link fw')
%span
Web Hooks
= nav_link(controller: :services) do
- = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
+ = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
= icon('cogs fw')
%span
Services
= nav_link(controller: :protected_branches) do
- = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
+ = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
= icon('lock fw')
%span
Protected Branches
- if @project.builds_enabled?
= nav_link(controller: :runners) do
- = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do
+ = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do
= icon('cog fw')
%span
Runners
= nav_link(controller: :variables) do
- = link_to namespace_project_variables_path(@project.namespace, @project) do
+ = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do
= icon('code fw')
%span
Variables
= nav_link path: 'triggers#index' do
- = link_to namespace_project_triggers_path(@project.namespace, @project) do
+ = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
= icon('retweet fw')
%span
Triggers
= nav_link path: 'ci_web_hooks#index' do
- = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project) do
+ = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project), title: 'CI Web Hooks' do
= icon('link fw')
%span
CI Web Hooks
= nav_link path: 'ci_settings#edit' do
- = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project) do
+ = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do
= icon('building fw')
%span
CI Settings
= nav_link controller: 'ci_services' do
- = link_to namespace_project_ci_services_path(@project.namespace, @project) do
+ = link_to namespace_project_ci_services_path(@project.namespace, @project), title: 'CI Services' do
= icon('share fw')
%span
CI Services
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index cd7b1b0fe03..319bdd57c39 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -23,10 +23,13 @@
%p.cgray
- if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "form-control"
- %div
- = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
- else
%span You don`t have one yet. Click generate to fix it.
+
+ .form-actions
+ - if current_user.private_token
+ = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
+ - else
= f.submit 'Generate', class: "btn btn-default btn-build-token"
- unless current_user.ldap_user?
@@ -54,7 +57,8 @@
%p
Each time you log in you’ll be required to provide your username and
password as usual, plus a randomly-generated code from your phone.
- %div
+
+ .form-actions
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if button_based_providers.any?
@@ -81,15 +85,16 @@
%p
Changing your username will change path to all personal projects!
%div
- = f.text_field :username, required: true, class: 'form-control'
+ .input-group
+ .input-group-addon
+ = "#{root_url}u/"
+ = f.text_field :username, required: true, class: 'form-control'
&nbsp;
.loading-gif.hide
%p
= icon('spinner spin')
Saving new username
- %p.light
- = user_url(@user)
- %div
+ .form-actions
= f.submit 'Save username', class: "btn btn-warning"
- if signup_enabled?
@@ -104,7 +109,8 @@
- rp = current_user.personal_projects.count
- unless rp.zero?
%li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
+ .form-actions
+ = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- else
- if @user.solo_owned_groups.present?
%p
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index b76a5b636ac..2a8800de60e 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -1,5 +1,5 @@
%div
- = form_for [:profile, @key], html: { class: 'form-horizontal' } do |f|
+ = form_for [:profile, @key], html: { class: 'form-horizontal js-requires-input' } do |f|
- if @key.errors.any?
.alert.alert-danger
%ul
@@ -9,12 +9,11 @@
.form-group
= f.label :key, class: 'control-label'
.col-sm-10
- = f.text_area :key, class: "form-control", rows: 8
+ = f.text_area :key, class: "form-control", rows: 8, autofocus: true, required: true
.form-group
= f.label :title, class: 'control-label'
- .col-sm-10= f.text_field :title, class: "form-control"
+ .col-sm-10= f.text_field :title, class: "form-control", required: true
.form-actions
= f.submit 'Add key', class: "btn btn-create"
= link_to "Cancel", profile_keys_path, class: "btn btn-cancel"
-
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index cc41d7dd813..877589dc390 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -54,4 +54,4 @@
.help-block
Choose what content you want to see on a project's home page.
.panel-footer
- = f.submit 'Save', class: 'btn btn-save'
+ = f.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index ac7355dde1f..9459d8a6295 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -43,7 +43,7 @@
.form-group
= f.label :public_email, class: "control-label"
.col-sm-10
- = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show in profile'}, class: "form-control"
+ = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2"
%span.help-block This email will be displayed on your public profile.
.form-group
= f.label :skype, class: "control-label"
@@ -96,8 +96,6 @@
= link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
- .row
- .col-md-7
- .form-group
- .col-sm-offset-2.col-sm-10
- = f.submit 'Save changes', class: "btn btn-success"
+ .form-actions
+ = f.submit 'Save changes', class: "btn btn-success"
+ = link_to "Cancel", user_path(current_user), class: "btn btn-cancel"
diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml
index 35f7e7bb34b..2fd3d9e1be4 100644
--- a/app/views/projects/_commit_button.html.haml
+++ b/app/views/projects/_commit_button.html.haml
@@ -1,6 +1,4 @@
.form-actions
- .commit-button-annotation
- = button_tag 'Commit Changes',
- class: 'btn commit-btn js-commit-button btn-create'
+ = button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
= link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index b30036966a7..c1669ac046b 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -12,11 +12,20 @@
Forked from
= link_to project_path(forked_from_project) do
= forked_from_project.namespace.try(:name)
- .cover-controls
- .visibility-level-label
- = visibility_level_icon(@project.visibility_level)
+ .cover-controls.left
+ .visibility-level-label.has_tooltip{title: project_visibility_level_description(@project.visibility_level), data: { container: 'body' } }
+ = visibility_level_icon(@project.visibility_level, fw: false)
= visibility_level_label(@project.visibility_level)
+ .cover-controls
+ - if can?(current_user, :admin_project, @project)
+ = link_to edit_project_path(@project), class: 'btn btn-gray' do
+ = icon('pencil')
+ - if current_user
+ &nbsp;
+ = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do
+ = icon('rss')
+
.project-repo-buttons
.split-one
= render 'projects/buttons/star'
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index 13b5ffd17ff..fc6c9f5fd09 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -5,19 +5,17 @@
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Create New Directory
.modal-body
- = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do
+ = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-requires-input' do
.form-group
- = label_tag :dir_name, 'Directory Name', class: 'control-label'
+ = label_tag :dir_name, 'Directory name', class: 'control-label'
.col-sm-10
- = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control'
+ = text_field_tag :dir_name, params[:dir_name], required: true, class: 'form-control'
= render 'shared/new_commit_form', placeholder: "Add new directory"
- .form-group
- .col-sm-offset-2.col-sm-10
- = submit_tag "Create directory", class: 'btn btn-primary btn-create'
- = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+ .form-actions
+ = submit_tag "Create directory", class: 'btn btn-create'
+ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:javascript
- disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create");
new NewCommitForm($('.js-create-dir-form'))
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 3bb61f0c944..ecc90a30e78 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -16,10 +16,9 @@
= render 'shared/new_commit_form', placeholder: placeholder
- .form-group
- .col-sm-offset-2.col-sm-10
- = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
- = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+ .form-actions
+ = button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
+ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:javascript
disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 1ff68005450..167fa615182 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,9 +1,8 @@
- page_title "New File", @path.presence, @ref
= render "header_title"
-.gray-content-block.top-block
- %h3.page-title
- Create New File
+%h3.page-title
+ New File
.file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index f5577042ca4..31943a2407a 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -6,25 +6,27 @@
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @error
%h3.page-title
- %i.fa.fa-code-fork
- New branch
+ New Branch
+%hr
+
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do
.form-group
- = label_tag :branch_name, 'Name for new branch', class: 'control-label'
+ = label_tag :branch_name, nil, class: 'control-label'
.col-sm-10
- = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control'
+ = text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control'
.form-group
= label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10
- = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control'
+ = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control'
+ .help-block Existing branch name, tag, or commit SHA
.form-actions
= button_tag 'Create branch', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel'
:javascript
- var availableTags = #{@project.repository.ref_names.to_json};
+ var availableRefs = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
- source: availableTags,
+ source: availableRefs,
minLength: 1
});
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index 91675b3738e..5e182af2669 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -1,5 +1,5 @@
%div
- = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f|
-if @key.errors.any?
.alert.alert-danger
%ul
@@ -8,16 +8,15 @@
.form-group
= f.label :title, class: "control-label"
- .col-sm-10= f.text_field :title, class: 'form-control'
+ .col-sm-10= f.text_field :title, class: 'form-control', autofocus: true, required: true
.form-group
= f.label :key, class: "control-label"
.col-sm-10
%p.light
Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh", "README")
- = f.text_area :key, class: "form-control thin_area", rows: 5
+ = f.text_area :key, class: "form-control thin_area", rows: 5, required: true
.form-actions
- = f.submit 'Create', class: "btn-create btn"
+ = f.submit 'Create Deploy Key', class: "btn-create btn"
= link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel"
-
diff --git a/app/views/projects/deploy_keys/new.html.haml b/app/views/projects/deploy_keys/new.html.haml
index 01c810aee18..01fab3008a7 100644
--- a/app/views/projects/deploy_keys/new.html.haml
+++ b/app/views/projects/deploy_keys/new.html.haml
@@ -1,5 +1,5 @@
- page_title "New Deploy Key"
-%h3.page-title New Deploy key
+%h3.page-title New Deploy Key
%hr
= render 'form'
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 0c10de1604c..b28ada909b7 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -14,7 +14,7 @@
= f.label :name, class: 'control-label' do
Project name
.col-sm-10
- = f.text_field :name, placeholder: "Example Project", class: "form-control", id: "project_name_edit"
+ = f.text_field :name, class: "form-control", id: "project_name_edit"
.form-group
@@ -22,7 +22,7 @@
Project description
%span.light (optional)
.col-sm-10
- = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250
+ = f.text_area :description, class: "form-control", rows: 3, maxlength: 250
- if @project.repository.exists? && @project.repository.branch_names.any?
.form-group
@@ -130,9 +130,11 @@
The project can be committed to.
%br
%strong Once active this project shows up in the search and on the dashboard.
- = link_to 'Unarchive', unarchive_namespace_project_path(@project.namespace, @project),
- data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." },
- method: :post, class: "btn btn-success"
+
+ .form-actions
+ = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project),
+ data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." },
+ method: :post, class: "btn btn-success"
- else
.panel.panel-warning
.panel-heading
@@ -144,9 +146,11 @@
It is hidden from the dashboard and doesn't show up in searches.
%br
%strong Archived projects cannot be committed to!
- = link_to 'Archive', archive_namespace_project_path(@project.namespace, @project),
- data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." },
- method: :post, class: "btn btn-warning"
+
+ .form-actions
+ = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project),
+ data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." },
+ method: :post, class: "btn btn-warning"
- else
.nothing-here-block Only the project owner can archive a project
@@ -160,7 +164,7 @@
Project name
.col-sm-9
.form-group
- = f.text_field :name, placeholder: "Example Project", class: "form-control"
+ = f.text_field :name, class: "form-control"
.form-group
= f.label :path, class: 'control-label' do
%span Path
@@ -170,12 +174,11 @@
.input-group-addon
#{URI.join(root_url, @project.namespace.path)}/
= f.text_field :path, class: 'form-control'
- %span.input-group-addon .git
%ul
%li Be careful. Renaming a project's repository can have unintended side effects.
%li You will need to update your local repositories to point to the new location.
.form-actions
- = f.submit 'Rename', class: "btn btn-warning"
+ = f.submit 'Rename project', class: "btn btn-warning"
- if can?(current_user, :change_namespace, @project)
.panel.panel-default.panel.panel-danger
@@ -194,7 +197,7 @@
%li You can only transfer the project to namespaces you manage.
%li You will need to update your local repositories to point to the new location.
.form-actions
- = f.submit 'Transfer', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) }
+ = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) }
- else
.nothing-here-block Only the project owner can transfer a project
@@ -209,7 +212,8 @@
#{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}.
%br
%strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
- = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
+ .form-actions
+ = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
- else
.nothing-here-block Only the project owner can remove the fork relationship.
@@ -222,8 +226,8 @@
Removing the project will delete its repository and all related resources including issues, merge requests etc.
%br
%strong Removed projects cannot be restored!
-
- = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
+ .form-actions
+ = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
- else
.nothing-here-block Only the project owner can remove a project.
diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml
index aef352029d0..917d5181689 100644
--- a/app/views/projects/issues/_closed_by_box.html.haml
+++ b/app/views/projects/issues/_closed_by_box.html.haml
@@ -1,3 +1,3 @@
.issue-closed-by-widget
= icon('check')
- This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted.
+ This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests))} is accepted.
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 8f0a1ed9be2..b5f522f2079 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -29,10 +29,3 @@
.issuable-affix
.context
= render 'shared/issuable/context', issuable: @issue
-
- - if @issue.labels.any?
- .issuable-context-title
- %label Labels
- .issue-show-labels
- - @issue.labels.each do |label|
- = link_to_label(label)
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index f39bb7d2574..6588d9bdbe1 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,9 +1,5 @@
-%div.issue-form-holder
- %h3.page-title= @issue.new_record? ? "Create Issue" : "Edit Issue ##{@issue.iid}"
- %hr
-
- = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f|
- = render 'shared/issuable/form', f: f, issuable: @issue
+= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form js-requires-input' } do |f|
+ = render 'shared/issuable/form', f: f, issuable: @issue
:javascript
$('.assign-to-me-link').on('click', function(e){
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index d7657ee7e40..1eb71990e55 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -6,35 +6,39 @@
.issue-title
%span.issue-title-text
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
- .issue-labels
- - issue.labels.each do |label|
- = link_to_label(label, project: issue.project)
.pull-right.light
- if issue.closed?
%span
CLOSED
- if issue.assignee
- = link_to_member(@project, issue.assignee, name: false)
+ = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
- note_count = issue.notes.user.count
- if note_count > 0
&nbsp;
- %span
- %i.fa.fa-comments
+ = link_to issue_path(issue) + "#notes" do
+ = icon('comments')
= note_count
- else
&nbsp;
- %span.issue-no-comments
- %i.fa.fa-comments
+ = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
+ = icon('comments')
= 0
.issue-info
- = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
+ #{issue.to_reference} &middot;
+ opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
+ by #{link_to_member(@project, issue.author, avatar: false)}
- if issue.milestone
&nbsp;
- %span
- %i.fa.fa-clock-o
+ = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do
+ = icon('clock-o')
= issue.milestone.title
+ - if issue.labels.any?
+ &nbsp;
+ - issue.labels.each do |label|
+ = link_to_label(label, project: issue.project)
- if issue.tasks?
+ &nbsp;
%span.task-status
= issue.task_status
diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml
index 53b6f0879c9..20216297d25 100644
--- a/app/views/projects/issues/edit.html.haml
+++ b/app/views/projects/issues/edit.html.haml
@@ -1,2 +1,8 @@
- page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues"
+= render "header_title"
+
+%h3.page-title
+ Edit Issue ##{@issue.iid}
+%hr
+
= render "form"
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index 153447baa1b..b317a0c1cf4 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1,4 +1,8 @@
- page_title "New Issue"
= render "header_title"
+%h3.page-title
+ New Issue
+%hr
+
= render "form"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index f01bf2505da..e2de17cee6d 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -11,7 +11,8 @@
Open
%span.issue-id Issue ##{@issue.iid}
%span.creator
- &middot; created by #{link_to_member(@project, @issue.author, size: 24)}
+ &middot;
+ opened by #{link_to_member(@project, @issue.author, size: 24)}
&middot;
= time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
- if @issue.updated_at != @issue.created_at
diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml
index 4cf13492e99..5ce2a7b985d 100644
--- a/app/views/projects/labels/_form.html.haml
+++ b/app/views/projects/labels/_form.html.haml
@@ -10,9 +10,9 @@
.form-group
= f.label :title, class: 'control-label'
.col-sm-10
- = f.text_field :title, class: "form-control js-quick-submit", required: true
+ = f.text_field :title, class: "form-control js-quick-submit", required: true, autofocus: true
.form-group
- = f.label :color, "Background Color", class: 'control-label'
+ = f.label :color, "Background color", class: 'control-label'
.col-sm-10
.input-group
.input-group-addon.label-color-preview &nbsp;
@@ -28,6 +28,8 @@
&nbsp;
.form-actions
- = f.submit 'Save', class: 'btn btn-save js-save-button'
+ - if @label.persisted?
+ = f.submit 'Save changes', class: 'btn btn-save js-save-button'
+ - else
+ = f.submit 'Create Label', class: 'btn btn-create js-save-button'
= link_to "Cancel", namespace_project_labels_path(@project.namespace, @project), class: 'btn btn-cancel'
-
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index bc4ab0ca27c..675a805e12f 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -1,11 +1,7 @@
- page_title "Edit", @label.name, "Labels"
= render "header_title"
-%h3
- Edit label
- %span.light #{@label.name}
-.back-link
- = link_to namespace_project_labels_path(@project.namespace, @project) do
- &larr; To labels list
+%h3.page-title
+ Edit Label
%hr
= render 'form'
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index 342ad4f3f95..e20fd7d6891 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,9 +1,7 @@
- page_title "New Label"
= render "header_title"
-%h3 New label
-.back-link
- = link_to namespace_project_labels_path(@project.namespace, @project) do
- &larr; To labels list
+%h3.page-title
+ New Label
%hr
= render 'form'
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index 4a192aeb2cd..ea462561668 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -26,10 +26,3 @@
.issuable-affix
.context
= render 'shared/issuable/context', issuable: @merge_request
-
- - if @merge_request.labels.any?
- .issuable-context-title
- %label Labels
- .merge-request-show-labels
- - @merge_request.labels.each do |label|
- = link_to_label(label)
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index 9cf389dbe38..3e4ab09c6d4 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -1,6 +1,5 @@
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f|
- .merge-request-form-info
- = render 'shared/issuable/form', f: f, issuable: @merge_request
+ = render 'shared/issuable/form', f: f, issuable: @merge_request
:javascript
$('.assign-to-me-link').on('click', function(e){
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 83e8ad11989..1d4c9b66c42 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -3,48 +3,52 @@
.merge-request-title
%span.merge-request-title-text
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
- .merge-request-labels
- - merge_request.labels.each do |label|
- = link_to_label(label, project: merge_request.project)
.pull-right.light
- if ci_commit
= render_ci_status(ci_commit)
- if merge_request.merged?
%span
- %i.fa.fa-check
+ = icon('check')
MERGED
- elsif merge_request.closed?
%span
- %i.fa.fa-ban
+ = icon('ban')
CLOSED
- note_count = merge_request.mr_and_commit_notes.user.count
- if merge_request.assignee
&nbsp;
- = link_to_member(merge_request.source_project, merge_request.assignee, name: false)
+ = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
- if note_count > 0
&nbsp;
- %span
- %i.fa.fa-comments
+ = link_to merge_request_path(merge_request) + "#notes" do
+ = icon('comments')
= note_count
- else
&nbsp;
- %span.merge-request-no-comments
- %i.fa.fa-comments
+ = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
+ = icon('comments')
= 0
.merge-request-info
- = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
- - if merge_request.milestone_id?
- &nbsp;
- %span
- %i.fa.fa-clock-o
- = merge_request.milestone.title
+ \##{merge_request.iid} &middot;
+ opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
+ by #{link_to_member(@project, merge_request.author, avatar: false)}
- if merge_request.target_project.default_branch != merge_request.target_branch
&nbsp;
- %span
- %i.fa.fa-code-fork
+ = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do
+ = icon('code-fork')
= merge_request.target_branch
+ - if merge_request.milestone
+ &nbsp;
+ = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do
+ = icon('clock-o')
+ = merge_request.milestone.title
+ - if merge_request.labels.any?
+ &nbsp;
+ - merge_request.labels.each do |label|
+ = link_to_label(label, project: merge_request.project)
- if merge_request.tasks?
+ &nbsp;
%span.task-status
= merge_request.task_status
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 6def9d6266f..236a545c840 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -1,4 +1,5 @@
-%p.lead Compare branches for new Merge Request
+%h3.page-title
+ New Merge Request
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f|
.hide.alert.alert-danger.mr-compare-errors
@@ -10,7 +11,7 @@
.panel-body
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted?, required: true })
&nbsp;
- = f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2', required: true})
+ = f.select(:source_branch, @merge_request.source_branches, { include_blank: true }, { class: 'source_branch select2 span2', required: true, data: { placeholder: "Select source branch" } })
.panel-footer
.mr_source_commit
@@ -22,7 +23,7 @@
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted?, required: true })
&nbsp;
- = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2', required: true})
+ = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', required: true, data: { placeholder: "Select target branch" } })
.panel-footer
.mr_target_commit
@@ -51,8 +52,8 @@
are the same.
- %div
- = f.submit 'Compare branches', class: "btn btn-new mr-compare-btn"
+ .form-actions
+ = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
:javascript
var source_branch = $("#merge_request_source_branch")
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 72132344c88..156922cea41 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- New merge request
+ New Merge Request
%p.slead
- source_title, target_title = format_mr_branch_names(@merge_request)
From
@@ -11,12 +11,11 @@
= link_to 'Change branches', mr_change_branches_path(@merge_request)
%hr
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f|
- .merge-request-form-info
- = render 'shared/issuable/form', f: f, issuable: @merge_request
- = f.hidden_field :source_project_id
- = f.hidden_field :source_branch
- = f.hidden_field :target_project_id
- = f.hidden_field :target_branch
+ = render 'shared/issuable/form', f: f, issuable: @merge_request
+ = f.hidden_field :source_project_id
+ = f.hidden_field :source_branch
+ = f.hidden_field :target_project_id
+ = f.hidden_field :target_branch
.mr-compare.merge-request
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index e7eb0066594..f5aff0877e7 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -26,15 +26,17 @@
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
.normal
%span Request to merge
- %span.label-branch #{source_branch_with_namespace(@merge_request)}
+ %span.label-branch
+ = source_branch_with_namespace(@merge_request)
%span into
- %span.label-branch #{@merge_request.target_branch}
+ = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
+ = @merge_request.target_branch
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/widget/show.html.haml"
- - if @merge_request.open? && @merge_request.can_be_merged?
- .light.append-bottom-20
+ - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user)
+ .light.prepend-top-default
You can also accept this merge request manually using the
= succeed '.' do
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml
index 303ca0a880b..fc62bb5bce9 100644
--- a/app/views/projects/merge_requests/edit.html.haml
+++ b/app/views/projects/merge_requests/edit.html.haml
@@ -2,6 +2,6 @@
= render "header_title"
%h3.page-title
- = "Edit merge request ##{@merge_request.iid}"
+ Edit Merge Request ##{@merge_request.iid}
%hr
= render 'form'
diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml
index 33321651e32..518ecb9f00f 100644
--- a/app/views/projects/merge_requests/merge.js.haml
+++ b/app/views/projects/merge_requests/merge.js.haml
@@ -1,6 +1,6 @@
- if @status
:plain
- merge_request_widget.mergeInProgress();
+ merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
- else
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");
diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml
index 9fdde80c6d9..d259968030e 100644
--- a/app/views/projects/merge_requests/new.html.haml
+++ b/app/views/projects/merge_requests/new.html.haml
@@ -1,7 +1,7 @@
- page_title "New Merge Request"
= render "header_title"
-- if @merge_request.can_be_created
+- if @merge_request.can_be_created && !params[:change_branches]
= render 'new_submit'
- else
= render 'new_compare'
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index 2bf9cd597a4..4dfe46e2b86 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -4,7 +4,7 @@
%span.issue-id Merge Request ##{@merge_request.iid}
%span.creator
&middot;
- created by #{link_to_member(@project, @merge_request.author, size: 24)}
+ opened by #{link_to_member(@project, @merge_request.author, size: 24)}
&middot;
= time_ago_with_tooltip(@merge_request.created_at)
- if @merge_request.updated_at != @merge_request.created_at
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index a788fcea23f..5c6fece8c5c 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -7,10 +7,11 @@
by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
%div
- - if !@merge_request.source_branch_exists?
+ - if !@merge_request.source_branch_exists? || (params[:delete_source] == 'true')
= succeed '.' do
The changes were merged into
- %span.label-branch= @merge_request.target_branch
+ = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
+ = @merge_request.target_branch
The source branch has been removed.
- elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
@@ -18,7 +19,8 @@
%p
= succeed '.' do
The changes were merged into
- %span.label-branch= @merge_request.target_branch
+ = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
+ = @merge_request.target_branch
You can remove the source branch now.
= link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do
%i.fa.fa-times
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 24879b19d2b..39aa2437e18 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,10 +1,3 @@
-%h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.iid}"
-.back-link
- = link_to namespace_project_milestones_path(@project.namespace, @project) do
- &larr; To milestones
-
-%hr
-
= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-requires-input'} do |f|
-if @milestone.errors.any?
.alert.alert-danger
@@ -16,8 +9,7 @@
.form-group
= f.label :title, "Title", class: "control-label"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true
- %p.hint Required
+ = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true
.form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml
index e9dc0b77462..43f8863163d 100644
--- a/app/views/projects/milestones/edit.html.haml
+++ b/app/views/projects/milestones/edit.html.haml
@@ -1,3 +1,9 @@
- page_title "Edit", @milestone.title, "Milestones"
= render "header_title"
+
+%h3.page-title
+ Edit Milestone ##{@milestone.iid}
+
+%hr
+
= render "form"
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index 9ba9acb6f77..0d016f78313 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1,3 +1,9 @@
- page_title "New Milestone"
= render "header_title"
+
+%h3.page-title
+ New Milestone
+
+%hr
+
= render "form"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index eab5333ca29..c3bda794c65 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,46 +1,50 @@
- page_title @milestone.title, "Milestones"
= render "header_title"
-%h4.page-title
- .issue-box{ class: issue_box_class(@milestone) }
- - if @milestone.closed?
- Closed
- - elsif @milestone.expired?
- Expired
- - else
- Open
- Milestone ##{@milestone.iid}
- %small.creator
- = @milestone.expires_at
- .pull-right
- - if can?(current_user, :admin_milestone, @project)
- = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
- %i.fa.fa-pencil-square-o
- Edit
- - if @milestone.active?
- = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
+.issuable-details
+ .page-title
+ .issue-box{ class: issue_box_class(@milestone) }
+ - if @milestone.closed?
+ Closed
+ - elsif @milestone.expired?
+ Expired
- else
- = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
- = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
- %i.fa.fa-trash-o
- Delete
+ Open
+ Milestone ##{@milestone.iid}
+ - if @milestone.expires_at
+ %span.creator
+ &middot;
+ = @milestone.expires_at
+ .pull-right
+ - if can?(current_user, :admin_milestone, @project)
+ = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
+ %i.fa.fa-pencil-square-o
+ Edit
+
+ - if @milestone.active?
+ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
+ - else
+ = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
+
+ = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
+ %i.fa.fa-trash-o
+ Delete
+
+ .gray-content-block.middle-block
+ %h2.issue-title
+ = gfm escape_once(@milestone.title)
+ %div
+ - if @milestone.description.present?
+ .description
+ .wiki
+ = preserve do
+ = markdown @milestone.description
-%hr
- if @milestone.issues.any? && @milestone.can_be_closed?
- .alert.alert-success
+ .alert.alert-success.prepend-top-default
%span All issues for this milestone are closed. You may close milestone now.
-%h3.issue-title
- = gfm escape_once(@milestone.title)
-%div
- - if @milestone.description.present?
- .description
- .wiki
- = preserve do
- = markdown @milestone.description
-
-%hr
-.context
+.context.prepend-top-default
%p.lead
Progress:
#{@milestone.closed_items_count} closed
@@ -51,8 +55,7 @@
%span.pull-right= @milestone.expires_at
= milestone_progress_bar(@milestone)
-
-%ul.nav.nav-tabs
+%ul.center-top-menu.no-top.no-bottom
%li.active
= link_to '#tab-issues', 'data-toggle' => 'tab' do
Issues
@@ -66,17 +69,21 @@
Participants
%span.badge= @users.count
- .pull-right
- - if can?(current_user, :create_issue, @project)
- = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do
- %i.fa.fa-plus
- New Issue
- - if can?(current_user, :read_issue, @project)
- = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped"
-
.tab-content
.tab-pane.active#tab-issues
- .row
+ .gray-content-block.middle-block
+ .pull-right
+ - if can?(current_user, :create_issue, @project)
+ = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do
+ %i.fa.fa-plus
+ New Issue
+ - if can?(current_user, :read_issue, @project)
+ = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+ .oneline
+ All issues in this milestone
+
+ .row.prepend-top-default
.col-md-4
= render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned')
.col-md-4
@@ -85,7 +92,15 @@
= render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed')
.tab-pane#tab-merge-requests
- .row
+ .gray-content-block.middle-block
+ .pull-right
+ - if can?(current_user, :read_merge_request, @project)
+ = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+ .oneline
+ All merge requests in this milestone
+
+ .row.prepend-top-default
.col-md-3
= render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned')
.col-md-3
@@ -100,6 +115,10 @@
= render 'merge_request', merge_request: merge_request
.tab-pane#tab-participants
+ .gray-content-block.middle-block
+ .oneline
+ All participants to this milestone
+
%ul.bordered-list
- @users.each do |user|
%li
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index c9d1fc3da21..25233112132 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,5 +1,10 @@
- page_title 'New Project'
-- header_title 'New Project'
+- header_title "Projects", root_path
+
+%h3.page-title
+ New Project
+%hr
+
.project-edit-container
.project-edit-errors
= render 'projects/errors'
@@ -11,16 +16,21 @@
Project path
.col-sm-10
.input-group
- = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true
- .input-group-addon
- \.git
-
- - if current_user.can_select_namespace?
- .form-group
- = f.label :namespace_id, class: 'control-label' do
- %span Namespace
- .col-sm-10
- = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2}
+ - if current_user.can_select_namespace?
+ .input-group-addon
+ = root_url
+ = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2', tabindex: 1}
+ .input-group-addon
+ \/
+ - else
+ .input-group-addon
+ #{root_url}#{current_user.username}/
+ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
+
+ - if current_user.can_create_group?
+ .help-block
+ Want to house several dependent projects under the same namespace?
+ = link_to "Create a group", new_group_path
- if import_sources_enabled?
.project-import.js-toggle-container
@@ -90,19 +100,12 @@
Description
%span.light (optional)
.col-sm-10
- = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250, tabindex: 3
+ = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
.form-actions
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
-
- - if current_user.can_create_group?
- .pull-right
- .light.inline
- .space-right
- Need a group for several dependent projects?
- = link_to new_group_path, class: "btn" do
- Create a group
+ = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
.save-project-loader.hide
.center
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
index a21c019986a..3ccda1b381c 100644
--- a/app/views/projects/notes/_edit_form.html.haml
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -6,6 +6,5 @@
= render 'projects/notes/hints'
.note-form-actions
- .buttons
- = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button'
- = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel'
+ = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button'
+ = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel'
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index 5dd84317e3b..88e711ab534 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -12,8 +12,7 @@
= render 'projects/notes/hints'
.error-alert
- .note-form-actions
- .buttons.clearfix
- = f.submit 'Add Comment', class: "btn btn-green comment-btn btn-grouped js-comment-button"
- = yield(:note_actions)
- %a.btn.grouped.js-close-discussion-note-form Cancel
+ .note-form-actions.clearfix
+ = f.submit 'Add Comment', class: "btn btn-create comment-btn btn-grouped js-comment-button"
+ = yield(:note_actions)
+ %a.btn.btn-cancel.js-close-discussion-note-form Cancel
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 52b3a50c1e6..2541105b007 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -22,7 +22,7 @@
.form-group
= f.label :name, "Branch", class: 'control-label'
.col-sm-10
- = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "select2"})
+ = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: true}, {class: "select2", data: {placeholder: "Select branch"}})
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
@@ -33,4 +33,3 @@
.form-actions
= f.submit 'Protect', class: "btn-create btn"
= render 'branches_list'
-
diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml
index a0324701690..eba03028af8 100644
--- a/app/views/projects/runners/edit.html.haml
+++ b/app/views/projects/runners/edit.html.haml
@@ -26,4 +26,4 @@
= f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control'
.help-block You can setup jobs to only use runners with specific tags
.form-actions
- = f.submit 'Save', class: 'btn btn-save'
+ = f.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index e1823b51198..1b70880043a 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -4,18 +4,15 @@
%p= @service.description
-.back-link
- = link_to namespace_project_services_path(@project.namespace, @project) do
- &larr; to services
-
%hr
= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
= render 'shared/service_settings', form: form
.form-actions
- = form.submit 'Save', class: 'btn btn-save'
+ = form.submit 'Save changes', class: 'btn btn-save'
&nbsp;
- if @service.valid? && @service.activated?
- disabled = @service.can_test? ? '':'disabled'
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}"
+ = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index e69f2d99709..dc3ea1fcf12 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -2,6 +2,6 @@
= render "header_title"
%h3.page-title
- Edit snippet
+ Edit Snippet
%hr
= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet), visibility_level: @snippet.visibility_level
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 67cd69fd215..e57237991b4 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -2,6 +2,6 @@
= render "header_title"
%h3.page-title
- New snippet
+ New Snippet
%hr
= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet), visibility_level: default_snippet_visibility
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 86aa15dc5b3..3a2f75fecaa 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -7,24 +7,24 @@
= @error
%h3.page-title
- New git tag
+ New Tag
%hr
-= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do
+= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form js-requires-input" do
.form-group
- = label_tag :tag_name, 'Name for new tag', class: 'control-label'
+ = label_tag :tag_name, nil, class: 'control-label'
.col-sm-10
- = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control'
+ = text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control'
.form-group
= label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10
- = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control'
+ = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control'
.help-block Branch name or commit SHA
.form-group
- = label_tag :message, 'Message', class: 'control-label'
+ = label_tag :message, nil, class: 'control-label'
.col-sm-10
- = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control'
- .help-block (Optional) Entering a message will create an annotated tag.
+ = text_field_tag :message, nil, required: false, tabindex: 3, class: 'form-control'
+ .help-block Optionally, enter a message to create an annotated tag.
%hr
.form-group
= label_tag :release_description, 'Release notes', class: 'control-label'
@@ -32,16 +32,15 @@
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control'
= render 'projects/notes/hints'
- .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page
+ .help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel'
:javascript
- disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create");
- var availableTags = #{@project.repository.ref_names.to_json};
+ var availableRefs = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
- source: availableTags,
+ source: availableRefs,
minLength: 1
});
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 9c94c43e747..1d257818dcd 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default' } do |f|
-if @page.errors.any?
#error_explanation
.alert.alert-danger
@@ -11,14 +11,7 @@
.col-sm-10
= f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control"
- .row
- .col-sm-offset-2.col-sm-10
- %p.cgray
- To link to a (new) page you can just type
- %code [Link Title](page-slug)
- \.
-
- .form-group.wiki-content
+ .form-group
= f.label :content, class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
@@ -27,6 +20,11 @@
.clearfix
.error-alert
+
+ .help-block
+ To link to a (new) page, simply type
+ %code [Link Title](page-slug)
+ \.
.form-group
= f.label :commit_message, class: 'control-label'
.col-sm-10= f.text_field :message, class: 'form-control', rows: 18
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 14f25822259..29bf5d62abe 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,9 +1,4 @@
%span.pull-right
- - if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new btn-grouped", "data-toggle" => "modal" do
- %i.fa.fa-plus
- New Page
-
- if (@page && @page.persisted?)
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
Page History
@@ -11,5 +6,7 @@
= link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
-
-= render 'projects/wikis/new'
+ - if can?(current_user, :admin_wiki, @project)
+ = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do
+ = icon('trash')
+ Delete
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index fffb4eb31ab..e6e6ad5bc4b 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,10 +1,19 @@
-%ul.center-top-menu
- = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
- = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
+.project-issuable-filter
+ .controls
+ - if can?(current_user, :create_wiki, @project)
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ %i.fa.fa-plus
+ New Page
- = nav_link(path: 'wikis#pages') do
- = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project)
+ = render 'projects/wikis/new'
- = nav_link(path: 'wikis#git_access') do
- = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
- Git Access
+ %ul.center-top-menu
+ = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
+ = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
+
+ = nav_link(path: 'wikis#pages') do
+ = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project)
+
+ = nav_link(path: 'wikis#git_access') do
+ = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
+ Git Access
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index dace172438c..f0547e9c057 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -12,5 +12,5 @@
The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and /
%p.hint
Please don't use spaces.
- .modal-footer
- = link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
+ .form-actions
+ = link_to 'Create Page', '#', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 0b709c3695b..23f64fbbd10 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,16 +1,16 @@
-- page_title "Edit", @page.title, "Wiki"
+- page_title "Edit", @page.title.capitalize, "Wiki"
= render "header_title"
= render 'nav'
-.pull-right
- = render 'main_links'
-%h3.page-title
- Editing -
- %span.light #{@page.title}
-%hr
-= render 'form'
+.gray-content-block
+ .pull-right
+ = render 'main_links'
+
+ %h3.page-title.oneline
+ %span.light Edit Page
+ - if @page.persisted?
+ = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
+ - else
+ = @page.title
-.pull-right
- - if @page.persisted? && can?(current_user, :admin_wiki, @project)
- = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-sm btn-remove" do
- Delete this page
+= render 'form'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 6417ef4a38b..11c8c4f0eba 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -5,7 +5,7 @@
.gray-content-block
.row
.col-sm-6
- %h3.page-title
+ %h3.page-title.oneline
Git access for
%strong= @project_wiki.path_with_namespace
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index d179a1abec1..aae1ad69ad9 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,11 +1,10 @@
-- page_title "All Pages", "Wiki"
+- page_title "Pages", "Wiki"
= render "header_title"
= render 'nav'
.gray-content-block
- = render 'main_links'
- %h3.page-title
- All Pages
+ All pages in this wiki are listed below.
+
%ul.content-list
- @wiki_pages.each do |wiki_page|
%li
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 55fbf5a8b6e..309d40f52bc 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -5,11 +5,12 @@
.gray-content-block
= render 'main_links'
- %h3.page-title
+ %h3.page-title.oneline
= @page.title.capitalize
- .wiki-last-edit-by
- Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
+ %span.wiki-last-edit-by
+ &middot;
+ last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
- if @page.historical?
.warning_message
@@ -21,8 +22,3 @@
.wiki
= preserve do
= render_wiki_content(@page)
-
-.gray-content-block.footer-block
- .wiki-last-edit-by
- Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
-
diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml
index 2a44817e05a..34241cd8aad 100644
--- a/app/views/shared/_confirm_modal.html.haml
+++ b/app/views/shared/_confirm_modal.html.haml
@@ -3,7 +3,8 @@
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
- %h4 Confirmation required
+ %h3.page-title
+ Confirmation required
.modal-body
%p.cred.lead.js-confirm-text
@@ -18,5 +19,5 @@
.form-group
= text_field_tag 'confirm_name_input', '', class: 'form-control js-confirm-danger-input'
- .form-group
+ .form-actions
= submit_tag 'Confirm', class: "btn btn-danger js-confirm-danger-submit"
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index c0a9923348e..67072b9fc2a 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -23,7 +23,7 @@
%li It will change the git path to repositories under this group.
.form-group.group-description-holder
- = f.label :description, 'Details', class: 'control-label'
+ = f.label :description, class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250,
class: 'form-control js-gfm-input', rows: 4
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index 0dbb6a04393..4b4c9e9eabe 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -3,8 +3,10 @@
.panel.panel-default.panel-small
- project = group[0]
.panel-heading
- = link_to_project project
- = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right'
+ = link_to project.name_with_namespace, namespace_project_issues_path(project.namespace, project)
+ - if can?(current_user, :create_issue, project)
+ .pull-right
+ = link_to 'New issue', new_namespace_project_issue_path(project.namespace, project)
%ul.well-list.issues-list
- group[1].each do |issue|
@@ -12,4 +14,3 @@
= paginate @issues, theme: "gitlab"
- else
.nothing-here-block No issues to show
-
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index c02c5af008a..be17a511b26 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -3,8 +3,11 @@
.panel.panel-default.panel-small
- project = group[0]
.panel-heading
- = link_to_project project
- = link_to 'show all', namespace_project_merge_requests_path(project.namespace, project), class: 'pull-right'
+ = link_to project.name_with_namespace, namespace_project_merge_requests_path(project.namespace, project)
+ - if can?(current_user, :create_merge_request, project)
+ .pull-right
+ = link_to 'New merge request', new_namespace_project_merge_request_path(project.namespace, project)
+
%ul.well-list.mr-list
- group[1].each do |merge_request|
= render 'projects/merge_requests/merge_request', merge_request: merge_request
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
index 8636341c60d..31b02ed93d0 100644
--- a/app/views/shared/_new_commit_form.html.haml
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -2,10 +2,9 @@
- unless @project.empty_repo?
.form-group.branch
- = label_tag 'branch', class: 'control-label' do
- Branch
+ = label_tag 'new_branch', 'Target branch', class: 'control-label'
.col-sm-10
- = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch"
+ = text_field_tag 'new_branch', @new_branch || @ref, required: true, class: "form-control js-new-branch"
.form-group.js-create-merge-request-form-group
.col-sm-offset-2.col-sm-10
diff --git a/app/views/shared/_project_limit.html.haml b/app/views/shared/_project_limit.html.haml
new file mode 100644
index 00000000000..960ff00b49d
--- /dev/null
+++ b/app/views/shared/_project_limit.html.haml
@@ -0,0 +1,8 @@
+- if cookies[:hide_project_limit_message].blank? && !current_user.hide_project_limit && !current_user.can_create_project?
+ .project-limit-message.alert.alert-warning.hidden-xs
+ You won't be able to create new projects because you have reached your project limit.
+
+ .pull-right
+ = link_to "Don't show again", profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link'
+ |
+ = link_to 'Remind later', '#', class: 'hide-project-limit-message alert-link'
diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml
index f1646b4ce64..2aa46662613 100644
--- a/app/views/shared/issuable/_context.html.haml
+++ b/app/views/shared/issuable/_context.html.haml
@@ -9,7 +9,7 @@
none
.issuable-context-selectbox
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true)
+ = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true)
%div.prepend-top-default.clearfix
.issuable-context-title
@@ -25,25 +25,32 @@
none
.issuable-context-selectbox
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'})
+ = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }})
= hidden_field_tag :issuable_context
= f.submit class: 'btn hide'
+ - if issuable.labels.any?
+ %div.prepend-top-default.clearfix
+ .issuable-context-title
+ %label Labels
+ .issuable-show-labels
+ - issuable.labels.each do |label|
+ = link_to_label(label)
+
- if current_user
- subscribed = issuable.subscribed?(current_user)
%div.prepend-top-default.clearfix
.issuable-context-title
- %label
- Subscription:
- %button.btn.btn-block.subscribe-button{:type => 'button'}
- = icon('eye')
- %span= subscribed ? 'Unsubscribe' : 'Subscribe'
+ %label Subscription
- subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
.subscription-status{data: {status: subscribtion_status}}
.description-block.unsubscribed{class: ( 'hidden' if subscribed )}
You're not receiving notifications from this thread.
.description-block.subscribed{class: ( 'hidden' unless subscribed )}
You're receiving notifications because you're subscribed to this thread.
+ %button.btn.btn-block.subscribe-button{:type => 'button'}
+ = icon('eye')
+ %span= subscribed ? 'Unsubscribe' : 'Subscribe'
:javascript
new Subscription("#{toggle_subscription_path(issuable)}");
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index d1231438ee4..ac6c248ccf1 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -31,11 +31,11 @@
.issues-other-filters
.filter-item.inline
= users_select_tag(:assignee_id, selected: params[:assignee_id],
- placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true, first_user: true, current_user: true)
+ placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true)
.filter-item.inline
= users_select_tag(:author_id, selected: params[:author_id],
- placeholder: 'Author', class: 'trigger-submit', any_user: true, first_user: true, current_user: true)
+ placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true)
.filter-item.inline.milestone-filter
= select_tag('milestone_title', projects_milestones_options,
@@ -53,12 +53,16 @@
- if controller.controller_name == 'issues'
.issues_bulk_update.hide
= form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
- = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
- = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true)
- = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
+ .filter-item.inline
+ = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), include_blank: true, data: { placeholder: "Status" })
+ .filter-item.inline
+ = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true)
+ .filter-item.inline
+ = select_tag('update[milestone_id]', bulk_update_milestone_options, include_blank: true, data: { placeholder: "Milestone" })
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :state_event, params[:state_event]
- = button_tag "Update issues", class: "btn update_selected_issues btn-save"
+ .filter-item.inline
+ = button_tag "Update issues", class: "btn update_selected_issues btn-save"
:javascript
new UsersSelect();
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 7558b37f83f..91ccd1ef660 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -6,8 +6,7 @@
%span= msg
%br
.form-group
- = f.label :title, class: 'control-label' do
- %strong= 'Title *'
+ = f.label :title, class: 'control-label'
.col-sm-10
= f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off',
class: 'form-control pad js-gfm-input js-quick-submit', required: true
@@ -30,29 +29,25 @@
= render 'projects/notes/hints'
.clearfix
.error-alert
- %hr
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
+ %hr
.form-group
.issue-assignee
- = f.label :assignee_id, class: 'control-label' do
- %i.fa.fa-user
- Assign to
+ = f.label :assignee_id, "Assignee", class: 'control-label'
.col-sm-10
= users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
- placeholder: 'Select a user', class: 'custom-form-control', null_user: true,
+ placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
selected: issuable.assignee_id, project: @target_project || @project,
- first_user: true, current_user: true)
+ first_user: true, current_user: true, include_blank: true)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
.issue-milestone
- = f.label :milestone_id, class: 'control-label' do
- %i.fa.fa-clock-o
- Milestone
+ = f.label :milestone_id, "Milestone", class: 'control-label'
.col-sm-10
- if milestone_options(issuable).present?
= f.select(:milestone_id, milestone_options(issuable),
- { include_blank: 'Select milestone' }, { class: 'select2' })
+ { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
- else
.prepend-top-10
%span.light No open milestones available.
@@ -60,13 +55,11 @@
- if can? current_user, :admin_milestone, issuable.project
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank
.form-group
- = f.label :label_ids, class: 'control-label' do
- %i.fa.fa-tag
- Labels
+ = f.label :label_ids, "Labels", class: 'control-label'
.col-sm-10
- if issuable.project.labels.any?
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
- { selected: issuable.label_ids }, multiple: true, class: 'select2'
+ { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
- else
.prepend-top-10
%span.light No labels yet.
@@ -78,32 +71,30 @@
%hr
- if @merge_request.new_record?
.form-group
- = f.label :source_branch, class: 'control-label' do
- %i.fa.fa-code-fork
- Source Branch
+ = f.label :source_branch, class: 'control-label'
.col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group
- = f.label :target_branch, class: 'control-label' do
- %i.fa.fa-code-fork
- Target Branch
+ = f.label :target_branch, class: 'control-label'
.col-sm-10
- = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? })
+ = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
- if @merge_request.new_record?
%p.help-block
= link_to 'Change branches', mr_change_branches_path(@merge_request)
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.gray-content-block{class: (is_footer ? "footer-block" : "middle-block")}
- - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted?
- %p
- Please review the
- %strong #{link_to 'guidelines for contribution', guide_url}
- to this repository.
- if issuable.new_record?
- = f.submit "Submit new #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
+ = f.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
- else
= f.submit 'Save changes', class: 'btn btn-save'
+
+ - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project))
+ .inline.prepend-left-10
+ Please review the
+ %strong #{link_to 'contribution guidelines', guide_url}
+ for this project.
+
- if issuable.new_record?
- cancel_project = issuable.source_project
- else
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 913b6744844..1041eccd1df 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -1,5 +1,5 @@
.snippet-form-holder
- = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form" } do |f|
+ = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f|
- if @snippet.errors.any?
.alert.alert-danger
%ul
@@ -8,7 +8,8 @@
.form-group
= f.label :title, class: 'control-label'
- .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true
+ .col-sm-10
+ = f.text_field :title, class: 'form-control', required: true, autofocus: true
= render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet
@@ -27,7 +28,7 @@
- if @snippet.new_record?
= f.submit 'Create snippet', class: "btn-create btn"
- else
- = f.submit 'Save', class: "btn-save btn"
+ = f.submit 'Save changes', class: "btn-save btn"
- if @snippet.project_id
= link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 35241029288..89c1d7122b0 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,7 +1,7 @@
.issuable-details
.page-title
- .snippet-box{class: visibility_level_color(@snippet.visibility_level)}
- = visibility_level_icon(@snippet.visibility_level)
+ .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }}
+ = visibility_level_icon(@snippet.visibility_level, fw: false)
= visibility_level_label(@snippet.visibility_level)
Snippet ##{@snippet.id}
%span.creator
diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml
index 1a380035661..82f44a9a5c3 100644
--- a/app/views/snippets/edit.html.haml
+++ b/app/views/snippets/edit.html.haml
@@ -1,5 +1,5 @@
- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title
- Edit snippet
+ Edit Snippet
%hr
= render 'shared/snippets/form', url: snippet_path(@snippet), visibility_level: @snippet.visibility_level
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index a74d5e792ad..79e2392490d 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1,5 +1,5 @@
- page_title "New Snippet"
%h3.page-title
- New snippet
+ New Snippet
%hr
= render "shared/snippets/form", url: snippets_path(@snippet), visibility_level: default_snippet_visibility
diff --git a/bin/background_jobs b/bin/background_jobs
index d4578f6a222..5c85fb339e6 100755
--- a/bin/background_jobs
+++ b/bin/background_jobs
@@ -37,7 +37,7 @@ start_no_deamonize()
start_sidekiq()
{
- bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
+ bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
}
load_ok()
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 62619241001..63d8ae17436 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -164,7 +164,7 @@ Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].
Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
diff --git a/db/migrate/20151203162133_add_hide_project_limit_to_users.rb b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb
new file mode 100644
index 00000000000..6ffadfa1894
--- /dev/null
+++ b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb
@@ -0,0 +1,5 @@
+class AddHideProjectLimitToUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :hide_project_limit, :boolean, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index fbcb711e569..fb59e187625 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20151118162244) do
+ActiveRecord::Schema.define(version: 20151203162133) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -814,6 +814,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do
t.integer "project_view", default: 0
t.integer "consumed_timestep"
t.integer "layout", default: 0
+ t.boolean "hide_project_limit", default: false
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 0cef09d5b27..82f2cef969f 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -101,6 +101,43 @@ Parameters:
}
```
+## Get single MR commits
+
+Get a list of merge request commits.
+
+```
+GET /projects/:id/merge_request/:merge_request_id/commits
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `merge_request_id` (required) - The ID of MR
+
+
+```json
+[
+ {
+ "id": "ed899a2f4b50b4370feeea94676502b42383c746",
+ "short_id": "ed899a2f4b5",
+ "title": "Replace sanitize with escape once",
+ "author_name": "Dmitriy Zaporozhets",
+ "author_email": "dzaporozhets@sphereconsultinginc.com",
+ "created_at": "2012-09-20T11:50:22+03:00",
+ "message": "Replace sanitize with escape once"
+ },
+ {
+ "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
+ "short_id": "6104942438c",
+ "title": "Sanitize for network graph",
+ "author_name": "randx",
+ "author_email": "dmitriy.zaporozhets@gmail.com",
+ "created_at": "2012-09-20T09:06:12+03:00",
+ "message": "Sanitize for network graph"
+ }
+]
+```
+
## Get single MR changes
Shows information about the merge request including its files and changes.
@@ -159,7 +196,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
- "files": [
+ "changes": [
{
"old_path": "VERSION",
"new_path": "VERSION",
diff --git a/doc/api/notes.md b/doc/api/notes.md
index e7f299c0994..4d7ef288df8 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -35,7 +35,9 @@ Parameters:
"created_at": "2013-10-02T09:22:45Z",
"system": true,
"upvote": false,
- "downvote": false
+ "downvote": false,
+ "noteable_id": 377,
+ "noteable_type": "Issue"
},
{
"id": 305,
@@ -52,7 +54,9 @@ Parameters:
"created_at": "2013-10-02T09:56:03Z",
"system": true,
"upvote": false,
- "downvote": false
+ "downvote": false,
+ "noteable_id": 121,
+ "noteable_type": "Issue"
}
]
```
@@ -219,7 +223,12 @@ Parameters:
"state": "active",
"created_at": "2013-09-30T13:46:01Z"
},
- "created_at": "2013-10-02T08:57:14Z"
+ "created_at": "2013-10-02T08:57:14Z",
+ "system": false,
+ "upvote": false,
+ "downvote": false,
+ "noteable_id": 2,
+ "noteable_type": "MergeRequest"
}
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 755cc6525c2..42919a312ae 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -245,9 +245,17 @@ Parameters:
"target_id": 830,
"target_type": "Issue",
"author_id": 1,
- "author_username": "john",
"data": null,
- "target_title": "Public project search field"
+ "target_title": "Public project search field",
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "root"
},
{
"title": null,
@@ -256,6 +264,14 @@ Parameters:
"target_id": null,
"target_type": null,
"author_id": 1,
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
"author_username": "john",
"data": {
"before": "50d4420237a9de7be1304607147aec22e4a14af7",
@@ -292,9 +308,56 @@ Parameters:
"target_id": 840,
"target_type": "Issue",
"author_id": 1,
- "author_username": "john",
"data": null,
- "target_title": "Finish & merge Code search PR"
+ "target_title": "Finish & merge Code search PR",
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "root"
+ },
+ {
+ "title": null,
+ "project_id": 15,
+ "action_name": "commented on",
+ "target_id": 1312,
+ "target_type": "Note",
+ "author_id": 1,
+ "data": null,
+ "target_title": null,
+ "created_at": "2015-12-04T10:33:58.089Z",
+ "note": {
+ "id": 1312,
+ "body": "What an awesome day!",
+ "attachment": null,
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "created_at": "2015-12-04T10:33:56.698Z",
+ "system": false,
+ "upvote": false,
+ "downvote": false,
+ "noteable_id": 377,
+ "noteable_type": "Issue"
+ },
+ "author": {
+ "name": "Dmitriy Zaporozhets",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
+ "web_url": "http://localhost:3000/u/root"
+ },
+ "author_username": "root"
}
]
```
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index ef8a7ec1e86..64e52eba3a2 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -60,11 +60,11 @@ This is image that have fully preconfigured `wordpress` and have `MySQL` server
```
Next time when you run your application the `tutum/wordpress` will be started
-and you will have access to it from your build container under hostname: `tutum_wordpress`.
+and you will have access to it from your build container under hostname: `tutum__wordpress`.
Alias hostname for the service is made from the image name:
1. Everything after `:` is stripped,
-2. '/' is replaced to `_`.
+2. '/' is replaced with `__`.
### Configuring services
Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment.
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 9753504ac8b..fe5b45dd432 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -15,7 +15,8 @@ Note: It is a best practice to use a password for an SSH key, but it is not
required and you can skip creating a password by pressing enter. Note that
the password you choose here can't be altered or retrieved.
-To generate a new SSH key, use the following commandGitLab```bash
+To generate a new SSH key, use the following command:
+```bash
ssh-keygen -t rsa -C "$your_email"
```
This command will prompt you for a location and filename to store the key
@@ -108,4 +109,4 @@ Note in the gitlab.com example above a username was specified to override the de
Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.
Public SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you'll
-have when pushing code via SSH. That's why it needs to uniquely map to a single user.
+have when pushing code via SSH. That's why it needs to uniquely map to a single user. \ No newline at end of file
diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature
new file mode 100644
index 00000000000..3e6e59a3808
--- /dev/null
+++ b/features/project/merge_requests/accept.feature
@@ -0,0 +1,17 @@
+Feature: Project Merge Requests Acceptance
+ Background:
+ Given There is an open Merge Request
+ And I am signed in as a developer of the project
+
+ @javascript
+ Scenario: Accepting the Merge Request and removing the source branch
+ Given I am on the Merge Request detail page
+ When I click on "Remove source branch" option
+ And I click on Accept Merge Request
+ Then I should not see the Remove Source Branch button
+
+ @javascript
+ Scenario: Accepting the Merge Request without removing the source branch
+ Given I am on the Merge Request detail page
+ When I click on Accept Merge Request
+ Then I should see the Remove Source Branch button
diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb
index 2d5db8f739e..2ea5dffdc66 100644
--- a/features/steps/admin/labels.rb
+++ b/features/steps/admin/labels.rb
@@ -45,21 +45,21 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps
step 'I submit new label \'support\'' do
visit new_admin_label_path
fill_in 'Title', with: 'support'
- fill_in 'Background Color', with: '#F95610'
+ fill_in 'Background color', with: '#F95610'
click_button 'Save'
end
step 'I submit new label \'bug\'' do
visit new_admin_label_path
fill_in 'Title', with: 'bug'
- fill_in 'Background Color', with: '#F95610'
+ fill_in 'Background color', with: '#F95610'
click_button 'Save'
end
step 'I submit new label with invalid color' do
visit new_admin_label_path
fill_in 'Title', with: 'support'
- fill_in 'Background Color', with: '#12'
+ fill_in 'Background color', with: '#12'
click_button 'Save'
end
@@ -101,7 +101,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps
step 'I change label \'bug\' to \'fix\'' do
fill_in 'Title', with: 'fix'
- fill_in 'Background Color', with: '#F15610'
+ fill_in 'Background color', with: '#F15610'
click_button 'Save'
end
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index 2a333222fb2..d3675060994 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -39,14 +39,14 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
select "fix", from: "merge_request_source_branch"
select "master", from: "merge_request_target_branch"
- click_button "Compare branches"
+ click_button "Compare branches and continue"
- expect(page).to have_content "New merge request"
+ expect(page).to have_content "New Merge Request"
fill_in "merge_request_title", with: "Merge Request On Forked Project"
end
step 'I submit the merge request' do
- click_button "Submit new merge request"
+ click_button "Submit merge request"
end
step 'I follow the target commit link' do
@@ -112,11 +112,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I fill out an invalid "Merge Request On Forked Project" merge request' do
- select "Select branch", from: "merge_request_target_branch"
expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s
expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s
expect(find(:select, "merge_request_source_branch", {}).value).to eq ""
- expect(find(:select, "merge_request_target_branch", {}).value).to eq ""
+ expect(find(:select, "merge_request_target_branch", {}).value).to eq "master"
click_button "Compare branches"
end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index af2da41badb..a13044c3ae1 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -65,20 +65,20 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I see current user as the first user' do
expect(page).to have_selector('.user-result', visible: true, count: 4)
users = page.all('.user-name')
- expect(users[0].text).to eq 'Any'
+ expect(users[0].text).to eq 'Any Assignee'
expect(users[1].text).to eq 'Unassigned'
expect(users[2].text).to eq current_user.name
end
step 'I submit new issue "500 error on profile"' do
fill_in "issue_title", with: "500 error on profile"
- click_button "Submit new issue"
+ click_button "Submit issue"
end
step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile"
select 'bug', from: "Labels"
- click_button "Submit new issue"
+ click_button "Submit issue"
end
step 'I click link "500 error on profile"' do
@@ -86,7 +86,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I should see label \'bug\' with issue' do
- page.within '.issue-show-labels' do
+ page.within '.issuable-show-labels' do
expect(page).to have_content 'bug'
end
end
diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb
index 047cf701bb0..e273bb391b3 100644
--- a/features/steps/project/issues/labels.rb
+++ b/features/steps/project/issues/labels.rb
@@ -31,20 +31,20 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
step 'I submit new label \'support\'' do
fill_in 'Title', with: 'support'
- fill_in 'Background Color', with: '#F95610'
- click_button 'Save'
+ fill_in 'Background color', with: '#F95610'
+ click_button 'Create Label'
end
step 'I submit new label \'bug\'' do
fill_in 'Title', with: 'bug'
- fill_in 'Background Color', with: '#F95610'
- click_button 'Save'
+ fill_in 'Background color', with: '#F95610'
+ click_button 'Create Label'
end
step 'I submit new label with invalid color' do
fill_in 'Title', with: 'support'
- fill_in 'Background Color', with: '#12'
- click_button 'Save'
+ fill_in 'Background color', with: '#12'
+ click_button 'Create Label'
end
step 'I should see label label exist error message' do
@@ -85,8 +85,8 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
step 'I change label \'bug\' to \'fix\'' do
fill_in 'Title', with: 'fix'
- fill_in 'Background Color', with: '#F15610'
- click_button 'Save'
+ fill_in 'Background color', with: '#F15610'
+ click_button 'Save changes'
end
step 'I should see label \'fix\'' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index d5f2c4209a1..822cf0ffe1c 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -86,7 +86,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
select "feature", from: "merge_request_target_branch"
click_button "Compare branches"
fill_in "merge_request_title", with: "Wiki Feature"
- click_button "Submit new merge request"
+ click_button "Submit merge request"
end
step 'project "Shop" have "Bug NS-04" open merge request' do
diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb
new file mode 100644
index 00000000000..6adecaa8385
--- /dev/null
+++ b/features/steps/project/merge_requests/acceptance.rb
@@ -0,0 +1,35 @@
+class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
+ include LoginHelpers
+ include GitlabRoutingHelper
+
+ step 'I am on the Merge Request detail page' do
+ visit merge_request_path(@merge_request)
+ end
+
+ step 'I click on "Remove source branch" option' do
+ check('Remove source branch')
+ end
+
+ step 'I click on Accept Merge Request' do
+ click_button('Accept Merge Request')
+ end
+
+ step 'I should see the Remove Source Branch button' do
+ expect(page).to have_link('Remove Source Branch')
+ end
+
+ step 'I should not see the Remove Source Branch button' do
+ expect(page).not_to have_link('Remove Source Branch')
+ end
+
+ step 'There is an open Merge Request' do
+ @user = create(:user)
+ @project = create(:project, :public)
+ @project_member = create(:project_member, user: @user, project: @project, access_level: ProjectMember::DEVELOPER)
+ @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project)
+ end
+
+ step 'I am signed in as a developer of the project' do
+ login_as(@user)
+ end
+end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index ceab23b9a4d..05d1346d006 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -142,7 +142,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I can see new file page' do
- expect(page).to have_content "Create New File"
+ expect(page).to have_content "New File"
expect(page).to have_content "Commit message"
end
diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb
index c78e86fa1a7..3a4f7a6e01c 100644
--- a/features/steps/project/source/markdown_render.rb
+++ b/features/steps/project/source/markdown_render.rb
@@ -238,7 +238,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I see new wiki page named test' do
expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "test")
- expect(page).to have_content "Editing"
+ expect(page).to have_content "Edit Page test"
end
When 'I go back to wiki page home' do
@@ -252,7 +252,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I see Gitlab API document' do
expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "api")
- expect(page).to have_content "Editing"
+ expect(page).to have_content "Edit Page api"
end
step 'I click on Rake tasks link' do
@@ -261,7 +261,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I see Rake tasks directory' do
expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "raketasks")
- expect(page).to have_content "Editing"
+ expect(page).to have_content "Edit Page raketasks"
end
step 'I go directory which contains README file' do
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index 02207dbffa6..91d227fadbf 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -5,7 +5,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
include SharedPaths
step 'I click on the Cancel button' do
- page.within(:css, ".form-actions") do
+ page.within(:css, ".wiki-form .form-actions") do
click_on "Cancel"
end
end
@@ -24,7 +24,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
expect(page).to have_content "link test"
click_link "link test"
- expect(page).to have_content "Editing"
+ expect(page).to have_content "Edit Page"
end
step 'I have an existing Wiki page' do
@@ -68,7 +68,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I click on the "Delete this page" button' do
- click_on "Delete this page"
+ click_on "Delete"
end
step 'The page should be deleted' do
@@ -120,13 +120,13 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
step 'I should see the new wiki page form' do
expect(current_path).to match('wikis/image.jpg')
expect(page).to have_content('New Wiki Page')
- expect(page).to have_content('Editing - image.jpg')
+ expect(page).to have_content('Edit Page image.jpg')
end
step 'I create a New page with paths' do
click_on 'New Page'
fill_in 'Page slug', with: 'one/two/three'
- click_on 'Build'
+ click_on 'Create Page'
fill_in "wiki_content", with: 'wiki content'
click_on "Create page"
expect(current_path).to include 'one/two/three'
@@ -135,7 +135,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
step 'I create a New page with an invalid name' do
click_on 'New Page'
fill_in 'Page slug', with: 'invalid name'
- click_on 'Build'
+ click_on 'Create Page'
end
step 'I should see an error message' do
@@ -156,7 +156,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see the Editing page' do
- expect(page).to have_content('Editing')
+ expect(page).to have_content('Edit Page')
end
step 'I view the page history of a Wiki page that has a path' do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9f337bc3cc6..96b73df6af9 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -194,6 +194,7 @@ module API
expose :author, using: Entities::UserBasic
expose :created_at
expose :system?, as: :system
+ expose :noteable_id, :noteable_type
# upvote? and downvote? are deprecated, always return false
expose :upvote?, as: :upvote
expose :downvote?, as: :downvote
@@ -224,6 +225,8 @@ module API
expose :target_id, :target_type, :author_id
expose :data, :target_title
expose :created_at
+ expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
+ expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author }
expose :author_username do |event, options|
if event.author
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 6eb84baf9cb..e7c5f808aea 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -76,6 +76,22 @@ module API
present merge_request, with: Entities::MergeRequest
end
+ # Show MR commits
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - The ID of MR
+ #
+ # Example:
+ # GET /projects/:id/merge_request/:merge_request_id/commits
+ #
+ get ':id/merge_request/:merge_request_id/commits' do
+ merge_request = user_project.merge_requests.
+ find(params[:merge_request_id])
+ authorize! :read_merge_request, merge_request
+ present merge_request.commits, with: Entities::RepoCommit
+ end
+
# Show MR changes
#
# Parameters:
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index aeec595782c..9bef9037ad6 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -1,6 +1,12 @@
module Gitlab
class ClosingIssueExtractor
- ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern)
+ ISSUE_CLOSING_REGEX = begin
+ link_pattern = URI.regexp(%w(http https))
+
+ pattern = Gitlab.config.gitlab.issue_closing_pattern
+ pattern = pattern.sub('%{issue_ref}', "(?:(?:#{link_pattern})|(?:#{Issue.reference_pattern}))")
+ Regexp.new(pattern).freeze
+ end
def initialize(project, current_user = nil)
@extractor = Gitlab::ReferenceExtractor.new(project, current_user)
@@ -9,10 +15,12 @@ module Gitlab
def closed_by_message(message)
return [] if message.nil?
- closing_statements = message.scan(ISSUE_CLOSING_REGEX).
- map { |ref| ref[0] }.join(" ")
+ closing_statements = []
+ message.scan(ISSUE_CLOSING_REGEX) do
+ closing_statements << Regexp.last_match[0]
+ end
- @extractor.analyze(closing_statements)
+ @extractor.analyze(closing_statements.join(" "))
@extractor.issues
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index b082bfc434b..886a09f52af 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -178,7 +178,6 @@ module Gitlab
Gitlab::Markdown::SanitizationFilter,
Gitlab::Markdown::UploadLinkFilter,
- Gitlab::Markdown::RelativeLinkFilter,
Gitlab::Markdown::EmojiFilter,
Gitlab::Markdown::TableOfContentsFilter,
Gitlab::Markdown::AutolinkFilter,
@@ -193,6 +192,8 @@ module Gitlab
Gitlab::Markdown::CommitReferenceFilter,
Gitlab::Markdown::LabelReferenceFilter,
+ Gitlab::Markdown::RelativeLinkFilter,
+
Gitlab::Markdown::TaskListFilter
]
end
diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb
index fd5b7eb9332..9488e980c08 100644
--- a/lib/gitlab/markdown/abstract_reference_filter.rb
+++ b/lib/gitlab/markdown/abstract_reference_filter.rb
@@ -2,8 +2,8 @@ require 'gitlab/markdown'
module Gitlab
module Markdown
- # Issues, Snippets and Merge Requests shares similar functionality in refernce filtering.
- # All this functionality moved to this class
+ # Issues, Merge Requests, Snippets, Commits and Commit Ranges share
+ # similar functionality in reference filtering.
class AbstractReferenceFilter < ReferenceFilter
include CrossProjectReference
@@ -26,21 +26,20 @@ module Gitlab
# Public: Find references in text (like `!123` for merge requests)
#
- # AnyReferenceFilter.references_in(text) do |match, object|
- # "<a href=...>PREFIX#{object}</a>"
+ # AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches|
+ # object = find_object(project_ref, id)
+ # "<a href=...>#{object.to_reference}</a>"
# end
#
- # PREFIX - symbol that detects reference (like ! for merge requests)
- # object - reference object (snippet, merget request etc)
# text - String text to search.
#
- # Yields the String match, the Integer referenced object ID, and an optional String
- # of the external project reference.
+ # Yields the String match, the Integer referenced object ID, an optional String
+ # of the external project reference, and all of the matchdata.
#
# Returns a String replaced with the return of the block.
- def self.references_in(text)
- text.gsub(object_class.reference_pattern) do |match|
- yield match, $~[object_sym].to_i, $~[:project]
+ def self.references_in(text, pattern = object_class.reference_pattern)
+ text.gsub(pattern) do |match|
+ yield match, $~[object_sym].to_i, $~[:project], $~
end
end
@@ -61,8 +60,27 @@ module Gitlab
end
def call
+ # `#123`
replace_text_nodes_matching(object_class.reference_pattern) do |content|
- object_link_filter(content)
+ object_link_filter(content, object_class.reference_pattern)
+ end
+
+ # `[Issue](#123)`, which is turned into
+ # `<a href="#123">Issue</a>`
+ replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
+ object_link_filter(link, object_class.reference_pattern, link_text: text)
+ end
+
+ # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
+ # `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
+ replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
+ object_link_filter(text, object_class.link_reference_pattern)
+ end
+
+ # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
+ # `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
+ replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
+ object_link_filter(link, object_class.link_reference_pattern, link_text: text)
end
end
@@ -70,30 +88,57 @@ module Gitlab
# to the referenced object's details page.
#
# text - String text to replace references in.
+ # pattern - Reference pattern to match against.
+ # link_text - Original content of the link being replaced.
#
# Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
- def object_link_filter(text)
- references_in(text) do |match, id, project_ref|
+ def object_link_filter(text, pattern, link_text: nil)
+ references_in(text, pattern) do |match, id, project_ref, matches|
project = project_from_ref(project_ref)
if project && object = find_object(project, id)
- title = escape_once("#{object_title}: #{object.title}")
+ title = escape_once(object_link_title(object))
klass = reference_class(object_sym)
- data = data_attribute(project: project.id, object_sym => object.id)
- url = url_for_object(object, project)
+
+ data = data_attribute(
+ original: link_text || match,
+ project: project.id,
+ object_sym => object.id
+ )
+
+ url = matches[:url] if matches.names.include?("url")
+ url ||= url_for_object(object, project)
+
+ text = link_text
+ unless text
+ text = object.reference_link_text(context[:project])
+
+ extras = object_link_text_extras(object, matches)
+ text += " (#{extras.join(", ")})" if extras.any?
+ end
%(<a href="#{url}" #{data}
title="#{title}"
- class="#{klass}">#{match}</a>)
+ class="#{klass}">#{text}</a>)
else
match
end
end
end
- def object_title
- object_class.name.titleize
+ def object_link_text_extras(object, matches)
+ extras = []
+
+ if matches.names.include?("anchor") && matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/
+ extras << "comment #{$1}"
+ end
+
+ extras
+ end
+
+ def object_link_title(object)
+ "#{object_class.name.titleize}: #{object.title}"
end
end
end
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
index e070edae0a4..36b3258ef76 100644
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_range_reference_filter.rb
@@ -5,24 +5,14 @@ module Gitlab
# HTML filter that replaces commit range references with links.
#
# This filter supports cross-project references.
- class CommitRangeReferenceFilter < ReferenceFilter
- include CrossProjectReference
+ class CommitRangeReferenceFilter < AbstractReferenceFilter
+ def self.object_class
+ CommitRange
+ end
- # Public: Find commit range references in text
- #
- # CommitRangeReferenceFilter.references_in(text) do |match, commit_range, project_ref|
- # "<a href=...>#{commit_range}</a>"
- # end
- #
- # text - String text to search.
- #
- # Yields the String match, the String commit range, and an optional String
- # of the external project reference.
- #
- # Returns a String replaced with the return of the block.
- def self.references_in(text)
- text.gsub(CommitRange.reference_pattern) do |match|
- yield match, $~[:commit_range], $~[:project]
+ def self.references_in(text, pattern = CommitRange.reference_pattern)
+ text.gsub(pattern) do |match|
+ yield match, $~[:commit_range], $~[:project], $~
end
end
@@ -31,9 +21,9 @@ module Gitlab
return unless project
id = node.attr("data-commit-range")
- range = CommitRange.new(id, project)
+ range = find_object(project, id)
- return unless range.valid_commits?
+ return unless range
{ commit_range: range }
end
@@ -44,49 +34,25 @@ module Gitlab
@commit_map = {}
end
- def call
- replace_text_nodes_matching(CommitRange.reference_pattern) do |content|
- commit_range_link_filter(content)
- end
- end
-
- # Replace commit range references in text with links to compare the commit
- # ranges.
- #
- # text - String text to replace references in.
- #
- # Returns a String with commit range references replaced with links. All
- # links have `gfm` and `gfm-commit_range` class names attached for
- # styling.
- def commit_range_link_filter(text)
- self.class.references_in(text) do |match, id, project_ref|
- project = self.project_from_ref(project_ref)
-
- range = CommitRange.new(id, project)
-
- if range.valid_commits?
- url = url_for_commit_range(project, range)
-
- title = range.reference_title
- klass = reference_class(:commit_range)
- data = data_attribute(project: project.id, commit_range: id)
+ def self.find_object(project, id)
+ range = CommitRange.new(id, project)
- project_ref += '@' if project_ref
+ range.valid_commits? ? range : nil
+ end
- %(<a href="#{url}" #{data}
- title="#{title}"
- class="#{klass}">#{project_ref}#{range}</a>)
- else
- match
- end
- end
+ def find_object(*args)
+ self.class.find_object(*args)
end
- def url_for_commit_range(project, range)
+ def url_for_object(range, project)
h = Gitlab::Application.routes.url_helpers
h.namespace_project_compare_url(project.namespace, project,
range.to_param.merge(only_path: context[:only_path]))
end
+
+ def object_link_title(range)
+ range.reference_title
+ end
end
end
end
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index 8cdbeb1f9cf..b4036578e60 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -5,24 +5,14 @@ module Gitlab
# HTML filter that replaces commit references with links.
#
# This filter supports cross-project references.
- class CommitReferenceFilter < ReferenceFilter
- include CrossProjectReference
+ class CommitReferenceFilter < AbstractReferenceFilter
+ def self.object_class
+ Commit
+ end
- # Public: Find commit references in text
- #
- # CommitReferenceFilter.references_in(text) do |match, commit, project_ref|
- # "<a href=...>#{commit}</a>"
- # end
- #
- # text - String text to search.
- #
- # Yields the String match, the String commit identifier, and an optional
- # String of the external project reference.
- #
- # Returns a String replaced with the return of the block.
- def self.references_in(text)
- text.gsub(Commit.reference_pattern) do |match|
- yield match, $~[:commit], $~[:project]
+ def self.references_in(text, pattern = Commit.reference_pattern)
+ text.gsub(pattern) do |match|
+ yield match, $~[:commit], $~[:project], $~
end
end
@@ -31,58 +21,32 @@ module Gitlab
return unless project
id = node.attr("data-commit")
- commit = commit_from_ref(project, id)
+ commit = find_object(project, id)
return unless commit
{ commit: commit }
end
- def call
- replace_text_nodes_matching(Commit.reference_pattern) do |content|
- commit_link_filter(content)
- end
- end
-
- # Replace commit references in text with links to the commit specified.
- #
- # text - String text to replace references in.
- #
- # Returns a String with commit references replaced with links. All links
- # have `gfm` and `gfm-commit` class names attached for styling.
- def commit_link_filter(text)
- self.class.references_in(text) do |match, id, project_ref|
- project = self.project_from_ref(project_ref)
-
- if commit = self.class.commit_from_ref(project, id)
- url = url_for_commit(project, commit)
-
- title = escape_once(commit.link_title)
- klass = reference_class(:commit)
- data = data_attribute(project: project.id, commit: id)
-
- project_ref += '@' if project_ref
-
- %(<a href="#{url}" #{data}
- title="#{title}"
- class="#{klass}">#{project_ref}#{commit.short_id}</a>)
- else
- match
- end
- end
- end
-
- def self.commit_from_ref(project, id)
+ def self.find_object(project, id)
if project && project.valid_repo?
project.commit(id)
end
end
- def url_for_commit(project, commit)
+ def find_object(*args)
+ self.class.find_object(*args)
+ end
+
+ def url_for_object(commit, project)
h = Gitlab::Application.routes.url_helpers
h.namespace_project_commit_url(project.namespace, project, commit,
only_path: context[:only_path])
end
+
+ def object_link_title(commit)
+ commit.link_title
+ end
end
end
end
diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb
index 8f86f13976a..14bdf5521fc 100644
--- a/lib/gitlab/markdown/external_issue_reference_filter.rb
+++ b/lib/gitlab/markdown/external_issue_reference_filter.rb
@@ -30,6 +30,10 @@ module Gitlab
replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
issue_link_filter(content)
end
+
+ replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text|
+ issue_link_filter(link, link_text: text)
+ end
end
# Replace `JIRA-123` issue references in text with links to the referenced
@@ -39,7 +43,7 @@ module Gitlab
#
# Returns a String with `JIRA-123` references replaced with links. All
# links have `gfm` and `gfm-issue` class names attached for styling.
- def issue_link_filter(text)
+ def issue_link_filter(text, link_text: nil)
project = context[:project]
self.class.references_in(text) do |match, issue|
@@ -49,9 +53,11 @@ module Gitlab
klass = reference_class(:issue)
data = data_attribute(project: project.id)
+ text = link_text || match
+
%(<a href="#{url}" #{data}
title="#{title}"
- class="#{klass}">#{match}</a>)
+ class="#{klass}">#{text}</a>)
end
end
diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb
index 29e51b6ade6..e09dfcb83c8 100644
--- a/lib/gitlab/markdown/external_link_filter.rb
+++ b/lib/gitlab/markdown/external_link_filter.rb
@@ -8,9 +8,9 @@ module Gitlab
class ExternalLinkFilter < HTML::Pipeline::Filter
def call
doc.search('a').each do |node|
- next unless node.has_attribute?('href')
+ link = node.attr('href')
- link = node.attribute('href').value
+ next unless link
# Skip non-HTTP(S) links
next unless link.start_with?('http')
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 13581b8fb13..a2026eecaeb 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -30,6 +30,10 @@ module Gitlab
replace_text_nodes_matching(Label.reference_pattern) do |content|
label_link_filter(content)
end
+
+ replace_link_nodes_with_href(Label.reference_pattern) do |link, text|
+ label_link_filter(link, link_text: text)
+ end
end
# Replace label references in text with links to the label specified.
@@ -38,7 +42,7 @@ module Gitlab
#
# Returns a String with label references replaced with links. All links
# have `gfm` and `gfm-label` class names attached for styling.
- def label_link_filter(text)
+ def label_link_filter(text, link_text: nil)
project = context[:project]
self.class.references_in(text) do |match, id, name|
@@ -47,10 +51,16 @@ module Gitlab
if label = project.labels.find_by(params)
url = url_for_label(project, label)
klass = reference_class(:label)
- data = data_attribute(project: project.id, label: label.id)
+ data = data_attribute(
+ original: link_text || match,
+ project: project.id,
+ label: label.id
+ )
+
+ text = link_text || render_colored_label(label)
%(<a href="#{url}" #{data}
- class="#{klass}">#{render_colored_label(label)}</a>)
+ class="#{klass}">#{text}</a>)
else
match
end
@@ -59,8 +69,8 @@ module Gitlab
def url_for_label(project, label)
h = Gitlab::Application.routes.url_helpers
- h.namespace_project_issues_path(project.namespace, project,
- label_name: label.name)
+ h.namespace_project_issues_url( project.namespace, project, label_name: label.name,
+ only_path: context[:only_path])
end
def render_colored_label(label)
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 1f47f03c94e..de71fc76a9b 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -20,6 +20,16 @@ module Gitlab
h.namespace_project_merge_request_url(project.namespace, project, mr,
only_path: context[:only_path])
end
+
+ def object_link_text_extras(object, matches)
+ extras = super
+
+ if matches.names.include?("path") && matches[:path] && matches[:path] == '/diffs'
+ extras.unshift "diffs"
+ end
+
+ extras
+ end
end
end
end
diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb
index a1f3a8a8ebf..bea714a01e7 100644
--- a/lib/gitlab/markdown/redactor_filter.rb
+++ b/lib/gitlab/markdown/redactor_filter.rb
@@ -12,7 +12,10 @@ module Gitlab
def call
doc.css('a.gfm').each do |node|
unless user_can_reference?(node)
- node.replace(node.text)
+ # The reference should be replaced by the original text,
+ # which is not always the same as the rendered text.
+ text = node.attr('data-original') || node.text
+ node.replace(text)
end
end
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index a4c560f578c..b6d93e05ec7 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -122,6 +122,80 @@ module Gitlab
doc
end
+ # Iterate through the document's link nodes, yielding the current node's
+ # content if:
+ #
+ # * The `project` context value is present AND
+ # * The node's content matches `pattern`
+ #
+ # pattern - Regex pattern against which to match the node's content
+ #
+ # Yields the current node's String contents. The result of the block will
+ # replace the node and update the current document.
+ #
+ # Returns the updated Nokogiri::HTML::DocumentFragment object.
+ def replace_link_nodes_with_text(pattern)
+ return doc if project.nil?
+
+ doc.search('a').each do |node|
+ klass = node.attr('class')
+ next if klass && klass.include?('gfm')
+
+ link = node.attr('href')
+ text = node.text
+
+ next unless link && text
+
+ link = URI.decode(link)
+ # Ignore ending punctionation like periods or commas
+ next unless link == text && text =~ /\A#{pattern}/
+
+ html = yield text
+
+ next if html == text
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
+ # Iterate through the document's link nodes, yielding the current node's
+ # content if:
+ #
+ # * The `project` context value is present AND
+ # * The node's HREF matches `pattern`
+ #
+ # pattern - Regex pattern against which to match the node's HREF
+ #
+ # Yields the current node's String HREF and String content.
+ # The result of the block will replace the node and update the current document.
+ #
+ # Returns the updated Nokogiri::HTML::DocumentFragment object.
+ def replace_link_nodes_with_href(pattern)
+ return doc if project.nil?
+
+ doc.search('a').each do |node|
+ klass = node.attr('class')
+ next if klass && klass.include?('gfm')
+
+ link = node.attr('href')
+ text = node.text
+
+ next unless link && text
+ link = URI.decode(link)
+ next unless link && link =~ /\A#{pattern}\z/
+
+ html = yield link, text
+
+ next if html == link
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
# Ensure that a :project key exists in context
#
# Note that while the key might exist, its value could be nil!
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 632be4d7542..692c51fd324 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -17,6 +17,9 @@ module Gitlab
return doc unless linkable_files?
doc.search('a').each do |el|
+ klass = el.attr('class')
+ next if klass && klass.include?('gfm')
+
process_link_attr el.attribute('href')
end
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index ab5e1f6fe9e..0a20d9c0347 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -52,6 +52,10 @@ module Gitlab
replace_text_nodes_matching(User.reference_pattern) do |content|
user_link_filter(content)
end
+
+ replace_link_nodes_with_href(User.reference_pattern) do |link, text|
+ user_link_filter(link, link_text: text)
+ end
end
# Replace `@user` user references in text with links to the referenced
@@ -61,12 +65,12 @@ module Gitlab
#
# Returns a String with `@user` references replaced with links. All links
# have `gfm` and `gfm-project_member` class names attached for styling.
- def user_link_filter(text)
+ def user_link_filter(text, link_text: nil)
self.class.references_in(text) do |match, username|
if username == 'all'
- link_to_all
+ link_to_all(link_text: link_text)
elsif namespace = Namespace.find_by(path: username)
- link_to_namespace(namespace) || match
+ link_to_namespace(namespace, link_text: link_text) || match
else
match
end
@@ -83,36 +87,36 @@ module Gitlab
reference_class(:project_member)
end
- def link_to_all
+ def link_to_all(link_text: nil)
project = context[:project]
url = urls.namespace_project_url(project.namespace, project,
only_path: context[:only_path])
data = data_attribute(project: project.id)
- text = User.reference_prefix + 'all'
+ text = link_text || User.reference_prefix + 'all'
link_tag(url, data, text)
end
- def link_to_namespace(namespace)
+ def link_to_namespace(namespace, link_text: nil)
if namespace.is_a?(Group)
- link_to_group(namespace.path, namespace)
+ link_to_group(namespace.path, namespace, link_text: link_text)
else
- link_to_user(namespace.path, namespace)
+ link_to_user(namespace.path, namespace, link_text: link_text)
end
end
- def link_to_group(group, namespace)
+ def link_to_group(group, namespace, link_text: nil)
url = urls.group_url(group, only_path: context[:only_path])
data = data_attribute(group: namespace.id)
- text = Group.reference_prefix + group
+ text = link_text || Group.reference_prefix + group
link_tag(url, data, text)
end
- def link_to_user(user, namespace)
+ def link_to_user(user, namespace, link_text: nil)
url = urls.user_url(user, only_path: context[:only_path])
data = data_attribute(user: namespace.owner_id)
- text = User.reference_prefix + user
+ text = link_text || User.reference_prefix + user
link_tag(url, data, text)
end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index da8df8a3025..3c3478a1271 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -41,14 +41,14 @@ module Gitlab
# Returns the results Array for the requested filter type
def pipeline_result(filter_type)
return [] if @text.blank?
-
+
klass = "#{filter_type.to_s.camelize}ReferenceFilter"
filter = Gitlab::Markdown.const_get(klass)
context = {
project: project,
current_user: current_user,
-
+
# We don't actually care about the links generated
only_path: true,
ignore_blockquotes: true,
@@ -58,7 +58,15 @@ module Gitlab
reference_filter: filter
}
- pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context)
+ # We need to autolink first to finds links to referables, and to prevent
+ # numeric anchors to be parsed as issue references.
+ filters = [
+ Gitlab::Markdown::AutolinkFilter,
+ filter,
+ Gitlab::Markdown::ReferenceGathererFilter
+ ]
+
+ pipeline = HTML::Pipeline.new(filters, context)
result = pipeline.call(@text)
values = result[:references][filter_type].uniq
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index f0a6c2b30e9..43fda6fa92e 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -327,7 +327,7 @@ print_status() {
printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n"
fi
fi
- if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then
+ if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then
printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
fi
}
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 32fd4065bb4..0af5e6fc1a6 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -61,7 +61,7 @@ describe 'Issues', feature: true do
it 'allows user to select unasigned', js: true do
visit edit_namespace_project_issue_path(project.namespace, project, issue)
- expect(page).to have_content "Assign to #{@user.name}"
+ expect(page).to have_content "Assignee #{@user.name}"
first('#s2id_issue_assignee_id').click
sleep 2 # wait for ajax stuff to complete
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 41d12afa9ce..e8dfc5c0eb1 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -153,6 +153,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Ignores invalid: <%= User.reference_prefix %>fake_user
- Ignored in code: `<%= user.to_reference %>`
- Ignored in links: [Link to <%= user.to_reference %>](#user-link)
+- Link to user by reference: [User](<%= user.to_reference %>)
#### IssueReferenceFilter
@@ -160,6 +161,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Issue in another project: <%= xissue.to_reference(project) %>
- Ignored in code: `<%= issue.to_reference %>`
- Ignored in links: [Link to <%= issue.to_reference %>](#issue-link)
+- Issue by URL: <%= urls.namespace_project_issue_url(issue.project.namespace, issue.project, issue) %>
+- Link to issue by reference: [Issue](<%= issue.to_reference %>)
+- Link to issue by URL: [Issue](<%= urls.namespace_project_issue_url(issue.project.namespace, issue.project, issue) %>)
#### MergeRequestReferenceFilter
@@ -167,6 +171,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Merge request in another project: <%= xmerge_request.to_reference(project) %>
- Ignored in code: `<%= merge_request.to_reference %>`
- Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link)
+- Merge request by URL: <%= urls.namespace_project_merge_request_url(merge_request.project.namespace, merge_request.project, merge_request) %>
+- Link to merge request by reference: [Merge request](<%= merge_request.to_reference %>)
+- Link to merge request by URL: [Merge request](<%= urls.namespace_project_merge_request_url(merge_request.project.namespace, merge_request.project, merge_request) %>)
#### SnippetReferenceFilter
@@ -174,6 +181,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Snippet in another project: <%= xsnippet.to_reference(project) %>
- Ignored in code: `<%= snippet.to_reference %>`
- Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link)
+- Snippet by URL: <%= urls.namespace_project_snippet_url(snippet.project.namespace, snippet.project, snippet) %>
+- Link to snippet by reference: [Snippet](<%= snippet.to_reference %>)
+- Link to snippet by URL: [Snippet](<%= urls.namespace_project_snippet_url(snippet.project.namespace, snippet.project, snippet) %>)
#### CommitRangeReferenceFilter
@@ -181,6 +191,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Range in another project: <%= xcommit_range.to_reference(project) %>
- Ignored in code: `<%= commit_range.to_reference %>`
- Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link)
+- Range by URL: <%= urls.namespace_project_compare_url(commit_range.project.namespace, commit_range.project, commit_range.to_param) %>
+- Link to range by reference: [Range](<%= commit_range.to_reference %>)
+- Link to range by URL: [Range](<%= urls.namespace_project_compare_url(commit_range.project.namespace, commit_range.project, commit_range.to_param) %>)
#### CommitReferenceFilter
@@ -188,6 +201,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Commit in another project: <%= xcommit.to_reference(project) %>
- Ignored in code: `<%= commit.to_reference %>`
- Ignored in links: [Link to <%= commit.to_reference %>](#commit-link)
+- Commit by URL: <%= urls.namespace_project_commit_url(commit.project.namespace, commit.project, commit) %>
+- Link to commit by reference: [Commit](<%= commit.to_reference %>)
+- Link to commit by URL: [Commit](<%= urls.namespace_project_commit_url(commit.project.namespace, commit.project, commit) %>)
#### LabelReferenceFilter
@@ -196,6 +212,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Label by name in quotes: <%= label.to_reference(:name) %>
- Ignored in code: `<%= simple_label.to_reference %>`
- Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link)
+- Link to label by reference: [Label](<%= label.to_reference %>)
### Task Lists
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 21254f778d3..fe1b94a484e 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -2,11 +2,18 @@ require 'spec_helper'
describe Gitlab::ClosingIssueExtractor do
let(:project) { create(:project) }
+ let(:project2) { create(:project) }
let(:issue) { create(:issue, project: project) }
+ let(:issue2) { create(:issue, project: project2) }
let(:reference) { issue.to_reference }
+ let(:cross_reference) { issue2.to_reference(project) }
subject { described_class.new(project, project.creator) }
+ before do
+ project2.team << [project.creator, :master]
+ end
+
describe "#closed_by_message" do
context 'with a single reference' do
it do
@@ -130,6 +137,27 @@ describe Gitlab::ClosingIssueExtractor do
end
end
+ context "with a cross-project reference" do
+ it do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to eq([issue2])
+ end
+ end
+
+ context "with a cross-project URL" do
+ it do
+ message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)}"
+ expect(subject.closed_by_message(message)).to eq([issue2])
+ end
+ end
+
+ context "with an invalid URL" do
+ it do
+ message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)}"
+ expect(subject.closed_by_message(message)).to eq([])
+ end
+ end
+
context 'with multiple references' do
let(:other_issue) { create(:issue, project: project) }
let(:third_issue) { create(:issue, project: project) }
@@ -171,6 +199,31 @@ describe Gitlab::ClosingIssueExtractor do
expect(subject.closed_by_message(message)).
to match_array([issue, other_issue, third_issue])
end
+
+ it "fetches cross-project references" do
+ message = "Closes #{reference} and #{cross_reference}"
+
+ expect(subject.closed_by_message(message)).
+ to match_array([issue, issue2])
+ end
+
+ it "fetches cross-project URL references" do
+ message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)} and #{reference}"
+
+ expect(subject.closed_by_message(message)).
+ to match_array([issue, issue2])
+ end
+
+ it "ignores invalid cross-project URL references" do
+ message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)} and #{reference}"
+
+ expect(subject.closed_by_message(message)).
+ to match_array([issue])
+ end
end
end
+
+ def urls
+ Gitlab::Application.routes.url_helpers
+ end
end
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index e5b8d723fe5..9ce63f9af46 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -5,11 +5,11 @@ module Gitlab::Markdown
include FilterSpecHelper
let(:project) { create(:project, :public) }
- let(:commit1) { project.commit }
- let(:commit2) { project.commit("HEAD~2") }
+ let(:commit1) { project.commit("HEAD~2") }
+ let(:commit2) { project.commit }
- let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") }
- let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") }
+ let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}", project) }
+ let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}", project) }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
@@ -18,7 +18,7 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Commit Range #{range.to_reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -27,14 +27,14 @@ module Gitlab::Markdown
let(:reference2) { range2.to_reference }
it 'links to a valid two-dot reference' do
- doc = filter("See #{reference2}")
+ doc = reference_filter("See #{reference2}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
end
it 'links to a valid three-dot reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
@@ -46,14 +46,14 @@ module Gitlab::Markdown
exp = commit1.short_id + '...' + commit2.short_id
- expect(filter("See #{reference}").css('a').first.text).to eq exp
- expect(filter("See #{reference2}").css('a').first.text).to eq exp
+ expect(reference_filter("See #{reference}").css('a').first.text).to eq exp
+ expect(reference_filter("See #{reference2}").css('a').first.text).to eq exp
end
it 'links with adjacent text' do
- doc = filter("See (#{reference}.)")
+ doc = reference_filter("See (#{reference}.)")
- exp = Regexp.escape(range.to_s)
+ exp = Regexp.escape(range.reference_link_text)
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
end
@@ -62,21 +62,22 @@ module Gitlab::Markdown
expect(project).to receive(:valid_repo?).and_return(true)
expect(project.repository).to receive(:commit).with(commit1.id.reverse)
- expect(filter(act).to_html).to eq exp
+ expect(project.repository).to receive(:commit).with(commit2.id)
+ expect(reference_filter(act).to_html).to eq exp
end
it 'includes a title attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('title')).to eq range.reference_title
end
it 'includes default classes' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range'
end
it 'includes a data-project attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -84,15 +85,15 @@ module Gitlab::Markdown
end
it 'includes a data-commit-range attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-commit-range')
- expect(link.attr('data-commit-range')).to eq range.to_reference
+ expect(link.attr('data-commit-range')).to eq range.to_s
end
it 'supports an :only_path option' do
- doc = filter("See #{reference}", only_path: true)
+ doc = reference_filter("See #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
@@ -115,25 +116,63 @@ module Gitlab::Markdown
end
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'links with adjacent text' do
- doc = filter("Fixed (#{reference}.)")
+ doc = reference_filter("Fixed (#{reference}.)")
- exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}")
+ exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}")
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("See #{reference}")
+ expect(result[:references][:commit_range]).not_to be_empty
+ end
+ end
+
+ context 'cross-project URL reference' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:range) { CommitRange.new("#{commit1.id}...master", project) }
+ let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') }
+
+ before do
+ range.project = project2
+ end
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq reference
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ exp = Regexp.escape(range.reference_link_text(project))
+ expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
+ expect(reference_filter(act).to_html).to eq exp
+
+ exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
+ expect(reference_filter(act).to_html).to eq exp
end
it 'adds to the results hash' do
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index d080efbf3d4..462a41b4756 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -14,7 +14,7 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Commit #{commit.id}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -24,7 +24,7 @@ module Gitlab::Markdown
# Let's test a variety of commit SHA sizes just to be paranoid
[6, 8, 12, 18, 20, 32, 40].each do |size|
it "links to a valid reference of #{size} characters" do
- doc = filter("See #{reference[0...size]}")
+ doc = reference_filter("See #{reference[0...size]}")
expect(doc.css('a').first.text).to eq commit.short_id
expect(doc.css('a').first.attr('href')).
@@ -33,15 +33,15 @@ module Gitlab::Markdown
end
it 'always uses the short ID as the link text' do
- doc = filter("See #{commit.id}")
+ doc = reference_filter("See #{commit.id}")
expect(doc.text).to eq "See #{commit.short_id}"
- doc = filter("See #{commit.id[0...6]}")
+ doc = reference_filter("See #{commit.id[0...6]}")
expect(doc.text).to eq "See #{commit.short_id}"
end
it 'links with adjacent text' do
- doc = filter("See (#{reference}.)")
+ doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/)
end
@@ -51,28 +51,28 @@ module Gitlab::Markdown
expect(project).to receive(:valid_repo?).and_return(true)
expect(project.repository).to receive(:commit).with(invalid)
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
it 'includes a title attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('title')).to eq commit.link_title
end
it 'escapes the title attribute' do
allow_any_instance_of(Commit).to receive(:title).and_return(%{"></a>whatever<a title="})
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.text).to eq "See #{commit.short_id}"
end
it 'includes default classes' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit'
end
it 'includes a data-project attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -80,7 +80,7 @@ module Gitlab::Markdown
end
it 'includes a data-commit attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-commit')
@@ -88,7 +88,7 @@ module Gitlab::Markdown
end
it 'supports an :only_path context' do
- doc = filter("See #{reference}", only_path: true)
+ doc = reference_filter("See #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
@@ -108,14 +108,14 @@ module Gitlab::Markdown
let(:reference) { commit.to_reference(project) }
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
end
it 'links with adjacent text' do
- doc = filter("Fixed (#{reference}.)")
+ doc = reference_filter("Fixed (#{reference}.)")
exp = Regexp.escape(project2.to_reference)
expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
@@ -123,7 +123,37 @@ module Gitlab::Markdown
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("See #{reference}")
+ expect(result[:references][:commit]).not_to be_empty
+ end
+ end
+
+ context 'cross-project URL reference' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:commit) { project2.commit }
+ let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.to_html).to match(/\(<a.+>#{commit.reference_link_text(project)}<\/a>\.\)/)
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ act = "Committed #{invalidate_reference(reference)}"
+ expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
end
it 'adds to the results hash' do
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 94c80ae6611..078ff3ed4b2 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -18,7 +18,7 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Issue #{issue.to_reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -29,18 +29,18 @@ module Gitlab::Markdown
expect(project).to receive(:get_issue).with(issue.iid).and_return(nil)
exp = act = "Issue #{reference}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
it 'links to a valid reference' do
- doc = filter("Fixed #{reference}")
+ doc = reference_filter("Fixed #{reference}")
expect(doc.css('a').first.attr('href')).
to eq helper.url_for_issue(issue.iid, project)
end
it 'links with adjacent text' do
- doc = filter("Fixed (#{reference}.)")
+ doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end
@@ -48,28 +48,28 @@ module Gitlab::Markdown
invalid = invalidate_reference(reference)
exp = act = "Fixed #{invalid}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
it 'includes a title attribute' do
- doc = filter("Issue #{reference}")
+ doc = reference_filter("Issue #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}"
end
it 'escapes the title attribute' do
issue.update_attribute(:title, %{"></a>whatever<a title="})
- doc = filter("Issue #{reference}")
+ doc = reference_filter("Issue #{reference}")
expect(doc.text).to eq "Issue #{reference}"
end
it 'includes default classes' do
- doc = filter("Issue #{reference}")
+ doc = reference_filter("Issue #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue'
end
it 'includes a data-project attribute' do
- doc = filter("Issue #{reference}")
+ doc = reference_filter("Issue #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -77,7 +77,7 @@ module Gitlab::Markdown
end
it 'includes a data-issue attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-issue')
@@ -85,7 +85,7 @@ module Gitlab::Markdown
end
it 'supports an :only_path context' do
- doc = filter("Issue #{reference}", only_path: true)
+ doc = reference_filter("Issue #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
@@ -109,25 +109,97 @@ module Gitlab::Markdown
with(issue.iid).and_return(nil)
exp = act = "Issue #{reference}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq helper.url_for_issue(issue.iid, project2)
end
it 'links with adjacent text' do
- doc = filter("Fixed (#{reference}.)")
+ doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end
it 'ignores invalid issue IDs on the referenced project' do
exp = act = "Fixed #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Fixed #{reference}")
+ expect(result[:references][:issue]).to eq [issue]
+ end
+ end
+
+ context 'cross-project URL reference' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq reference
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/)
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Fixed #{reference}")
+ expect(result[:references][:issue]).to eq [issue]
+ end
+ end
+
+ context 'cross-project reference in link href' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { %Q{<a href="#{issue.to_reference(project)}">Reference</a>} }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq helper.url_for_issue(issue.iid, project2)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Fixed #{reference}")
+ expect(result[:references][:issue]).to eq [issue]
+ end
+ end
+
+ context 'cross-project URL in link href' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { %Q{<a href="#{helper.url_for_issue(issue.iid, project2) + "#note_123"}">Reference</a>} }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
end
it 'adds to the results hash' do
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index ae286c8be2b..ef6dd524aba 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -16,17 +16,17 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Label #{reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
it 'includes default classes' do
- doc = filter("Label #{reference}")
+ doc = reference_filter("Label #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label'
end
it 'includes a data-project attribute' do
- doc = filter("Label #{reference}")
+ doc = reference_filter("Label #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -34,7 +34,7 @@ module Gitlab::Markdown
end
it 'includes a data-label attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-label')
@@ -42,7 +42,7 @@ module Gitlab::Markdown
end
it 'supports an :only_path context' do
- doc = filter("Label #{reference}", only_path: true)
+ doc = reference_filter("Label #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
@@ -56,33 +56,33 @@ module Gitlab::Markdown
describe 'label span element' do
it 'includes default classes' do
- doc = filter("Label #{reference}")
+ doc = reference_filter("Label #{reference}")
expect(doc.css('a span').first.attr('class')).to eq 'label color-label'
end
it 'includes a style attribute' do
- doc = filter("Label #{reference}")
+ doc = reference_filter("Label #{reference}")
expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/)
end
end
context 'Integer-based references' do
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_path(project.namespace, project, label_name: label.name)
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
end
it 'links with adjacent text' do
- doc = filter("Label (#{reference}.)")
+ doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end
it 'ignores invalid label IDs' do
exp = act = "Label #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -91,22 +91,22 @@ module Gitlab::Markdown
let(:reference) { "#{Label.reference_prefix}#{label.name}" }
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_path(project.namespace, project, label_name: label.name)
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See gfm'
end
it 'links with adjacent text' do
- doc = filter("Label (#{reference}.)")
+ doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end
it 'ignores invalid label names' do
exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -115,29 +115,66 @@ module Gitlab::Markdown
let(:reference) { label.to_reference(:name) }
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_path(project.namespace, project, label_name: label.name)
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See gfm references'
end
it 'links with adjacent text' do
- doc = filter("Label (#{reference}.)")
+ doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end
it 'ignores invalid label names' do
exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}")
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
describe 'edge cases' do
it 'gracefully handles non-references matching the pattern' do
exp = act = '(format nil "~0f" 3.0) ; 3.0'
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'referencing a label in a link href' do
+ let(:reference) { %Q{<a href="#{label.to_reference}">Label</a>} }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Label (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+>Label</a>\.\)))
+ end
+
+ it 'includes a data-project attribute' do
+ doc = reference_filter("Label #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-project')
+ expect(link.attr('data-project')).to eq project.id.to_s
+ end
+
+ it 'includes a data-label attribute' do
+ doc = reference_filter("See #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-label')
+ expect(link.attr('data-label')).to eq label.id.to_s
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Label #{reference}")
+ expect(result[:references][:label]).to eq [label]
end
end
end
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index 3ef6cdfff33..4a232051127 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -14,7 +14,7 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Merge #{merge.to_reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -22,42 +22,42 @@ module Gitlab::Markdown
let(:reference) { merge.to_reference }
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_merge_request_url(project.namespace, project, merge)
end
it 'links with adjacent text' do
- doc = filter("Merge (#{reference}.)")
+ doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end
it 'ignores invalid merge IDs' do
exp = act = "Merge #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
it 'includes a title attribute' do
- doc = filter("Merge #{reference}")
+ doc = reference_filter("Merge #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}"
end
it 'escapes the title attribute' do
merge.update_attribute(:title, %{"></a>whatever<a title="})
- doc = filter("Merge #{reference}")
+ doc = reference_filter("Merge #{reference}")
expect(doc.text).to eq "Merge #{reference}"
end
it 'includes default classes' do
- doc = filter("Merge #{reference}")
+ doc = reference_filter("Merge #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request'
end
it 'includes a data-project attribute' do
- doc = filter("Merge #{reference}")
+ doc = reference_filter("Merge #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -65,7 +65,7 @@ module Gitlab::Markdown
end
it 'includes a data-merge-request attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-merge-request')
@@ -73,7 +73,7 @@ module Gitlab::Markdown
end
it 'supports an :only_path context' do
- doc = filter("Merge #{reference}", only_path: true)
+ doc = reference_filter("Merge #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
@@ -89,26 +89,50 @@ module Gitlab::Markdown
context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, :public, namespace: namespace) }
- let(:merge) { create(:merge_request, source_project: project2) }
+ let(:merge) { create(:merge_request, source_project: project2, target_project: project2) }
let(:reference) { merge.to_reference(project) }
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_merge_request_url(project2.namespace,
- project, merge)
+ project2, merge)
end
it 'links with adjacent text' do
- doc = filter("Merge (#{reference}.)")
+ doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end
it 'ignores invalid merge IDs on the referenced project' do
exp = act = "Merge #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Merge #{reference}")
+ expect(result[:references][:merge_request]).to eq [merge]
+ end
+ end
+
+ context 'cross-project URL reference' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:merge) { create(:merge_request, source_project: project2, target_project: project2) }
+ let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq reference
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Merge (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/)
end
it 'adds to the results hash' do
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index 9d9652dba46..3a9acc9d6d4 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -15,48 +15,48 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Snippet #{reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
context 'internal reference' do
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_snippet_url(project.namespace, project, snippet)
end
it 'links with adjacent text' do
- doc = filter("Snippet (#{reference}.)")
+ doc = reference_filter("Snippet (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end
it 'ignores invalid snippet IDs' do
exp = act = "Snippet #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
it 'includes a title attribute' do
- doc = filter("Snippet #{reference}")
+ doc = reference_filter("Snippet #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}"
end
it 'escapes the title attribute' do
snippet.update_attribute(:title, %{"></a>whatever<a title="})
- doc = filter("Snippet #{reference}")
+ doc = reference_filter("Snippet #{reference}")
expect(doc.text).to eq "Snippet #{reference}"
end
it 'includes default classes' do
- doc = filter("Snippet #{reference}")
+ doc = reference_filter("Snippet #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet'
end
it 'includes a data-project attribute' do
- doc = filter("Snippet #{reference}")
+ doc = reference_filter("Snippet #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -64,7 +64,7 @@ module Gitlab::Markdown
end
it 'includes a data-snippet attribute' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-snippet')
@@ -72,7 +72,7 @@ module Gitlab::Markdown
end
it 'supports an :only_path context' do
- doc = filter("Snippet #{reference}", only_path: true)
+ doc = reference_filter("Snippet #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
@@ -92,21 +92,51 @@ module Gitlab::Markdown
let(:reference) { snippet.to_reference(project) }
it 'links to a valid reference' do
- doc = filter("See #{reference}")
+ doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
it 'links with adjacent text' do
- doc = filter("See (#{reference}.)")
+ doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end
it 'ignores invalid snippet IDs on the referenced project' do
exp = act = "See #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Snippet #{reference}")
+ expect(result[:references][:snippet]).to eq [snippet]
+ end
+ end
+
+ context 'cross-project URL reference' do
+ let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("See (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/)
+ end
+
+ it 'ignores invalid snippet IDs on the referenced project' do
+ act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
end
it 'adds to the results hash' do
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index d9e0d7c42db..25379f0670e 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -14,13 +14,13 @@ module Gitlab::Markdown
it 'ignores invalid users' do
exp = act = "Hey #{invalidate_reference(reference)}"
- expect(filter(act).to_html).to eq(exp)
+ expect(reference_filter(act).to_html).to eq(exp)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Hey #{reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
+ expect(reference_filter(act).to_html).to eq exp
end
end
@@ -32,7 +32,7 @@ module Gitlab::Markdown
end
it 'supports a special @all mention' do
- doc = filter("Hey #{reference}")
+ doc = reference_filter("Hey #{reference}")
expect(doc.css('a').length).to eq 1
expect(doc.css('a').first.attr('href'))
.to eq urls.namespace_project_url(project.namespace, project)
@@ -46,26 +46,26 @@ module Gitlab::Markdown
context 'mentioning a user' do
it 'links to a User' do
- doc = filter("Hey #{reference}")
+ doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
end
it 'links to a User with a period' do
user = create(:user, name: 'alphA.Beta')
- doc = filter("Hey #{user.to_reference}")
+ doc = reference_filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1
end
it 'links to a User with an underscore' do
user = create(:user, name: 'ping_pong_king')
- doc = filter("Hey #{user.to_reference}")
+ doc = reference_filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1
end
it 'includes a data-user attribute' do
- doc = filter("Hey #{reference}")
+ doc = reference_filter("Hey #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-user')
@@ -83,12 +83,12 @@ module Gitlab::Markdown
let(:reference) { group.to_reference }
it 'links to the Group' do
- doc = filter("Hey #{reference}")
+ doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
end
it 'includes a data-group attribute' do
- doc = filter("Hey #{reference}")
+ doc = reference_filter("Hey #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-group')
@@ -102,21 +102,48 @@ module Gitlab::Markdown
end
it 'links with adjacent text' do
- doc = filter("Mention me (#{reference}.)")
+ doc = reference_filter("Mention me (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{reference}<\/a>\.\)/)
end
it 'includes default classes' do
- doc = filter("Hey #{reference}")
+ doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member'
end
it 'supports an :only_path context' do
- doc = filter("Hey #{reference}", only_path: true)
+ doc = reference_filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
expect(link).to eq urls.user_path(user)
end
+
+ context 'referencing a user in a link href' do
+ let(:reference) { %Q{<a href="#{user.to_reference}">User</a>} }
+
+ it 'links to a User' do
+ doc = reference_filter("Hey #{reference}")
+ expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Mention me (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>User<\/a>\.\)/)
+ end
+
+ it 'includes a data-user attribute' do
+ doc = reference_filter("Hey #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-user')
+ expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Hey #{reference}")
+ expect(result[:references][:user]).to eq [user]
+ end
+ end
end
end
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index 1031af097bd..3c1009a2eb0 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -7,50 +7,72 @@ describe CommitRange do
it { is_expected.to include_module(Referable) }
end
- let(:sha_from) { 'f3f85602' }
- let(:sha_to) { 'e86e1013' }
+ let!(:project) { create(:project, :public) }
+ let!(:commit1) { project.commit("HEAD~2") }
+ let!(:commit2) { project.commit }
- let(:range) { described_class.new("#{sha_from}...#{sha_to}") }
- let(:range2) { described_class.new("#{sha_from}..#{sha_to}") }
+ let(:sha_from) { commit1.short_id }
+ let(:sha_to) { commit2.short_id }
+
+ let(:full_sha_from) { commit1.id }
+ let(:full_sha_to) { commit2.id }
+
+ let(:range) { described_class.new("#{sha_from}...#{sha_to}", project) }
+ let(:range2) { described_class.new("#{sha_from}..#{sha_to}", project) }
it 'raises ArgumentError when given an invalid range string' do
- expect { described_class.new("Foo") }.to raise_error(ArgumentError)
+ expect { described_class.new("Foo", project) }.to raise_error(ArgumentError)
end
describe '#to_s' do
it 'is correct for three-dot syntax' do
- expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}"
+ expect(range.to_s).to eq "#{full_sha_from}...#{full_sha_to}"
end
it 'is correct for two-dot syntax' do
- expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}"
+ expect(range2.to_s).to eq "#{full_sha_from}..#{full_sha_to}"
end
end
describe '#to_reference' do
- let(:project) { double('project', to_reference: 'namespace1/project') }
+ let(:cross) { create(:project) }
+
+ it 'returns a String reference to the object' do
+ expect(range.to_reference).to eq "#{full_sha_from}...#{full_sha_to}"
+ end
+
+ it 'returns a String reference to the object' do
+ expect(range2.to_reference).to eq "#{full_sha_from}..#{full_sha_to}"
+ end
+
+ it 'supports a cross-project reference' do
+ expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{full_sha_from}...#{full_sha_to}"
+ end
+ end
- before do
- range.project = project
+ describe '#reference_link_text' do
+ let(:cross) { create(:project) }
+
+ it 'returns a String reference to the object' do
+ expect(range.reference_link_text).to eq "#{sha_from}...#{sha_to}"
end
it 'returns a String reference to the object' do
- expect(range.to_reference).to eq range.to_s
+ expect(range2.reference_link_text).to eq "#{sha_from}..#{sha_to}"
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{range.to_s}"
+ expect(range.reference_link_text(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}"
end
end
describe '#reference_title' do
it 'returns the correct String for three-dot ranges' do
- expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}"
+ expect(range.reference_title).to eq "Commits #{full_sha_from} through #{full_sha_to}"
end
it 'returns the correct String for two-dot ranges' do
- expect(range2.reference_title).to eq "Commits #{sha_from}^ through #{sha_to}"
+ expect(range2.reference_title).to eq "Commits #{full_sha_from}^ through #{full_sha_to}"
end
end
@@ -60,11 +82,11 @@ describe CommitRange do
end
it 'includes the correct values for a three-dot range' do
- expect(range.to_param).to eq({ from: sha_from, to: sha_to })
+ expect(range.to_param).to eq({ from: full_sha_from, to: full_sha_to })
end
it 'includes the correct values for a two-dot range' do
- expect(range2.to_param).to eq({ from: sha_from + '^', to: sha_to })
+ expect(range2.to_param).to eq({ from: full_sha_from + '^', to: full_sha_to })
end
end
@@ -79,64 +101,37 @@ describe CommitRange do
end
describe '#valid_commits?' do
- context 'without a project' do
- it 'returns nil' do
- expect(range.valid_commits?).to be_nil
+ context 'with a valid repo' do
+ before do
+ expect(project).to receive(:valid_repo?).and_return(true)
end
- end
-
- it 'accepts an optional project argument' do
- project1 = double('project1').as_null_object
- project2 = double('project2').as_null_object
-
- # project1 gets assigned through the accessor, but ignored when not given
- # as an argument to `valid_commits?`
- expect(project1).not_to receive(:present?)
- range.project = project1
-
- # project2 gets passed to `valid_commits?`
- expect(project2).to receive(:present?).and_return(false)
- range.valid_commits?(project2)
- end
-
- context 'with a project' do
- let(:project) { double('project', repository: double('repository')) }
+ it 'is false when `sha_from` is invalid' do
+ expect(project).to receive(:commit).with(sha_from).and_return(nil)
+ expect(project).to receive(:commit).with(sha_to).and_call_original
- context 'with a valid repo' do
- before do
- expect(project).to receive(:valid_repo?).and_return(true)
- range.project = project
- end
+ expect(range).not_to be_valid_commits
+ end
- it 'is false when `sha_from` is invalid' do
- expect(project.repository).to receive(:commit).with(sha_from).and_return(false)
- expect(project.repository).not_to receive(:commit).with(sha_to)
- expect(range).not_to be_valid_commits
- end
+ it 'is false when `sha_to` is invalid' do
+ expect(project).to receive(:commit).with(sha_from).and_call_original
+ expect(project).to receive(:commit).with(sha_to).and_return(nil)
- it 'is false when `sha_to` is invalid' do
- expect(project.repository).to receive(:commit).with(sha_from).and_return(true)
- expect(project.repository).to receive(:commit).with(sha_to).and_return(false)
- expect(range).not_to be_valid_commits
- end
+ expect(range).not_to be_valid_commits
+ end
- it 'is true when both `sha_from` and `sha_to` are valid' do
- expect(project.repository).to receive(:commit).with(sha_from).and_return(true)
- expect(project.repository).to receive(:commit).with(sha_to).and_return(true)
- expect(range).to be_valid_commits
- end
+ it 'is true when both `sha_from` and `sha_to` are valid' do
+ expect(range).to be_valid_commits
end
+ end
- context 'without a valid repo' do
- before do
- expect(project).to receive(:valid_repo?).and_return(false)
- range.project = project
- end
+ context 'without a valid repo' do
+ before do
+ expect(project).to receive(:valid_repo?).and_return(false)
+ end
- it 'returns false' do
- expect(range).not_to be_valid_commits
- end
+ it 'returns false' do
+ expect(range).not_to be_valid_commits
end
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 90be9324951..974b52c1833 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -24,6 +24,17 @@ describe Commit do
end
end
+ describe '#reference_link_text' do
+ it 'returns a String reference to the object' do
+ expect(commit.reference_link_text).to eq commit.short_id
+ end
+
+ it 'supports a cross-project reference' do
+ cross = double('project')
+ expect(commit.reference_link_text(cross)).to eq "#{project.to_reference}@#{commit.short_id}"
+ end
+ end
+
describe '#title' do
it "returns no_commit_message when safe_message is blank" do
allow(commit).to receive(:safe_message).and_return('')
@@ -77,14 +88,10 @@ eos
let(:other_issue) { create :issue, project: other_project }
it 'detects issues that this commit is marked as closing' do
- allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid}")
- expect(commit.closes_issues).to eq([issue])
- end
-
- it 'does not detect issues from other projects' do
ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
- allow(commit).to receive(:safe_message).and_return("Fixes #{ext_ref}")
- expect(commit.closes_issues).to be_empty
+ allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid} and #{ext_ref}")
+ expect(commit.closes_issues).to include(issue)
+ expect(commit.closes_issues).to include(other_issue)
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index a68c7b1e461..c6d3aef0af9 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -131,6 +131,23 @@ describe API::API, api: true do
end
end
+ describe 'GET /projects/:id/merge_request/:merge_request_id/commits' do
+ context 'valid merge request' do
+ before { get api("/projects/#{project.id}/merge_request/#{merge_request.id}/commits", user) }
+ let(:commit) { merge_request.commits.first }
+
+ it { expect(response.status).to eq 200 }
+ it { expect(json_response.size).to eq(merge_request.commits.size) }
+ it { expect(json_response.first['id']).to eq(commit.id) }
+ it { expect(json_response.first['title']).to eq(commit.title) }
+ end
+
+ it 'returns a 404 when merge_request_id not found' do
+ get api("/projects/#{project.id}/merge_request/999/commits", user)
+ expect(response.status).to eq(404)
+ end
+ end
+
describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do
it 'should return the change information of the merge_request' do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 9fc294118ae..c59ee7af8ab 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -389,14 +389,30 @@ describe API::API, api: true do
describe 'GET /projects/:id/events' do
before { project_member2 }
- it 'should return a project events' do
- get api("/projects/#{project.id}/events", user)
- expect(response.status).to eq(200)
- json_event = json_response.first
+ context 'valid request' do
+ before do
+ note = create(:note_on_issue, note: 'What an awesome day!', project: project)
+ EventCreateService.new.leave_note(note, note.author)
+ get api("/projects/#{project.id}/events", user)
+ end
+
+ it { expect(response.status).to eq(200) }
+
+ context 'joined event' do
+ let(:json_event) { json_response[1] }
- expect(json_event['action_name']).to eq('joined')
- expect(json_event['project_id'].to_i).to eq(project.id)
- expect(json_event['author_username']).to eq(user3.username)
+ it { expect(json_event['action_name']).to eq('joined') }
+ it { expect(json_event['project_id'].to_i).to eq(project.id) }
+ it { expect(json_event['author_username']).to eq(user3.username) }
+ it { expect(json_event['author']['name']).to eq(user3.name) }
+ end
+
+ context 'comment event' do
+ let(:json_event) { json_response.first }
+
+ it { expect(json_event['action_name']).to eq('commented on') }
+ it { expect(json_event['note']['body']).to eq('What an awesome day!') }
+ end
end
it 'should return a 404 error if not found' do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index a4e2b2953cc..35fa412ed80 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -45,6 +45,7 @@ describe NotificationService do
project.team << [issue.author, :master]
project.team << [issue.assignee, :master]
project.team << [note.author, :master]
+ create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@subscribed_participant cc this guy')
end
describe :new_note do
@@ -60,6 +61,7 @@ describe NotificationService do
should_email(note.noteable.assignee)
should_email(@u_mentioned)
should_email(@subscriber)
+ should_email(@subscribed_participant)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
@@ -381,18 +383,19 @@ describe NotificationService do
def add_users_with_subscription(project, issuable)
@subscriber = create :user
@unsubscriber = create :user
+ @subscribed_participant = create(:user, username: 'subscribed_participant', notification_level: Notification::N_PARTICIPATING)
+ project.team << [@subscribed_participant, :master]
project.team << [@subscriber, :master]
project.team << [@unsubscriber, :master]
issuable.subscriptions.create(user: @subscriber, subscribed: true)
+ issuable.subscriptions.create(user: @subscribed_participant, subscribed: true)
issuable.subscriptions.create(user: @unsubscriber, subscribed: false)
end
def sent_to_user?(user)
- ActionMailer::Base.deliveries.any? do |message|
- message.to.include?(user.email)
- end
+ ActionMailer::Base.deliveries.map(&:to).flatten.count(user.email) == 1
end
def should_email(user)
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index 97e5c270a59..91e3bee13c1 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -35,11 +35,24 @@ module FilterSpecHelper
pipeline.call(body)
end
- def reference_pipeline_result(body, contexts = {})
+ def reference_pipeline(contexts = {})
contexts.reverse_merge!(project: project) if defined?(project)
- pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts)
- pipeline.call(body)
+ filters = [
+ Gitlab::Markdown::AutolinkFilter,
+ described_class,
+ Gitlab::Markdown::ReferenceGathererFilter
+ ]
+
+ HTML::Pipeline.new(filters, contexts)
+ end
+
+ def reference_pipeline_result(body, contexts = {})
+ reference_pipeline(contexts).call(body)
+ end
+
+ def reference_filter(html, contexts = {})
+ reference_pipeline(contexts).to_document(html)
end
# Modify a String reference to make it invalid
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index bedc1a7f1db..d6d3062a197 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -93,6 +93,10 @@ class MarkdownFeature
end
end
+ def urls
+ Gitlab::Application.routes.url_helpers
+ end
+
def raw_markdown
markdown = File.read(Rails.root.join('spec/fixtures/markdown.md.erb'))
ERB.new(markdown).result(binding)
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 7500d0fdf80..7eadcd58c1f 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -71,7 +71,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-project_member', count: 3)
+ expect(actual).to have_selector('a.gfm.gfm-project_member', count: 4)
end
end
@@ -80,7 +80,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-issue', count: 3)
+ expect(actual).to have_selector('a.gfm.gfm-issue', count: 6)
end
end
@@ -89,7 +89,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 3)
+ expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 6)
expect(actual).to have_selector('em a.gfm-merge_request')
end
end
@@ -99,7 +99,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-snippet', count: 2)
+ expect(actual).to have_selector('a.gfm.gfm-snippet', count: 5)
end
end
@@ -108,7 +108,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 2)
+ expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 5)
end
end
@@ -117,7 +117,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-commit', count: 2)
+ expect(actual).to have_selector('a.gfm.gfm-commit', count: 5)
end
end
@@ -126,7 +126,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-label', count: 3)
+ expect(actual).to have_selector('a.gfm.gfm-label', count: 4)
end
end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index 3bb568f4d49..33d2b14583c 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -10,12 +10,12 @@ def common_mentionable_setup
let(:mentioned_issue) { create(:issue, project: project) }
let!(:mentioned_mr) { create(:merge_request, :simple, source_project: project) }
- let(:mentioned_commit) { project.commit }
+ let(:mentioned_commit) { project.commit("HEAD~1") }
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let(:ext_mr) { create(:merge_request, :simple, source_project: ext_proj) }
- let(:ext_commit) { ext_proj.commit }
+ let(:ext_commit) { ext_proj.commit("HEAD~2") }
# Override to add known commits to the repository stub.
let(:extra_commits) { [] }
@@ -45,14 +45,11 @@ def common_mentionable_setup
before do
# Wire the project's repository to return the mentioned commit, and +nil+
# for any unrecognized commits.
- commitmap = {
- mentioned_commit.id => mentioned_commit
- }
- extra_commits.each { |c| commitmap[c.short_id] = c }
-
- allow(Project).to receive(:find).and_call_original
- allow(Project).to receive(:find).with(project.id.to_s).and_return(project)
- allow(project.repository).to receive(:commit) { |sha| commitmap[sha] }
+ allow_any_instance_of(::Repository).to receive(:commit).and_call_original
+ allow_any_instance_of(::Repository).to receive(:commit).with(mentioned_commit.short_id).and_return(mentioned_commit)
+ extra_commits.each do |commit|
+ allow_any_instance_of(::Repository).to receive(:commit).with(commit.short_id).and_return(commit)
+ end
set_mentionable_text.call(ref_string)
end