This instance will be upgraded to Heptapod 0.31.0 (final) on 2022-05-24 at 14:00 UTC+2 (a few minutes of down time)

Commit 064c8949 authored by Jarka Kadlecova's avatar Jarka Kadlecova
Browse files

CE port of code changed for epics

parent 072176e352f0
......@@ -7,6 +7,54 @@ module IssuableActions
before_action :authorize_admin_issuable!, only: :bulk_update
end
def show
respond_to do |format|
format.html do
render show_view
end
format.json do
render json: serializer.represent(issuable, serializer: params[:serializer])
end
end
end
def update
@issuable = update_service.execute(issuable)
respond_to do |format|
format.html do
recaptcha_check_with_fallback { render :edit }
end
format.json do
render_entity_json
end
end
rescue ActiveRecord::StaleObjectError
render_conflict_response
end
def realtime_changes
Gitlab::PollingInterval.set_header(response, interval: 3_000)
response = {
title: view_context.markdown_field(issuable, :title),
title_text: issuable.title,
description: view_context.markdown_field(issuable, :description),
description_text: issuable.description,
task_status: issuable.task_status
}
if issuable.edited?
response[:updated_at] = issuable.updated_at
response[:updated_by_name] = issuable.last_edited_by.name
response[:updated_by_path] = user_path(issuable.last_edited_by)
end
render json: response
end
def destroy
issuable.destroy
destroy_method = "destroy_#{issuable.class.name.underscore}".to_sym
......@@ -68,6 +116,10 @@ def authorize_admin_issuable!
end
end
def authorize_update_issuable!
render_404 unless can?(current_user, :"update_#{resource_name}", issuable)
end
def bulk_update_params
permitted_keys = [
:issuable_ids,
......@@ -92,4 +144,24 @@ def bulk_update_params
def resource_name
@resource_name ||= controller_name.singularize
end
def render_entity_json
if @issuable.valid?
render json: serializer.represent(@issuable)
else
render json: { errors: @issuable.errors.full_messages }, status: :unprocessable_entity
end
end
def show_view
'show'
end
def serializer
raise NotImplementedError
end
def update_service
raise NotImplementedError
end
end
......@@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :authorize_create_issue!, only: [:new, :create]
# Allow modify issue
before_action :authorize_update_issue!, only: [:edit, :update, :move]
before_action :authorize_update_issuable!, only: [:edit, :update, :move]
# Allow create a new branch and empty WIP merge request from current issue
before_action :authorize_create_merge_request!, only: [:create_merge_request]
......@@ -67,18 +67,6 @@ def edit
respond_with(@issue)
end
def show
@noteable = @issue
@note = @project.notes.new(noteable: @issue)
respond_to do |format|
format.html
format.json do
render json: serializer.represent(@issue, serializer: params[:serializer])
end
end
end
def discussions
notes = @issue.notes
.inc_relations_for_view
......@@ -120,25 +108,6 @@ def create
end
end
def update
update_params = issue_params.merge(spammable_params)
@issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
respond_to do |format|
format.html do
recaptcha_check_with_fallback { render :edit }
end
format.json do
render_issue_json
end
end
rescue ActiveRecord::StaleObjectError
render_conflict_response
end
def move
params.require(:move_to_project_id)
......@@ -196,26 +165,6 @@ def can_create_branch
end
end
def realtime_changes
Gitlab::PollingInterval.set_header(response, interval: 3_000)
response = {
title: view_context.markdown_field(@issue, :title),
title_text: @issue.title,
description: view_context.markdown_field(@issue, :description),
description_text: @issue.description,
task_status: @issue.task_status
}
if @issue.edited?
response[:updated_at] = @issue.updated_at
response[:updated_by_name] = @issue.last_edited_by.name
response[:updated_by_path] = user_path(@issue.last_edited_by)
end
render json: response
end
def create_merge_request
result = ::MergeRequests::CreateFromIssueService.new(project, current_user, issue_iid: issue.iid).execute
......@@ -231,7 +180,8 @@ def create_merge_request
def issue
return @issue if defined?(@issue)
# The Sortable default scope causes performance issues when used with find_by
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
@issuable = @noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
@note = @project.notes.new(noteable: @issuable)
return render_404 unless can?(current_user, :read_issue, @issue)
......@@ -246,14 +196,6 @@ def spammable_path
project_issue_path(@project, @issue)
end
def authorize_update_issue!
render_404 unless can?(current_user, :update_issue, @issue)
end
def authorize_admin_issues!
render_404 unless can?(current_user, :admin_issue, @project)
end
def authorize_create_merge_request!
render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user)
end
......@@ -305,4 +247,9 @@ def authenticate_user!
def serializer
IssueSerializer.new(current_user: current_user, project: issue.project)
end
def update_service
update_params = issue_params.merge(spammable_params)
Issues::UpdateService.new(project, current_user, update_params)
end
end
......@@ -9,7 +9,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
skip_before_action :merge_request, only: [:index, :bulk_update]
skip_before_action :ensure_ref_fetched, only: [:index, :bulk_update]
before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :remove_wip, :sort]
before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort]
before_action :authenticate_user!, only: [:assign_related_issues]
......@@ -256,14 +256,6 @@ def ci_environments_status
alias_method :issuable, :merge_request
alias_method :awardable, :merge_request
def authorize_update_merge_request!
return render_404 unless can?(current_user, :update_merge_request, @merge_request)
end
def authorize_admin_merge_request!
return render_404 unless can?(current_user, :admin_merge_request, @merge_request)
end
def validates_merge_request
# Show git not found page
# if there is no saved commits between source & target branch
......
......@@ -71,11 +71,13 @@ def commit_url(entity, *args)
project_commit_url(entity.project, entity.sha, *args)
end
def preview_markdown_path(project, *args)
def preview_markdown_path(parent, *args)
return group_preview_markdown_path(parent) if parent.is_a?(Group)
if @snippet.is_a?(PersonalSnippet)
preview_markdown_snippets_path
else
preview_markdown_project_path(project, *args)
preview_markdown_project_path(parent, *args)
end
end
......
......@@ -211,15 +211,13 @@ def issuable_filter_present?
def issuable_initial_data(issuable)
data = {
endpoint: project_issue_path(@project, issuable),
canUpdate: can?(current_user, :update_issue, issuable),
canDestroy: can?(current_user, :destroy_issue, issuable),
endpoint: issuable_path(issuable),
canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable),
canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable),
issuableRef: issuable.to_reference,
markdownPreviewPath: preview_markdown_path(@project),
markdownPreviewPath: preview_markdown_path(parent),
markdownDocsPath: help_page_path('user/markdown'),
issuableTemplates: issuable_templates(issuable),
projectPath: ref_project.path,
projectNamespace: ref_project.namespace.full_path,
initialTitleHtml: markdown_field(issuable, :title),
initialTitleText: issuable.title,
initialDescriptionHtml: markdown_field(issuable, :description),
......@@ -227,6 +225,12 @@ def issuable_initial_data(issuable)
initialTaskStatus: issuable.task_status
}
if parent.is_a?(Group)
data[:groupPath] = parent.path
else
data.merge!(projectPath: ref_project.path, projectNamespace: ref_project.namespace.full_path)
end
data.merge!(updated_at_by(issuable))
data.to_json
......@@ -263,12 +267,7 @@ def close_reopen_issuable_path(issuable, should_inverse = false)
end
def issuable_path(issuable, *options)
case issuable
when Issue
issue_path(issuable, *options)
when MergeRequest
merge_request_path(issuable, *options)
end
polymorphic_path(issuable, *options)
end
def issuable_url(issuable, *options)
......@@ -369,4 +368,8 @@ def issuable_sidebar_options(issuable, can_edit_issuable)
fullPath: @project.full_path
}
end
def parent
@project || @group
end
end
......@@ -49,7 +49,8 @@ def banzai_render_context(field)
# Always include a project key, or Banzai complains
project = self.project if self.respond_to?(:project)
context = cached_markdown_fields[field].merge(project: project)
group = self.group if self.respond_to?(:group)
context = cached_markdown_fields[field].merge(project: project, group: group)
# Banzai is less strict about authors, so don't always have an author key
context[:author] = self.author if self.respond_to?(:author)
......
......@@ -14,7 +14,6 @@ module Issuable
include StripAttribute
include Awardable
include Taskable
include TimeTrackable
include Importable
include Editable
include AfterCommitQueue
......@@ -95,8 +94,6 @@ def award_emojis_loaded?
strip_attributes :title
acts_as_paranoid
after_save :record_metrics, unless: :imported?
# We want to use optimistic lock for cases when only title or description are involved
......
# Placeholder class for model that is implemented in EE
# It will reserve (ee#3853) '&' as a reference prefix, but the table does not exists in CE
class Epic < ActiveRecord::Base
prepend EE::Epic
# TODO: this will be implemented as part of #3853
def to_reference
end
end
......@@ -180,6 +180,12 @@ def add_owner(user, current_user = nil)
add_user(user, :owner, current_user: current_user)
end
def member?(user, min_access_level = Gitlab::Access::GUEST)
return false unless user
max_member_access_for_user(user) >= min_access_level
end
def has_owner?(user)
return false unless user
......
......@@ -10,6 +10,7 @@ class Issue < ActiveRecord::Base
include FasterCacheKeys
include RelativePositioning
include CreatedAtFilterable
include TimeTrackable
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
......@@ -74,6 +75,8 @@ class Issue < ActiveRecord::Base
end
end
acts_as_paranoid
def self.reference_prefix
'#'
end
......
......@@ -6,6 +6,7 @@ class MergeRequest < ActiveRecord::Base
include Sortable
include IgnorableColumn
include CreatedAtFilterable
include TimeTrackable
ignore_column :locked_at
......@@ -119,6 +120,8 @@ class MergeRequest < ActiveRecord::Base
after_save :keep_around_commit
acts_as_paranoid
def self.reference_prefix
'!'
end
......
......@@ -69,7 +69,7 @@ def values
delegate :title, to: :noteable, allow_nil: true
validates :note, presence: true
validates :project, presence: true, unless: :for_personal_snippet?
validates :project, presence: true, if: :for_project_noteable?
# Attachments are deprecated and are handled by Markdown uploader
validates :attachment, file_size: { maximum: :max_attachment_size }
......@@ -114,7 +114,7 @@ def values
after_initialize :ensure_discussion_id
before_validation :nullify_blank_type, :nullify_blank_line_code
before_validation :set_discussion_id, on: :create
after_save :keep_around_commit, unless: :for_personal_snippet?
after_save :keep_around_commit, if: :for_project_noteable?
after_save :expire_etag_cache
after_destroy :expire_etag_cache
......@@ -208,6 +208,10 @@ def for_personal_snippet?
noteable.is_a?(PersonalSnippet)
end
def for_project_noteable?
!for_personal_snippet?
end
def skip_project_check?
for_personal_snippet?
end
......
class IssuableEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :iid
expose :author_id
expose :description
expose :lock_version
expose :milestone_id
expose :state
expose :title
expose :updated_by_id
expose :created_at
expose :updated_at
expose :deleted_at
expose :time_estimate
expose :total_time_spent
expose :human_time_estimate
expose :human_total_time_spent
expose :milestone, using: API::Entities::Milestone
expose :labels, using: LabelEntity
end
class IssueEntity < IssuableEntity
include RequestAwareEntity
include TimeTrackableEntity
expose :state
expose :deleted_at
expose :branch_name
expose :confidential
expose :discussion_locked
......
class MergeRequestEntity < IssuableEntity
include RequestAwareEntity
include TimeTrackableEntity
expose :state
expose :deleted_at
expose :in_progress_merge_commit_sha
expose :merge_commit_sha
expose :merge_error
......
module TimeTrackableEntity
extend ActiveSupport::Concern
extend Grape
included do
expose :time_estimate
expose :total_time_spent
expose :human_time_estimate
expose :human_total_time_spent
end
end
module Issuable
class CommonSystemNotesService < ::BaseService
attr_reader :issuable
def execute(issuable, old_labels)
@issuable = issuable
if issuable.previous_changes.include?('title')
create_title_change_note(issuable.previous_changes['title'].first)
end
handle_description_change_note
handle_time_tracking_note if issuable.is_a?(TimeTrackable)
create_labels_note(old_labels) if issuable.labels != old_labels
create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
create_milestone_note if issuable.previous_changes.include?('milestone_id')
end
private
def handle_time_tracking_note
if issuable.previous_changes.include?('time_estimate')
create_time_estimate_note
end
if issuable.time_spent?
create_time_spent_note
end
end
def handle_description_change_note
if issuable.previous_changes.include?('description')
if issuable.tasks? && issuable.updated_tasks.any?
create_task_status_note
else
# TODO: Show this note if non-task content was modified.
# https://gitlab.com/gitlab-org/gitlab-ce/issues/33577
create_description_change_note
end
end
end
def create_labels_note(old_labels)
added_labels = issuable.labels - old_labels
removed_labels = old_labels - issuable.labels
SystemNoteService.change_label(issuable, issuable.project, current_user, added_labels, removed_labels)
end
def create_title_change_note(old_title)
SystemNoteService.change_title(issuable, issuable.project, current_user, old_title)
end
def create_description_change_note
SystemNoteService.change_description(issuable, issuable.project, current_user)
end
def create_task_status_note
issuable.updated_tasks.each do |task|
SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
end
end
def create_time_estimate_note
SystemNoteService.change_time_estimate(issuable, issuable.project, current_user)
end
def create_time_spent_note
SystemNoteService.change_time_spent(issuable, issuable.project, issuable.time_spent_user)
end
def create_milestone_note
SystemNoteService.change_milestone(issuable, issuable.project, current_user, issuable.milestone)
end
def create_discussion_lock_note
SystemNoteService.discussion_lock(issuable, current_user)
end
end
end
class IssuableBaseService < BaseService
private
def create_milestone_note(issuable)
SystemNoteService.change_milestone(
issuable, issuable.project, current_user, issuable.milestone)
end
def create_labels_note(issuable, old_labels)
added_labels = issuable.labels - old_labels
removed_labels = old_labels - issuable.labels
SystemNoteService.change_label(
issuable, issuable.project, current_user, added_labels, removed_labels)
end
def create_title_change_note(issuable, old_title)
SystemNoteService.change_title(
issuable, issuable.project, current_user, old_title)
end
def create_description_change_note(issuable)
SystemNoteService.change_description(issuable, issuable.project, current_user)
end
def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
SystemNoteService.change_branch(
issuable, issuable.project, current_user, branch_type,
old_branch, new_branch)
end
def create_task_status_note(issuable)
issuable.updated_tasks.each do |task|
SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
end
end
def create_time_estimate_note(issuable)
SystemNoteService.change_time_estimate(issuable, issuable.project, current_user)
end
def create_time_spent_note(issuable)
SystemNoteService.change_time_spent(issuable, issuable.project, current_user)
end
def create_discussion_lock_note(issuable)
SystemNoteService.discussion_lock(issuable, current_user)
end
def filter_params(issuable)
ability_name = :"admin_#{issuable.to_ability_name}"
unless can?(current_user, ability_name, project)
unless can?(current_user, ability_name, issuable)
params.delete(:milestone_id)
params.delete(:labels)