Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-04-23 12:10:03 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-23 12:10:03 +0300
commit65f7976d0cd11d91a4c0945b2c63a1aa2f888b07 (patch)
tree07a0e774b12b29352ca6b3bd87b108879ebb00b9 /app
parent1165608bfd217a96e133487d6049a989a15789c4 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue126
-rw-r--r--app/channels/graphql_channel.rb56
-rw-r--r--app/controllers/graphql_controller.rb2
-rw-r--r--app/graphql/gitlab_schema.rb2
-rw-r--r--app/graphql/graphql_triggers.rb7
-rw-r--r--app/graphql/subscriptions/base_subscription.rb31
-rw-r--r--app/graphql/subscriptions/issuable_updated.rb29
-rw-r--r--app/graphql/types/issuable_type.rb23
-rw-r--r--app/graphql/types/subscription_type.rb10
-rw-r--r--app/helpers/application_settings_helper.rb2
-rw-r--r--app/models/concerns/routable.rb4
-rw-r--r--app/models/project_services/youtrack_service.rb9
-rw-r--r--app/models/user.rb4
-rw-r--r--app/services/issues/update_service.rb20
-rw-r--r--app/services/packages/debian/generate_distribution_key_service.rb106
-rw-r--r--app/services/system_notes/base_service.rb6
-rw-r--r--app/views/search/results/_user.html.haml6
17 files changed, 397 insertions, 46 deletions
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index 288d6711682..07cc0ce46bc 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -20,6 +20,7 @@ import axios from '~/lib/utils/axios_utils';
import csrf from '~/lib/utils/csrf';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
+import validation from '~/vue_shared/directives/validation';
const PRIVATE_VISIBILITY = 'private';
const INTERNAL_VISIBILITY = 'internal';
@@ -31,6 +32,13 @@ const ALLOWED_VISIBILITY = {
public: [INTERNAL_VISIBILITY, PRIVATE_VISIBILITY, PUBLIC_VISIBILITY],
};
+const initFormField = ({ value, required = true, skipValidation = false }) => ({
+ value,
+ required,
+ state: skipValidation ? true : null,
+ feedback: null,
+});
+
export default {
components: {
GlForm,
@@ -46,6 +54,9 @@ export default {
GlFormRadioGroup,
GlFormSelect,
},
+ directives: {
+ validation: validation(),
+ },
inject: {
newGroupPath: {
default: '',
@@ -77,7 +88,8 @@ export default {
},
projectDescription: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
projectVisibility: {
type: String,
@@ -85,16 +97,30 @@ export default {
},
},
data() {
+ const form = {
+ state: false,
+ showValidation: false,
+ fields: {
+ namespace: initFormField({
+ value: null,
+ }),
+ name: initFormField({ value: this.projectName }),
+ slug: initFormField({ value: this.projectPath }),
+ description: initFormField({
+ value: this.projectDescription,
+ required: false,
+ skipValidation: true,
+ }),
+ visibility: initFormField({
+ value: this.projectVisibility,
+ skipValidation: true,
+ }),
+ },
+ };
return {
isSaving: false,
namespaces: [],
- selectedNamespace: {},
- fork: {
- name: this.projectName,
- slug: this.projectPath,
- description: this.projectDescription,
- visibility: this.projectVisibility,
- },
+ form,
};
},
computed: {
@@ -106,7 +132,7 @@ export default {
},
namespaceAllowedVisibility() {
return (
- ALLOWED_VISIBILITY[this.selectedNamespace.visibility] ||
+ ALLOWED_VISIBILITY[this.form.fields.namespace.value?.visibility] ||
ALLOWED_VISIBILITY[PUBLIC_VISIBILITY]
);
},
@@ -139,16 +165,17 @@ export default {
},
},
watch: {
- selectedNamespace(newVal) {
+ // eslint-disable-next-line func-names
+ 'form.fields.namespace.value': function (newVal) {
const { visibility } = newVal;
if (this.projectAllowedVisibility.includes(visibility)) {
- this.fork.visibility = visibility;
+ this.form.fields.visibility.value = visibility;
}
},
// eslint-disable-next-line func-names
- 'fork.name': function (newVal) {
- this.fork.slug = kebabCase(newVal);
+ 'form.fields.name.value': function (newVal) {
+ this.form.fields.slug.value = kebabCase(newVal);
},
},
mounted() {
@@ -166,19 +193,25 @@ export default {
);
},
async onSubmit() {
+ this.form.showValidation = true;
+
+ if (!this.form.state) {
+ return;
+ }
+
this.isSaving = true;
+ this.form.showValidation = false;
const { projectId } = this;
- const { name, slug, description, visibility } = this.fork;
- const { id: namespaceId } = this.selectedNamespace;
+ const { name, slug, description, visibility, namespace } = this.form.fields;
const postParams = {
id: projectId,
- name,
- namespace_id: namespaceId,
- path: slug,
- description,
- visibility,
+ name: name.value,
+ namespace_id: namespace.value.id,
+ path: slug.value,
+ description: description.value,
+ visibility: visibility.value,
};
const forkProjectPath = `/api/:version/projects/:id/fork`;
@@ -198,16 +231,34 @@ export default {
</script>
<template>
- <gl-form method="POST" @submit.prevent="onSubmit">
+ <gl-form novalidate method="POST" @submit.prevent="onSubmit">
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
- <gl-form-group label="Project name" label-for="fork-name">
- <gl-form-input id="fork-name" v-model="fork.name" data-testid="fork-name-input" required />
+ <gl-form-group
+ :label="__('Project name')"
+ label-for="fork-name"
+ :invalid-feedback="form.fields.name.feedback"
+ >
+ <gl-form-input
+ id="fork-name"
+ v-model="form.fields.name.value"
+ v-validation:[form.showValidation]
+ name="name"
+ data-testid="fork-name-input"
+ :state="form.fields.name.state"
+ required
+ />
</gl-form-group>
<div class="gl-md-display-flex">
<div class="gl-flex-basis-half">
- <gl-form-group label="Project URL" label-for="fork-url" class="gl-md-mr-3">
+ <gl-form-group
+ :label="__('Project URL')"
+ label-for="fork-url"
+ class="gl-md-mr-3"
+ :state="form.fields.namespace.state"
+ :invalid-feedback="s__('ForkProject|Please select a namespace')"
+ >
<gl-form-input-group>
<template #prepend>
<gl-input-group-text>
@@ -216,9 +267,12 @@ export default {
</template>
<gl-form-select
id="fork-url"
- v-model="selectedNamespace"
+ v-model="form.fields.namespace.value"
+ v-validation:[form.showValidation]
+ name="namespace"
data-testid="fork-url-input"
data-qa-selector="fork_namespace_dropdown"
+ :state="form.fields.namespace.state"
required
>
<template slot="first">
@@ -232,11 +286,19 @@ export default {
</gl-form-group>
</div>
<div class="gl-flex-basis-half">
- <gl-form-group label="Project slug" label-for="fork-slug" class="gl-md-ml-3">
+ <gl-form-group
+ :label="__('Project slug')"
+ label-for="fork-slug"
+ class="gl-md-ml-3"
+ :invalid-feedback="form.fields.slug.feedback"
+ >
<gl-form-input
id="fork-slug"
- v-model="fork.slug"
+ v-model="form.fields.slug.value"
+ v-validation:[form.showValidation]
data-testid="fork-slug-input"
+ name="slug"
+ :state="form.fields.slug.state"
required
/>
</gl-form-group>
@@ -250,11 +312,13 @@ export default {
</gl-link>
</p>
- <gl-form-group label="Project description (optional)" label-for="fork-description">
+ <gl-form-group :label="__('Project description (optional)')" label-for="fork-description">
<gl-form-textarea
id="fork-description"
- v-model="fork.description"
+ v-model="form.fields.description.value"
data-testid="fork-description-textarea"
+ name="description"
+ :state="form.fields.description.state"
/>
</gl-form-group>
@@ -266,8 +330,9 @@ export default {
</gl-link>
</label>
<gl-form-radio-group
- v-model="fork.visibility"
+ v-model="form.fields.visibility.value"
data-testid="fork-visibility-radio-group"
+ name="visibility"
required
>
<gl-form-radio
@@ -291,6 +356,7 @@ export default {
type="submit"
category="primary"
variant="confirm"
+ class="js-no-auto-disable"
data-testid="submit-button"
data-qa-selector="fork_project_button"
:loading="isSaving"
diff --git a/app/channels/graphql_channel.rb b/app/channels/graphql_channel.rb
new file mode 100644
index 00000000000..d364cc2b64b
--- /dev/null
+++ b/app/channels/graphql_channel.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+# This is based on https://github.com/rmosolgo/graphql-ruby/blob/v1.11.8/lib/graphql/subscriptions/action_cable_subscriptions.rb#L19-L82
+# modified to work with our own ActionCableLink client
+
+class GraphqlChannel < ApplicationCable::Channel # rubocop:disable Gitlab/NamespacedClass
+ def subscribed
+ @subscription_ids = []
+
+ query = params['query']
+ variables = Gitlab::Graphql::Variables.new(params['variables']).to_h
+ operation_name = params['operationName']
+
+ result = GitlabSchema.execute(
+ query,
+ context: context,
+ variables: variables,
+ operation_name: operation_name
+ )
+
+ payload = {
+ result: result.to_h,
+ more: result.subscription?
+ }
+
+ # Track the subscription here so we can remove it
+ # on unsubscribe.
+ if result.context[:subscription_id]
+ @subscription_ids << result.context[:subscription_id]
+ end
+
+ transmit(payload)
+ end
+
+ def unsubscribed
+ @subscription_ids.each do |sid|
+ GitlabSchema.subscriptions.delete_subscription(sid)
+ end
+ end
+
+ rescue_from Gitlab::Graphql::Variables::Invalid do |exception|
+ transmit({ errors: [{ message: exception.message }] })
+ end
+
+ private
+
+ # When modifying the context, also update GraphqlController#context if needed
+ # so that we have similar context when executing queries, mutations, and subscriptions
+ #
+ # Objects added to the context may also need to be reloaded in
+ # `Subscriptions::BaseSubscription` so that they are not stale
+ def context
+ # is_sessionless_user is always false because we only support cookie auth in ActionCable
+ { channel: self, current_user: current_user, is_sessionless_user: false }
+ end
+end
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index a13ec1daddb..b7daff04373 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -109,6 +109,8 @@ class GraphqlController < ApplicationController
end
end
+ # When modifying the context, also update GraphqlChannel#context if needed
+ # so that we have similar context when executing queries, mutations, and subscriptions
def context
@context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user?, request: request }
end
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index 8369d0e120f..3778c928ec5 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -10,6 +10,7 @@ class GitlabSchema < GraphQL::Schema
DEFAULT_MAX_DEPTH = 15
AUTHENTICATED_MAX_DEPTH = 20
+ use GraphQL::Subscriptions::ActionCableSubscriptions
use GraphQL::Pagination::Connections
use BatchLoader::GraphQL
use Gitlab::Graphql::Pagination::Connections
@@ -24,6 +25,7 @@ class GitlabSchema < GraphQL::Schema
query Types::QueryType
mutation Types::MutationType
+ subscription Types::SubscriptionType
default_max_page_size 100
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
new file mode 100644
index 00000000000..671c7c2cd25
--- /dev/null
+++ b/app/graphql/graphql_triggers.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module GraphqlTriggers
+ def self.issuable_assignees_updated(issuable)
+ GitlabSchema.subscriptions.trigger('issuableAssigneesUpdated', { issuable_id: issuable.to_gid }, issuable)
+ end
+end
diff --git a/app/graphql/subscriptions/base_subscription.rb b/app/graphql/subscriptions/base_subscription.rb
new file mode 100644
index 00000000000..5f7931787df
--- /dev/null
+++ b/app/graphql/subscriptions/base_subscription.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Subscriptions
+ class BaseSubscription < GraphQL::Schema::Subscription
+ object_class Types::BaseObject
+ field_class Types::BaseField
+
+ def initialize(object:, context:, field:)
+ super
+
+ # Reset user so that we don't use a stale user for authorization
+ current_user.reset if current_user
+ end
+
+ def authorized?(*)
+ raise NotImplementedError
+ end
+
+ private
+
+ def unauthorized!
+ unsubscribe if context.query.subscription_update?
+
+ raise GraphQL::ExecutionError, 'Unauthorized subscription'
+ end
+
+ def current_user
+ context[:current_user]
+ end
+ end
+end
diff --git a/app/graphql/subscriptions/issuable_updated.rb b/app/graphql/subscriptions/issuable_updated.rb
new file mode 100644
index 00000000000..c1d82bfcf9c
--- /dev/null
+++ b/app/graphql/subscriptions/issuable_updated.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Subscriptions
+ class IssuableUpdated < BaseSubscription
+ include Gitlab::Graphql::Laziness
+
+ payload_type Types::IssuableType
+
+ argument :issuable_id, Types::GlobalIDType[Issuable],
+ required: true,
+ description: 'ID of the issuable.'
+
+ def subscribe(issuable_id:)
+ nil
+ end
+
+ def authorized?(issuable_id:)
+ # TODO: remove this check when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ raise Gitlab::Graphql::Errors::ArgumentError, 'Invalid IssuableID' unless issuable_id.is_a?(GlobalID)
+
+ issuable = force(GitlabSchema.find_by_gid(issuable_id))
+
+ unauthorized! unless issuable && Ability.allowed?(current_user, :"read_#{issuable.to_ability_name}", issuable)
+
+ true
+ end
+ end
+end
diff --git a/app/graphql/types/issuable_type.rb b/app/graphql/types/issuable_type.rb
new file mode 100644
index 00000000000..fc57491bb59
--- /dev/null
+++ b/app/graphql/types/issuable_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ class IssuableType < BaseUnion
+ graphql_name 'Issuable'
+ description 'Represents an issuable.'
+
+ possible_types Types::IssueType, Types::MergeRequestType
+
+ def self.resolve_type(object, context)
+ case object
+ when Issue
+ Types::IssueType
+ when MergeRequest
+ Types::MergeRequestType
+ else
+ raise 'Unsupported issuable type'
+ end
+ end
+ end
+end
+
+Types::IssuableType.prepend_if_ee('::EE::Types::IssuableType')
diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb
new file mode 100644
index 00000000000..5356a998f0d
--- /dev/null
+++ b/app/graphql/types/subscription_type.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ class SubscriptionType < ::Types::BaseObject
+ graphql_name 'Subscription'
+
+ field :issuable_assignees_updated, subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the assignees of an issuable are updated.'
+ end
+end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index cda87cbd212..4373f47877f 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -393,7 +393,7 @@ module ApplicationSettingsHelper
end
def integration_expanded?(substring)
- @application_setting.errors.any? { |k| k.to_s.start_with?(substring) }
+ @application_setting.errors.messages.any? { |k, _| k.to_s.start_with?(substring) }
end
def instance_clusters_enabled?
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 71d8e06de76..5881997ea56 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -124,7 +124,9 @@ module Routable
def set_path_errors
route_path_errors = self.errors.delete(:"route.path")
- self.errors[:path].concat(route_path_errors) if route_path_errors
+ route_path_errors&.each do |msg|
+ self.errors.add(:path, msg)
+ end
end
def full_name_changed?
diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
index 30abd0159b3..94cbd6c5959 100644
--- a/app/models/project_services/youtrack_service.rb
+++ b/app/models/project_services/youtrack_service.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class YoutrackService < IssueTrackerService
+ include ActionView::Helpers::UrlHelper
+
validates :project_url, :issues_url, presence: true, public_url: true, if: :activated?
# {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1, gl-030
@@ -17,7 +19,12 @@ class YoutrackService < IssueTrackerService
end
def description
- s_('IssueTracker|YouTrack issue tracker')
+ s_("IssueTracker|Use YouTrack as this project's issue tracker.")
+ end
+
+ def help
+ docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/youtrack'), target: '_blank', rel: 'noopener noreferrer'
+ s_("IssueTracker|Use YouTrack as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
diff --git a/app/models/user.rb b/app/models/user.rb
index 92d11d231ec..a5ef8d64133 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1414,7 +1414,9 @@ class User < ApplicationRecord
if namespace_path_errors.include?('has already been taken') && !User.exists?(username: username)
self.errors.add(:base, :username_exists_as_a_different_namespace)
else
- self.errors[:username].concat(namespace_path_errors)
+ namespace_path_errors.each do |msg|
+ self.errors.add(:username, msg)
+ end
end
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 702527d80a7..e05df029ae3 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -61,12 +61,7 @@ module Issues
todo_service.update_issue(issue, current_user, old_mentioned_users)
end
- if issue.assignees != old_assignees
- create_assignee_note(issue, old_assignees)
- notification_service.async.reassigned_issue(issue, current_user, old_assignees)
- todo_service.reassigned_assignable(issue, current_user, old_assignees)
- track_incident_action(current_user, issue, :incident_assigned)
- end
+ handle_assignee_changes(issue, old_assignees)
if issue.previous_changes.include?('confidential')
# don't enqueue immediately to prevent todos removal in case of a mistake
@@ -90,6 +85,19 @@ module Issues
end
end
+ def handle_assignee_changes(issue, old_assignees)
+ return if issue.assignees == old_assignees
+
+ create_assignee_note(issue, old_assignees)
+ notification_service.async.reassigned_issue(issue, current_user, old_assignees)
+ todo_service.reassigned_assignable(issue, current_user, old_assignees)
+ track_incident_action(current_user, issue, :incident_assigned)
+
+ if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project)
+ GraphqlTriggers.issuable_assignees_updated(issue)
+ end
+ end
+
def handle_task_changes(issuable)
todo_service.resolve_todos_for_target(issuable, current_user)
todo_service.update_issue(issuable, current_user)
diff --git a/app/services/packages/debian/generate_distribution_key_service.rb b/app/services/packages/debian/generate_distribution_key_service.rb
new file mode 100644
index 00000000000..28c97c7681e
--- /dev/null
+++ b/app/services/packages/debian/generate_distribution_key_service.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+module Packages
+ module Debian
+ class GenerateDistributionKeyService
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(current_user:, params: {})
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute
+ raise ArgumentError, 'Please provide a user' unless current_user.is_a?(User)
+
+ generate_key
+ end
+
+ private
+
+ attr_reader :current_user, :params
+
+ def passphrase
+ strong_memoize(:passphrase) do
+ params[:passphrase] || ::User.random_password
+ end
+ end
+
+ def pinentry_script_content
+ escaped_passphrase = Shellwords.escape(passphrase)
+
+ <<~EOF
+ #!/bin/sh
+
+ echo OK Pleased to meet you
+
+ while read -r cmd; do
+ case "$cmd" in
+ GETPIN) echo D #{escaped_passphrase}; echo OK;;
+ *) echo OK;;
+ esac
+ done
+ EOF
+ end
+
+ def using_pinentry
+ Gitlab::Gpg.using_tmp_keychain do
+ home_dir = Gitlab::Gpg.current_home_dir
+
+ File.write("#{home_dir}/pinentry.sh", pinentry_script_content, mode: 'w', perm: 0755)
+
+ File.write("#{home_dir}/gpg-agent.conf", "pinentry-program #{home_dir}/pinentry.sh\n", mode: 'w')
+
+ GPGME::Ctx.new(armor: true, offline: true) do |ctx|
+ yield ctx
+ end
+ end
+ end
+
+ def generate_key_params
+ # https://www.gnupg.org/documentation/manuals/gnupg/Unattended-GPG-key-generation.html
+ '<GnupgKeyParms format="internal">' + "\n" +
+ {
+ 'Key-Type': params[:key_type] || 'RSA',
+ 'Key-Length': params[:key_length] || 4096,
+ 'Key-Usage': params[:key_usage] || 'sign',
+ 'Name-Real': params[:name_real] || 'GitLab Debian repository',
+ 'Name-Email': params[:name_email] || Gitlab.config.gitlab.email_reply_to,
+ 'Name-Comment': params[:name_comment] || 'GitLab Debian repository automatic signing key',
+ 'Expire-Date': params[:expire_date] || 0,
+ 'Passphrase': passphrase
+ }.map { |k, v| "#{k}: #{v}\n" }.join +
+ '</GnupgKeyParms>'
+ end
+
+ def generate_key
+ using_pinentry do |ctx|
+ # Generate key
+ ctx.generate_key generate_key_params
+
+ key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
+ fingerprint = key.fingerprint
+
+ # Export private key
+ data = GPGME::Data.new
+ ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
+ data.seek 0
+ private_key = data.read
+
+ # Export public key
+ data = GPGME::Data.new
+ ctx.export_keys fingerprint, data
+ data.seek 0
+ public_key = data.read
+
+ {
+ private_key: private_key,
+ public_key: public_key,
+ passphrase: passphrase,
+ fingerprint: fingerprint
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/system_notes/base_service.rb b/app/services/system_notes/base_service.rb
index 7341a25b133..ee7784c127b 100644
--- a/app/services/system_notes/base_service.rb
+++ b/app/services/system_notes/base_service.rb
@@ -13,10 +13,10 @@ module SystemNotes
protected
def create_note(note_summary)
- note = Note.create(note_summary.note.merge(system: true))
- note.system_note_metadata = SystemNoteMetadata.new(note_summary.metadata) if note_summary.metadata?
+ note_params = note_summary.note.merge(system: true)
+ note_params[:system_note_metadata] = SystemNoteMetadata.new(note_summary.metadata) if note_summary.metadata?
- note
+ Note.create(note_params)
end
def content_tag(*args)
diff --git a/app/views/search/results/_user.html.haml b/app/views/search/results/_user.html.haml
index 8060a1577e4..9e70d9c9baa 100644
--- a/app/views/search/results/_user.html.haml
+++ b/app/views/search/results/_user.html.haml
@@ -1,9 +1,9 @@
%ul.content-list
%li
- .avatar-cell.d-none.d-sm-block
- = user_avatar(user: user, user_name: user.name, css_class: 'd-none d-sm-inline avatar s40')
+ .avatar-cell
+ = user_avatar(user: user, size: 40, user_name: user.name)
.user-info
- = link_to user_path(user), class: 'd-none d-sm-inline' do
+ = link_to user_path(user) do
.item-title
= user.name
= user_status(user)