summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG-EE.md11
-rw-r--r--CHANGELOG.md7
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js68
-rw-r--r--app/assets/javascripts/projects/project_new.js4
-rw-r--r--app/controllers/groups_controller.rb1
-rw-r--r--app/services/concerns/users/participable_service.rb3
-rw-r--r--app/services/issues/duplicate_service.rb2
-rw-r--r--app/views/groups/settings/_permissions.html.haml7
-rw-r--r--changelogs/unreleased/3963-auto-related-duplicated-issues.yml5
-rw-r--r--changelogs/unreleased/feat-group-mentions-prevention.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.75.0.yml5
-rw-r--r--changelogs/unreleased/salesforcedx_project_template.yml5
-rw-r--r--db/migrate/20191114132259_add_mentions_disabled_to_namespaces.rb9
-rw-r--r--db/schema.rb3
-rw-r--r--doc/user/group/index.md17
-rw-r--r--doc/user/project/quick_actions.md2
-rw-r--r--lib/banzai/reference_parser/user_parser.rb4
-rw-r--r--lib/gitlab/project_template.rb1
-rw-r--r--lib/gitlab/tracking.rb4
-rw-r--r--locale/gitlab.pot12
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js109
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb20
-rw-r--r--spec/lib/gitlab/project_template_spec.rb1
-rw-r--r--spec/lib/gitlab/tracking_spec.rb4
-rw-r--r--spec/services/issues/update_service_spec.rb5
-rw-r--r--spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb36
-rw-r--r--vendor/project_templates/salesforcedx.tar.gzbin0 -> 432083 bytes
28 files changed, 262 insertions, 90 deletions
diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md
index 8bf716580a5..9226e23795b 100644
--- a/CHANGELOG-EE.md
+++ b/CHANGELOG-EE.md
@@ -1,5 +1,16 @@
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
### Security (6 changes)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 806abf85e10..5c550c8c9db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,13 +16,6 @@ entry.
- 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
### Security (11 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 22d6771a47d..7c7053aa238 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.72.1
+1.75.0
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index db3ad0bb4c9..e25c9d90f60 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -3,11 +3,44 @@ import 'at.js';
import _ from 'underscore';
import glRegexp from './lib/utils/regexp';
import AjaxCache from './lib/utils/ajax_cache';
+import { spriteIcon } from './lib/utils/common_utils';
function sanitize(str) {
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 = {
emojis: true,
members: true,
@@ -167,12 +200,13 @@ class GfmAutoComplete {
alias: 'users',
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
- const { avatarTag, username, title } = value;
+ const { avatarTag, username, title, icon } = value;
if (username != null) {
tmpl = GfmAutoComplete.Members.templateFunction({
avatarTag,
username,
title,
+ icon,
});
}
return tmpl;
@@ -185,33 +219,7 @@ class GfmAutoComplete {
data: GfmAutoComplete.defaultLoadingData,
callbacks: {
...this.getDefaultCallbacks(),
- beforeSave(members) {
- 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}`),
- };
- });
- },
+ beforeSave: membersBeforeSave,
},
});
}
@@ -624,8 +632,8 @@ GfmAutoComplete.Emoji = {
};
// Team Members
GfmAutoComplete.Members = {
- templateFunction({ avatarTag, username, title }) {
- return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small></li>`;
+ templateFunction({ avatarTag, username, title, icon }) {
+ return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small> ${icon}</li>`;
},
};
GfmAutoComplete.Labels = {
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 2429da9c061..dac105d7243 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -182,6 +182,10 @@ const bindEvents = () => {
text: s__('ProjectTemplates|Netlify/Hexo'),
icon: '.template-option .icon-netlify',
},
+ salesforcedx: {
+ text: s__('ProjectTemplates|SalesforceDX'),
+ icon: '.template-option svg.icon-gitlab',
+ },
serverless_framework: {
text: s__('ProjectTemplates|Serverless Framework/JS'),
icon: '.template-option .icon-serverless_framework',
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 755d97b091c..0953ca96317 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -181,6 +181,7 @@ class GroupsController < Groups::ApplicationController
:avatar,
:description,
:emails_disabled,
+ :mentions_disabled,
:lfs_enabled,
:name,
:path,
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index 1c828234f1b..6fde9abfdb0 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -55,7 +55,8 @@ module Users
username: group.full_path,
name: group.full_name,
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
diff --git a/app/services/issues/duplicate_service.rb b/app/services/issues/duplicate_service.rb
index 82c226f601e..c936d75e277 100644
--- a/app/services/issues/duplicate_service.rb
+++ b/app/services/issues/duplicate_service.rb
@@ -25,3 +25,5 @@ module Issues
end
end
end
+
+Issues::DuplicateService.prepend_if_ee('EE::Issues::DuplicateService')
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index 4c88660ccb9..618cfe57be4 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -23,6 +23,13 @@
%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.')
+ .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/allowed_email_domain', f: f, group: @group
= render 'groups/settings/lfs', f: f
diff --git a/changelogs/unreleased/3963-auto-related-duplicated-issues.yml b/changelogs/unreleased/3963-auto-related-duplicated-issues.yml
new file mode 100644
index 00000000000..670f71d32dc
--- /dev/null
+++ b/changelogs/unreleased/3963-auto-related-duplicated-issues.yml
@@ -0,0 +1,5 @@
+---
+title: Relate issues when they are marked as duplicated
+merge_request: 20161
+author: minghuan lei
+type: added
diff --git a/changelogs/unreleased/feat-group-mentions-prevention.yml b/changelogs/unreleased/feat-group-mentions-prevention.yml
new file mode 100644
index 00000000000..dd85a226111
--- /dev/null
+++ b/changelogs/unreleased/feat-group-mentions-prevention.yml
@@ -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
diff --git a/changelogs/unreleased/gitaly-version-v1.75.0.yml b/changelogs/unreleased/gitaly-version-v1.75.0.yml
new file mode 100644
index 00000000000..a4367d676e7
--- /dev/null
+++ b/changelogs/unreleased/gitaly-version-v1.75.0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Gitaly v1.75.0
+merge_request: 21045
+author:
+type: changed
diff --git a/changelogs/unreleased/salesforcedx_project_template.yml b/changelogs/unreleased/salesforcedx_project_template.yml
new file mode 100644
index 00000000000..3cdf55ed85b
--- /dev/null
+++ b/changelogs/unreleased/salesforcedx_project_template.yml
@@ -0,0 +1,5 @@
+---
+title: Add SalesforceDX project template
+merge_request: 20831
+author:
+type: added
diff --git a/db/migrate/20191114132259_add_mentions_disabled_to_namespaces.rb b/db/migrate/20191114132259_add_mentions_disabled_to_namespaces.rb
new file mode 100644
index 00000000000..dbd69568ef2
--- /dev/null
+++ b/db/migrate/20191114132259_add_mentions_disabled_to_namespaces.rb
@@ -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
diff --git a/db/schema.rb b/db/schema.rb
index 4be04185af5..2715c5717f7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -349,6 +349,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
t.boolean "sourcegraph_enabled", default: false, null: false
t.string "sourcegraph_url", limit: 255
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.string "encrypted_akismet_api_key_iv", limit: 255
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.text "encrypted_slack_app_verification_token"
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 ["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"
@@ -2603,6 +2603,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do
t.boolean "emails_disabled"
t.integer "max_pages_size"
t.integer "max_artifacts_size"
+ t.boolean "mentions_disabled"
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 ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id"
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 5f45a462f94..ad16aaa34ff 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -431,6 +431,23 @@ To enable this feature:
1. Expand the **Permissions, LFS, 2FA** section, and select **Disable email notifications**.
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
- **Projects**: View all projects within that group, add members to each project,
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 88dc8cbcabe..97ae429a33f 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -60,7 +60,7 @@ The following quick actions are applicable to descriptions, discussions and thre
| `/remove_epic` | ✓ | | | Remove from epic **(ULTIMATE)** |
| `/promote` | ✓ | | | Promote issue to epic **(ULTIMATE)** |
| `/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 |
| `/relate #issue1 #issue2` | ✓ | | | Mark issues as related **(STARTER)** |
| `/move <path/to/project>` | ✓ | | | Move this issue to another project |
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index 067b06b7590..36c41c6615f 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -97,7 +97,9 @@ module Banzai
def find_users_for_groups(ids)
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
def find_users_for_projects(ids)
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index 279fc4aa375..b4ee8818925 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -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('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('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')
].freeze
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index 91e2ff0b10d..37688d6e0e7 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -29,14 +29,14 @@ module Gitlab
def event(category, action, label: nil, property: nil, value: nil, context: nil)
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
def self_describing_event(schema_url, event_data_json, context: nil)
return unless enabled?
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
def snowplow_options(group)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 405254c6278..68d3e795434 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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."
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}."
msgstr ""
@@ -8860,6 +8863,9 @@ msgstr ""
msgid "GroupSettings|Disable email notifications"
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."
msgstr ""
@@ -8908,6 +8914,9 @@ msgstr ""
msgid "GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects."
msgstr ""
+msgid "GroupSettings|This setting will prevent group members from being notified if the group is mentioned."
+msgstr ""
+
msgid "GroupSettings|Transfer group"
msgstr ""
@@ -13721,6 +13730,9 @@ msgstr ""
msgid "ProjectTemplates|Ruby on Rails"
msgstr ""
+msgid "ProjectTemplates|SalesforceDX"
+msgstr ""
+
msgid "ProjectTemplates|Serverless Framework/JS"
msgstr ""
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 8af49fd47a2..28ab2f2d6c4 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -1,6 +1,7 @@
/* eslint no-param-reassign: "off" */
import $ from 'jquery';
+import { membersBeforeSave } from '~/gfm_auto_complete';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
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', () => {
it('should return default template', () => {
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', () => {
const dataSources = {
labels: `${TEST_HOST}/autocomplete_sources/labels`,
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index 931fb1e3953..71d2e1de3b6 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -19,15 +19,23 @@ describe Banzai::ReferenceParser::UserParser do
link['data-group'] = project.group.id.to_s
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
expect(subject.referenced_by([link])).to eq([])
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
context 'using a non-existing group ID' do
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
index 5559b1e4291..a2e3e2146f3 100644
--- a/spec/lib/gitlab/project_template_spec.rb
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -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('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('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')
]
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index dc877f20cae..efb07d9dc95 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -97,7 +97,7 @@ describe Gitlab::Tracking do
'_property_',
'_value_',
nil,
- timestamp.to_i
+ (timestamp.to_f * 1000).to_i
)
track_event
@@ -130,7 +130,7 @@ describe Gitlab::Tracking do
expect(tracker).to receive(:track_self_describing_event).with(
'_event_json_',
nil,
- timestamp.to_i
+ (timestamp.to_f * 1000).to_i
)
track_event
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 604befd7225..b8a0e118479 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -689,8 +689,9 @@ describe Issues::UpdateService, :mailer do
context 'valid canonical_issue_id' do
it 'calls the duplicate service with both issues' do
- expect_any_instance_of(Issues::DuplicateService)
- .to receive(:execute).with(issue, canonical_issue)
+ expect_next_instance_of(Issues::DuplicateService) do |service|
+ expect(service).to receive(:execute).with(issue, canonical_issue)
+ end
update_issue(canonical_issue_id: canonical_issue.id)
end
diff --git a/spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb
deleted file mode 100644
index 3834b8b2b87..00000000000
--- a/spec/support/shared_examples/quick_actions/issue/duplicate_quick_action_shared_examples.rb
+++ /dev/null
@@ -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
diff --git a/vendor/project_templates/salesforcedx.tar.gz b/vendor/project_templates/salesforcedx.tar.gz
new file mode 100644
index 00000000000..f92721a453f
--- /dev/null
+++ b/vendor/project_templates/salesforcedx.tar.gz
Binary files differ