diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2017-09-06 21:00:47 +0200 |
---|---|---|
committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2017-09-06 21:00:47 +0200 |
commit | cd8ea329f0d64c04e5dee00fb8916268dae7a6f7 (patch) | |
tree | a77f550a9acfce3c64484ff9f300a560b6587bd0 /app | |
parent | 632f6ba267bc09a658defc3721d2b52de05cf7e6 (diff) | |
parent | bb1ad6edcd62d8e8b8a1a37ed0782bdf7bf2bbd1 (diff) | |
download | gitlab-ce-cd8ea329f0d64c04e5dee00fb8916268dae7a6f7.tar.gz |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into zj/gitlab-ce-zj-auto-devops-table
Diffstat (limited to 'app')
120 files changed, 577 insertions, 394 deletions
diff --git a/app/assets/javascripts/breadcrumb.js b/app/assets/javascripts/breadcrumb.js new file mode 100644 index 00000000000..10fbcfe96cf --- /dev/null +++ b/app/assets/javascripts/breadcrumb.js @@ -0,0 +1,28 @@ +export const addTooltipToEl = (el) => { + const textEl = el.querySelector('.js-breadcrumb-item-text'); + + if (textEl && textEl.scrollWidth > textEl.offsetWidth) { + el.setAttribute('title', el.textContent); + el.setAttribute('data-container', 'body'); + el.classList.add('has-tooltip'); + } +}; + +export default () => { + const breadcrumbs = document.querySelector('.js-breadcrumbs-list'); + + if (breadcrumbs) { + const topLevelLinks = [...breadcrumbs.children].filter(el => !el.classList.contains('dropdown')) + .map(el => el.querySelector('a')) + .filter(el => el); + const $expander = $('.js-breadcrumbs-collapsed-expander'); + + topLevelLinks.forEach(el => addTooltipToEl(el)); + + $expander.closest('.dropdown') + .on('show.bs.dropdown hide.bs.dropdown', (e) => { + $('.js-breadcrumbs-collapsed-expander', e.currentTarget).toggleClass('open') + .tooltip('hide'); + }); + } +}; diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 3dec4de06ec..6db0b18ae5a 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -41,7 +41,6 @@ import Issue from './issue'; import BindInOut from './behaviors/bind_in_out'; import DeleteModal from './branches/branches_delete_modal'; import Group from './group'; -import GroupName from './group_name'; import GroupsList from './groups_list'; import ProjectsList from './projects_list'; import setupProjectEdit from './project_edit'; @@ -489,6 +488,8 @@ import initChangesDropdown from './init_changes_dropdown'; initSettingsPanels(); break; case 'projects:settings:ci_cd:show': + // Initialize expandable settings panels + initSettingsPanels(); case 'groups:settings:ci_cd:show': new gl.ProjectVariables(); break; @@ -554,9 +555,6 @@ import initChangesDropdown from './init_changes_dropdown'; case 'root': new UserCallout(); break; - case 'groups': - new GroupName(); - break; case 'profiles': new NotificationsForm(); new NotificationsDropdown(); @@ -564,7 +562,6 @@ import initChangesDropdown from './init_changes_dropdown'; case 'projects': new Project(); new ProjectAvatar(); - new GroupName(); switch (path[1]) { case 'compare': new CompareAutocomplete(); diff --git a/app/assets/javascripts/group_name.js b/app/assets/javascripts/group_name.js deleted file mode 100644 index 6677391c689..00000000000 --- a/app/assets/javascripts/group_name.js +++ /dev/null @@ -1,67 +0,0 @@ -import _ from 'underscore'; - -export default class GroupName { - constructor() { - this.titleContainer = document.querySelector('.js-title-container'); - this.title = this.titleContainer.querySelector('.title'); - - if (this.title) { - this.titleWidth = this.title.offsetWidth; - this.groupTitle = this.titleContainer.querySelector('.group-title'); - this.groups = this.titleContainer.querySelectorAll('.group-path'); - this.toggle = null; - this.isHidden = false; - this.init(); - } - } - - init() { - if (this.groups.length > 0) { - this.groups[this.groups.length - 1].classList.remove('hidable'); - this.toggleHandler(); - window.addEventListener('resize', _.debounce(this.toggleHandler.bind(this), 100)); - } - this.render(); - } - - toggleHandler() { - if (this.titleWidth > this.titleContainer.offsetWidth) { - if (!this.toggle) this.createToggle(); - this.showToggle(); - } else if (this.toggle) { - this.hideToggle(); - } - } - - createToggle() { - this.toggle = document.createElement('button'); - this.toggle.setAttribute('type', 'button'); - this.toggle.className = 'text-expander group-name-toggle'; - this.toggle.setAttribute('aria-label', 'Toggle full path'); - this.toggle.innerHTML = '<i class="fa fa-ellipsis-h" aria-hidden="true"></i>'; - this.toggle.addEventListener('click', this.toggleGroups.bind(this)); - this.title.insertBefore(this.toggle, this.groupTitle); - this.toggleGroups(); - } - - showToggle() { - this.title.classList.add('wrap'); - this.toggle.classList.remove('hidden'); - if (this.isHidden) this.groupTitle.classList.add('hidden'); - } - - hideToggle() { - this.title.classList.remove('wrap'); - this.toggle.classList.add('hidden'); - if (this.isHidden) this.groupTitle.classList.remove('hidden'); - } - - toggleGroups() { - this.isHidden = !this.isHidden; - this.groupTitle.classList.toggle('hidden'); - } - - render() { - this.title.classList.remove('initializing'); - } -} diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index f14458c8d41..0bc31a56684 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -144,6 +144,7 @@ import './smart_interval'; import './star'; import './subscription'; import './subscription_select'; +import initBreadcrumbs from './breadcrumb'; import './dispatcher'; @@ -181,6 +182,8 @@ $(function () { var bootstrapBreakpoint = bp.getBreakpointSize(); var fitSidebarForSize; + initBreadcrumbs(); + // Set the default path for all cookies to GitLab's root directory Cookies.defaults.path = gon.relative_url_root || '/'; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 6b21def33a6..5f397f08936 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -829,6 +829,7 @@ } } +@include new-style-dropdown('.breadcrumbs-list .dropdown '); @include new-style-dropdown('.js-namespace-select + '); header.navbar-gitlab-new .header-content .dropdown-menu.projects-dropdown-menu { diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index 4deb7431284..e4b52ab480d 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -1,6 +1,7 @@ @import "framework/variables"; @import 'framework/tw_bootstrap_variables'; @import "bootstrap/variables"; +@import "framework/mixins"; .content-wrapper.page-with-new-nav { margin-top: $new-navbar-height; @@ -422,109 +423,38 @@ header.navbar-gitlab-new { .breadcrumbs { display: flex; - min-height: 61px; + min-height: 48px; color: $gl-text-color; - border-bottom: 1px solid $border-color; - - .dropdown-toggle-caret { - position: relative; - top: -1px; - padding: 0 5px; - color: $gl-text-color-secondary; - font-size: 10px; - line-height: 1; - background: none; - border: 0; - - &:focus { - outline: 0; - } - } - - // TODO: fallback to global style - .dropdown-menu { - .divider { - margin: 6px 0; - } - - li { - padding: 0 1px; - - a { - border-radius: 0; - padding: 8px 16px; - - &.is-focused, - &:hover, - &:active, - &:focus { - background-color: $gray-darker; - } - } - } - } } .breadcrumbs-container { + display: -webkit-flex; display: flex; width: 100%; position: relative; + padding-top: $gl-padding; + padding-bottom: $gl-padding; align-items: center; - - .dropdown-menu-projects { - margin-top: -$gl-padding; - margin-left: $gl-padding; - } + border-bottom: 1px solid $border-color; } .breadcrumbs-links { + -webkit-flex: 1; flex: 1; min-width: 0; align-self: center; - color: $gl-text-color-quaternary; - - a { - color: $gl-text-color-secondary; - - &:not(:first-child), - &.group-path { - margin-left: 4px; - } - - &:not(:last-of-type), - &.group-path { - margin-right: 3px; - } - } - - .title { - display: inline-block; - - > a { - &:last-of-type:not(:first-child) { - font-weight: $gl-font-weight-bold; - } - } - } + color: $gl-text-color-secondary; .avatar-tile { - margin-right: 5px; + margin-right: 4px; border: 1px solid $border-color; border-radius: 50%; vertical-align: sub; - - &.identicon { - float: left; - width: 16px; - height: 16px; - margin-top: 2px; - font-size: 10px; - } } .text-expander { - margin-left: 4px; - margin-right: 4px; + margin-left: 0; + margin-right: 2px; > i { position: relative; @@ -533,37 +463,52 @@ header.navbar-gitlab-new { } } -.breadcrumbs-extra { +.breadcrumbs-list { + display: -webkit-flex; display: flex; - flex: 0 0 auto; - margin-left: auto; -} + flex-wrap: wrap; + margin-bottom: 0; + line-height: 16px; -.breadcrumbs-sub-title { - margin: 2px 0; - font-size: 16px; - font-weight: $gl-font-weight-normal; - line-height: 1; - - ul { - margin: 0; - } - - li { - display: inline-block; + > li { + display: flex; + align-items: center; + position: relative; &:not(:last-child) { - &::after { - content: "/"; - margin: 0 2px 0 5px; - color: rgba($black, .65); - } + margin-right: 20px; } - &:last-child a { - font-weight: $gl-font-weight-bold; + > a { + font-size: 12px; + color: currentColor; } } +} + +.breadcrumb-item-text { + @include str-truncated(128px); +} + +.breadcrumbs-list-angle { + position: absolute; + right: -12px; + top: 50%; + color: $gl-text-color-tertiary; + transform: translateY(-50%); +} + +.breadcrumbs-extra { + display: flex; + flex: 0 0 auto; + margin-left: auto; +} + +.breadcrumbs-sub-title { + margin: 0; + font-size: 12px; + font-weight: 600; + line-height: 1; a { color: $gl-text-color; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 90b0a543c5c..fd5e344d8c9 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -45,7 +45,6 @@ $new-sidebar-collapsed-width: 50px; margin-right: 2px; a { - border-bottom: 1px solid $border-color; font-weight: $gl-font-weight-bold; display: flex; align-items: center; @@ -389,6 +388,10 @@ $new-sidebar-collapsed-width: 50px; } } + .nav-icon-container { + margin-right: 0; + } + .toggle-sidebar-button { width: $new-sidebar-collapsed-width - 2px; padding: 16px 18px; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index bfa2a155cdf..994707422bb 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -141,17 +141,17 @@ display: inline-block; background: $white-light; color: $gl-text-color-secondary; - padding: 0 5px; + padding: 0 4px; cursor: pointer; border: 1px solid $border-gray-dark; border-radius: $border-radius-default; margin-left: 5px; - font-size: $gl-font-size; + font-size: 12px; line-height: $gl-font-size; outline: none; &.open { - background: $gray-light; + background-color: darken($gray-light, 10%); box-shadow: inset 0 0 2px rgba($black, 0.2); } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 45f2aed1531..e437bad4912 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -516,7 +516,7 @@ ul.notes { } .note-actions-item { - margin-left: 15px; + margin-left: 12px; display: flex; align-items: center; @@ -620,15 +620,25 @@ ul.notes { .note-role { position: relative; - padding: 0 7px; + display: inline-block; color: $notes-role-color; font-size: 12px; line-height: 20px; - border: 1px solid $border-color; - border-radius: $label-border-radius; + margin: 0 3px; + + &.note-role-access { + padding: 0 7px; + border: 1px solid $border-color; + border-radius: $label-border-radius; + } + + &.note-role-special { + text-shadow: 0 0 15px $gl-text-color-inverted; + } } + /** * Line note button on the side of diffs */ diff --git a/app/controllers/admin/logs_controller.rb b/app/controllers/admin/logs_controller.rb index bdc4332ae69..12a27cede75 100644 --- a/app/controllers/admin/logs_controller.rb +++ b/app/controllers/admin/logs_controller.rb @@ -1,6 +1,13 @@ class Admin::LogsController < Admin::ApplicationController + before_action :loggers + def show - @loggers = [ + end + + private + + def loggers + @loggers ||= [ Gitlab::AppLogger, Gitlab::GitLogger, Gitlab::EnvironmentLogger, diff --git a/app/controllers/concerns/renders_commits.rb b/app/controllers/concerns/renders_commits.rb new file mode 100644 index 00000000000..bb2c1dfa00a --- /dev/null +++ b/app/controllers/concerns/renders_commits.rb @@ -0,0 +1,7 @@ +module RendersCommits + def prepare_commits_for_rendering(commits) + Banzai::CommitRenderer.render(commits, @project, current_user) + + commits + end +end diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb index 41c3114ad1e..4791bc561a4 100644 --- a/app/controllers/concerns/renders_notes.rb +++ b/app/controllers/concerns/renders_notes.rb @@ -1,7 +1,8 @@ module RendersNotes - def prepare_notes_for_rendering(notes) + def prepare_notes_for_rendering(notes, noteable = nil) preload_noteable_for_regular_notes(notes) preload_max_access_for_authors(notes, @project) + preload_first_time_contribution_for_authors(noteable, notes) Banzai::NoteRenderer.render(notes, @project, current_user) notes @@ -19,4 +20,10 @@ module RendersNotes def preload_noteable_for_regular_notes(notes) ActiveRecord::Associations::Preloader.new.preload(notes.reject(&:for_commit?), :noteable) end + + def preload_first_time_contribution_for_authors(noteable, notes) + return unless noteable.is_a?(Issuable) && noteable.first_contribution? + + notes.each {|n| n.specialize_for_first_contribution!(noteable)} + end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 076076fd1b3..d83824fef06 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -9,8 +9,6 @@ class ProfilesController < Profiles::ApplicationController end def update - user_params.except!(:email) if @user.external_email? - respond_to do |format| result = Users::UpdateService.new(@user, user_params).execute diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index f637a9a803b..eb010923466 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -7,7 +7,7 @@ class Projects::ArtifactsController < Projects::ApplicationController before_action :authorize_update_build!, only: [:keep] before_action :extract_ref_name_and_path before_action :validate_artifacts! - before_action :set_path_and_entry, only: [:file, :raw] + before_action :entry, only: [:file] def download if artifacts_file.file_storage? @@ -41,7 +41,10 @@ class Projects::ArtifactsController < Projects::ApplicationController end def raw - send_artifacts_entry(build, @entry) + path = Gitlab::Ci::Build::Artifacts::Path + .new(params[:path]) + + send_artifacts_entry(build, path) end def keep @@ -93,9 +96,8 @@ class Projects::ArtifactsController < Projects::ApplicationController @artifacts_file ||= build.artifacts_file end - def set_path_and_entry - @path = params[:path] - @entry = build.artifacts_metadata_entry(@path) + def entry + @entry = build.artifacts_metadata_entry(params[:path]) render_404 unless @entry.exists? end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 6de125e7e80..1a775def506 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -127,7 +127,7 @@ class Projects::CommitController < Projects::ApplicationController @discussions = commit.discussions @notes = (@grouped_diff_discussions.values.flatten + @discussions).flat_map(&:notes) - @notes = prepare_notes_for_rendering(@notes) + @notes = prepare_notes_for_rendering(@notes, @commit) end def assign_change_commit_vars diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 2de9900d449..4a841bf2073 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -2,6 +2,7 @@ require "base64" class Projects::CommitsController < Projects::ApplicationController include ExtractsPath + include RendersCommits before_action :require_non_empty_project before_action :assign_ref_vars @@ -56,5 +57,7 @@ class Projects::CommitsController < Projects::ApplicationController else @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) end + + @commits = prepare_commits_for_rendering(@commits) end end diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index c8613c0d634..193549663ac 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -3,6 +3,7 @@ require 'addressable/uri' class Projects::CompareController < Projects::ApplicationController include DiffForPath include DiffHelper + include RendersCommits # Authorize before_action :require_non_empty_project @@ -50,7 +51,7 @@ class Projects::CompareController < Projects::ApplicationController .execute(@project, @start_ref) if @compare - @commits = @compare.commits + @commits = prepare_commits_for_rendering(@compare.commits) @diffs = @compare.diffs(diff_options) environment_params = @repository.branch_exists?(@head_ref) ? { ref: @head_ref } : { commit: @compare.commit } diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index dc9e6f71152..ab9f132b502 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -85,7 +85,7 @@ class Projects::IssuesController < Projects::ApplicationController @note = @project.notes.new(noteable: @issue) @discussions = @issue.discussions - @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes)) + @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable) respond_to do |format| format.html diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index f35d53896ba..1096afbb798 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -1,6 +1,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::ApplicationController include DiffForPath include DiffHelper + include RendersCommits skip_before_action :merge_request skip_before_action :ensure_ref_fetched @@ -107,7 +108,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap @target_project = @merge_request.target_project @source_project = @merge_request.source_project - @commits = @merge_request.commits + @commits = prepare_commits_for_rendering(@merge_request.commits) @commit = @merge_request.diff_head_commit @note_counts = Note.where(commit_id: @commits.map(&:id)) diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 330b7df4541..109418c73f7 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -61,6 +61,6 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic @use_legacy_diff_notes = !@merge_request.has_complete_diff_refs? @grouped_diff_discussions = @merge_request.grouped_diff_discussions(@compare.diff_refs) - @notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes)) + @notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes), @merge_request) end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 5095d7fd445..3aa5dadb5ca 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -2,6 +2,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo include ToggleSubscriptionAction include IssuableActions include RendersNotes + include RendersCommits include ToggleAwardEmoji include IssuableCollections @@ -60,12 +61,12 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @discussions = @merge_request.discussions - @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes)) - @noteable = @merge_request @commits_count = @merge_request.commits_count + @discussions = @merge_request.discussions + @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable) + labels set_pipeline_variables @@ -94,7 +95,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def commits # Get commits from repository # or from cache if already merged - @commits = @merge_request.commits + @commits = prepare_commits_for_rendering(@merge_request.commits) @note_counts = Note.where(commit_id: @commits.map(&:id)) .group(:commit_id).count diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index d07143d294f..7c19aa7bb23 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -64,7 +64,7 @@ class Projects::SnippetsController < Projects::ApplicationController @noteable = @snippet @discussions = @snippet.discussions - @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes)) + @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable) render 'show' end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index d58c8d14a75..fbad9ba7db8 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -2,6 +2,7 @@ class SearchController < ApplicationController skip_before_action :authenticate_user! include SearchHelper + include RendersCommits layout 'search' @@ -20,6 +21,8 @@ class SearchController < ApplicationController @search_results = search_service.search_results @search_objects = search_service.search_objects + render_commits if @scope == 'commits' + check_single_commit_result end @@ -38,6 +41,10 @@ class SearchController < ApplicationController private + def render_commits + @search_objects = prepare_commits_for_rendering(@search_objects) + end + def check_single_commit_result if @search_results.single_commit_result? only_commit = @search_results.objects('commits').first diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 8c3abd0a085..c1cdc7c9831 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -66,7 +66,7 @@ class SnippetsController < ApplicationController @noteable = @snippet @discussions = @snippet.discussions - @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes)) + @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable) respond_to do |format| format.html do diff --git a/app/helpers/blame_helper.rb b/app/helpers/blame_helper.rb index d1dc4d94560..089d9e3e387 100644 --- a/app/helpers/blame_helper.rb +++ b/app/helpers/blame_helper.rb @@ -11,11 +11,15 @@ module BlameHelper end def age_map_class(commit_date, duration) - commit_date_days_ago = (duration[:now] - commit_date).to_i / 1.day - # Numbers 0 to 10 come from this calculation, but only commits on the oldest - # day get number 10 (all other numbers can be multiple days), so the range - # is normalized to 0-9 - age_group = [(10 * commit_date_days_ago) / duration[:started_days_ago], 9].min - "blame-commit-age-#{age_group}" + if duration[:started_days_ago] == 0 + "blame-commit-age-0" + else + commit_date_days_ago = (duration[:now] - commit_date).to_i / 1.day + # Numbers 0 to 10 come from this calculation, but only commits on the oldest + # day get number 10 (all other numbers can be multiple days), so the range + # is normalized to 0-9 + age_group = [(10 * commit_date_days_ago) / duration[:started_days_ago], 9].min + "blame-commit-age-#{age_group}" + end end end diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb index abe8edd6a8c..ee1b7ed083e 100644 --- a/app/helpers/breadcrumbs_helper.rb +++ b/app/helpers/breadcrumbs_helper.rb @@ -22,4 +22,16 @@ module BreadcrumbsHelper @breadcrumb_title = title end + + def breadcrumb_list_item(link) + content_tag "li" do + link + icon("angle-right", class: "breadcrumbs-list-angle") + end + end + + def add_to_breadcrumb_dropdown(link, location: :before) + @breadcrumb_dropdown_links ||= {} + @breadcrumb_dropdown_links[location] ||= [] + @breadcrumb_dropdown_links[location] << link + end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index c4c41eac5db..eab1feb8a1f 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -15,18 +15,20 @@ module GroupsHelper @has_group_title = true full_title = '' - group.ancestors.reverse.each do |parent| - full_title += group_title_link(parent, hidable: true) - - full_title += '<span class="hidable"> / </span>'.html_safe + group.ancestors.reverse.each_with_index do |parent, index| + if index > 0 + add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true), location: :before) + else + full_title += breadcrumb_list_item group_title_link(parent, hidable: false) + end end - full_title += group_title_link(group) - full_title += ' · '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path') if name + full_title += render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :before, title: _("Show parent subgroups") - content_tag :span, class: 'group-title' do - full_title.html_safe - end + full_title += breadcrumb_list_item group_title_link(group) + full_title += ' · '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path breadcrumb-item-text js-breadcrumb-item-text') if name + + full_title.html_safe end def projects_lfs_status(group) @@ -65,11 +67,11 @@ module GroupsHelper private - def group_title_link(group, hidable: false) - link_to(group_path(group), class: "group-path #{'hidable' if hidable}") do + def group_title_link(group, hidable: false, show_avatar: false) + link_to(group_path(group), class: "group-path breadcrumb-item-text js-breadcrumb-item-text #{'hidable' if hidable}") do output = - if !Rails.env.test? - image_tag(group_icon(group), class: "avatar-tile", width: 16, height: 16) + if (group.try(:avatar_url) || show_avatar) && !Rails.env.test? + image_tag(group_icon(group), class: "avatar-tile", width: 15, height: 15) else "" end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 717abf2082d..ce2999e6696 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -126,22 +126,20 @@ module IssuablesHelper end def issuable_meta(issuable, project, text) - output = content_tag(:strong, class: "identifier") do - concat("#{text} ") - concat(to_url_reference(issuable)) - end - - output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe + output = "" + output << "Opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe output << content_tag(:strong) do author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true) author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") end output << " ".html_safe + output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip', title: _('1st contribution!')) + output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "hidden-xs hidden-sm") output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "hidden-md hidden-lg") - output + output.html_safe end def issuable_todo(issuable) @@ -173,6 +171,13 @@ module IssuablesHelper html.html_safe end + def issuable_first_contribution_icon + content_tag(:span, class: 'fa-stack') do + concat(icon('certificate', class: "fa-stack-2x")) + concat(content_tag(:strong, '1', class: 'fa-inverse fa-stack-1x')) + end + end + def assigned_issuables_count(issuable_type) case issuable_type when :issues diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 941cfce8370..46bced00c72 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -21,25 +21,28 @@ module MarkupHelper end # Use this in places where you would normally use link_to(gfm(...), ...). - # + def link_to_markdown(body, url, html_options = {}) + return '' if body.blank? + + link_to_html(markdown(body, pipeline: :single_line), url, html_options) + end + + def link_to_markdown_field(object, field, url, html_options = {}) + rendered_field = markdown_field(object, field) + + link_to_html(rendered_field, url, html_options) + end + # It solves a problem occurring with nested links (i.e. # "<a>outer text <a>gfm ref</a> more outer text</a>"). This will not be # interpreted as intended. Browsers will parse something like # "<a>outer text </a><a>gfm ref</a> more outer text" (notice the last part is - # not linked any more). link_to_gfm corrects that. It wraps all parts to + # not linked any more). link_to_html corrects that. It wraps all parts to # explicitly produce the correct linking behavior (i.e. # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>"). - def link_to_gfm(body, url, html_options = {}) - return '' if body.blank? + def link_to_html(redacted, url, html_options = {}) + fragment = Nokogiri::HTML::DocumentFragment.parse(redacted) - context = { - project: @project, - current_user: (current_user if defined?(current_user)), - pipeline: :single_line - } - gfm_body = Banzai.render(body, context) - - fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) if fragment.children.size == 1 && fragment.children[0].name == 'a' # Fragment has only one node, and it's a link generated by `gfm`. # Replace it with our requested link. @@ -82,7 +85,10 @@ module MarkupHelper def markdown_field(object, field) object = object.for_display if object.respond_to?(:for_display) + redacted_field_html = object.try(:"redacted_#{field}_html") + return '' unless object.present? + return redacted_field_html if redacted_field_html html = Banzai.render_field(object, field) prepare_for_rendering(html, object.banzai_render_context(field)) diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 083ace65b09..ce028195e51 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -73,7 +73,7 @@ module NotesHelper end def note_max_access_for_user(note) - note.project.team.human_max_access(note.author_id) + note.project.team.max_member_access(note.author_id) end def discussion_path(discussion) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index d83c7411c9d..5946c475835 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -80,7 +80,9 @@ module PageLayoutHelper @header_title = title @header_title_url = title_url else - @header_title_url ? link_to(@header_title, @header_title_url) : @header_title + return @header_title unless @header_title_url + + breadcrumb_list_item(link_to(@header_title, @header_title_url)) end end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 45238f12ac7..5a4fda0724c 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -1,7 +1,12 @@ module ProfilesHelper - def email_provider_label - return unless current_user.external_email? - - current_user.email_provider.present? ? Gitlab::OAuth::Provider.label_for(current_user.email_provider) : "LDAP" + def attribute_provider_label(attribute) + user_synced_attributes_metadata = current_user.user_synced_attributes_metadata + if user_synced_attributes_metadata&.synced?(attribute) + if user_synced_attributes_metadata.provider + Gitlab::OAuth::Provider.label_for(user_synced_attributes_metadata.provider) + else + 'LDAP' + end + end end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index cec7656773c..86665ea2aec 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -54,25 +54,28 @@ module ProjectsHelper def project_title(project) namespace_link = if project.group - group_title(project.group) + group_title(project.group, nil, nil) else owner = project.namespace.owner link_to(simple_sanitize(owner.name), user_path(owner)) end - project_link = link_to project_path(project), { class: "project-item-select-holder" } do + project_link = link_to project_path(project) do output = - if !Rails.env.test? - project_icon(project, alt: project.name, class: 'avatar-tile', width: 16, height: 16) + if project.avatar_url && !Rails.env.test? + project_icon(project, alt: project.name, class: 'avatar-tile', width: 15, height: 15) else "" end - output << simple_sanitize(project.name) + output << content_tag("span", simple_sanitize(project.name), class: "breadcrumb-item-text js-breadcrumb-item-text") output.html_safe end - "#{namespace_link} / #{project_link}".html_safe + namespace_link = breadcrumb_list_item(namespace_link) unless project.group + project_link = breadcrumb_list_item project_link + + "#{namespace_link} #{project_link}".html_safe end def remove_project_message(project) diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 99212a3438f..815fab9e061 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -10,4 +10,15 @@ module WikiHelper .map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize } .join(' / ') end + + def wiki_breadcrumb_dropdown_links(page_slug) + page_slug_split = page_slug.split('/') + page_slug_split.pop(1) + current_slug = "" + page_slug_split + .map do |dir_or_page| + current_slug = "#{current_slug}#{dir_or_page}/" + add_to_breadcrumb_dropdown link_to(WikiPage.unhyphenize(dir_or_page).capitalize, project_wiki_path(@project, current_slug)), location: :after + end + end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index c439997d636..5ebe6f180e6 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -27,7 +27,6 @@ module Ci validates :coverage, numericality: true, allow_blank: true validates :ref, presence: true - validates :protected, inclusion: { in: [true, false], unless: :importing? }, on: :create scope :unstarted, ->() { where(runner_id: nil) } scope :ignore_failures, ->() { where(allow_failure: false) } diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 0e1b3b29e07..871c76fbad3 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -36,7 +36,6 @@ module Ci validates :sha, presence: { unless: :importing? } validates :ref, presence: { unless: :importing? } validates :status, presence: { unless: :importing? } - validates :protected, inclusion: { in: [true, false], unless: :importing? }, on: :create validate :valid_commit_sha, unless: :importing? after_initialize :set_config_source, if: :new_record? diff --git a/app/models/commit.rb b/app/models/commit.rb index ba3845df867..2ae8890c1b3 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -16,6 +16,8 @@ class Commit participant :notes_with_associations attr_accessor :project, :author + attr_accessor :redacted_description_html + attr_accessor :redacted_title_html DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] @@ -26,6 +28,13 @@ class Commit # The SHA can be between 7 and 40 hex characters. COMMIT_SHA_PATTERN = '\h{7,40}'.freeze + def banzai_render_context(field) + context = { pipeline: :single_line, project: self.project } + context[:author] = self.author if self.author + + context + end + class << self def decorate(commits, project) commits.map do |commit| diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 681c3241dbb..265f6e48540 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -334,4 +334,11 @@ module Issuable metrics = self.metrics || create_metrics metrics.record! end + + ## + # Override in issuable specialization + # + def first_contribution? + false + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b82f49d7073..2a56bab48a3 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -960,6 +960,12 @@ class MergeRequest < ActiveRecord::Base Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache end + def first_contribution? + return false if project.team.max_member_access(author_id) > Gitlab::Access::GUEST + + project.merge_requests.merged.where(author_id: author_id).empty? + end + private def write_ref diff --git a/app/models/note.rb b/app/models/note.rb index 1073c115630..f44590e2144 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -15,6 +15,16 @@ class Note < ActiveRecord::Base include IgnorableColumn include Editable + module SpecialRole + FIRST_TIME_CONTRIBUTOR = :first_time_contributor + + class << self + def values + constants.map {|const| self.const_get(const)} + end + end + end + ignore_column :original_discussion_id cache_markdown_field :note, pipeline: :note, issuable_state_filter_enabled: true @@ -32,9 +42,12 @@ class Note < ActiveRecord::Base # Banzai::ObjectRenderer attr_accessor :user_visible_reference_count - # Attribute used to store the attributes that have ben changed by quick actions. + # Attribute used to store the attributes that have been changed by quick actions. attr_accessor :commands_changes + # A special role that may be displayed on issuable's discussions + attr_accessor :special_role + default_value_for :system, false attr_mentionable :note, pipeline: :note @@ -141,6 +154,10 @@ class Note < ActiveRecord::Base .group(:noteable_id) .where(noteable_type: type, noteable_id: ids) end + + def has_special_role?(role, note) + note.special_role == role + end end def cross_reference? @@ -206,6 +223,22 @@ class Note < ActiveRecord::Base super(noteable_type.to_s.classify.constantize.base_class.to_s) end + def special_role=(role) + raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.values.include?(role) + + @special_role = role + end + + def has_special_role?(role) + self.class.has_special_role?(role, self) + end + + def specialize_for_first_contribution!(noteable) + return unless noteable.author_id == self.author_id + + self.special_role = Note::SpecialRole::FIRST_TIME_CONTRIBUTOR + end + def editable? !system? end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 674eacd28e8..09049824ff7 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -150,7 +150,7 @@ class ProjectTeam end def human_max_access(user_id) - Gitlab::Access.options_with_owner.key(max_member_access(user_id)) + Gitlab::Access.human_access(max_member_access(user_id)) end # Determine the maximum access level for a group of users in bulk. diff --git a/app/models/user.rb b/app/models/user.rb index c5b5f09722f..105eb62f1fa 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -15,10 +15,12 @@ class User < ActiveRecord::Base include IgnorableColumn include FeatureGate include CreatedAtFilterable + include IgnorableColumn DEFAULT_NOTIFICATION_LEVEL = :participating - ignore_column :authorized_projects_populated + ignore_column :external_email + ignore_column :email_provider add_authentication_token_field :authentication_token add_authentication_token_field :incoming_email_token @@ -85,6 +87,7 @@ class User < ActiveRecord::Base has_many :identities, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :chat_names, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_one :user_synced_attributes_metadata, autosave: true # Groups has_many :members, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -161,6 +164,7 @@ class User < ActiveRecord::Base after_update :update_emails_with_primary_email, if: :email_changed? before_save :ensure_authentication_token, :ensure_incoming_email_token before_save :ensure_user_rights_and_limits, if: :external_changed? + before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) } after_save :ensure_namespace_correct after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') } after_initialize :set_projects_limit @@ -1045,6 +1049,22 @@ class User < ActiveRecord::Base self.email == email end + def sync_attribute?(attribute) + return true if ldap_user? && attribute == :email + + attributes = Gitlab.config.omniauth.sync_profile_attributes + + if attributes.is_a?(Array) + attributes.include?(attribute.to_s) + else + attributes + end + end + + def read_only_attribute?(attribute) + user_synced_attributes_metadata&.read_only?(attribute) + end + protected # override, from Devise::Validatable diff --git a/app/models/user_synced_attributes_metadata.rb b/app/models/user_synced_attributes_metadata.rb new file mode 100644 index 00000000000..9f374304164 --- /dev/null +++ b/app/models/user_synced_attributes_metadata.rb @@ -0,0 +1,25 @@ +class UserSyncedAttributesMetadata < ActiveRecord::Base + belongs_to :user + + validates :user, presence: true + + SYNCABLE_ATTRIBUTES = %i[name email location].freeze + + def read_only?(attribute) + Gitlab.config.omniauth.sync_profile_from_provider && synced?(attribute) + end + + def read_only_attributes + return [] unless Gitlab.config.omniauth.sync_profile_from_provider + + SYNCABLE_ATTRIBUTES.select { |key| synced?(key) } + end + + def synced?(attribute) + read_attribute("#{attribute}_synced") + end + + def set_attribute_synced(attribute, value) + write_attribute("#{attribute}_synced", value) + end +end diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb index 2f9855273dc..6188b8a4349 100644 --- a/app/services/users/update_service.rb +++ b/app/services/users/update_service.rb @@ -34,6 +34,10 @@ module Users private def assign_attributes(&block) + if @user.user_synced_attributes_metadata + params.except!(*@user.user_synced_attributes_metadata.read_only_attributes) + end + @user.assign_attributes(params) if params.any? end end diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml index 13b583e6072..13c408914bb 100644 --- a/app/views/admin/applications/edit.html.haml +++ b/app/views/admin/applications/edit.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Applications", admin_applications_path +- breadcrumb_title @application.name - page_title "Edit", @application.name, "Applications" %h3.page-title Edit application diff --git a/app/views/admin/cohorts/index.html.haml b/app/views/admin/cohorts/index.html.haml index be8644c0ca6..bff53da1d9a 100644 --- a/app/views/admin/cohorts/index.html.haml +++ b/app/views/admin/cohorts/index.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Cohorts" - @no_container = true = render "admin/dashboard/head" diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 8e94e68bc11..069f8f89e0b 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Dashboard" = render "admin/dashboard/head" %div{ class: container_class } diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 2aadc071c75..3e02f7b1e16 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Groups", admin_groups_path +- breadcrumb_title @group.name - page_title @group.name, "Groups" %h3.page-title Group: #{@group.full_name} diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml index 665e8c7e74f..efb15ccc8df 100644 --- a/app/views/admin/hooks/edit.html.haml +++ b/app/views/admin/hooks/edit.html.haml @@ -1,3 +1,4 @@ +- add_to_breadcrumbs "System Hooks", admin_hooks_path - page_title 'Edit System Hook' %h3.page-title Edit System Hook diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml index 09be17f07be..aa6e9db3900 100644 --- a/app/views/admin/jobs/index.html.haml +++ b/app/views/admin/jobs/index.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Jobs" - @no_container = true = render "admin/dashboard/head" diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml index 309aedceded..96f0d404ac4 100644 --- a/app/views/admin/labels/edit.html.haml +++ b/app/views/admin/labels/edit.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Labels", admin_labels_path +- breadcrumb_title "Edit Label" - page_title "Edit", @label.name, "Labels" %h3.page-title Edit Label diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 7b1b15cfeb8..ab4165c0bf2 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Projects", admin_projects_path +- breadcrumb_title @project.name_with_namespace - page_title @project.name_with_namespace, "Projects" %h3.page-title Project: #{@project.name_with_namespace} diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 126550ee10e..6793ce557c4 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Runners" - @no_container = true = render "admin/dashboard/head" diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml index 53d970e33c1..512176649e6 100644 --- a/app/views/admin/services/edit.html.haml +++ b/app/views/admin/services/edit.html.haml @@ -1,2 +1,4 @@ +- add_to_breadcrumbs "Service Templates", admin_application_settings_services_path +- breadcrumb_title @service.title - page_title @service.title, "Service Templates" = render 'form' diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index b556ff056c0..98ff592eb64 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Users", admin_users_path +- breadcrumb_title @user.name - page_title @user.name, "Users" = render 'admin/users/head' diff --git a/app/views/ci/variables/_content.html.haml b/app/views/ci/variables/_content.html.haml index 98f618ca3b8..fbfe3e56588 100644 --- a/app/views/ci/variables/_content.html.haml +++ b/app/views/ci/variables/_content.html.haml @@ -1,9 +1,3 @@ -%h4.prepend-top-0 - Secret variables - = link_to icon('question-circle'), help_page_path('ci/variables/README', anchor: 'secret-variables'), target: '_blank' -%p - These variables will be set to environment by the runner, and could be protected by exposing only to protected branches or tags. -%p - So you can use them for passwords, secret keys or whatever you want. -%p - The value of the variable can be visible in job log if explicitly asked to do so. +%p.append-bottom-default + Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. + You can use variables for passwords, secret keys, or whatever you want. diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index 007c2344b5a..2bac69bc536 100644 --- a/app/views/ci/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -1,7 +1,5 @@ .row.prepend-top-default.append-bottom-default - .col-lg-4 - = render "ci/variables/content" - .col-lg-8 + .col-lg-12 %h5.prepend-top-0 Add a variable = render "ci/variables/form", btn_text: "Add new variable" diff --git a/app/views/ci/variables/_show.html.haml b/app/views/ci/variables/_show.html.haml index 2bfb290629d..6d75ae96124 100644 --- a/app/views/ci/variables/_show.html.haml +++ b/app/views/ci/variables/_show.html.haml @@ -4,6 +4,6 @@ .col-lg-3 = render "ci/variables/content" .col-lg-9 - %h5.prepend-top-0 + %h4.prepend-top-0 Update variable = render "ci/variables/form", btn_text: "Save variable" diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 9ebb3894c55..839f23e69fd 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "General Settings" = render "groups/settings_head" .panel.panel-default.prepend-top-default .panel-heading diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 7a2e688a114..7f3f2f707f7 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Projects" = render "groups/settings_head" .panel.panel-default.prepend-top-default diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml index bf36baf48ab..9f9ae01e7c5 100644 --- a/app/views/groups/settings/ci_cd/show.html.haml +++ b/app/views/groups/settings/ci_cd/show.html.haml @@ -1,4 +1,5 @@ -- page_title "Pipelines" +- breadcrumb_title "CI / CD Settings" +- page_title "CI / CD" = render "groups/settings_head" = render 'ci/variables/index' diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index e07f61c94e4..f4f76887422 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,5 +1,5 @@ - @no_container = true -- breadcrumb_title "Group" +- breadcrumb_title "Details" = content_for :meta_tags do = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") diff --git a/app/views/groups/subgroups.html.haml b/app/views/groups/subgroups.html.haml index 8f0724c0677..7abc84412c6 100644 --- a/app/views/groups/subgroups.html.haml +++ b/app/views/groups/subgroups.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Details" - @no_container = true = render 'head' diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index ff0f6489861..feffd7707dc 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -1,28 +1,22 @@ -- breadcrumb_link = breadcrumb_title_link - container = @no_breadcrumb_container ? 'container-fluid' : container_class - hide_top_links = @hide_top_links || false -%nav.breadcrumbs{ role: "navigation" } +%nav.breadcrumbs{ role: "navigation", class: [container, @content_class] } .breadcrumbs-container{ class: [container, @content_class] } - if defined?(@left_sidebar) = button_tag class: 'toggle-mobile-nav', type: 'button' do %span.sr-only Open sidebar = icon ('bars') .breadcrumbs-links.js-title-container - - unless hide_top_links - .title - = link_to "GitLab", root_path - \/ - - if content_for?(:header_title_before) - = yield :header_title_before - \/ + %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list + - unless hide_top_links = header_title - %h2.breadcrumbs-sub-title - %ul.list-unstyled - - if @breadcrumbs_extra_links - - @breadcrumbs_extra_links.each do |extra| - %li= link_to extra[:text], extra[:link] - %li= link_to @breadcrumb_title, breadcrumb_link + - if @breadcrumbs_extra_links + - @breadcrumbs_extra_links.each do |extra| + = breadcrumb_list_item link_to(extra[:text], extra[:link]) + = render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :after + %li + %h2.breadcrumbs-sub-title= @breadcrumb_title - if content_for?(:breadcrumbs_extra) .breadcrumbs-extra.hidden-xs= yield :breadcrumbs_extra = yield :header_content diff --git a/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml b/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml new file mode 100644 index 00000000000..28022eebb19 --- /dev/null +++ b/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml @@ -0,0 +1,11 @@ +- dropdown_location = local_assigns.fetch(:location, nil) +- button_tooltip = local_assigns.fetch(:title, _("Show parent pages")) +- if defined?(@breadcrumb_dropdown_links) && @breadcrumb_dropdown_links.key?(dropdown_location) + %li.dropdown + %button.text-expander.has-tooltip.js-breadcrumbs-collapsed-expander{ type: "button", data: { toggle: "dropdown", container: "body" }, "aria-label": button_tooltip, title: button_tooltip } + = icon("ellipsis-h") + = icon("angle-right", class: "breadcrumbs-list-angle") + .dropdown-menu + %ul + - @breadcrumb_dropdown_links[dropdown_location].each_with_index do |link, index| + %li{ style: "text-indent: #{[index * 16, 60].min}px;" }= link diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 985bb79508f..c606b5a1e6c 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Edit Password" - page_title "Password" - @content_class = "limit-container-width" unless fluid_layout diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index 2216708d354..06bb72b9f0d 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "Access Tokens" - page_title "Personal Access Tokens" - @content_class = "limit-container-width" unless fluid_layout diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index a8ae0b92334..35ad280b037 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -1,4 +1,4 @@ -- breadcrumb_title "Profile" +- breadcrumb_title "Edit Profile" - @content_class = "limit-container-width" unless fluid_layout = render 'profiles/head' @@ -45,12 +45,15 @@ Some options are unavailable for LDAP accounts .col-lg-8 .row - = f.text_field :name, required: true, wrapper: { class: 'col-md-9' }, - help: 'Enter your name, so people you know can recognize you.' + - if @user.read_only_attribute?(:name) + = f.text_field :name, required: true, readonly: true, wrapper: { class: 'col-md-9' }, + help: "Your name was automatically set based on your #{ attribute_provider_label(:name) } account, so people you know can recognize you." + - else + = f.text_field :name, required: true, wrapper: { class: 'col-md-9' }, help: "Enter your name, so people you know can recognize you." = f.text_field :id, readonly: true, label: 'User ID', wrapper: { class: 'col-md-3' } - - if @user.external_email? - = f.text_field :email, required: true, readonly: true, help: "Your email address was automatically set based on your #{email_provider_label} account." + - if @user.read_only_attribute?(:email) + = f.text_field :email, required: true, readonly: true, help: "Your email address was automatically set based on your #{ attribute_provider_label(:email) } account." - else = f.text_field :email, required: true, value: (@user.email unless @user.temp_oauth_email?), help: user_email_help_text(@user) @@ -64,7 +67,10 @@ = f.text_field :linkedin = f.text_field :twitter = f.text_field :website_url, label: 'Website' - = f.text_field :location + - if @user.read_only_attribute?(:location) + = f.text_field :location, readonly: true, help: "Your location was automatically set based on your #{ attribute_provider_label(:location) } account." + - else + = f.text_field :location = f.text_field :organization = f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.' .prepend-top-default.append-bottom-default diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index 2897c955c78..f80dadb8037 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -1,7 +1,7 @@ - @no_container = true -- add_to_breadcrumbs(_("Project"), project_path(@project)) - page_title _("Activity") + = render "projects/head" = render 'projects/last_push' diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index a33743c2f57..4cc3218d967 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -1,8 +1,12 @@ +- breadcrumb_title _('Artifacts') - page_title @path.presence, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs' = render "projects/pipelines/head" = render "projects/jobs/header", show_controls: false +- add_to_breadcrumbs(_('Jobs'), project_jobs_path(@project)) +- add_to_breadcrumbs("##{@build.id}", project_jobs_path(@project)) + .tree-holder .nav-block %ul.breadcrumb.repo-breadcrumb diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index c7359d873d9..60ac202bde0 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -22,7 +22,7 @@ = author_avatar(commit, size: 36) .commit-row-title %span.item-title.str-truncated-100 - = link_to_gfm commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title + = link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title .pull-right = link_to commit.short_id, project_commit_path(@project, commit), class: "commit-sha" diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index 67d9db9aefe..303e20e8780 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -1,8 +1,8 @@ - @no_breadcrumb_container = true - @no_container = true - @content_class = "issue-boards-content" +- breadcrumb_title "Issue Board" - page_title "Boards" -- add_to_breadcrumbs("Issues", project_issues_path(@project)) - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index 18fbb81c167..7892019bb15 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -4,6 +4,6 @@ = link_to commit.short_id, project_commit_path(project, commit.id), class: "commit-sha" · %span.str-truncated - = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" + = link_to_markdown commit.title, project_commit_path(project, commit.id), class: "commit-row-message" · #{time_ago_with_tooltip(commit.committed_date)} diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index b1834de0bad..73583c6bbc2 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -1,7 +1,5 @@ - @no_container = true - page_title "Branches" -- add_to_breadcrumbs("Repository", project_tree_path(@project)) - = render "projects/commits/head" %div{ class: container_class } diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 07c83c0a590..717de85c5d2 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Commits", project_commits_path(@project) +- breadcrumb_title @commit.short_id - container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : '' - limited_container_width = fluid_layout ? '' : 'limit-container-width' - @content_class = limited_container_width diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 1214aabe837..b8655808d89 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -16,7 +16,7 @@ .commit-detail .commit-content - = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message item-title" + = link_to_markdown_field(commit, :title, project_commit_path(project, commit.id), class: "commit-row-message item-title") %span.commit-row-message.visible-xs-inline · = commit.short_id @@ -28,7 +28,8 @@ - if commit.description? %pre.commit-row-description.js-toggle-content - = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) + = preserve(markdown_field(commit, :description)) + .commiter - commit_author_link = commit_author_link(commit, avatar: false, size: 24) - commit_timeago = time_ago_with_tooltip(commit.committed_date) diff --git a/app/views/projects/commits/_inline_commit.html.haml b/app/views/projects/commits/_inline_commit.html.haml index 48cefbe45f2..26385d2f534 100644 --- a/app/views/projects/commits/_inline_commit.html.haml +++ b/app/views/projects/commits/_inline_commit.html.haml @@ -3,6 +3,6 @@ = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha" %span.str-truncated - = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" + = link_to_markdown_field(commit, :title, project_commit_path(project, commit.id), class: "commit-row-message") .pull-right #{time_ago_with_tooltip(commit.committed_date)} diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 0f048079ef5..e873b931683 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -5,8 +5,6 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") -- add_to_breadcrumbs("Repository", project_tree_path(@project)) - = content_for :sub_nav do = render "head" diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index ecef91855af..2632fea6eba 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,6 +1,6 @@ - @no_container = true +- breadcrumb_title "Compare Revisions" - page_title "Compare" -- add_to_breadcrumbs("Repository", project_tree_path(@project)) = render "projects/commits/head" %div{ class: container_class } diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index eca733a933a..7cc42455394 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -1,7 +1,6 @@ - @no_container = true -- breadcrumb_title "Compare" +- add_to_breadcrumbs "Compare Revisions", project_compare_index_path(@project) - page_title "#{params[:from]}...#{params[:to]}" -- add_to_breadcrumbs("Repository", project_tree_path(@project)) = render "projects/commits/head" %div{ class: container_class } diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 5691024b997..8d008be5aae 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Cycle Analytics" -- add_to_breadcrumbs("Project", project_path(@project)) - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('cycle_analytics') diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml index 4c22166c256..014486be868 100644 --- a/app/views/projects/deployments/_commit.html.haml +++ b/app/views/projects/deployments/_commit.html.haml @@ -12,6 +12,6 @@ %span.flex-truncate-child - if commit_title = deployment.commit_title = author_avatar(deployment.commit, size: 20) - = link_to_gfm commit_title, project_commit_path(@project, deployment.sha), class: "commit-row-message" + = link_to_markdown commit_title, project_commit_path(@project, deployment.sha), class: "commit-row-message" - else Cant find HEAD commit for this branch diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 9e26bdecd31..994119051d2 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -1,3 +1,4 @@ +- breadcrumb_title "General Settings" - page_title "General" - @content_class = "limit-container-width" unless fluid_layout - expanded = Rails.env.test? diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index d17709380d5..5e980314307 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- breadcrumb_title "Details" = render partial: 'flash_messages', locals: { project: @project } diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index 0ce0f5465fc..c35d1b5aaee 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Environments", project_environments_path(@project) +- breadcrumb_title @environment.name - page_title "Environments" = render "projects/pipelines/head" diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index 44bd8d1f9ad..f0ef647ddb3 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Charts" -- add_to_breadcrumbs("Repository", project_tree_path(@project)) - content_for :page_specific_javascripts do = webpack_bundle_tag('common_d3') = webpack_bundle_tag('graphs') diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 2f60078796e..08b38428b50 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -5,8 +5,6 @@ = webpack_bundle_tag('graphs') = webpack_bundle_tag('graphs_show') -- add_to_breadcrumbs("Repository", project_tree_path(@project)) - = render 'projects/commits/head' .js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 04b4ed95a2d..fbaf88356bf 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,4 +1,6 @@ - @content_class = "limit-container-width" unless fluid_layout +- add_to_breadcrumbs "Issues", project_issues_path(@project) +- breadcrumb_title @issue.to_reference - page_title "#{@issue.title} (#{@issue.to_reference})", "Issues" - page_description @issue.description - page_card_attributes @issue.card_attributes diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index 9751de0f938..8604c7d3ea4 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -2,8 +2,6 @@ - page_title "Jobs" = render "projects/pipelines/head" -- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - %div{ class: container_class } .top-area - build_path_proc = ->(scope) { project_jobs_path(@project, scope: scope) } diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index fa086413fbe..975c08c06e6 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Jobs", project_jobs_path(@project) +- breadcrumb_title "##{@build.id}" - page_title "#{@build.name} (##{@build.id})", "Jobs" = render "projects/pipelines/head" diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index d27e121beb4..c2d16f7e731 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -1,4 +1,6 @@ - @content_class = "limit-container-width" unless fluid_layout +- add_to_breadcrumbs "Merge Requests", project_merge_requests_path(@project) +- breadcrumb_title @merge_request.to_reference - page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests" - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 0bf0e11c107..1f5f18801ad 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Milestones", project_milestones_path(@project) +- breadcrumb_title @milestone.title - page_title @milestone.title, "Milestones" - page_description @milestone.description = render "shared/mr_head" diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 2defd45a0b2..e29cb277389 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -2,7 +2,6 @@ - page_title "Graph", @ref - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('network') -- add_to_breadcrumbs("Repository", project_tree_path(@project)) = render "projects/commits/head" = render "head" %div{ class: container_class } diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index fb07141d2ac..de76832331a 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -1,6 +1,8 @@ -- access = note_max_access_for_user(note) -- if access - %span.note-role= access +- if note.has_special_role?(Note::SpecialRole::FIRST_TIME_CONTRIBUTOR) + %span.note-role.note-role-special.has-tooltip{ title: _("This is the author's first Merge Request to this project. Handle with care.") } + = issuable_first_contribution_icon +- if access = note_max_access_for_user(note) + %span.note-role.note-role-access= Gitlab::Access.human_access(access) - if note.resolvable? - can_resolve = can?(current_user, :resolve_note, note) diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml index 7e854186973..88085c7185b 100644 --- a/app/views/projects/notes/_more_actions_dropdown.html.haml +++ b/app/views/projects/notes/_more_actions_dropdown.html.haml @@ -7,7 +7,7 @@ = custom_icon('ellipsis_v') %ul.dropdown-menu.more-actions-dropdown.dropdown-open-left %li - = clipboard_button(text: noteable_note_url(note), title: "Copy reference to clipboard", button_text: 'Copy link', hide_tooltip: true, hide_button_icon: true) + = clipboard_button(text: noteable_note_url(note), title: 'Copy reference to clipboard', button_text: 'Copy link', class: 'btn-clipboard', hide_tooltip: true, hide_button_icon: true) - unless is_current_user %li = link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do diff --git a/app/views/projects/pipeline_schedules/edit.html.haml b/app/views/projects/pipeline_schedules/edit.html.haml index 9b2a7b5821d..d95fa6da903 100644 --- a/app/views/projects/pipeline_schedules/edit.html.haml +++ b/app/views/projects/pipeline_schedules/edit.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs _("Schedules"), pipeline_schedules_path(@project) +- breadcrumb_title "##{@schedule.id}" - page_title _("Edit"), @schedule.description, _("Pipeline Schedule") %h3.page-title diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index 83b51bae73d..d9957b54a4d 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -1,4 +1,4 @@ -- breadcrumb_title "Schedules" +- breadcrumb_title _("Schedules") - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' @@ -11,8 +11,6 @@ - content_for :breadcrumbs_extra do = link_to _('New schedule'), new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' -- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - = render "projects/pipelines/head" %div{ class: container_class } diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml index d710ba92cdd..487ac87186d 100644 --- a/app/views/projects/pipelines/charts.html.haml +++ b/app/views/projects/pipelines/charts.html.haml @@ -1,6 +1,6 @@ - @no_container = true +- breadcrumb_title "CI / CD Charts" - page_title _("Charts"), _("Pipelines") -- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_d3') = page_specific_javascript_bundle_tag('graphs') diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 63f85fc69a2..7cc9fe79afd 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Pipelines", project_pipelines_path(@project) +- breadcrumb_title "##{@pipeline.id}" - page_title "Pipeline" = render "projects/pipelines/head" diff --git a/app/views/projects/pipelines_settings/_badge.html.haml b/app/views/projects/pipelines_settings/_badge.html.haml index 3de518c8b9a..e8028059487 100644 --- a/app/views/projects/pipelines_settings/_badge.html.haml +++ b/app/views/projects/pipelines_settings/_badge.html.haml @@ -1,34 +1,32 @@ %div{ class: badge.title.gsub(' ', '-') } - .col-lg-4.profile-settings-sidebar - %h4.prepend-top-0 + .col-lg-12 + %h4 = badge.title.capitalize - .col-lg-8 - .prepend-top-10 - .panel.panel-default - .panel-heading - %b - = badge.title.capitalize - · - = badge.to_html - .pull-right - = render 'shared/ref_switcher', destination: 'badges', align_right: true - .panel-body - .row - .col-md-2.text-center - Markdown - .col-md-10.code.js-syntax-highlight - = highlight('.md', badge.to_markdown) - .row - %hr - .row - .col-md-2.text-center - HTML - .col-md-10.code.js-syntax-highlight - = highlight('.html', badge.to_html) - .row - %hr - .row - .col-md-2.text-center - AsciiDoc - .col-md-10.code.js-syntax-highlight - = highlight('.adoc', badge.to_asciidoc) + .panel.panel-default + .panel-heading + %b + = badge.title.capitalize + · + = badge.to_html + .pull-right + = render 'shared/ref_switcher', destination: 'badges', align_right: true + .panel-body + .row + .col-md-2.text-center + Markdown + .col-md-10.code.js-syntax-highlight + = highlight('.md', badge.to_markdown) + .row + %hr + .row + .col-md-2.text-center + HTML + .col-md-10.code.js-syntax-highlight + = highlight('.html', badge.to_html) + .row + %hr + .row + .col-md-2.text-center + AsciiDoc + .col-md-10.code.js-syntax-highlight + = highlight('.adoc', badge.to_asciidoc) diff --git a/app/views/projects/pipelines_settings/_show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml index c0a368dc475..324cd423ede 100644 --- a/app/views/projects/pipelines_settings/_show.html.haml +++ b/app/views/projects/pipelines_settings/_show.html.haml @@ -1,8 +1,5 @@ .row.prepend-top-default - .col-lg-4.profile-settings-sidebar - %h4.prepend-top-0 - Pipelines - .col-lg-8 + .col-lg-12 = form_for @project, url: project_pipelines_settings_path(@project) do |f| %fieldset.builds-feature .form-group diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 5c3339cc749..25153fd0b6f 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,7 +1,5 @@ - page_title "Members" -- add_to_breadcrumbs("Settings", edit_project_path(@project)) - .row.prepend-top-default .col-lg-12 %h4 diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 0a5a38a3694..c786298e341 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Tags", project_tags_path(@project) +- breadcrumb_title @tag.name - page_title "Edit", @tag.name, "Tags" = render "projects/commits/head" diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 2a829cd6c3d..eaf374bcb83 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -1,10 +1,54 @@ - @content_class = "limit-container-width" unless fluid_layout -- page_title "Pipelines" -- add_to_breadcrumbs("Settings", edit_project_path(@project)) +- page_title "CI / CD Settings" +- page_title "CI / CD" = render "projects/settings/head" -= render 'projects/runners/index' -= render 'ci/variables/index' -= render 'projects/triggers/index' -= render 'projects/pipelines_settings/show' +- expanded = Rails.env.test? + +%section.settings + .settings-header + %h4 + General pipelines settings + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + Update your CI/CD configuration, like job timeout. + .settings-content.no-animate{ class: ('expanded' if expanded) } + = render 'projects/pipelines_settings/show' + +%section.settings + .settings-header + %h4 + Runners settings + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + Register and see your runners for this project. + .settings-content.no-animate{ class: ('expanded' if expanded) } + = render 'projects/runners/index' + +%section.settings + .settings-header + %h4 + Secret variables + = link_to icon('question-circle'), help_page_path('ci/variables/README', anchor: 'secret-variables'), target: '_blank' + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + = render "ci/variables/content" + .settings-content.no-animate{ class: ('expanded' if expanded) } + = render 'ci/variables/index' + +%section.settings + .settings-header + %h4 + Pipeline triggers + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will + impersonate their associated user including their access to projects and their project + permissions. + .settings-content.no-animate{ class: ('expanded' if expanded) } + = render 'projects/triggers/index' diff --git a/app/views/projects/settings/integrations/show.html.haml b/app/views/projects/settings/integrations/show.html.haml index 1efa5907b3a..933daa7f549 100644 --- a/app/views/projects/settings/integrations/show.html.haml +++ b/app/views/projects/settings/integrations/show.html.haml @@ -1,6 +1,6 @@ - @content_class = "limit-container-width" unless fluid_layout +- breadcrumb_title "Integrations Settings" - page_title 'Integrations' -- add_to_breadcrumbs("Settings", edit_project_path(@project)) = render "projects/settings/head" = render 'projects/hooks/index' = render 'projects/services/index' diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index d4d461f6f22..6d4af72b8ea 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -1,6 +1,6 @@ +- breadcrumb_title "Repository Settings" - page_title "Repository" - @content_class = "limit-container-width" unless fluid_layout -- add_to_breadcrumbs("Settings", edit_project_path(@project)) = render "projects/settings/head" diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index a9b39cedb1d..3f0a24cfe83 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,5 +1,5 @@ - @no_container = true -- breadcrumb_title "Project" +- breadcrumb_title "Details" - @content_class = "limit-container-width" unless fluid_layout = content_for :meta_tags do diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index d41cc8e0425..32844f5204a 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Snippets", project_snippets_path(@project) +- breadcrumb_title @snippet.to_reference - page_title "Edit", "#{@snippet.title} (#{@snippet.to_reference})", "Snippets" %h3.page-title diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index d3e6b456f48..1359a815429 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Snippets", project_snippets_path(@project) +- breadcrumb_title "New" - page_title "New Snippets" %h3.page-title diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index d8e448dd2af..fda068f08c2 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -1,4 +1,6 @@ - @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout +- add_to_breadcrumbs "Snippets", dashboard_snippets_path +- breadcrumb_title @snippet.to_reference - page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets" = render 'shared/snippets/header' diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index d02cd70f4c3..5d6eb4f4026 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Tags", project_tags_path(@project) +- breadcrumb_title @tag.name - page_title @tag.name, "Tags" = render "projects/commits/head" diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml index f3d4706809f..abb3e918e87 100644 --- a/app/views/projects/tree/_tree_commit_column.html.haml +++ b/app/views/projects/tree/_tree_commit_column.html.haml @@ -1,2 +1,2 @@ %span.str-truncated - = link_to_gfm commit.full_title, project_commit_path(@project, commit.id), class: "tree-commit-link" + = link_to_markdown commit.full_title, project_commit_path(@project, commit.id), class: "tree-commit-link" diff --git a/app/views/projects/triggers/_content.html.haml b/app/views/projects/triggers/_content.html.haml index ea32eac2ae2..6c2d603d95d 100644 --- a/app/views/projects/triggers/_content.html.haml +++ b/app/views/projects/triggers/_content.html.haml @@ -1,14 +1,8 @@ -%h4.prepend-top-0 - Triggers -%p.prepend-top-20 - Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will - impersonate their associated user including their access to projects and their project - permissions. -%p.prepend-top-20 +%p.append-bottom-default Triggers with the %span.label.label-primary legacy label do not have an associated user and only have access to the current project. -%p.append-bottom-0 + %br = succeed '.' do Learn more in the = link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank' diff --git a/app/views/projects/triggers/_index.html.haml b/app/views/projects/triggers/_index.html.haml index e9a2f803edd..0f655e4ed83 100644 --- a/app/views/projects/triggers/_index.html.haml +++ b/app/views/projects/triggers/_index.html.haml @@ -1,7 +1,6 @@ .row.prepend-top-default.append-bottom-default.triggers-container - .col-lg-4 + .col-lg-12 = render "projects/triggers/content" - .col-lg-8 .panel.panel-default .panel-heading %h4.panel-title diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index dece1fad0bb..d533c611a38 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -1,4 +1,6 @@ - @no_container = true +- add_to_breadcrumbs "Wiki", get_project_wiki_path(@project) +- breadcrumb_title "Pages" - page_title "Pages", "Wiki" %div{ class: container_class } diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 9dadd685ea2..b066a812ec8 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -1,14 +1,13 @@ - @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout -- breadcrumb_title "Wiki" +- breadcrumb_title @page.title.capitalize +- wiki_breadcrumb_dropdown_links(@page.slug) - page_title @page.title.capitalize, "Wiki" +- add_to_breadcrumbs "Wiki", get_project_wiki_path(@project) .wiki-page-header.has-sidebar-toggle %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } = icon('angle-double-left') - .wiki-breadcrumb - %span= breadcrumb(@page.slug) - .nav-text %h2.wiki-page-title= @page.title.capitalize %span.wiki-last-edit-by diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index 7174855e176..4f00a9f2759 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -28,7 +28,7 @@ commented - if note.system %span.system-note-message - = note.redacted_note_html + = markdown_field(note, :note) %a{ href: "##{dom_id(note)}" } = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago') - unless note.system? @@ -39,7 +39,7 @@ = render 'projects/notes/actions', note: note, note_editable: note_editable .note-body{ class: note_editable ? 'js-task-list-container' : '' } .note-text.md - = note.redacted_note_html + = markdown_field(note, :note) = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago') .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } } #{note.note} diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index f4f155c8d94..52a8fe8bb67 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -31,8 +31,7 @@ - if show_last_commit_as_description .description.prepend-top-5 - = link_to_gfm project.commit.title, project_commit_path(project, project.commit), - class: "commit-row-message" + = link_to_markdown(project.commit.title, project_commit_path(project, project.commit), class: "commit-row-message") - elsif project.description.present? .description.prepend-top-5 = markdown_field(project, :description) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 17b34c5eeb3..119d189f21d 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -3,10 +3,8 @@ %span.sr-only = visibility_level_label(@snippet.visibility_level) = visibility_level_icon(@snippet.visibility_level, fw: false) - %strong.item-title - Snippet #{@snippet.to_reference} %span.creator - authored + Authored = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "hidden-xs")} diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 706f13dd004..578327883e5 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,5 +1,7 @@ - @hide_top_links = true - @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout +- add_to_breadcrumbs "Snippets", dashboard_snippets_path +- breadcrumb_title @snippet.to_reference - page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets" = render 'shared/snippets/header' |