Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2b339d4e89
commit
cd4cb29b2c
|
|
@ -1,5 +1,16 @@
|
||||||
Please view this file on the master branch, on stable branches it's out of date.
|
Please view this file on the master branch, on stable branches it's out of date.
|
||||||
|
|
||||||
|
## 12.5.3
|
||||||
|
|
||||||
|
### Performance (1 change)
|
||||||
|
|
||||||
|
- Geo - Improve query performance to determine job artifacts to sync when selective sync is enabled. !19583
|
||||||
|
|
||||||
|
### Other (1 change)
|
||||||
|
|
||||||
|
- Geo - Does not schedule duplicated jobs while backfilling uploads, LFS objects and job artifacts. !20324
|
||||||
|
|
||||||
|
|
||||||
## 12.5.1
|
## 12.5.1
|
||||||
|
|
||||||
### Security (6 changes)
|
### Security (6 changes)
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,6 @@ entry.
|
||||||
- Flatten exception details in API and controller logs. !20434
|
- Flatten exception details in API and controller logs. !20434
|
||||||
|
|
||||||
|
|
||||||
## 12.5.2
|
|
||||||
|
|
||||||
### Security (1 change)
|
|
||||||
|
|
||||||
- Fix 500 error caused by invalid byte sequences in links.
|
|
||||||
|
|
||||||
|
|
||||||
## 12.5.1
|
## 12.5.1
|
||||||
|
|
||||||
### Security (11 changes)
|
### Security (11 changes)
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.72.1
|
1.75.0
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,44 @@ import 'at.js';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import glRegexp from './lib/utils/regexp';
|
import glRegexp from './lib/utils/regexp';
|
||||||
import AjaxCache from './lib/utils/ajax_cache';
|
import AjaxCache from './lib/utils/ajax_cache';
|
||||||
|
import { spriteIcon } from './lib/utils/common_utils';
|
||||||
|
|
||||||
function sanitize(str) {
|
function sanitize(str) {
|
||||||
return str.replace(/<(?:.|\n)*?>/gm, '');
|
return str.replace(/<(?:.|\n)*?>/gm, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function membersBeforeSave(members) {
|
||||||
|
return _.map(members, member => {
|
||||||
|
const GROUP_TYPE = 'Group';
|
||||||
|
|
||||||
|
let title = '';
|
||||||
|
if (member.username == null) {
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
title = member.name;
|
||||||
|
if (member.count && !member.mentionsDisabled) {
|
||||||
|
title += ` (${member.count})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoCompleteAvatar = member.avatar_url || member.username.charAt(0).toUpperCase();
|
||||||
|
|
||||||
|
const rectAvatarClass = member.type === GROUP_TYPE ? 'rect-avatar' : '';
|
||||||
|
const imgAvatar = `<img src="${member.avatar_url}" alt="${member.username}" class="avatar ${rectAvatarClass} avatar-inline center s26"/>`;
|
||||||
|
const txtAvatar = `<div class="avatar ${rectAvatarClass} center avatar-inline s26">${autoCompleteAvatar}</div>`;
|
||||||
|
const avatarIcon = member.mentionsDisabled
|
||||||
|
? spriteIcon('notifications-off', 's16 vertical-align-middle prepend-left-5')
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
username: member.username,
|
||||||
|
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
|
||||||
|
title: sanitize(title),
|
||||||
|
search: sanitize(`${member.username} ${member.name}`),
|
||||||
|
icon: avatarIcon,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const defaultAutocompleteConfig = {
|
export const defaultAutocompleteConfig = {
|
||||||
emojis: true,
|
emojis: true,
|
||||||
members: true,
|
members: true,
|
||||||
|
|
@ -167,12 +200,13 @@ class GfmAutoComplete {
|
||||||
alias: 'users',
|
alias: 'users',
|
||||||
displayTpl(value) {
|
displayTpl(value) {
|
||||||
let tmpl = GfmAutoComplete.Loading.template;
|
let tmpl = GfmAutoComplete.Loading.template;
|
||||||
const { avatarTag, username, title } = value;
|
const { avatarTag, username, title, icon } = value;
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
tmpl = GfmAutoComplete.Members.templateFunction({
|
tmpl = GfmAutoComplete.Members.templateFunction({
|
||||||
avatarTag,
|
avatarTag,
|
||||||
username,
|
username,
|
||||||
title,
|
title,
|
||||||
|
icon,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return tmpl;
|
return tmpl;
|
||||||
|
|
@ -185,33 +219,7 @@ class GfmAutoComplete {
|
||||||
data: GfmAutoComplete.defaultLoadingData,
|
data: GfmAutoComplete.defaultLoadingData,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
...this.getDefaultCallbacks(),
|
...this.getDefaultCallbacks(),
|
||||||
beforeSave(members) {
|
beforeSave: membersBeforeSave,
|
||||||
return $.map(members, m => {
|
|
||||||
let title = '';
|
|
||||||
if (m.username == null) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
title = m.name;
|
|
||||||
if (m.count) {
|
|
||||||
title += ` (${m.count})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GROUP_TYPE = 'Group';
|
|
||||||
|
|
||||||
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
|
|
||||||
|
|
||||||
const rectAvatarClass = m.type === GROUP_TYPE ? 'rect-avatar' : '';
|
|
||||||
const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar ${rectAvatarClass} avatar-inline center s26"/>`;
|
|
||||||
const txtAvatar = `<div class="avatar ${rectAvatarClass} center avatar-inline s26">${autoCompleteAvatar}</div>`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
username: m.username,
|
|
||||||
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
|
|
||||||
title: sanitize(title),
|
|
||||||
search: sanitize(`${m.username} ${m.name}`),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -624,8 +632,8 @@ GfmAutoComplete.Emoji = {
|
||||||
};
|
};
|
||||||
// Team Members
|
// Team Members
|
||||||
GfmAutoComplete.Members = {
|
GfmAutoComplete.Members = {
|
||||||
templateFunction({ avatarTag, username, title }) {
|
templateFunction({ avatarTag, username, title, icon }) {
|
||||||
return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small></li>`;
|
return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small> ${icon}</li>`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
GfmAutoComplete.Labels = {
|
GfmAutoComplete.Labels = {
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,10 @@ const bindEvents = () => {
|
||||||
text: s__('ProjectTemplates|Netlify/Hexo'),
|
text: s__('ProjectTemplates|Netlify/Hexo'),
|
||||||
icon: '.template-option .icon-netlify',
|
icon: '.template-option .icon-netlify',
|
||||||
},
|
},
|
||||||
|
salesforcedx: {
|
||||||
|
text: s__('ProjectTemplates|SalesforceDX'),
|
||||||
|
icon: '.template-option svg.icon-gitlab',
|
||||||
|
},
|
||||||
serverless_framework: {
|
serverless_framework: {
|
||||||
text: s__('ProjectTemplates|Serverless Framework/JS'),
|
text: s__('ProjectTemplates|Serverless Framework/JS'),
|
||||||
icon: '.template-option .icon-serverless_framework',
|
icon: '.template-option .icon-serverless_framework',
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ class GroupsController < Groups::ApplicationController
|
||||||
:avatar,
|
:avatar,
|
||||||
:description,
|
:description,
|
||||||
:emails_disabled,
|
:emails_disabled,
|
||||||
|
:mentions_disabled,
|
||||||
:lfs_enabled,
|
:lfs_enabled,
|
||||||
:name,
|
:name,
|
||||||
:path,
|
:path,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,8 @@ module Users
|
||||||
username: group.full_path,
|
username: group.full_path,
|
||||||
name: group.full_name,
|
name: group.full_name,
|
||||||
avatar_url: group.avatar_url,
|
avatar_url: group.avatar_url,
|
||||||
count: group_counts.fetch(group.id, 0)
|
count: group_counts.fetch(group.id, 0),
|
||||||
|
mentionsDisabled: group.mentions_disabled
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -25,3 +25,5 @@ module Issues
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Issues::DuplicateService.prepend_if_ee('EE::Issues::DuplicateService')
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,13 @@
|
||||||
%span.d-block= s_('GroupSettings|Disable email notifications')
|
%span.d-block= s_('GroupSettings|Disable email notifications')
|
||||||
%span.text-muted= s_('GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects.')
|
%span.text-muted= s_('GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects.')
|
||||||
|
|
||||||
|
.form-group.append-bottom-default
|
||||||
|
.form-check
|
||||||
|
= f.check_box :mentions_disabled, checked: @group.mentions_disabled?, class: 'form-check-input'
|
||||||
|
= f.label :mentions_disabled, class: 'form-check-label' do
|
||||||
|
%span.d-block= s_('GroupSettings|Disable group mentions')
|
||||||
|
%span.text-muted= s_('GroupSettings|This setting will prevent group members from being notified if the group is mentioned.')
|
||||||
|
|
||||||
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
|
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
|
||||||
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
|
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
|
||||||
= render 'groups/settings/lfs', f: f
|
= render 'groups/settings/lfs', f: f
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Relate issues when they are marked as duplicated
|
||||||
|
merge_request: 20161
|
||||||
|
author: minghuan lei
|
||||||
|
type: added
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Allow groups to disable mentioning their members, if the group is mentioned
|
||||||
|
merge_request: 20184
|
||||||
|
author: Fabio Huser
|
||||||
|
type: added
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Upgrade to Gitaly v1.75.0
|
||||||
|
merge_request: 21045
|
||||||
|
author:
|
||||||
|
type: changed
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add SalesforceDX project template
|
||||||
|
merge_request: 20831
|
||||||
|
author:
|
||||||
|
type: added
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddMentionsDisabledToNamespaces < ActiveRecord::Migration[5.2]
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column :namespaces, :mentions_disabled, :boolean
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -349,6 +349,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
|
||||||
t.boolean "sourcegraph_enabled", default: false, null: false
|
t.boolean "sourcegraph_enabled", default: false, null: false
|
||||||
t.string "sourcegraph_url", limit: 255
|
t.string "sourcegraph_url", limit: 255
|
||||||
t.boolean "sourcegraph_public_only", default: true, null: false
|
t.boolean "sourcegraph_public_only", default: true, null: false
|
||||||
|
t.bigint "snippet_size_limit", default: 52428800, null: false
|
||||||
t.text "encrypted_akismet_api_key"
|
t.text "encrypted_akismet_api_key"
|
||||||
t.string "encrypted_akismet_api_key_iv", limit: 255
|
t.string "encrypted_akismet_api_key_iv", limit: 255
|
||||||
t.text "encrypted_elasticsearch_aws_secret_access_key"
|
t.text "encrypted_elasticsearch_aws_secret_access_key"
|
||||||
|
|
@ -361,7 +362,6 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
|
||||||
t.string "encrypted_slack_app_secret_iv", limit: 255
|
t.string "encrypted_slack_app_secret_iv", limit: 255
|
||||||
t.text "encrypted_slack_app_verification_token"
|
t.text "encrypted_slack_app_verification_token"
|
||||||
t.string "encrypted_slack_app_verification_token_iv", limit: 255
|
t.string "encrypted_slack_app_verification_token_iv", limit: 255
|
||||||
t.bigint "snippet_size_limit", default: 52428800, null: false
|
|
||||||
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
|
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
|
||||||
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
|
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
|
||||||
t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id"
|
t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id"
|
||||||
|
|
@ -2603,6 +2603,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
|
||||||
t.boolean "emails_disabled"
|
t.boolean "emails_disabled"
|
||||||
t.integer "max_pages_size"
|
t.integer "max_pages_size"
|
||||||
t.integer "max_artifacts_size"
|
t.integer "max_artifacts_size"
|
||||||
|
t.boolean "mentions_disabled"
|
||||||
t.index ["created_at"], name: "index_namespaces_on_created_at"
|
t.index ["created_at"], name: "index_namespaces_on_created_at"
|
||||||
t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)"
|
t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)"
|
||||||
t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id"
|
t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id"
|
||||||
|
|
|
||||||
|
|
@ -431,6 +431,23 @@ To enable this feature:
|
||||||
1. Expand the **Permissions, LFS, 2FA** section, and select **Disable email notifications**.
|
1. Expand the **Permissions, LFS, 2FA** section, and select **Disable email notifications**.
|
||||||
1. Click **Save changes**.
|
1. Click **Save changes**.
|
||||||
|
|
||||||
|
#### Disabling group mentions
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/21301) in GitLab 12.6.
|
||||||
|
|
||||||
|
You can prevent users from being added to a conversation and getting notified when
|
||||||
|
anyone mentions a group in which those users are members.
|
||||||
|
|
||||||
|
Groups with disabled mentions are visualized accordingly in the autocompletion dropdown.
|
||||||
|
|
||||||
|
This is particularly helpful for groups with a large number of users.
|
||||||
|
|
||||||
|
To enable this feature:
|
||||||
|
|
||||||
|
1. Navigate to the group's **Settings > General** page.
|
||||||
|
1. Expand the **Permissions, LFS, 2FA** section, and select **Disable group mentions**.
|
||||||
|
1. Click **Save changes**.
|
||||||
|
|
||||||
### Advanced settings
|
### Advanced settings
|
||||||
|
|
||||||
- **Projects**: View all projects within that group, add members to each project,
|
- **Projects**: View all projects within that group, add members to each project,
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ The following quick actions are applicable to descriptions, discussions and thre
|
||||||
| `/remove_epic` | ✓ | | | Remove from epic **(ULTIMATE)** |
|
| `/remove_epic` | ✓ | | | Remove from epic **(ULTIMATE)** |
|
||||||
| `/promote` | ✓ | | | Promote issue to epic **(ULTIMATE)** |
|
| `/promote` | ✓ | | | Promote issue to epic **(ULTIMATE)** |
|
||||||
| `/confidential` | ✓ | | | Make confidential |
|
| `/confidential` | ✓ | | | Make confidential |
|
||||||
| `/duplicate <#issue>` | ✓ | | | Mark this issue as a duplicate of another issue |
|
| `/duplicate <#issue>` | ✓ | | | Mark this issue as a duplicate of another issue and relate them for **(STARTER)** |
|
||||||
| `/create_merge_request <branch name>` | ✓ | | | Create a new merge request starting from the current issue |
|
| `/create_merge_request <branch name>` | ✓ | | | Create a new merge request starting from the current issue |
|
||||||
| `/relate #issue1 #issue2` | ✓ | | | Mark issues as related **(STARTER)** |
|
| `/relate #issue1 #issue2` | ✓ | | | Mark issues as related **(STARTER)** |
|
||||||
| `/move <path/to/project>` | ✓ | | | Move this issue to another project |
|
| `/move <path/to/project>` | ✓ | | | Move this issue to another project |
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,9 @@ module Banzai
|
||||||
def find_users_for_groups(ids)
|
def find_users_for_groups(ids)
|
||||||
return [] if ids.empty?
|
return [] if ids.empty?
|
||||||
|
|
||||||
User.joins(:group_members).where(members: { source_id: ids }).to_a
|
User.joins(:group_members).where(members: {
|
||||||
|
source_id: Namespace.where(id: ids).where('mentions_disabled IS NOT TRUE').select(:id)
|
||||||
|
}).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_users_for_projects(ids)
|
def find_users_for_projects(ids)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ module Gitlab
|
||||||
ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
|
ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
|
||||||
ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'),
|
ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'),
|
||||||
ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg'),
|
ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg'),
|
||||||
|
ProjectTemplate.new('salesforcedx', 'SalesforceDX', _('A project boilerplate for Salesforce App development with Salesforce Developer tools.'), 'https://gitlab.com/gitlab-org/project-templates/salesforcedx'),
|
||||||
ProjectTemplate.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg')
|
ProjectTemplate.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg')
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ module Gitlab
|
||||||
def event(category, action, label: nil, property: nil, value: nil, context: nil)
|
def event(category, action, label: nil, property: nil, value: nil, context: nil)
|
||||||
return unless enabled?
|
return unless enabled?
|
||||||
|
|
||||||
snowplow.track_struct_event(category, action, label, property, value, context, Time.now.to_i)
|
snowplow.track_struct_event(category, action, label, property, value, context, (Time.now.to_f * 1000).to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self_describing_event(schema_url, event_data_json, context: nil)
|
def self_describing_event(schema_url, event_data_json, context: nil)
|
||||||
return unless enabled?
|
return unless enabled?
|
||||||
|
|
||||||
event_json = SnowplowTracker::SelfDescribingJson.new(schema_url, event_data_json)
|
event_json = SnowplowTracker::SelfDescribingJson.new(schema_url, event_data_json)
|
||||||
snowplow.track_self_describing_event(event_json, context, Time.now.to_i)
|
snowplow.track_self_describing_event(event_json, context, (Time.now.to_f * 1000).to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def snowplow_options(group)
|
def snowplow_options(group)
|
||||||
|
|
|
||||||
|
|
@ -696,6 +696,9 @@ msgstr ""
|
||||||
msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
|
msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "A project boilerplate for Salesforce App development with Salesforce Developer tools."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
|
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -8860,6 +8863,9 @@ msgstr ""
|
||||||
msgid "GroupSettings|Disable email notifications"
|
msgid "GroupSettings|Disable email notifications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "GroupSettings|Disable group mentions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility."
|
msgid "GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -8908,6 +8914,9 @@ msgstr ""
|
||||||
msgid "GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects."
|
msgid "GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "GroupSettings|This setting will prevent group members from being notified if the group is mentioned."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "GroupSettings|Transfer group"
|
msgid "GroupSettings|Transfer group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -13721,6 +13730,9 @@ msgstr ""
|
||||||
msgid "ProjectTemplates|Ruby on Rails"
|
msgid "ProjectTemplates|Ruby on Rails"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ProjectTemplates|SalesforceDX"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectTemplates|Serverless Framework/JS"
|
msgid "ProjectTemplates|Serverless Framework/JS"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/* eslint no-param-reassign: "off" */
|
/* eslint no-param-reassign: "off" */
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
import { membersBeforeSave } from '~/gfm_auto_complete';
|
||||||
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
|
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
|
||||||
|
|
||||||
import 'jquery.caret';
|
import 'jquery.caret';
|
||||||
|
|
@ -262,6 +263,79 @@ describe('GfmAutoComplete', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('membersBeforeSave', () => {
|
||||||
|
const mockGroup = {
|
||||||
|
username: 'my-group',
|
||||||
|
name: 'My Group',
|
||||||
|
count: 2,
|
||||||
|
avatar_url: './group.jpg',
|
||||||
|
type: 'Group',
|
||||||
|
mentionsDisabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return the original object when username is null', () => {
|
||||||
|
expect(membersBeforeSave([{ ...mockGroup, username: null }])).toEqual([
|
||||||
|
{ ...mockGroup, username: null },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the text avatar if avatar_url is null', () => {
|
||||||
|
expect(membersBeforeSave([{ ...mockGroup, avatar_url: null }])).toEqual([
|
||||||
|
{
|
||||||
|
username: 'my-group',
|
||||||
|
avatarTag: '<div class="avatar rect-avatar center avatar-inline s26">M</div>',
|
||||||
|
title: 'My Group (2)',
|
||||||
|
search: 'my-group My Group',
|
||||||
|
icon: '',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the image avatar if avatar_url is given', () => {
|
||||||
|
expect(membersBeforeSave([mockGroup])).toEqual([
|
||||||
|
{
|
||||||
|
username: 'my-group',
|
||||||
|
avatarTag:
|
||||||
|
'<img src="./group.jpg" alt="my-group" class="avatar rect-avatar avatar-inline center s26"/>',
|
||||||
|
title: 'My Group (2)',
|
||||||
|
search: 'my-group My Group',
|
||||||
|
icon: '',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set mentions disabled icon if mentionsDisabled is set', () => {
|
||||||
|
expect(membersBeforeSave([{ ...mockGroup, mentionsDisabled: true }])).toEqual([
|
||||||
|
{
|
||||||
|
username: 'my-group',
|
||||||
|
avatarTag:
|
||||||
|
'<img src="./group.jpg" alt="my-group" class="avatar rect-avatar avatar-inline center s26"/>',
|
||||||
|
title: 'My Group',
|
||||||
|
search: 'my-group My Group',
|
||||||
|
icon:
|
||||||
|
'<svg class="s16 vertical-align-middle prepend-left-5"><use xlink:href="undefined#notifications-off" /></svg>',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the right image classes for User type members', () => {
|
||||||
|
expect(
|
||||||
|
membersBeforeSave([
|
||||||
|
{ username: 'my-user', name: 'My User', avatar_url: './users.jpg', type: 'User' },
|
||||||
|
]),
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
username: 'my-user',
|
||||||
|
avatarTag:
|
||||||
|
'<img src="./users.jpg" alt="my-user" class="avatar avatar-inline center s26"/>',
|
||||||
|
title: 'My User',
|
||||||
|
search: 'my-user My User',
|
||||||
|
icon: '',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Issues.insertTemplateFunction', () => {
|
describe('Issues.insertTemplateFunction', () => {
|
||||||
it('should return default template', () => {
|
it('should return default template', () => {
|
||||||
expect(GfmAutoComplete.Issues.insertTemplateFunction({ id: 5, title: 'Some Issue' })).toBe(
|
expect(GfmAutoComplete.Issues.insertTemplateFunction({ id: 5, title: 'Some Issue' })).toBe(
|
||||||
|
|
@ -298,6 +372,41 @@ describe('GfmAutoComplete', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Members.templateFunction', () => {
|
||||||
|
it('should return html with avatarTag and username', () => {
|
||||||
|
expect(
|
||||||
|
GfmAutoComplete.Members.templateFunction({
|
||||||
|
avatarTag: 'IMG',
|
||||||
|
username: 'my-group',
|
||||||
|
title: '',
|
||||||
|
icon: '',
|
||||||
|
}),
|
||||||
|
).toBe('<li>IMG my-group <small></small> </li>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add icon if icon is set', () => {
|
||||||
|
expect(
|
||||||
|
GfmAutoComplete.Members.templateFunction({
|
||||||
|
avatarTag: 'IMG',
|
||||||
|
username: 'my-group',
|
||||||
|
title: '',
|
||||||
|
icon: '<i class="icon"/>',
|
||||||
|
}),
|
||||||
|
).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add escaped title if title is set', () => {
|
||||||
|
expect(
|
||||||
|
GfmAutoComplete.Members.templateFunction({
|
||||||
|
avatarTag: 'IMG',
|
||||||
|
username: 'my-group',
|
||||||
|
title: 'MyGroup+',
|
||||||
|
icon: '<i class="icon"/>',
|
||||||
|
}),
|
||||||
|
).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('labels', () => {
|
describe('labels', () => {
|
||||||
const dataSources = {
|
const dataSources = {
|
||||||
labels: `${TEST_HOST}/autocomplete_sources/labels`,
|
labels: `${TEST_HOST}/autocomplete_sources/labels`,
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,23 @@ describe Banzai::ReferenceParser::UserParser do
|
||||||
link['data-group'] = project.group.id.to_s
|
link['data-group'] = project.group.id.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the users of the group' do
|
|
||||||
create(:group_member, group: group, user: user)
|
|
||||||
|
|
||||||
expect(subject.referenced_by([link])).to eq([user])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns an empty Array when the group has no users' do
|
it 'returns an empty Array when the group has no users' do
|
||||||
expect(subject.referenced_by([link])).to eq([])
|
expect(subject.referenced_by([link])).to eq([])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when group has members' do
|
||||||
|
let!(:group_member) { create(:group_member, group: group, user: user) }
|
||||||
|
|
||||||
|
it 'returns the users of the group' do
|
||||||
|
expect(subject.referenced_by([link])).to eq([user])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty Array when the group has mentions disabled' do
|
||||||
|
group.update!(mentions_disabled: true)
|
||||||
|
|
||||||
|
expect(subject.referenced_by([link])).to eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'using a non-existing group ID' do
|
context 'using a non-existing group ID' do
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ describe Gitlab::ProjectTemplate do
|
||||||
described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'),
|
described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'),
|
||||||
described_class.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook'),
|
described_class.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook'),
|
||||||
described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo'),
|
described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo'),
|
||||||
|
described_class.new('salesforcedx', 'SalesforceDX', _('A project boilerplate for Salesforce App development with Salesforce Developer tools.'), 'https://gitlab.com/gitlab-org/project-templates/salesforcedx'),
|
||||||
described_class.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg')
|
described_class.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ describe Gitlab::Tracking do
|
||||||
'_property_',
|
'_property_',
|
||||||
'_value_',
|
'_value_',
|
||||||
nil,
|
nil,
|
||||||
timestamp.to_i
|
(timestamp.to_f * 1000).to_i
|
||||||
)
|
)
|
||||||
|
|
||||||
track_event
|
track_event
|
||||||
|
|
@ -130,7 +130,7 @@ describe Gitlab::Tracking do
|
||||||
expect(tracker).to receive(:track_self_describing_event).with(
|
expect(tracker).to receive(:track_self_describing_event).with(
|
||||||
'_event_json_',
|
'_event_json_',
|
||||||
nil,
|
nil,
|
||||||
timestamp.to_i
|
(timestamp.to_f * 1000).to_i
|
||||||
)
|
)
|
||||||
|
|
||||||
track_event
|
track_event
|
||||||
|
|
|
||||||
|
|
@ -689,8 +689,9 @@ describe Issues::UpdateService, :mailer do
|
||||||
|
|
||||||
context 'valid canonical_issue_id' do
|
context 'valid canonical_issue_id' do
|
||||||
it 'calls the duplicate service with both issues' do
|
it 'calls the duplicate service with both issues' do
|
||||||
expect_any_instance_of(Issues::DuplicateService)
|
expect_next_instance_of(Issues::DuplicateService) do |service|
|
||||||
.to receive(:execute).with(issue, canonical_issue)
|
expect(service).to receive(:execute).with(issue, canonical_issue)
|
||||||
|
end
|
||||||
|
|
||||||
update_issue(canonical_issue_id: canonical_issue.id)
|
update_issue(canonical_issue_id: canonical_issue.id)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
shared_examples 'duplicate quick action' do
|
|
||||||
context 'mark issue as duplicate' do
|
|
||||||
let(:original_issue) { create(:issue, project: project) }
|
|
||||||
|
|
||||||
context 'when the current user can update issues' do
|
|
||||||
it 'does not create a note, and marks the issue as a duplicate' do
|
|
||||||
add_note("/duplicate ##{original_issue.to_reference}")
|
|
||||||
|
|
||||||
expect(page).not_to have_content "/duplicate #{original_issue.to_reference}"
|
|
||||||
expect(page).to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
|
|
||||||
|
|
||||||
expect(issue.reload).to be_closed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the current user cannot update the issue' do
|
|
||||||
let(:guest) { create(:user) }
|
|
||||||
before do
|
|
||||||
project.add_guest(guest)
|
|
||||||
gitlab_sign_out
|
|
||||||
sign_in(guest)
|
|
||||||
visit project_issue_path(project, issue)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not create a note, and does not mark the issue as a duplicate' do
|
|
||||||
add_note("/duplicate ##{original_issue.to_reference}")
|
|
||||||
|
|
||||||
expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
|
|
||||||
|
|
||||||
expect(issue.reload).to be_open
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue