Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-09-16 15:09:32 +00:00
parent 591b0e86e3
commit 6de7d2c195
62 changed files with 737 additions and 246 deletions

View File

@ -10,7 +10,7 @@ notify-update-gitaly:
extends:
- .notify-slack
rules:
- if: '$CI_MERGE_REQUEST_IID && $CI_COMMIT_BRANCH == $GITALY_UPDATE_BRANCH'
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == $GITALY_UPDATE_BRANCH'
when: on_failure
allow_failure: true
variables:

View File

@ -128,6 +128,7 @@
- "{,ee/}spec/**/*.rb"
- ".gitlab-ci.yml"
- ".gitlab/ci/**/*"
- "*_VERSION"
.db-patterns: &db-patterns
- "{,ee/}{,spec/}{db,migrations}/**/*"

View File

@ -27,7 +27,7 @@ After your merge request has been approved according to our [approval guidelines
* At this point, it might be easy to squash the commits from the MR into one
* You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [secpick documentation]
- [ ] Create each MR targeting the stable branch `X-Y-stable`, using the [Security Release merge request template].
* Every merge request will have its own set of TODOs, so make sure to complete those.
* Every merge request will have its own set of to-dos, so make sure to complete those.
- [ ] On the "Related merge requests" section, ensure that `4` merge requests are associated: The one targeting `master` and the `3` backports.
- [ ] If this issue requires less than `4` merge requests, post a message on the Security Release Tracking Issue and ping the Release Managers.

View File

@ -1,10 +1,14 @@
<script>
/* eslint-disable vue/no-v-html */
import { GlButton } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
export default {
components: {
GlButton,
},
mixins: [modalMixin],
props: {
newIssuePath: {
@ -54,17 +58,22 @@ export default {
<div class="text-content">
<h4>{{ contents.title }}</h4>
<p v-html="contents.content"></p>
<a v-if="activeTab === 'all'" :href="newIssuePath" class="btn btn-success btn-inverted">{{
__('New issue')
}}</a>
<button
<gl-button
v-if="activeTab === 'all'"
:href="newIssuePath"
category="secondary"
variant="success"
>
{{ __('New issue') }}
</gl-button>
<gl-button
v-if="activeTab === 'selected'"
class="btn btn-default"
type="button"
category="primary"
variant="default"
@click="changeTab('all')"
>
{{ __('Open issues') }}
</button>
</gl-button>
</div>
</div>
</div>

View File

@ -2,21 +2,50 @@
import { GlBanner } from '@gitlab/ui';
import { s__ } from '~/locale';
import { parseBoolean, setCookie, getCookie } from '~/lib/utils/common_utils';
import Tracking from '~/tracking';
const trackingMixin = Tracking.mixin();
export default {
components: {
GlBanner,
},
inject: ['svgPath', 'inviteMembersPath', 'isDismissedKey'],
mixins: [trackingMixin],
inject: ['svgPath', 'inviteMembersPath', 'isDismissedKey', 'trackLabel'],
data() {
return {
isDismissed: parseBoolean(getCookie(this.isDismissedKey)),
tracking: {
label: this.trackLabel,
},
};
},
created() {
this.$nextTick(() => {
this.addTrackingAttributesToButton();
});
},
mounted() {
this.trackOnShow();
},
methods: {
handleClose() {
setCookie(this.isDismissedKey, true);
this.isDismissed = true;
this.track(this.$options.dismissEvent);
},
trackOnShow() {
if (!this.isDismissed) this.track(this.$options.displayEvent);
},
addTrackingAttributesToButton() {
if (this.$refs.banner === undefined) return;
const button = this.$refs.banner.$el.querySelector(`[href='${this.inviteMembersPath}']`);
if (button) {
button.setAttribute('data-track-event', this.$options.buttonClickEvent);
button.setAttribute('data-track-label', this.trackLabel);
}
},
},
i18n: {
@ -26,6 +55,9 @@ export default {
),
button_text: s__('InviteMembersBanner|Invite your colleagues'),
},
displayEvent: 'invite_members_banner_displayed',
buttonClickEvent: 'invite_members_banner_button_clicked',
dismissEvent: 'invite_members_banner_dismissed',
};
</script>

View File

@ -8,7 +8,7 @@ export default function initInviteMembersBanner() {
return false;
}
const { svgPath, inviteMembersPath, isDismissedKey } = el.dataset;
const { svgPath, inviteMembersPath, isDismissedKey, trackLabel } = el.dataset;
return new Vue({
el,
@ -16,6 +16,7 @@ export default function initInviteMembersBanner() {
svgPath,
inviteMembersPath,
isDismissedKey,
trackLabel,
},
render: createElement => createElement(InviteMembersBanner),
});

View File

@ -1,12 +1,13 @@
<script>
import { mapGetters } from 'vuex';
import { GlLoadingIcon, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import { __, sprintf } from '~/locale';
import resolvedStatusMixin from '~/batch_comments/mixins/resolved_status';
import ReplyButton from './note_actions/reply_button.vue';
import eventHub from '~/sidebar/event_hub';
import Api from '~/api';
import { deprecatedCreateFlash as flash } from '~/flash';
import { splitCamelCase } from '../../lib/utils/text_utility';
export default {
name: 'NoteActions',
@ -47,6 +48,26 @@ export default {
required: false,
default: null,
},
isAuthor: {
type: Boolean,
required: false,
default: false,
},
isContributor: {
type: Boolean,
required: false,
default: false,
},
noteableType: {
type: String,
required: false,
default: '',
},
projectName: {
type: String,
required: false,
default: '',
},
showReply: {
type: Boolean,
required: true,
@ -121,6 +142,9 @@ export default {
targetType() {
return this.getNoteableData.targetType;
},
noteableDisplayName() {
return splitCamelCase(this.noteableType).toLowerCase();
},
assignees() {
return this.getNoteableData.assignees || [];
},
@ -130,6 +154,22 @@ export default {
canAssign() {
return this.getNoteableData.current_user?.can_update && this.isIssue;
},
displayAuthorBadgeText() {
return sprintf(__('This user is the author of this %{noteable}.'), {
noteable: this.noteableDisplayName,
});
},
displayMemberBadgeText() {
return sprintf(__('This user is a %{access} of the %{name} project.'), {
access: this.accessLevel.toLowerCase(),
name: this.projectName,
});
},
displayContributorBadgeText() {
return sprintf(__('This user has previously committed to the %{name} project.'), {
name: this.projectName,
});
},
},
methods: {
onEdit() {
@ -175,7 +215,24 @@ export default {
<template>
<div class="note-actions">
<span v-if="accessLevel" class="note-role user-access-role">{{ accessLevel }}</span>
<span
v-if="isAuthor"
class="note-role user-access-role has-tooltip d-none d-md-inline-block"
:title="displayAuthorBadgeText"
>{{ __('Author') }}</span
>
<span
v-if="accessLevel"
class="note-role user-access-role has-tooltip"
:title="displayMemberBadgeText"
>{{ accessLevel }}</span
>
<span
v-else-if="isContributor"
class="note-role user-access-role has-tooltip"
:title="displayContributorBadgeText"
>{{ __('Contributor') }}</span
>
<div v-if="canResolve" class="note-actions-item">
<button
ref="resolveButton"

View File

@ -389,6 +389,10 @@ export default {
:note-id="note.id"
:note-url="note.noteable_note_url"
:access-level="note.human_access"
:is-contributor="note.is_contributor"
:is-author="note.is_noteable_author"
:project-name="note.project_name"
:noteable-type="note.noteable_type"
:show-reply="showReplyButton"
:can-edit="note.current_user.can_edit"
:can-award-emoji="note.current_user.can_award_emoji"

View File

@ -143,7 +143,7 @@ export default {
:button-title="
sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), { modifierKey })
"
:shortcuts="['command+b', 'ctrl+b']"
shortcuts="mod+b"
icon="bold"
/>
<toolbar-button
@ -151,7 +151,7 @@ export default {
:button-title="
sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), { modifierKey })
"
:shortcuts="['command+i', 'ctrl+i']"
shortcuts="mod+i"
icon="italic"
/>
<toolbar-button
@ -207,7 +207,7 @@ export default {
:button-title="
sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), { modifierKey })
"
:shortcuts="['command+k', 'ctrl+k']"
shortcuts="mod+k"
icon="link"
/>
</div>

View File

@ -5,7 +5,6 @@ module RendersNotes
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)
preload_author_status(notes)
Notes::RenderService.new(current_user).execute(notes)
@ -19,7 +18,8 @@ module RendersNotes
return unless project
user_ids = notes.map(&:author_id)
project.team.max_member_access_for_user_ids(user_ids)
access = project.team.max_member_access_for_user_ids(user_ids).select { |k, v| v == Gitlab::Access::NO_ACCESS }.keys
project.team.contribution_check_for_user_ids(access)
end
# rubocop: disable CodeReuse/ActiveRecord
@ -28,12 +28,6 @@ module RendersNotes
end
# rubocop: enable CodeReuse/ActiveRecord
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
# rubocop: disable CodeReuse/ActiveRecord
def preload_author_status(notes)
ActiveRecord::Associations::Preloader.new.preload(notes, { author: :status })

View File

@ -205,6 +205,12 @@ module IssuablesHelper
author_output
end
if access = project.team.human_max_access(issuable.author_id)
output << content_tag(:span, access, class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3 ", title: _("This user is a %{access} of the %{name} project.") % { access: access.downcase, name: project.name })
elsif project.team.contributor?(issuable.author_id)
output << content_tag(:span, _("Contributor"), class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3", title: _("This user has previously committed to the %{name} project.") % { name: project.name })
end
output << content_tag(:span, (sprite_icon('first-contribution', css_class: 'gl-icon gl-vertical-align-middle') if issuable.first_contribution?), class: 'has-tooltip gl-ml-2', title: _('1st contribution!'))
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block gl-ml-3")

View File

@ -85,6 +85,10 @@ module NotesHelper
note.project.team.max_member_access(note.author_id)
end
def note_human_max_access(note)
note.project.team.human_max_access(note.author_id)
end
def discussion_path(discussion)
if discussion.for_merge_request?
return unless discussion.diff_discussion?

View File

@ -410,10 +410,17 @@ class Group < Namespace
.where(namespaces: { id: self_and_descendants.select(:id) })
end
def max_member_access_for_user(user)
# Return the highest access level for a user
#
# A special case is handled here when the user is a GitLab admin
# which implies it has "OWNER" access everywhere, but should not
# officially appear as a member of a group unless specifically added to it
#
# @param user [User]
# @param only_concrete_membership [Bool] whether require admin concrete membership status
def max_member_access_for_user(user, only_concrete_membership: false)
return GroupMember::NO_ACCESS unless user
return GroupMember::OWNER if user.admin?
return GroupMember::OWNER if user.admin? && !only_concrete_membership
max_member_access = members_with_parents.where(user_id: user)
.reorder(access_level: :desc)

View File

@ -1603,7 +1603,7 @@ class MergeRequest < ApplicationRecord
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?
!project.merge_requests.merged.exists?(author_id: author_id)
end
# TODO: remove once production database rename completes

View File

@ -20,20 +20,6 @@ class Note < ApplicationRecord
include ThrottledTouch
include FromUnion
module SpecialRole
FIRST_TIME_CONTRIBUTOR = :first_time_contributor
class << self
def values
constants.map {|const| self.const_get(const, false)}
end
def value?(val)
values.include?(val)
end
end
end
cache_markdown_field :note, pipeline: :note, issuable_state_filter_enabled: true
redact_field :note
@ -60,9 +46,6 @@ class Note < ApplicationRecord
# 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_reader :special_role
default_value_for :system, false
attr_mentionable :note, pipeline: :note
@ -220,10 +203,6 @@ class Note < ApplicationRecord
.where(noteable_type: type, noteable_id: ids)
end
def has_special_role?(role, note)
note.special_role == role
end
def search(query)
fuzzy_search(query, [:note])
end
@ -342,20 +321,20 @@ class Note < ApplicationRecord
noteable.author_id == user.id
end
def special_role=(role)
raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.value?(role)
def contributor?
return false unless ::Feature.enabled?(:show_contributor_on_note, project)
@special_role = role
project&.team&.contributor?(self.author_id)
end
def has_special_role?(role)
self.class.has_special_role?(role, self)
def noteable_author?(noteable)
return false unless ::Feature.enabled?(:show_author_on_note, project)
noteable.author == self.author
end
def specialize_for_first_contribution!(noteable)
return unless noteable.author_id == self.author_id
self.special_role = Note::SpecialRole::FIRST_TIME_CONTRIBUTOR
def project_name
project&.name
end
def confidential?(include_noteable: false)

View File

@ -178,6 +178,40 @@ class ProjectTeam
max_member_access_for_user_ids([user_id])[user_id]
end
def contribution_check_for_user_ids(user_ids)
user_ids = user_ids.uniq
key = "contribution_check_for_users:#{project.id}"
Gitlab::SafeRequestStore[key] ||= {}
contributors = Gitlab::SafeRequestStore[key] || {}
user_ids -= contributors.keys
return contributors if user_ids.empty?
resource_contributors = project.merge_requests
.merged
.where(author_id: user_ids, target_branch: project.default_branch.to_s)
.pluck(:author_id)
.product([true]).to_h
contributors.merge!(resource_contributors)
missing_resource_ids = user_ids - resource_contributors.keys
missing_resource_ids.each do |resource_id|
contributors[resource_id] = false
end
contributors
end
def contributor?(user_id)
return false if max_member_access(user_id) >= Gitlab::Access::GUEST
contribution_check_for_user_ids([user_id])[user_id]
end
private
def fetch_members(level = nil)

View File

@ -46,6 +46,10 @@ class NoteEntity < API::Entities::Note
SystemNoteHelper.system_note_icon_name(note)
end
expose :is_noteable_author do |note|
note.noteable_author?(request.noteable)
end
expose :discussion_id do |note|
note.discussion_id(request.noteable)
end

View File

@ -5,6 +5,14 @@ class ProjectNoteEntity < NoteEntity
note.project.team.human_max_access(note.author_id)
end
expose :is_contributor, if: -> (note, _) { note.project.present? } do |note|
note.contributor?
end
expose :project_name, if: -> (note, _) { note.project.present? } do |note|
note.project.name
end
expose :toggle_award_path, if: -> (note, _) { note.emoji_awardable? } do |note|
toggle_award_emoji_project_note_path(note.project, note.id)
end

View File

@ -114,8 +114,13 @@ module Projects
# completes), and any other affected users in the background
def setup_authorizations
if @project.group
current_user.project_authorizations.create!(project: @project,
access_level: @project.group.max_member_access_for_user(current_user))
group_access_level = @project.group.max_member_access_for_user(current_user,
only_concrete_membership: true)
if group_access_level > GroupMember::NO_ACCESS
current_user.project_authorizations.create!(project: @project,
access_level: group_access_level)
end
if Feature.enabled?(:specialized_project_authorization_workers)
AuthorizedProjectUpdate::ProjectCreateWorker.perform_async(@project.id)

View File

@ -7,6 +7,7 @@
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
.js-group-invite-members-banner{ data: { svg_path: image_path('illustrations/merge_requests.svg'),
is_dismissed_key: "invite_#{@group.id}_#{current_user.id}",
track_label: 'invite_members_banner',
invite_members_path: group_group_members_path(@group) } }
= content_for :meta_tags do

View File

@ -1,9 +1,10 @@
- access = note_max_access_for_user(note)
- 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.") }
= sprite_icon('first-contribution', css_class: 'gl-icon gl-vertical-align-top')
- if access.nonzero?
%span.note-role.user-access-role= Gitlab::Access.human_access(access)
- access = note_human_max_access(note)
- if note.noteable_author?(@noteable)
%span{ class: 'note-role user-access-role has-tooltip d-none d-md-inline-block', title: _("This user is the author of this %{noteable}.") % { noteable: @noteable.human_class_name } }= _("Author")
- if access
%span{ class: 'note-role user-access-role has-tooltip', title: _("This user is a %{access} of the %{name} project.") % { access: access.downcase, name: note.project_name } }= access
- elsif note.contributor?
%span{ class: 'note-role user-access-role has-tooltip', title: _("This user has previously committed to the %{name} project.") % { name: note.project_name } }= _("Contributor")
- if note.resolvable?
- can_resolve = can?(current_user, :resolve_note, note)

View File

@ -2,18 +2,18 @@
.md-header-toolbar.active
= markdown_toolbar_button({ icon: "bold",
data: { "md-tag" => "**", "md-shortcuts": '["command+b","ctrl+b"]' },
data: { "md-tag" => "**", "md-shortcuts": '["mod+b"]' },
title: sprintf(s_("MarkdownEditor|Add bold text (%{modifier_key}B)") % { modifier_key: modifier_key }) })
= markdown_toolbar_button({ icon: "italic",
data: { "md-tag" => "_", "md-shortcuts": '["command+i","ctrl+i"]' },
data: { "md-tag" => "_", "md-shortcuts": '["mod+i"]' },
title: sprintf(s_("MarkdownEditor|Add italic text (%{modifier_key}I)") % { modifier_key: modifier_key }) })
= markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: _("Insert a quote") })
= markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: _("Insert code") })
= markdown_toolbar_button({ icon: "link",
data: { "md-tag" => "[{text}](url)", "md-select" => "url", "md-shortcuts": '["command+k","ctrl+k"]' },
data: { "md-tag" => "[{text}](url)", "md-select" => "url", "md-shortcuts": '["mod+k"]' },
title: sprintf(s_("MarkdownEditor|Add a link (%{modifier_key}K)") % { modifier_key: modifier_key }) })
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "- ", "md-prepend" => true }, title: _("Add a bullet list") })

View File

@ -0,0 +1,5 @@
---
title: Do not add admins as owners to project authorizations during project creation
merge_request: 42335
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Display Contributor and Author badges on notes
merge_request: 40198
author: Mycroft Kang @TaehyeokKang
type: added

View File

@ -0,0 +1,5 @@
---
title: Stop applying Ctrl keyboard shortcuts inside Markdown editors on Mac
merge_request: 42239
author:
type: fixed

View File

@ -0,0 +1,7 @@
---
name: show_author_on_note
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40198
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/250282
group: group::project management
type: development
default_enabled: false

View File

@ -0,0 +1,7 @@
---
name: show_contributor_on_note
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40198
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249179
group: group::project management
type: development
default_enabled: false

View File

@ -126,7 +126,7 @@ The following documentation relates to the DevOps **Plan** stage:
| [Roadmap](user/group/roadmap/index.md) **(ULTIMATE)** | Visualize epic timelines. |
| [Service Desk](user/project/service_desk.md) | A simple way to allow people to create issues in your GitLab instance without needing their own user account. |
| [Time Tracking](user/project/time_tracking.md) | Track time spent on issues and merge requests. |
| [Todos](user/todos.md) | Keep track of work requiring attention with a chronological list displayed on a simple dashboard. |
| [To-Do List](user/todos.md) | Keep track of work requiring attention with a chronological list displayed on a simple dashboard. |
<div align="right">
<a type="button" class="btn btn-default" href="#overview">
@ -159,7 +159,7 @@ The following documentation relates to the DevOps **Create** stage:
| [Issue Analytics](user/group/issues_analytics/index.md) **(PREMIUM)** | Check how many issues were created per month. |
| [Merge Request Analytics](user/analytics/merge_request_analytics.md) **(PREMIUM)** | Check your throughput productivity - how many merge requests were merged per month. |
| [Projects](user/project/index.md), including [project access](public_access/public_access.md)<br/>and [settings](user/project/settings/index.md) | Host source code, and control your project's visibility and set configuration. |
| [Search through GitLab](user/search/index.md) | Search for issues, merge requests, projects, groups, and todos. |
| [Search through GitLab](user/search/index.md) | Search for issues, merge requests, projects, groups, and to-dos. |
| [Snippets](user/snippets.md) | Snippets allow you to create little bits of code. |
| [Web IDE](user/project/web_ide/index.md) | Edit files within GitLab's user interface. |
| [Static Site Editor](user/project/static_site_editor/index.md) | Edit content on static websites. |

View File

@ -151,7 +151,7 @@ The following API resources are available outside of project and group contexts
| [Sidekiq metrics](sidekiq_metrics.md) | `/sidekiq` |
| [Suggestions](suggestions.md) | `/suggestions` |
| [System hooks](system_hooks.md) | `/hooks` |
| [Todos](todos.md) | `/todos` |
| [To-dos](todos.md) | `/todos` |
| [Users](users.md) | `/users` |
| [Validate `.gitlab-ci.yml` file](lint.md) | `/lint` |
| [Version](version.md) | `/version` |

View File

@ -422,10 +422,10 @@ DELETE /groups/:id/epics/:epic_iid
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/epics/5"
```
## Create a todo
## Create a to-do
Manually creates a todo for the current user on an epic. If
there already exists a todo for the user on that epic, status code `304` is
Manually creates a to-do for the current user on an epic. If
there already exists a to-do for the user on that epic, status code `304` is
returned.
```plaintext

View File

@ -1496,10 +1496,10 @@ Example response:
}
```
## Create a todo
## Create a to-do
Manually creates a todo for the current user on an issue. If
there already exists a todo for the user on that issue, status code `304` is
Manually creates a to-do for the current user on an issue. If
there already exists a to-do for the user on that issue, status code `304` is
returned.
```plaintext

View File

@ -2085,10 +2085,10 @@ the `approvals_before_merge` parameter:
}
```
## Create a todo
## Create a to-do
Manually creates a todo for the current user on a merge request.
If there already exists a todo for the user on that merge request,
Manually creates a to-do for the current user on a merge request.
If there already exists a to-do for the user on that merge request,
status code `304` is returned.
```plaintext

View File

@ -4,13 +4,13 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Todos API
# To-dos API
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/3188) in GitLab 8.10.
## Get a list of todos
## Get a list of to-dos
Returns a list of todos. When no filter is applied, it returns all pending todos
Returns a list of to-dos. When no filter is applied, it returns all pending to-dos
for the current user. Different filters allow the user to precise the request.
```plaintext
@ -25,8 +25,8 @@ Parameters:
| `author_id` | integer | no | The ID of an author |
| `project_id` | integer | no | The ID of a project |
| `group_id` | integer | no | The ID of a group |
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
| `type` | string | no | The type of a todo. Can be either `Issue`, `MergeRequest`, `DesignManagement::Design` or `AlertManagement::Alert` |
| `state` | string | no | The state of the to-do. Can be either `pending` or `done` |
| `type` | string | no | The type of a to-do. Can be either `Issue`, `MergeRequest`, `DesignManagement::Design` or `AlertManagement::Alert` |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/todos"
@ -187,10 +187,10 @@ Example Response:
]
```
## Mark a todo as done
## Mark a to-do as done
Marks a single pending todo given by its ID for the current user as done. The
todo marked as done is returned in the response.
Marks a single pending to-do given by its ID for the current user as done. The
to-do marked as done is returned in the response.
```plaintext
POST /todos/:id/mark_as_done
@ -200,7 +200,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a todo |
| `id` | integer | yes | The ID of a to-do |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/todos/130/mark_as_done"
@ -285,9 +285,9 @@ Example Response:
}
```
## Mark all todos as done
## Mark all to-dos as done
Marks all pending todos for the current user as done. It returns the HTTP status code `204` with an empty response.
Marks all pending to-dos for the current user as done. It returns the HTTP status code `204` with an empty response.
```plaintext
POST /todos/mark_as_done

View File

@ -74,8 +74,8 @@ Below are the changes made between V3 and V4.
- `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline`
- Require description when creating a new trigger `POST /projects/:id/triggers`
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9675)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9530)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, to-dos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, to-dos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9530)
- Change initial page from `0` to `1` on `GET /projects/:id/repository/commits` (like on the rest of the API) [!9679](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9679)
- Return correct `Link` header data for `GET /projects/:id/repository/commits` [!9679](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9679)
- Update endpoints for repository files [!9637](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9637)

View File

@ -547,6 +547,7 @@ The order of precedence for variables is (from highest to lowest):
and [manual pipeline run variables](#override-a-variable-by-manually-running-a-pipeline).
1. Project-level [variables](#custom-environment-variables) or [protected variables](#protect-a-custom-variable).
1. Group-level [variables](#group-level-environment-variables) or [protected variables](#protect-a-custom-variable).
1. Instance-level [variables](#instance-level-cicd-environment-variables) or [protected variables](#protect-a-custom-variable).
1. [Inherited environment variables](#inherit-environment-variables).
1. YAML-defined [job-level variables](../yaml/README.md#variables).
1. YAML-defined [global variables](../yaml/README.md#variables).

View File

@ -175,7 +175,7 @@ guide on how you can add a new custom validator.
validates the parameter value for different cases. Mainly, it checks whether a
path is relative and does it contain `../../` relative traversal using
`File::Separator` or not, and whether the path is absolute, for example
`/etc/passwd/`. By default, absolute paths are not allowed. However, you can optionally pass in an allowlist for allowed absolute paths in the following way:
`/etc/passwd/`. By default, absolute paths are not allowed. However, you can optionally pass in an allowlist for allowed absolute paths in the following way:
`requires :file_path, type: String, file_path: { allowlist: ['/foo/bar/', '/home/foo/', '/app/home'] }`
- `Git SHA`:
@ -249,7 +249,7 @@ most basic entity, with successive entities building upon that scope.
The `with_api_entity_associations` scope will also [automatically preload
data](https://gitlab.com/gitlab-org/gitlab/blob/19f74903240e209736c7668132e6a5a735954e7c/app%2Fmodels%2Ftodo.rb#L34)
for `Todo` _targets_ when returned in the Todos API.
for `Todo` _targets_ when returned in the [to-dos API](../api/todos.md).
For more context and discussion about preloading see
[this merge request](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25711)

View File

@ -119,7 +119,7 @@ use:
# Feature Name
> - [Introduced](link-to-issue) in GitLab 12.0.
> - It was deployed [deployed behind a feature flag](<replace with path to>/user/feature_flags.md), disabled by default.
> - It was [deployed behind a feature flag](<replace with path to>/user/feature_flags.md), disabled by default.
> - [Became enabled by default](link-to-issue) on GitLab 12.1.
> - It's enabled on GitLab.com.
> - It's recommended for production use.

View File

@ -369,6 +369,7 @@ create an issue or an MR to propose a change to the user interface text.
- milestones
- reorder issues
- runner, runners, shared runners
- a to-do, to-dos
- *Some features are capitalized*, typically nouns naming GitLab-specific
capabilities or tools. For example:
- GitLab CI/CD

View File

@ -448,7 +448,9 @@ so we need to set some guidelines for their use going forward:
- `let!` variables should be used only in case if strict evaluation with defined
order is required, otherwise `let` will suffice. Remember that `let` is lazy and won't
be evaluated until it is referenced.
- Use named `subject(:name)` over un-named `subject` in examples, as this gives the subject a contextual name.
- Avoid referencing `subject` in examples. Use a named subject `subject(:name)`, or a `let` variable instead, so
the variable has a contextual name.
- If the `subject` is never referenced inside examples, then it's acceptable to define the `subject` without a name.
### Common test setup

View File

@ -30,12 +30,12 @@ The **Overview** tab provides basic information about the alert:
![Alert Full Details](./img/alert_detail_full_v13_1.png)
### Update an Alert's status
### Update an alert's status
The Alert detail view enables you to update the Alert Status.
See [Create and manage alerts in GitLab](./alerts.md) for more details.
### Create an Issue from an Alert
### Create an issue from an alert
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1.
@ -47,7 +47,7 @@ alert by clicking the **View Issue** button.
Closing a GitLab issue associated with an alert changes the alert's status to Resolved.
See [Create and manage alerts in GitLab](alerts.md) for more details about alert statuses.
### Update an Alert's assignee
### Update an alert's assignee
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
@ -73,7 +73,7 @@ GitLab currently only supports a single assignee per alert.
**{angle-double-right}** **Expand sidebar** to expand it.
1. In the right sidebar, locate the **Assignee** and click **Edit**. From the
dropdown menu, select each user you want to assign to the alert. GitLab creates
a [To-Do list item](../../user/todos.md) for each user.
a [to-do list item](../../user/todos.md) for each user.
![Alert Details View Assignee(s)](./img/alert_todo_assignees_v13_1.png)
@ -96,12 +96,12 @@ The following actions will result in a system note:
![Alert Details View System Notes](./img/alert_detail_system_notes_v13_1.png)
### Create a To-Do from an Alert
### Create a to-do from an alert
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
You can manually create [To-Do list items](../../user/todos.md) for yourself from the
Alert details screen, and view them later on your **To-Do List**. To add a To-Do:
Alert details screen, and view them later on your **To-Do List**. To add a to-do:
1. To display the list of current alerts, click
**{cloud-gear}** **Operations > Alerts**.
@ -110,11 +110,11 @@ Alert details screen, and view them later on your **To-Do List**. To add a To-Do
![Alert Details Add A To Do](./img/alert_detail_add_todo_v13_1.png)
Click the **To-Do** **{todo-done}** in the navigation bar to view your current To-Do list.
Click the **To-Do** **{todo-done}** in the navigation bar to view your current to-do list.
![Alert Details Added to Do](./img/alert_detail_added_todo_v13_1.png)
### View an Alert's metrics data
### View an alert's metrics data
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.2.
@ -154,7 +154,7 @@ unassign their account from the alert when their role is complete.
The alert status can be updated on the [Alert list](./alerts.md) to
reflect if the alert has been resolved.
## View an Alert's logs
## View an alert's logs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.3.

View File

@ -47,7 +47,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
1. [Repositories, Projects and Groups - Video](https://www.youtube.com/watch?v=4TWfh1aKHHw&index=1&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
1. [Creating a Project in GitLab - Video](https://www.youtube.com/watch?v=7p0hrpNaJ14)
1. [How to Create Files and Directories](https://about.gitlab.com/blog/2016/02/10/feature-highlight-create-files-and-directories-from-files-page/)
1. [GitLab Todos](https://about.gitlab.com/blog/2016/03/02/gitlab-todos-feature-highlight/)
1. [GitLab To-Do List](https://about.gitlab.com/blog/2016/03/02/gitlab-todos-feature-highlight/)
1. [GitLab's Work in Progress (WIP) Flag](https://about.gitlab.com/blog/2016/01/08/feature-highlight-wip/)
### 1.5. Migrating from other Source Control

View File

@ -22,11 +22,11 @@ known response, the result is cached for 6 hours.
If the external authorization is enabled, GitLab will further block pages and
functionality that render cross-project data. That includes:
- most pages under Dashboard (Activity, Milestones, Snippets, Assigned merge
requests, Assigned issues, Todos)
- under a specific group (Activity, Contribution analytics, Issues, Issue boards,
Labels, Milestones, Merge requests)
- Global and Group search will be disabled
- Most pages under Dashboard (Activity, Milestones, Snippets, Assigned merge
requests, Assigned issues, To-Do List).
- Under a specific group (Activity, Contribution analytics, Issues, Issue boards,
Labels, Milestones, Merge requests).
- Global and Group search will be disabled.
This is to prevent performing to many requests at once to the external
authorization service.

View File

@ -88,8 +88,9 @@ investigate it for potential threats by
The **Threat Monitoring** page's **Policy** tab displays deployed
network policies for all available environments. You can check a
network policy's `yaml` manifest and toggle the policy's enforcement
status. This section has the following prerequisites:
network policy's `yaml` manifest, toggle the policy's enforcement
status, and create and edit deployed policies. This section has the
following prerequisites:
- Your project contains at least one [environment](../../../ci/environments/index.md)
- You've [installed Cilium](../../clusters/applications.md#install-cilium-using-gitlab-cicd)
@ -124,3 +125,47 @@ Disabled network policies have the
`podSelector` block. This narrows the scope of such a policy and as a
result it doesn't affect any pods. The policy itself is still deployed
to the corresponding deployment namespace.
### Container Network Policy editor
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3403) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4.
The policy editor allows you to create, edit, and delete policies. To
create a new policy click the **New policy** button located in the
**Policy** tab's header. To edit an existing policy, click**Edit
policy** in the selected policy drawer.
NOTE: **Note:**
The policy editor only supports the
[CiliumNetworkPolicy](https://docs.cilium.io/en/v1.8/policy/)specification. Regular
Kubernetes
[NetworkPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#networkpolicy-v1-networking-k8s-io)
resources aren't supported.
The policy editor has two modes:
- The visual _Rule_ mode allows you to construct and preview policy
rules using rule blocks and related controls.
- YAML mode allows you to enter a policy definition in `.yaml` format
and is aimed at expert users and cases that the Rule mode doesn't
support.
You can use both modes interchangeably and switch between them at any
time. If a YAML resource is incorrect, Rule mode is automatically
disabled. You must use YAML mode to fix your policy before Rule mode
is available again.
Rule mode supports the following rule types:
- [Labels](https://docs.cilium.io/en/v1.8/policy/language/#labels-based).
- [Entities](https://docs.cilium.io/en/v1.8/policy/language/#entities-based).
- [IP/CIDR](https://docs.cilium.io/en/v1.8/policy/language/#ip-cidr-based). Only
the `toCIDR` block without `except` is supported.
- [DNS](https://docs.cilium.io/en/v1.8/policy/language/#dns-based).
- [Level 4](https://docs.cilium.io/en/v1.8/policy/language/#layer-4-examples)
can be added to all other rules.
Once your policy is complete, save it by pressing the **Save policy**
button at the bottom of the editor. Existing policies can also be
removed from the editor interface by clicking the **Delete policy**
button at the bottom of the editor.

View File

@ -361,7 +361,7 @@ Here are possible causes and solutions:
Getting both of these errors at the same time suggests the NameID capitalization provided by the Identity Provider didn't exactly match the previous value for that user.
This can be prevented by configuring the [NameID](#nameid) to return a consistent value. Fixing this for an individual user involves [unlinking SAML in the GitLab account](#unlinking-accounts), although this will cause group membership and Todos to be lost.
This can be prevented by configuring the [NameID](#nameid) to return a consistent value. Fixing this for an individual user involves [unlinking SAML in the GitLab account](#unlinking-accounts), although this will cause group membership and to-dos to be lost.
### Message: "Request to link SAML account must be authorized"

View File

@ -134,10 +134,10 @@ the best of GitLab Flavored Markdown in your threads, comments,
issues and merge requests descriptions, and everywhere else GFM is
supported.
## Todos
## To-Do List
Never forget to reply to your collaborators. [GitLab Todos](todos.md)
are a tool for working faster and more effectively with your team,
Never forget to reply to your collaborators. [GitLab To-Do List](todos.md)
is a tool for working faster and more effectively with your team,
by listing all user or group mentions, as well as issues and merge
requests you're assigned to.

View File

@ -144,7 +144,7 @@ Users will be notified of the following events:
| New email added | User | Security email, always sent. |
| Email changed | User | Security email, always sent. |
| Password changed | User | Security email, always sent when user changes their own password |
| Password changed by administrator | User | Security email, always sent when an adminstrator changes the password of another user |
| Password changed by administrator | User | Security email, always sent when an administrator changes the password of another user |
| Two-factor authentication disabled | User | Security email, always sent. |
| New user created | User | Sent on user creation, except for OmniAuth (LDAP)|
| User added to project | User | Sent when user is added to project |

View File

@ -114,7 +114,7 @@ You have 8 options here that you can use for your default dashboard view:
- Your projects' activity
- Starred projects' activity
- Your groups
- Your [Todos](../todos.md)
- Your [to-dos](../todos.md)
- Assigned Issues
- Assigned Merge Requests
- Operations Dashboard **(PREMIUM)**

View File

@ -44,9 +44,9 @@ the icon and the date colored red. You can sort issues by those that are
![Issues with due dates in the issues index page](img/due_dates_issues_index_page.png)
Due dates also appear in your [todos list](../../todos.md).
Due dates also appear in your [to-do list](../../todos.md).
![Issues with due dates in the todos](img/due_dates_todos.png)
![Issues with due dates in the to-dos](img/due_dates_todos.png)
The day before an open issue is due, an email will be sent to all participants
of the issue. Like the due date, the "day before the due date" is determined by the

View File

@ -196,7 +196,7 @@ allowing many formatting options.
### Mentions
You can mention a user or a group present in your GitLab instance with `@username` or
`@groupname` and they will be notified via todos and email, unless they have disabled
`@groupname` and they will be notified via to-dos and email, unless they have disabled
all notifications in their profile settings. This is controlled in the
[notification settings](../../profile/notifications.md).

View File

@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: index, reference
---
# Reviewing and managing merge requests
# Reviewing and managing merge requests **(CORE)**
Merge requests are the primary method of making changes to files in a GitLab project.
Changes are proposed by [creating and submitting a merge request](creating_merge_requests.md),
@ -203,6 +203,11 @@ If there's an [environment](../../../ci/environments/index.md) and the applicati
successfully deployed to it, the deployed environment and the link to the
Review App will be shown as well.
NOTE: **Note:**
When the default branch (for example, `main`) is red due to a failed CI pipeline, the `merge` button
When the pipeline fails in a merge request but it can be merged nonetheless,
the **Merge** button will be colored in red.
### Post-merge pipeline status
When a merge request is merged, you can see the post-merge pipeline status of

View File

@ -5,67 +5,75 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# GitLab To-Do List
# GitLab To-Do List **(CORE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/2817) in GitLab 8.5.
When you log into GitLab, you normally want to see where you should spend your
time, take some action, or know what you need to keep an eye on without
a huge pile of e-mail notifications. GitLab is where you do your work,
so being able to get started quickly is important.
When you sign in to GitLab, you normally want to determine where you should
spend your time. This can include taking an action, or keeping track of things
(without having to read lots of email notifications). Because GitLab is where you
do your work, being able to get started quickly is important.
Your To-Do List offers a chronological list of items that are waiting for your input, all
in a simple dashboard.
Your *To-Do List* offers a chronological list of items waiting for your input
(known as *to-dos*) in a single dashboard.
![To Do screenshot showing a list of items to check on](img/todos_index.png)
The To-Do List supports tracking [actions](#what-triggers-a-to-do) related to
the following:
You can quickly access your To-Do List by clicking the checkmark icon next to the
search bar in the top navigation. If the count is:
- Issues
- Merge Requests
- Epics **(ULTIMATE)**
- Less than 100, the number in blue is the number of To-Do items.
- 100 or more, the number displays as 99+. The exact number displays
on the To-Do List.
you still have open. Otherwise, the number displays as 99+. The exact number
displays on the To-Do List.
![to-do screenshot showing a list of items to check on](img/todos_index.png)
You can access your To-Do List by clicking the **{task-done}** To-Do List icon
next to the search bar in the top navigation. If the to-do item count is:
- *Less than 100*, the number in blue is the number of to-do items.
- *100 or more*, the number displays as 99+. The exact number displays in the
To-Do List.
![To Do icon](img/todos_icon.png)
## What triggers a To Do
## What triggers a to-do
A To Do appears on your To-Do List when:
A to-do item appears on your To-Do List when:
- An issue or merge request is assigned to you
- You are `@mentioned` in the description or comment of an:
- Issue
- Merge Request
- Epic **(ULTIMATE)**
- An issue or merge request is assigned to you.
- You're `@mentioned` in the description or comment of an issue or merge request
(or epic **(ULTIMATE)**).
- You are `@mentioned` in a comment on a:
- Commit
- Design
- The CI/CD pipeline for your merge request failed
- An open merge request becomes unmergeable due to conflict, and one of the following is true:
- You are the author
- You are the user that set it to automatically merge once the pipeline succeeds
- [Since GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/12136), a merge request
is removed from a [merge train](../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md)
and you are the user that added it. **(PREMIUM)**
- The CI/CD pipeline for your merge request failed.
- An open merge request becomes unmergeable due to conflict, and one of the
following is true:
- You're the author.
- You're the user that set the merge request to automatically merge after a
pipeline succeeds.
- [Since GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/12136), a
merge request is removed from a
[merge train](../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md),
and you're the user that added it. **(PREMIUM)**
When multiple trigger actions occur for the same user on the same object (for example, an issue)
only the first is displayed as a single to-do on their To-Do List.
When several trigger actions occur for the same user on the same object (for
example, an issue), GitLab displays only the first action as a single to-do
item.
To-do triggers are not affected by [GitLab Notification Email settings](profile/notifications.md).
To-do triggers aren't affected by [GitLab notification email settings](profile/notifications.md).
NOTE: **Note:**
When a user no longer has access to a resource related to a To Do (like an issue, merge request,
project, or group) the related To-Do items are deleted within the next hour for security reasons.
The delete is delayed to prevent data loss, in case the user's access was revoked by mistake.
When a user no longer has access to a resource related to a to-do (such as an
issue, merge request, project, or group), for security reasons GitLab deletes
any related to-do items within the next hour. Deletion is delayed to prevent
data loss, in the case where a user's access is accidentally revoked.
### Directly addressing a To Do
### Directly addressing a to-do
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7926) in GitLab 9.0.
If you are mentioned at the start of a line, the To Do you receive will be listed
as 'directly addressed'. For example, in this comment:
If you're mentioned at the start of a line, the to-do you receive will be listed
as *directly addressed*. For example, in the following comment:
```markdown
@alice What do you think? cc: @bob
@ -79,81 +87,71 @@ as 'directly addressed'. For example, in this comment:
@erin @frank thank you!
```
The people receiving directly addressed To-Do items are `@alice`, `@erin`, and
`@frank`. Directly addressed To-Do items only differ from mentions in their type
The people receiving directly addressed to-do items are `@alice`, `@erin`, and
`@frank`. Directly addressed to-do items only differ from mentions in their type
for filtering purposes; otherwise, they appear as normal.
### Manually creating a To Do
### Manually creating a to-do
You can also add the following to your To-Do List by clicking the **Add a To Do** button on an:
- Issue
- Merge Request
- Epic **(ULTIMATE)**
You can add an issue or merge request (or epic **(ULTIMATE)**) to your
To-Do List by clicking its **Add a To Do** button.
![Adding a To Do from the issuable sidebar](img/todos_add_todo_sidebar.png)
## Marking a To Do as done
## Marking a to-do as done
Any action to the following will mark the corresponding To Do as done:
Any action to an issue or merge request (or epic **(ULTIMATE)**) will mark its
corresponding to-do as done.
- Issue
- Merge Request
- Epic **(ULTIMATE)**
Actions that dismiss To-Do items include:
Actions that dismiss to-do items include:
- Changing the assignee
- Changing the milestone
- Adding/removing a label
- Commenting on the issue
Your To-Do List is personal, and items are only marked as done if the action comes from
you. If you close the issue or merge request, your To Do is automatically
marked as done.
Your To-Do List is personal, and items are only marked as done if you take
action. If you close the issue or merge request, your to-do is marked as done.
To prevent other users from closing issues without you being notified, if someone else closes, merges, or takes action on the any of the following, your To Do will remain pending:
To prevent other users from closing issues without you being notified, if
someone else closes, merges, or takes action on an issue or merge request (or
epic **(ULTIMATE)**), your to-do will remain pending.
- Issue
- Merge request
- Epic **(ULTIMATE)**
There's just one to-do for each of these, so mentioning a user many times in an
issue will only trigger one to-do item.
There is just one To Do for each of these, so mentioning a user a hundred times in an issue will only trigger one To Do.
If no action is needed, you can manually mark the to-do as done by clicking its
corresponding **Done** button to have GitLab remove the item from your
To-Do List.
If no action is needed, you can manually mark the To Do as done by clicking the
corresponding **Done** button, and it will disappear from your To-Do List.
![A to-do in the To-Do List](img/todos_todo_list_item.png)
![A To Do in the To-Do List](img/todos_todo_list_item.png)
You can also mark a To Do as done by clicking the **Mark as done** button in the sidebar of the following:
- Issue
- Merge Request
- Epic **(ULTIMATE)**
You can also mark a to-do as done by clicking the **Mark as done** button in the
sidebar of an issue or merge request (or epic **(ULTIMATE)**).
![Mark as done from the issuable sidebar](img/todos_mark_done_sidebar.png)
You can mark all your To-Do items as done at once by clicking the **Mark all as
done** button.
You can mark all your to-do items as done at once by clicking the
**Mark all as done** button.
## Filtering your To-Do List
There are four kinds of filters you can use on your To-Do List.
You can use the following types of filters with your To-Do List:
| Filter | Description |
| ------- | ----------- |
| Project | Filter by project |
| Group | Filter by group |
| Author | Filter by the author that triggered the To Do |
| Type | Filter by issue, merge request, design, or epic **(ULTIMATE)** |
| Action | Filter by the action that triggered the To Do |
| Filter | Description |
| ------- | ---------------------------------------------------------------- |
| Project | Filter by project. |
| Group | Filter by group. |
| Author | Filter by the author that triggered the To Do. |
| Type | Filter by issue, merge request, design, or epic. **(ULTIMATE)** |
| Action | Filter by the action that triggered the To Do. |
You can also filter by more than one of these at the same time. The possible Actions are
[described above](#what-triggers-a-to-do) and include:
You can also filter by more than one of these at the same time. The previously
described [triggering actions](#what-triggers-a-to-do) include:
- Any Action
- Any action
- Assigned
- Mentioned
- Added
- Pipelines
- Directly Addressed
- Directly addressed

View File

@ -17,7 +17,7 @@ module API
end
def process_metrics
Sidekiq::ProcessSet.new.map do |process|
Sidekiq::ProcessSet.new(false).map do |process|
{
hostname: process['hostname'],
pid: process['pid'],

View File

@ -7005,6 +7005,9 @@ msgstr ""
msgid "Contributions per group member"
msgstr ""
msgid "Contributor"
msgstr ""
msgid "Contributors"
msgstr ""
@ -25843,9 +25846,6 @@ msgstr ""
msgid "This is a security log of important events involving your account."
msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
@ -26083,6 +26083,15 @@ msgstr ""
msgid "This user has no identities"
msgstr ""
msgid "This user has previously committed to the %{name} project."
msgstr ""
msgid "This user is a %{access} of the %{name} project."
msgstr ""
msgid "This user is the author of this %{noteable}."
msgstr ""
msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""

View File

@ -6,6 +6,10 @@ RSpec.describe 'Markdown keyboard shortcuts', :js do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let(:is_mac) { page.evaluate_script('navigator.platform').include?('Mac') }
let(:modifier_key) { is_mac ? :command : :control }
let(:other_modifier_key) { is_mac ? :control : :command }
before do
project.add_developer(user)
@ -16,7 +20,7 @@ RSpec.describe 'Markdown keyboard shortcuts', :js do
wait_for_requests
end
shared_examples 'keyboard shortcuts for modifier key' do
shared_examples 'keyboard shortcuts' do
it 'bolds text when <modifier>+B is pressed' do
type_and_select('bold')
@ -57,17 +61,29 @@ RSpec.describe 'Markdown keyboard shortcuts', :js do
end
end
shared_examples 'keyboard shortcuts for implementation' do
context 'Ctrl key' do
let(:modifier_key) { :control }
shared_examples 'no side effects' do
it 'does not bold text when <other modifier>+B is pressed' do
type_and_select('bold')
it_behaves_like 'keyboard shortcuts for modifier key'
markdown_field.send_keys([@other_modifier_key, 'b'])
expect(markdown_field.value).not_to eq('**bold**')
end
context '⌘ key' do
let(:modifier_key) { :command }
it 'does not italicize text when <other modifier>+I is pressed' do
type_and_select('italic')
it_behaves_like 'keyboard shortcuts for modifier key'
markdown_field.send_keys([@other_modifier_key, 'i'])
expect(markdown_field.value).not_to eq('_italic_')
end
it 'does not link text when <other modifier>+K is pressed' do
type_and_select('link')
markdown_field.send_keys([@other_modifier_key, 'k'])
expect(markdown_field.value).not_to eq('[link](url)')
end
end
@ -76,7 +92,8 @@ RSpec.describe 'Markdown keyboard shortcuts', :js do
let(:markdown_field) { find_field('Release notes') }
let(:non_markdown_field) { find_field('Release title') }
it_behaves_like 'keyboard shortcuts for implementation'
it_behaves_like 'keyboard shortcuts'
it_behaves_like 'no side effects'
end
context 'Haml markdown editor' do
@ -84,7 +101,8 @@ RSpec.describe 'Markdown keyboard shortcuts', :js do
let(:markdown_field) { find_field('Description') }
let(:non_markdown_field) { find_field('Title') }
it_behaves_like 'keyboard shortcuts for implementation'
it_behaves_like 'keyboard shortcuts'
it_behaves_like 'no side effects'
end
def type_and_select(text)

View File

@ -60,6 +60,9 @@
"resolve_with_issue_path": { "type": "string" },
"cached_markdown_version": { "type": "integer" },
"human_access": { "type": ["string", "null"] },
"is_noteable_author": { "type": "boolean" },
"is_contributor": { "type": "boolean" },
"project_name": { "type": "string" },
"toggle_award_path": { "type": "string" },
"path": { "type": "string" },
"commands_changes": { "type": "object", "additionalProperties": true },

View File

@ -1,7 +1,8 @@
import { shallowMount } from '@vue/test-utils';
import { GlBanner } from '@gitlab/ui';
import InviteMembersBanner from '~/groups/components/invite_members_banner.vue';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { setCookie, parseBoolean } from '~/lib/utils/common_utils';
import InviteMembersBanner from '~/groups/components/invite_members_banner.vue';
jest.mock('~/lib/utils/common_utils');
@ -12,6 +13,7 @@ const body =
const svgPath = '/illustrations/background';
const inviteMembersPath = 'groups/members';
const buttonText = 'Invite your colleagues';
const trackLabel = 'invite_members_banner';
const createComponent = (stubs = {}) => {
return shallowMount(InviteMembersBanner, {
@ -19,6 +21,7 @@ const createComponent = (stubs = {}) => {
svgPath,
inviteMembersPath,
isDismissedKey,
trackLabel,
},
stubs,
});
@ -26,10 +29,51 @@ const createComponent = (stubs = {}) => {
describe('InviteMembersBanner', () => {
let wrapper;
let trackingSpy;
beforeEach(() => {
document.body.dataset.page = 'any:page';
trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
unmockTracking();
});
describe('tracking', () => {
beforeEach(() => {
wrapper = createComponent({ GlBanner });
});
const trackCategory = undefined;
const displayEvent = 'invite_members_banner_displayed';
const buttonClickEvent = 'invite_members_banner_button_clicked';
const dismissEvent = 'invite_members_banner_dismissed';
it('sends the displayEvent when the banner is displayed', () => {
expect(trackingSpy).toHaveBeenCalledWith(trackCategory, displayEvent, {
label: trackLabel,
});
});
it('sets the button attributes for the buttonClickEvent', () => {
const button = wrapper.find(`[href='${wrapper.vm.inviteMembersPath}']`);
expect(button.attributes()).toMatchObject({
'data-track-event': buttonClickEvent,
'data-track-label': trackLabel,
});
});
it('sends the dismissEvent when the banner is dismissed', () => {
wrapper.find(GlBanner).vm.$emit('close');
expect(trackingSpy).toHaveBeenCalledWith(trackCategory, dismissEvent, {
label: trackLabel,
});
});
});
describe('rendering', () => {

View File

@ -35,8 +35,12 @@ describe('noteActions', () => {
canEdit: true,
canAwardEmoji: true,
canReportAsAbuse: true,
isAuthor: true,
isContributor: false,
noteableType: 'MergeRequest',
noteId: '539',
noteUrl: `${TEST_HOST}/group/project/-/merge_requests/1#note_1`,
projectName: 'project',
reportAbusePath: `${TEST_HOST}/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26`,
showReply: false,
};
@ -60,15 +64,43 @@ describe('noteActions', () => {
wrapper = shallowMountNoteActions(props);
});
it('should render noteable author badge', () => {
expect(
wrapper
.findAll('.note-role')
.at(0)
.text()
.trim(),
).toEqual('Author');
});
it('should render access level badge', () => {
expect(
wrapper
.find('.note-role')
.findAll('.note-role')
.at(1)
.text()
.trim(),
).toEqual(props.accessLevel);
});
it('should render contributor badge', () => {
wrapper.setProps({
accessLevel: null,
isContributor: true,
});
return wrapper.vm.$nextTick().then(() => {
expect(
wrapper
.findAll('.note-role')
.at(1)
.text()
.trim(),
).toBe('Contributor');
});
});
it('should render emoji link', () => {
expect(wrapper.find('.js-add-award').exists()).toBe(true);
expect(wrapper.find('.js-add-award').attributes('data-position')).toBe('right');

View File

@ -63,9 +63,9 @@ describe('Shortcuts', () => {
// Get all shortcuts specified with md-shortcuts attributes in the fixture.
// `shortcuts` will look something like this:
// [
// [ 'command+b', 'ctrl+b' ],
// [ 'command+i', 'ctrl+i' ],
// [ 'command+k', 'ctrl+k' ]
// [ 'mod+b' ],
// [ 'mod+i' ],
// [ 'mod+k' ]
// ]
shortcuts = $('.edit-note .js-md')
.map(function getShortcutsFromToolbarBtn() {

View File

@ -653,6 +653,19 @@ RSpec.describe Group do
expect(shared_group.max_member_access_for_user(user)).to eq(Gitlab::Access::MAINTAINER)
end
end
context 'evaluating admin access level' do
let_it_be(:admin) { create(:admin) }
it 'returns OWNER by default' do
expect(group.max_member_access_for_user(admin)).to eq(Gitlab::Access::OWNER)
end
it 'returns NO_ACCESS when only concrete membership should be considered' do
expect(group.max_member_access_for_user(admin, only_concrete_membership: true))
.to eq(Gitlab::Access::NO_ACCESS)
end
end
end
describe '#members_with_parents' do

View File

@ -286,6 +286,56 @@ RSpec.describe Note do
end
end
describe "noteable_author?" do
let(:user1) { create(:user) }
let(:user2) { create(:user) }
let(:project) { create(:project, :public, :repository) }
context 'when note is on commit' do
let(:noteable) { create(:commit, project: project, author: user1) }
context 'if user is the noteable author' do
let(:note) { create(:discussion_note_on_commit, commit_id: noteable.id, project: project, author: user1) }
let(:diff_note) { create(:diff_note_on_commit, commit_id: noteable.id, project: project, author: user1) }
it 'returns true' do
expect(note.noteable_author?(noteable)).to be true
expect(diff_note.noteable_author?(noteable)).to be true
end
end
context 'if user is not the noteable author' do
let(:note) { create(:discussion_note_on_commit, commit_id: noteable.id, project: project, author: user2) }
let(:diff_note) { create(:diff_note_on_commit, commit_id: noteable.id, project: project, author: user2) }
it 'returns false' do
expect(note.noteable_author?(noteable)).to be false
expect(diff_note.noteable_author?(noteable)).to be false
end
end
end
context 'when note is on issue' do
let(:noteable) { create(:issue, project: project, author: user1) }
context 'if user is the noteable author' do
let(:note) { create(:note, noteable: noteable, author: user1, project: project) }
it 'returns true' do
expect(note.noteable_author?(noteable)).to be true
end
end
context 'if user is not the noteable author' do
let(:note) { create(:note, noteable: noteable, author: user2, project: project) }
it 'returns false' do
expect(note.noteable_author?(noteable)).to be false
end
end
end
end
describe "edited?" do
let(:note) { build(:note, updated_by_id: nil, created_at: Time.current, updated_at: Time.current + 5.hours) }
@ -1228,22 +1278,6 @@ RSpec.describe Note do
end
end
describe '#special_role=' do
let(:role) { Note::SpecialRole::FIRST_TIME_CONTRIBUTOR }
it 'assigns role' do
subject.special_role = role
expect(subject.special_role).to eq(role)
end
it 'does not assign unknown role' do
expect { subject.special_role = :bogus }.to raise_error(/Role is undefined/)
expect(subject.special_role).to be_nil
end
end
describe '#parent' do
it 'returns project for project notes' do
project = create(:project)

View File

@ -3,6 +3,8 @@
require "spec_helper"
RSpec.describe ProjectTeam do
include ProjectForksHelper
let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
@ -237,6 +239,35 @@ RSpec.describe ProjectTeam do
end
end
describe '#contributor?' do
let(:project) { create(:project, :public, :repository) }
context 'when user is a member of project' do
before do
project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
end
it { expect(project.team.contributor?(maintainer.id)).to be false }
it { expect(project.team.contributor?(reporter.id)).to be false }
it { expect(project.team.contributor?(guest.id)).to be false }
end
context 'when user has at least one merge request merged into default_branch' do
let(:contributor) { create(:user) }
let(:user_without_access) { create(:user) }
let(:first_fork_project) { fork_project(project, contributor, repository: true) }
before do
create(:merge_request, :merged, author: contributor, target_project: project, source_project: first_fork_project, target_branch: project.default_branch.to_s)
end
it { expect(project.team.contributor?(contributor.id)).to be true }
it { expect(project.team.contributor?(user_without_access.id)).to be false }
end
end
describe '#max_member_access' do
let(:requester) { create(:user) }
@ -366,6 +397,66 @@ RSpec.describe ProjectTeam do
end
end
describe '#contribution_check_for_user_ids', :request_store do
let(:project) { create(:project, :public, :repository) }
let(:contributor) { create(:user) }
let(:second_contributor) { create(:user) }
let(:user_without_access) { create(:user) }
let(:first_fork_project) { fork_project(project, contributor, repository: true) }
let(:second_fork_project) { fork_project(project, second_contributor, repository: true) }
let(:users) do
[contributor, second_contributor, user_without_access].map(&:id)
end
let(:expected) do
{
contributor.id => true,
second_contributor.id => true,
user_without_access.id => false
}
end
before do
create(:merge_request, :merged, author: contributor, target_project: project, source_project: first_fork_project, target_branch: project.default_branch.to_s)
create(:merge_request, :merged, author: second_contributor, target_project: project, source_project: second_fork_project, target_branch: project.default_branch.to_s)
end
def contributors(users)
project.team.contribution_check_for_user_ids(users)
end
it 'does not perform extra queries when asked for users who have already been found' do
contributors(users)
expect { contributors([contributor.id]) }.not_to exceed_query_limit(0)
expect(contributors([contributor.id])).to eq(expected)
end
it 'only requests the extra users when uncached users are passed' do
new_contributor = create(:user)
new_fork_project = fork_project(project, new_contributor, repository: true)
second_new_user = create(:user)
all_users = users + [new_contributor.id, second_new_user.id]
create(:merge_request, :merged, author: new_contributor, target_project: project, source_project: new_fork_project, target_branch: project.default_branch.to_s)
expected_all = expected.merge(new_contributor.id => true,
second_new_user.id => false)
contributors(users)
queries = ActiveRecord::QueryRecorder.new { contributors(all_users) }
expect(queries.count).to eq(1)
expect(contributors([new_contributor.id])).to eq(expected_all)
end
it 'returns correct contributors' do
expect(contributors(users)).to eq(expected)
end
end
shared_examples 'max member access for users' do
let(:project) { create(:project) }
let(:group) { create(:group) }
@ -438,9 +529,9 @@ RSpec.describe ProjectTeam do
it 'does not perform extra queries when asked for users who have already been found' do
access_levels(users)
expect { access_levels(users) }.not_to exceed_query_limit(0)
expect { access_levels([maintainer.id]) }.not_to exceed_query_limit(0)
expect(access_levels(users)).to eq(expected)
expect(access_levels([maintainer.id])).to eq(expected)
end
it 'only requests the extra users when uncached users are passed' do

View File

@ -490,6 +490,25 @@ RSpec.describe API::Projects do
expect(json_response.first['name']).to eq(project4.name)
expect(json_response.first['owner']['username']).to eq(user4.username)
end
context 'when admin creates a project' do
before do
group = create(:group)
project_create_opts = {
name: 'GitLab',
namespace_id: group.id
}
Projects::CreateService.new(admin, project_create_opts).execute
end
it 'does not list as owned project for admin' do
get api('/projects', admin), params: { owned: true }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
end
end
context 'and with starred=true' do