Commit 2c630ba9 authored by Alexandru Croitor's avatar Alexandru Croitor

Remove useless .becomes(Namespace) calls

After https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12566
most route helpers methods do not need to take project.namespace as a
parameter and should be enogh to pass only project.

Removing the need to pass project.namespace helps to reduce some cases
where N+1 is generated due to project.namespace.becomes(Namespace)
creating a new instance of the namepsace thus not making use of
eager loaded associations
parent 0e418163
...@@ -105,7 +105,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -105,7 +105,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
action_or_env_url = action_or_env_url =
if stop_action if stop_action
polymorphic_url([project.namespace.becomes(Namespace), project, stop_action]) polymorphic_url([project, stop_action])
else else
project_environment_url(project, @environment) project_environment_url(project, @environment)
end end
......
...@@ -12,7 +12,7 @@ module AwardEmojiHelper ...@@ -12,7 +12,7 @@ module AwardEmojiHelper
toggle_award_emoji_project_note_path(@project, awardable.id) toggle_award_emoji_project_note_path(@project, awardable.id)
end end
else else
url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) url_for([:toggle_award_emoji, @project, awardable])
end end
end end
end end
......
...@@ -2,10 +2,8 @@ ...@@ -2,10 +2,8 @@
module CustomMetricsHelper module CustomMetricsHelper
def custom_metrics_data(project, metric) def custom_metrics_data(project, metric)
custom_metrics_path = project.namespace.becomes(::Namespace)
{ {
'custom-metrics-path' => url_for([custom_metrics_path, project, metric]), 'custom-metrics-path' => url_for([project, metric]),
'metric-persisted' => metric.persisted?.to_s, 'metric-persisted' => metric.persisted?.to_s,
'edit-project-service-path' => edit_project_service_path(project, PrometheusService), 'edit-project-service-path' => edit_project_service_path(project, PrometheusService),
'validate-query-path' => validate_query_project_prometheus_metrics_path(project), 'validate-query-path' => validate_query_project_prometheus_metrics_path(project),
......
...@@ -19,7 +19,7 @@ module EnvironmentHelper ...@@ -19,7 +19,7 @@ module EnvironmentHelper
end end
def deployment_path(deployment) def deployment_path(deployment)
[deployment.project.namespace.becomes(Namespace), deployment.project, deployment.deployable] [deployment.project, deployment.deployable]
end end
def deployment_link(deployment, text: nil) def deployment_link(deployment, text: nil)
......
...@@ -199,8 +199,7 @@ module EventsHelper ...@@ -199,8 +199,7 @@ module EventsHelper
elsif event.design_note? elsif event.design_note?
design_url(event.note_target, anchor: dom_id(event.note)) design_url(event.note_target, anchor: dom_id(event.note))
else else
polymorphic_url([event.project.namespace.becomes(Namespace), polymorphic_url([event.project, event.note_target],
event.project, event.note_target],
anchor: dom_id(event.target)) anchor: dom_id(event.target))
end end
end end
......
...@@ -100,8 +100,12 @@ module GitlabRoutingHelper ...@@ -100,8 +100,12 @@ module GitlabRoutingHelper
toggle_award_emoji_snippet_path(*args) toggle_award_emoji_snippet_path(*args)
end end
def toggle_award_emoji_namespace_project_project_snippet_path(*args) def toggle_award_emoji_project_project_snippet_path(*args)
toggle_award_emoji_namespace_project_snippet_path(*args) toggle_award_emoji_project_snippet_path(*args)
end
def toggle_award_emoji_project_project_snippet_url(*args)
toggle_award_emoji_project_snippet_url(*args)
end end
## Members ## Members
......
...@@ -126,7 +126,7 @@ module NotesHelper ...@@ -126,7 +126,7 @@ module NotesHelper
if @snippet.is_a?(PersonalSnippet) if @snippet.is_a?(PersonalSnippet)
[@note] [@note]
else else
[@project.namespace.becomes(Namespace), @project, @note] [@project, @note]
end end
end end
......
...@@ -40,7 +40,7 @@ module TimeboxesHelper ...@@ -40,7 +40,7 @@ module TimeboxesHelper
opts = { milestone_title: milestone.title, state: state } opts = { milestone_title: milestone.title, state: state }
if @project if @project
polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts) polymorphic_path([@project, type], opts)
elsif @group elsif @group
polymorphic_url([type, @group], opts) polymorphic_url([type, @group], opts)
else else
......
...@@ -375,7 +375,6 @@ class JiraService < IssueTrackerService ...@@ -375,7 +375,6 @@ class JiraService < IssueTrackerService
def build_entity_url(noteable_type, entity_id) def build_entity_url(noteable_type, entity_id)
polymorphic_url( polymorphic_url(
[ [
self.project.namespace.becomes(Namespace),
self.project, self.project,
noteable_type.to_sym noteable_type.to_sym
], ],
......
...@@ -24,7 +24,7 @@ class EventPresenter < Gitlab::View::Presenter::Delegated ...@@ -24,7 +24,7 @@ class EventPresenter < Gitlab::View::Presenter::Delegated
when Group when Group
[event.group, event.target] [event.group, event.target]
when Project when Project
[event.project.namespace.becomes(Namespace), event.project, event.target] [event.project, event.target]
else else
'' ''
end end
......
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
- @projects.each do |project| - @projects.each do |project|
%li %li
%strong %strong
= link_to project.full_name, [:admin, project.namespace.becomes(Namespace), project] = link_to project.full_name, [:admin, project]
%span.badge.badge-pill %span.badge.badge-pill
= storage_counter(project.statistics.storage_size) = storage_counter(project.statistics.storage_size)
%span.float-right.light %span.float-right.light
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
- shared_projects.each do |project| - shared_projects.each do |project|
%li %li
%strong %strong
= link_to project.full_name, [:admin, project.namespace.becomes(Namespace), project] = link_to project.full_name, [:admin, project]
%span.badge.badge-pill %span.badge.badge-pill
= storage_counter(project.statistics.storage_size) = storage_counter(project.statistics.storage_size)
%span.float-right.light %span.float-right.light
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
%td %td
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer' = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer'
%td %td
= link_to project.full_path, [project.namespace.becomes(Namespace), project] = link_to project.full_path, project
%td.job-status %td.job-status
- case project.import_status - case project.import_status
- when 'finished' - when 'finished'
......
<%= 'Label'.pluralize(@label_names.size) %> added: <%= @label_names.to_sentence %> <%= 'Label'.pluralize(@label_names.size) %> added: <%= @label_names.to_sentence %>
<%= url_for([issuable.project.namespace.becomes(Namespace), issuable.project, issuable, { only_path: false }]) %> <%= url_for([issuable.project, issuable, { only_path: false }]) %>
Reassigned Issue <%= @issue.iid %> Reassigned Issue <%= @issue.iid %>
<%= url_for([@issue.project.namespace.becomes(Namespace), @issue.project, @issue, { only_path: false }]) %> <%= url_for([@issue.project, @issue, { only_path: false }]) %>
Assignee changed <%= "from #{sanitize_name(@previous_assignees.map(&:name).to_sentence)}" if @previous_assignees.any? -%> Assignee changed <%= "from #{sanitize_name(@previous_assignees.map(&:name).to_sentence)}" if @previous_assignees.any? -%>
to <%= "#{@issue.assignees.any? ? @issue.assignee_list : 'Unassigned'}" %> to <%= "#{@issue.assignees.any? ? @issue.assignee_list : 'Unassigned'}" %>
Reassigned Merge Request <%= @merge_request.iid %> Reassigned Merge Request <%= @merge_request.iid %>
<%= url_for([@merge_request.project.namespace.becomes(Namespace), @merge_request.project, @merge_request, { only_path: false }]) %> <%= url_for([@merge_request.project, @merge_request, { only_path: false }]) %>
Assignee changed <%= "from #{sanitize_name(@previous_assignees.map(&:name).to_sentence)}" if @previous_assignees.any? -%> Assignee changed <%= "from #{sanitize_name(@previous_assignees.map(&:name).to_sentence)}" if @previous_assignees.any? -%>
to <%= "#{@merge_request.assignees.any? ? @merge_request.assignee_list : 'Unassigned'}" %> to <%= "#{@merge_request.assignees.any? ? @merge_request.assignee_list : 'Unassigned'}" %>
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
.modal-body .modal-body
- if description - if description
%p= description %p= description
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "js-#{type}-form js-requires-input" do = form_tag [type.underscore, @project, commit], method: :post, remote: false, class: "js-#{type}-form js-requires-input" do
.form-group.branch .form-group.branch
= label_tag 'start_branch', branch_label, class: 'label-bold' = label_tag 'start_branch', branch_label, class: 'label-bold'
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= _('Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one.') = _('Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one.')
.settings-content .settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, anchor: 'default-branch-settings' }, authenticity_token: true do |f| = form_for @project, remote: true, html: { multipart: true, anchor: 'default-branch-settings' }, authenticity_token: true do |f|
%fieldset %fieldset
- if @project.empty_repo? - if @project.empty_repo?
.text-secondary .text-secondary
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
%hr %hr
%div %div
= form_for [@project.namespace.becomes(Namespace), @project, @deploy_key], include_id: false, html: { class: 'js-requires-input' } do |f| = form_for [@project, @deploy_key], include_id: false, html: { class: 'js-requires-input' } do |f|
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key } = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
.form-actions .form-actions
= f.submit 'Save changes', class: 'btn-success btn' = f.submit 'Save changes', class: 'btn-success btn'
......
...@@ -10,5 +10,5 @@ ...@@ -10,5 +10,5 @@
- actions.each do |action| - actions.each do |action|
- next unless can?(current_user, :update_build, action) - next unless can?(current_user, :update_build, action)
%li %li
= link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow' do = link_to [:play, @project, action], method: :post, rel: 'nofollow' do
%span= action.name %span= action.name
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= s_('Environments|This action will run the job defined by %{environment_name} for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?').html_safe % {commit_id: commit_sha, environment_name: @environment.name} = s_('Environments|This action will run the job defined by %{environment_name} for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?').html_safe % {commit_id: commit_sha, environment_name: @environment.name}
.modal-footer .modal-footer
= button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' } = button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' }
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-danger' do = link_to [:retry, @project, deployment.deployable], method: :post, class: 'btn btn-danger' do
- if deployment.last? - if deployment.last?
= s_('Environments|Re-deploy') = s_('Environments|Re-deploy')
- else - else
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
%p= _('Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions.') %p= _('Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions.')
.settings-content .settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f| = form_for @project, remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' } %input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' }
%template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data_json(@project) %template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data_json(@project)
.js-project-permissions-form .js-project-permissions-form
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
.settings-content .settings-content
= render_if_exists 'shared/promotions/promote_mr_features' = render_if_exists 'shared/promotions/promote_mr_features'
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form js-mr-settings-form" }, authenticity_token: true do |f| = form_for @project, remote: true, html: { multipart: true, class: "merge-request-settings-form js-mr-settings-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' } %input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' }
= render 'projects/merge_request_settings', form: f = render 'projects/merge_request_settings', form: f
= f.submit _('Save changes'), class: "btn btn-success qa-save-merge-request-changes rspec-save-merge-request-changes" = f.submit _('Save changes'), class: "btn btn-success qa-save-merge-request-changes rspec-save-merge-request-changes"
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
.sub-section.rename-repository .sub-section.rename-repository
%h4.warning-title= _('Change path') %h4.warning-title= _('Change path')
= render 'projects/errors' = render 'projects/errors'
= form_for([@project.namespace.becomes(Namespace), @project]) do |f| = form_for @project do |f|
.form-group .form-group
= f.label :path, _('Path'), class: 'label-bold' = f.label :path, _('Path'), class: 'label-bold'
.form-group .form-group
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
- if can?(current_user, :change_namespace, @project) - if can?(current_user, :change_namespace, @project)
.sub-section .sub-section
%h4.danger-title= _('Transfer project') %h4.danger-title= _('Transfer project')
= form_for([@project.namespace.becomes(Namespace), @project], url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f| = form_for @project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } do |f|
.form-group .form-group
= label_tag :new_namespace_id, nil, class: 'label-bold' do = label_tag :new_namespace_id, nil, class: 'label-bold' do
%span= _('Select a new namespace') %span= _('Select a new namespace')
...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
%h4.danger-title= _('Remove fork relationship') %h4.danger-title= _('Remove fork relationship')
%p= remove_fork_project_description_message(@project) %p= remove_fork_project_description_message(@project)
= form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| = form_for @project, url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' } do |f|
%p %p
%strong= _('Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.') %strong= _('Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.')
= button_to _('Remove fork relationship'), '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_warning_message(@project) } = button_to _('Remove fork relationship'), '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_warning_message(@project) }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
- link_to_read_more = link_to(_("More information"), help_page_path("ci/environments/index.md")) - link_to_read_more = link_to(_("More information"), help_page_path("ci/environments/index.md"))
= _("Environments allow you to track deployments of your application %{link_to_read_more}.").html_safe % { link_to_read_more: link_to_read_more } = _("Environments allow you to track deployments of your application %{link_to_read_more}.").html_safe % { link_to_read_more: link_to_read_more }
= form_for [@project.namespace.becomes(Namespace), @project, @environment], html: { class: 'col-lg-9' } do |f| = form_for [@project, @environment], html: { class: 'col-lg-9' } do |f|
= form_errors(@environment) = form_errors(@environment)
.form-group .form-group
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= render 'shared/web_hooks/title_and_docs', hook: @hook = render 'shared/web_hooks/title_and_docs', hook: @hook
.col-lg-9.gl-mb-3 .col-lg-9.gl-mb-3
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f| = form_for [@project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
%span>= f.submit 'Save changes', class: 'btn btn-success gl-mr-3' %span>= f.submit 'Save changes', class: 'btn btn-success gl-mr-3'
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= render 'shared/web_hooks/title_and_docs', hook: @hook = render 'shared/web_hooks/title_and_docs', hook: @hook
.col-lg-8.gl-mb-3 .col-lg-8.gl-mb-3
= form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f| = form_for @hook, as: :hook, url: polymorphic_path([@project, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Add webhook', class: 'btn btn-success' = f.submit 'Add webhook', class: 'btn btn-success'
......
= form_for [@project.namespace.becomes(Namespace), @project, @issue], = form_for [@project, @issue],
html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' } do |f| html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' } do |f|
= render 'shared/issuable/form', f: f, issuable: @issue = render 'shared/issuable/form', f: f, issuable: @issue
- form = [@project.namespace.becomes(Namespace), @project, @issue] = render layout: 'layouts/recaptcha_verification', locals: { spammable: @issue } do
= render layout: 'layouts/recaptcha_verification', locals: { spammable: @issue, form: form } do
= hidden_field_tag(:merge_request_to_resolve_discussions_of, params[:merge_request_to_resolve_discussions_of]) = hidden_field_tag(:merge_request_to_resolve_discussions_of, params[:merge_request_to_resolve_discussions_of])
= hidden_field_tag(:discussion_to_resolve, params[:discussion_to_resolve]) = hidden_field_tag(:discussion_to_resolve, params[:discussion_to_resolve])
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], = form_for [@project, @merge_request],
html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
= render 'shared/issuable/form', f: f, issuable: @merge_request, presenter: @mr_presenter = render 'shared/issuable/form', f: f, issuable: @merge_request, presenter: @mr_presenter
%h3.page-title %h3.page-title
New Merge Request New Merge Request
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f| = form_for [@project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form js-requires-input" } do |f|
- if params[:nav_source].present? - if params[:nav_source].present?
= hidden_field_tag(:nav_source, params[:nav_source]) = hidden_field_tag(:nav_source, params[:nav_source])
.hide.alert.alert-danger.mr-compare-errors .hide.alert.alert-danger.mr-compare-errors
......
%h3.page-title %h3.page-title
New Merge Request New Merge Request
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| = form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
= render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter = render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter
= f.hidden_field :source_project_id = f.hidden_field :source_project_id
= f.hidden_field :source_branch = f.hidden_field :source_branch
......
= form_for [@project.namespace.becomes(Namespace), @project, @milestone], = form_for [@project, @milestone],
html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f| html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
= form_errors(@milestone) = form_errors(@milestone)
.row .row
......
= form_for @project, url: namespace_project_pages_path(@project.namespace.becomes(Namespace), @project), html: { class: 'inline', title: pages_https_only_title } do |f| = form_for @project, url: project_pages_path(@project), html: { class: 'inline', title: pages_https_only_title } do |f|
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https - if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
= render_if_exists 'shared/pages/max_pages_size_input', form: f = render_if_exists 'shared/pages/max_pages_size_input', form: f
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= _("New Pages Domain") = _("New Pages Domain")
= render 'projects/pages_domains/helper_text' = render 'projects/pages_domains/helper_text'
%div %div
= form_for [@project.namespace.becomes(Namespace), @project, domain_presenter], html: { class: 'fieldset-form' } do |f| = form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f } = render 'form', { f: f }
.form-actions .form-actions
= f.submit _('Create New Domain'), class: "btn btn-success" = f.submit _('Create New Domain'), class: "btn btn-success"
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
= _('Pages Domain') = _('Pages Domain')
= render 'projects/pages_domains/helper_text' = render 'projects/pages_domains/helper_text'
%div %div
= form_for [@project.namespace.becomes(Namespace), @project, domain_presenter], html: { class: 'fieldset-form' } do |f| = form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f } = render 'form', { f: f }
.form-actions.d-flex.justify-content-between .form-actions.d-flex.justify-content-between
= f.submit _('Save Changes'), class: "btn btn-success" = f.submit _('Save Changes'), class: "btn btn-success"
......
= form_for [@project.namespace.becomes(Namespace), @project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "js-pipeline-schedule-form pipeline-schedule-form" } do |f| = form_for [@project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "js-pipeline-schedule-form pipeline-schedule-form" } do |f|
= form_errors(@schedule) = form_errors(@schedule)
.form-group.row .form-group.row
.col-md-9 .col-md-9
......
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f| = form_for [@project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' } %input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
.card .card
.card-header .card-header
......
...@@ -20,4 +20,4 @@ ...@@ -20,4 +20,4 @@
- if can_admin_project - if can_admin_project
%td %td
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning" = link_to 'Unprotect', [@project, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning"
= form_for [@project.namespace.becomes(Namespace), @project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f| = form_for [@project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-tags-settings' } %input{ type: 'hidden', name: 'update_section', value: 'js-protected-tags-settings' }
.card .card
.card-header .card-header
......
...@@ -19,4 +19,4 @@ ...@@ -19,4 +19,4 @@
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
%td %td
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_tag, { update_section: 'js-protected-tags-settings' }], data: { confirm: 'Tag will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning' = link_to 'Unprotect', [@project, protected_tag, { update_section: 'js-protected-tags-settings' }], data: { confirm: 'Tag will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
- runner_project = @project.runner_projects.find_by(runner_id: runner) # rubocop: disable CodeReuse/ActiveRecord - runner_project = @project.runner_projects.find_by(runner_id: runner) # rubocop: disable CodeReuse/ActiveRecord
= link_to _('Disable for this project'), project_runner_project_path(@project, runner_project), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm' = link_to _('Disable for this project'), project_runner_project_path(@project, runner_project), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm'
- elsif runner.project_type? - elsif runner.project_type?
= form_for [@project.namespace.becomes(Namespace), @project, @project.runner_projects.new] do |f| = form_for [@project, @project.runner_projects.new] do |f|
= f.hidden_field :runner_id, value: runner.id = f.hidden_field :runner_id, value: runner.id
= f.submit _('Enable for this project'), class: 'btn btn-sm' = f.submit _('Enable for this project'), class: 'btn btn-sm'
.float-right .float-right
......
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project js-general-settings-form" }, authenticity_token: true do |f| = form_for [@project], remote: true, html: { multipart: true, class: "edit-project js-general-settings-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-general-settings' } %input{ name: 'update_section', type: 'hidden', value: 'js-general-settings' }
= form_errors(@project) = form_errors(@project)
......
- form = [@project.namespace.becomes(Namespace), @project, @snippet.becomes(Snippet)] = render 'layouts/recaptcha_verification', spammable: @snippet
= render 'layouts/recaptcha_verification', spammable: @snippet, form: form
= form_for [@project.namespace.becomes(Namespace), @project, @trigger], html: { class: 'gl-show-field-errors' } do |f| = form_for [@project, @trigger], html: { class: 'gl-show-field-errors' } do |f|
= form_errors(@trigger) = form_errors(@trigger)
- if @trigger.token - if @trigger.token
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= search_entries_info(@search_objects, @scope, @search_term) = search_entries_info(@search_objects, @scope, @search_term)
- unless @show_snippets - unless @show_snippets
- if @project - if @project
- link_to_project = link_to(@project.full_name, [@project.namespace.becomes(Namespace), @project], class: 'ml-md-1') - link_to_project = link_to(@project.full_name, @project, class: 'ml-md-1')
- if @scope == 'blobs' - if @scope == 'blobs'
- repository_ref = params[:repository_ref].to_s.presence || @project.default_branch - repository_ref = params[:repository_ref].to_s.presence || @project.default_branch
= s_("SearchCodeResults|in") = s_("SearchCodeResults|in")
......
.search-result-row .search-result-row
%h4 %h4
= confidential_icon(issue) = confidential_icon(issue)
= link_to namespace_project_issue_path(issue.project.namespace.becomes(Namespace), issue.project, issue) do = link_to project_issue_path(issue.project, issue) do
%span.term.str-truncated= issue.title %span.term.str-truncated= issue.title
- if issue.closed? - if issue.closed?
%span.badge.badge-danger.gl-ml-2= _("Closed") %span.badge.badge-danger.gl-ml-2= _("Closed")
......
.search-result-row .search-result-row
%h4 %h4
= link_to namespace_project_merge_request_path(merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request) do = link_to project_merge_request_path(merge_request.target_project, merge_request) do
%span.term.str-truncated= merge_request.title %span.term.str-truncated= merge_request.title
- if merge_request.merged? - if merge_request.merged?
%span.badge.badge-primary.gl-ml-2= _("Merged") %span.badge.badge-primary.gl-ml-2= _("Merged")
......
.search-result-row .search-result-row
%h4 %h4
= link_to namespace_project_milestone_path(milestone.project.namespace.becomes(Namespace), milestone.project, milestone) do = link_to project_milestone_path(milestone.project, milestone) do
%span.term.str-truncated= milestone.title %span.term.str-truncated= milestone.title
- if milestone.description.present? - if milestone.description.present?
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- noteable_text = show_unsubscribe_title?(noteable) ? %(#{noteable.title} (#{noteable.to_reference})) : %(#{noteable.to_reference}) - noteable_text = show_unsubscribe_title?(noteable) ? %(#{noteable.title} (#{noteable.to_reference})) : %(#{noteable.to_reference})
- show_project_path = can_read_project?(@sent_notification.project) - show_project_path = can_read_project?(@sent_notification.project)
- project_path = show_project_path ? @sent_notification.project.full_name : _("GitLab / Unsubscribe") - project_path = show_project_path ? @sent_notification.project.full_name : _("GitLab / Unsubscribe")
- noteable_url = show_project_path ? url_for([@sent_notification.project.namespace.becomes(Namespace), @sent_notification.project, noteable]) : breadcrumb_title_link - noteable_url = show_project_path ? url_for([@sent_notification.project, noteable]) : breadcrumb_title_link
- page_title _('Unsubscribe'), noteable_text, noteable_type.pluralize, project_path - page_title _('Unsubscribe'), noteable_text, noteable_type.pluralize, project_path
%h3.page-title %h3.page-title
......
= form_for [@project.namespace.becomes(Namespace), @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f| = form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
= form_errors(@deploy_keys.new_key) = form_errors(@deploy_keys.new_key)
.form-group.row .form-group.row
= f.label :title, class: "label-bold" = f.label :title, class: "label-bold"
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } } %aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } }
.issuable-sidebar.hidden .issuable-sidebar.hidden
= form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update" do = form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do
.block.issuable-sidebar-header .block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.float-left .filter-item.inline.update-issues-btn.float-left
= button_tag _('Update all'), class: "btn update-selected-issues btn-info", disabled: true = button_tag _('Update all'), class: "btn update-selected-issues btn-info", disabled: true
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.alert.alert-danger .alert.alert-danger
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did. Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
Please check out Please check out
= link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank", rel: 'noopener noreferrer' = link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project, issuable]), target: "_blank", rel: 'noopener noreferrer'
and make sure your changes will not unintentionally remove theirs and make sure your changes will not unintentionally remove theirs
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form = render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
...@@ -63,11 +63,11 @@ ...@@ -63,11 +63,11 @@
.row-content-block{ class: (is_footer ? "footer-block" : "middle-block") } .row-content-block{ class: (is_footer ? "footer-block" : "middle-block") }
.float-right .float-right
- if issuable.new_record? - if issuable.new_record?
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class]), class: 'btn btn-cancel' = link_to 'Cancel', polymorphic_path([@project, issuable.class]), class: 'btn btn-cancel'
- else - else
- if can?(current_user, :"destroy_#{issuable.to_ability_name}", @project) - if can?(current_user, :"destroy_#{issuable.to_ability_name}", @project)
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable], params: { destroy_confirm: true }), data: { confirm: "#{issuable.human_class_name} will be removed! Are you sure?" }, method: :delete, class: 'btn btn-danger btn-grouped' = link_to 'Delete', polymorphic_path([@project, issuable], params: { destroy_confirm: true }), data: { confirm: "#{issuable.human_class_name} will be removed! Are you sure?" }, method: :delete, class: 'btn btn-danger btn-grouped'
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel' = link_to 'Cancel', polymorphic_path([@project, issuable]), class: 'btn btn-grouped btn-cancel'
%span.gl-mr-3 %span.gl-mr-3
- if issuable.new_record? - if issuable.new_record?
......
- form = [@snippet.becomes(Snippet)] = render 'layouts/recaptcha_verification', spammable: @snippet
= render 'layouts/recaptcha_verification', spammable: @snippet, form: form
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
- if event.note? - if event.note?
= link_to event.note_target.to_reference, event_note_target_url(event), class: 'has-tooltip', title: event.target_title = link_to event.note_target.to_reference, event_note_target_url(event), class: 'has-tooltip', title: event.target_title
- elsif event.target - elsif event.target
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title = link_to event.target.to_reference, [event.project, event.target], class: 'has-tooltip', title: event.target_title
= s_('UserProfile|at') = s_('UserProfile|at')
%strong %strong
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
= link_to _("Learn more about approvals."), help_page_path("user/project/merge_requests/merge_request_approvals"), target: '_blank' = link_to _("Learn more about approvals."), help_page_path("user/project/merge_requests/merge_request_approvals"), target: '_blank'
.settings-content .settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { class: "merge-request-approval-settings-form js-mr-approvals-form" }, authenticity_token: true do |f| = form_for @project, remote: true, html: { class: "merge-request-approval-settings-form js-mr-approvals-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-approval-settings' } %input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-approval-settings' }
= render 'projects/merge_request_approvals_settings_form', form: f, project: @project = render 'projects/merge_request_approvals_settings_form', form: f, project: @project
= f.submit _("Save changes"), class: "btn btn-success qa-save-merge-request-approval-settings-button" = f.submit _("Save changes"), class: "btn btn-success qa-save-merge-request-approval-settings-button"
= form_for [@project.namespace.becomes(Namespace), @project, @protected_environment], html: { class: 'new-protected-environment js-new-protected-environment' } do |f| = form_for [@project, @protected_environment], html: { class: 'new-protected-environment js-new-protected-environment' } do |f|
.card .card
.card-header .card-header
%h3.card-title %h3.card-title
......
...@@ -9,4 +9,4 @@ ...@@ -9,4 +9,4 @@
- if can_admin_project - if can_admin_project
%td %td
= link_to s_('ProtectedEnvironment|Unprotect'), [@project.namespace.becomes(Namespace), @project, protected_environment], disabled: local_assigns[:disabled], data: { confirm: s_('ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?') % { environment_name: protected_environment.name } }, method: :delete, class: 'btn btn-warning' = link_to s_('ProtectedEnvironment|Unprotect'), [@project, protected_environment], disabled: local_assigns[:disabled], data: { confirm: s_('ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?') % { environment_name: protected_environment.name } }, method: :delete, class: 'btn btn-warning'
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
.settings-content .settings-content
%h5 %h5
Add new push rule Add new push rule
= form_for [@project.namespace.becomes(Namespace), @project, @push_rule] do |f| = form_for [@project, @push_rule] do |f|
= form_errors(@push_rule) = form_errors(@push_rule)
= render "shared/push_rules/form", f: f, context: @project = render "shared/push_rules/form", f: f, context: @project
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
%p= _('Set a default template for issue descriptions.') %p= _('Set a default template for issue descriptions.')
.settings-content .settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "issue-settings-form" }, authenticity_token: true do |f| = form_for @project, remote: true, html: { multipart: true, class: "issue-settings-form" }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-issue-settings' } %input{ type: 'hidden', name: 'update_section', value: 'js-issue-settings' }
.row .row
.form-group.col-md-9 .form-group.col-md-9
......
...@@ -9,7 +9,7 @@ RSpec.describe 'routes to the proper webhooks controller', type: :routing do ...@@ -9,7 +9,7 @@ RSpec.describe 'routes to the proper webhooks controller', type: :routing do
it 'routes the test action' do it 'routes the test action' do
expect( expect(
post: polymorphic_path([project.namespace.becomes(Namespace), project, project_hook], action: :test) post: polymorphic_path([project, project_hook], action: :test)
).to route_to(controller: 'projects/hooks', ).to route_to(controller: 'projects/hooks',
action: 'test', action: 'test',
namespace_id: project.namespace.path, namespace_id: project.namespace.path,
...@@ -19,7 +19,7 @@ RSpec.describe 'routes to the proper webhooks controller', type: :routing do ...@@ -19,7 +19,7 @@ RSpec.describe 'routes to the proper webhooks controller', type: :routing do
it 'routes a single record' do it 'routes a single record' do
expect( expect(
delete: polymorphic_path([project.namespace.becomes(Namespace), project, project_hook]) delete: polymorphic_path([project, project_hook])
).to route_to(controller: 'projects/hooks', ).to route_to(controller: 'projects/hooks',
action: 'destroy', action: 'destroy',
namespace_id: project.namespace.path, namespace_id: project.namespace.path,
......
...@@ -45,7 +45,7 @@ module Gitlab ...@@ -45,7 +45,7 @@ module Gitlab
reverts_for_type('namespace') do |path_before_rename, current_path| reverts_for_type('namespace') do |path_before_rename, current_path|
matches_path = MigrationClasses::Route.arel_table[:path].matches(current_path) matches_path = MigrationClasses::Route.arel_table[:path].matches(current_path)
namespace = MigrationClasses::Namespace.joins(:route) namespace = MigrationClasses::Namespace.joins(:route)
.find_by(matches_path)&.becomes(MigrationClasses::Namespace) .find_by(matches_path)&.becomes(MigrationClasses::Namespace) # rubocop: disable Cop/AvoidBecomes
if namespace if namespace
perform_rename(namespace, current_path, path_before_rename) perform_rename(namespace, current_path, path_before_rename)
......
...@@ -69,7 +69,6 @@ module Gitlab ...@@ -69,7 +69,6 @@ module Gitlab
def resource_url def resource_url
url_for( url_for(
[ [
resource.project.namespace.becomes(Namespace),
resource.project, resource.project,
resource resource
] ]
......
...@@ -22,7 +22,7 @@ module Gitlab ...@@ -22,7 +22,7 @@ module Gitlab
def attachments def attachments
resource.map do |issue| resource.map do |issue|
url = "[#{issue.to_reference}](#{url_for([namespace, project, issue])})" url = "[#{issue.to_reference}](#{url_for([project, issue])})"
{ {
color: color(issue), color: color(issue),
...@@ -39,10 +39,6 @@ module Gitlab ...@@ -39,10 +39,6 @@ module Gitlab
def project def project
@project ||= resource.first.project @project ||= resource.first.project
end end
def namespace
@namespace ||= project.namespace.becomes(Namespace)
end
end end
end end
end end
......
# frozen_string_literal: true
module RuboCop
module Cop
# Cop that blacklists the use of ".becomes(SomeConstant)".
#
# The use of becomes() will result in a new object being created, throwing
# away any eager loaded assocations. This in turn can cause N+1 query
# problems, even when a developer eager loaded all necessary associations.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/23182 for more information.
class AvoidBecomes < RuboCop::Cop::Cop
MSG = 'Avoid the use of becomes(SomeConstant), as this creates a ' \
'new object and throws away any eager loaded associations. ' \
'When creating URLs in views, just use the path helpers directly. ' \
'For example, instead of `link_to(..., [group.becomes(Namespace), ...])` ' \
'use `link_to(..., namespace_foo_path(group, ...))`. Most of the time there is no ' \
'need to pass in namespace to the path helpers after implementaton of ' \
'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12566'
def_node_matcher :becomes?, <<~PATTERN
(send {send ivar lvar} :becomes ...)
PATTERN
def on_send(node)
add_offense(node, location: :expression) if becomes?(node)
end
end
end
end
...@@ -334,7 +334,7 @@ RSpec.describe Projects::MergeRequestsController do ...@@ -334,7 +334,7 @@ RSpec.describe Projects::MergeRequestsController do
it 'closes MR without errors' do it 'closes MR without errors' do
update_merge_request(state_event: 'close') update_merge_request(state_event: 'close')
expect(response).to redirect_to([merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request]) expect(response).to redirect_to([merge_request.target_project, merge_request])
expect(merge_request.reload.closed?).to be_truthy expect(merge_request.reload.closed?).to be_truthy
end end
...@@ -343,7 +343,7 @@ RSpec.describe Projects::MergeRequestsController do ...@@ -343,7 +343,7 @@ RSpec.describe Projects::MergeRequestsController do
update_merge_request(title: 'New title') update_merge_request(title: 'New title')
expect(response).to redirect_to([merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request]) expect(response).to redirect_to([merge_request.target_project, merge_request])
expect(merge_request.reload.title).to eq 'New title' expect(merge_request.reload.title).to eq 'New title'
end end
......
...@@ -284,7 +284,7 @@ RSpec.describe NotesHelper do ...@@ -284,7 +284,7 @@ RSpec.describe NotesHelper do
@snippet = create(:project_snippet, project: @project) @snippet = create(:project_snippet, project: @project)
@note = create(:note_on_personal_snippet) @note = create(:note_on_personal_snippet)
expect(helper.form_resources).to eq([@project.namespace, @project, @note]) expect(helper.form_resources).to eq([@project, @note])
end end
it 'returns namespace, project and note path for other noteables' do it 'returns namespace, project and note path for other noteables' do
...@@ -292,7 +292,7 @@ RSpec.describe NotesHelper do ...@@ -292,7 +292,7 @@ RSpec.describe NotesHelper do
@project = create(:project, path: 'test', namespace: namespace) @project = create(:project, path: 'test', namespace: namespace)
@note = create(:note_on_issue, project: @project) @note = create(:note_on_issue, project: @project)
expect(helper.form_resources).to eq([@project.namespace, @project, @note]) expect(helper.form_resources).to eq([@project, @note])
end end
end end
......
...@@ -14,7 +14,7 @@ RSpec.describe ManualInverseAssociation do ...@@ -14,7 +14,7 @@ RSpec.describe ManualInverseAssociation do
stub_const("#{described_class}::Model", model) stub_const("#{described_class}::Model", model)
end end
let(:instance) { create(:merge_request).becomes(model) } let(:instance) { create(:merge_request).becomes(model) } # rubocop: disable Cop/AvoidBecomes
describe '.manual_inverse_association' do describe '.manual_inverse_association' do
context 'when the relation exists' do context 'when the relation exists' do
......
...@@ -35,7 +35,7 @@ RSpec.describe EventPresenter do ...@@ -35,7 +35,7 @@ RSpec.describe EventPresenter do
context 'with project label' do context 'with project label' do
subject { project_event.present.target_link_options } subject { project_event.present.target_link_options }
it { is_expected.to eq([group.becomes(Namespace), project, target]) } it { is_expected.to eq([project, target]) }
end end
end end
end end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../rubocop/cop/avoid_becomes'
RSpec.describe RuboCop::Cop::AvoidBecomes, type: :rubocop do
include CopHelper
subject(:cop) { described_class.new }
it 'flags the use of becomes with a constant parameter' do
inspect_source('foo.becomes(Project)')
expect(cop.offenses.size).to eq(1)
end
it 'flags the use of becomes with a namespaced constant parameter' do
inspect_source('foo.becomes(Namespace::Group)')
expect(cop.offenses.size).to eq(1)
end
it 'flags the use of becomes with a dynamic parameter' do
inspect_source(<<~RUBY)
model = Namespace
project = Project.first
project.becomes(model)
RUBY
expect(cop.offenses.size).to eq(1)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment