Class: PostsController

Inherits:
ApplicationController show all
Defined in:
app/controllers/posts_controller.rb

Overview

rubocop:disable Metrics/ClassLength rubocop:disable Metrics/MethodLength

Instance Method Summary collapse

Methods inherited from ApplicationController

#dashboard, #keyboard_tools

Instance Method Details

#change_categoryObject



502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'app/controllers/posts_controller.rb', line 502

def change_category
  @target = Category.find params[:target_id]
  unless helpers.can_change_category(current_user, @target)
    render json: { success: false, errors: [helpers.i18ns('posts.cant_change_category')] }, status: :forbidden
    return
  end

  unless @target.post_type_ids.include? @post.post_type_id
    render json: { success: false, errors: [helpers.i18ns('posts.type_not_included', type: @target.name)] },
           status: :conflict
    return
  end

  before = @post.category
  @post.category = @target
  new_tags = @post.tags.map do |tag|
    existing = Tag.where(tag_set: @target.tag_set, name: tag.name).first
    existing.nil? ? Tag.create(tag_set: @target.tag_set, name: tag.name) : existing
  end
  @post.tags = new_tags
  success = @post.save
  AuditLog.action_audit(event_type: 'change_category', related: @post, user: current_user,
                        comment: "from <<#{before.id}: #{before.name}>>\nto <<#{@target.id}: #{@target.name}>>")
  render json: { success: success, errors: success ? [] : @post.errors.full_messages }, status: success ? 200 : 409
end

#closeObject



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'app/controllers/posts_controller.rb', line 314

def close
  unless check_your_privilege('flag_close', nil, false) || @post.user.id == current_user.id
    render json: { status: 'failed', message: helpers.ability_err_msg(:flag_close, 'close this post') },
           status: :forbidden
    return
  end

  if @post.closed
    render json: { status: 'failed', message: 'Cannot close a closed post.' }, status: :bad_request
    return
  end

  reason = CloseReason.find_by id: params[:reason_id]
  if reason.nil?
    render json: { status: 'failed', message: 'Close reason not found.' }, status: :not_found
    return
  end

  if reason.requires_other_post
    other = Post.find_by(id: params[:other_post])
    if other.nil? || !top_level_post_types.include?(other.post_type_id)
      render json: { status: 'failed', message: 'Invalid input for other post.' }, status: :bad_request
      return
    end

    if other == @post
      render json: { status: 'failed', message: 'You can not close a post as a duplicate of itself' },
             status: :bad_request
      return
    end

    duplicate_of = Question.find(params[:other_post])
  else
    duplicate_of = nil
  end

  if @post.update(closed: true, closed_by: current_user, closed_at: DateTime.now, last_activity: DateTime.now,
                  last_activity_by: current_user, close_reason: reason, duplicate_post: duplicate_of)
    PostHistory.question_closed(@post, current_user)
    render json: { status: 'success' }
  else
    render json: { status: 'failed', message: helpers.i18ns('posts.cant_close_post'),
                   errors: @post.errors.full_messages }
  end
end

#createObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'app/controllers/posts_controller.rb', line 35

def create
  @parent = Post.where(id: params[:parent]).first
  @post_type = if @parent.present? && @parent.post_type.answer_type.present?
                 @parent.post_type.answer_type
               else
                 PostType.find(params[:post][:post_type_id])
               end
  @category = if @post_type.has_category
                if params[:post][:category_id].present?
                  Category.find(params[:post][:category_id])
                elsif @parent.present?
                  @parent.category
                end
              end || nil
  @post = Post.new(post_params.merge(user: current_user, body: helpers.post_markdown(:post, :body_markdown),
                                     category: @category, post_type: @post_type, parent: @parent))

  if @post.title? && (@post.title.include? '$$')
    flash[:danger] = I18n.t 'posts.no_block_mathjax_title'
    render :new, status: :bad_request
    return
  end

  if @post_type.has_parent? && @parent.nil?
    flash[:danger] = helpers.i18ns('posts.type_requires_parent', type: @post_type.name)
    redirect_back fallback_location: root_path
    return
  end

  if @post_type.has_category? && @category.nil? && @parent.nil?
    flash[:danger] = helpers.i18ns('posts.type_requires_category', type: @post_type.name)
    redirect_back fallback_location: root_path
    return
  end

  if @category.present? && @category.min_trust_level.present? && @category.min_trust_level > current_user.trust_level
    @post.errors.add(:base, helpers.i18ns('posts.category_low_trust_level', name: @category.name))
    render :new, status: :forbidden
    return
  end

  if @post_type.system? && !check_permissions
    return
  end

  level_name = @post_type.is_top_level? ? 'TopLevel' : 'SecondLevel'
  level_type_ids = @post_type.is_top_level? ? top_level_post_types : second_level_post_types
  recent_level_posts = Post.where(created_at: 24.hours.ago..DateTime.now, user: current_user)
                           .where(post_type_id: level_type_ids).count
  setting_name = current_user.privilege?('unrestricted') ? "RL_#{level_name}Posts" : "RL_NewUser#{level_name}Posts"
  max_posts = SiteSetting[setting_name]
  limit_msg = if current_user.privilege?('unrestricted')
                helpers.i18ns('rate_limit.posts', count: max_posts, level: level_name.underscore.humanize.downcase)
              else
                helpers.i18ns('rate_limit.new_user_posts',
                              count: max_posts, level: level_name.underscore.humanize.downcase)
              end

  if recent_level_posts >= max_posts
    @post.errors.add :base, limit_msg
    AuditLog.rate_limit_log(event_type: "#{level_name.underscore}_post", related: @category, user: current_user,
                            comment: "limit: #{max_posts}\n\npost:\n#{@post.attributes_print}")
    render :new, status: :forbidden
    return
  end

  if @post.save
    @post.update(last_activity: @post.created_at, last_activity_by: current_user)
    if @post_type.has_parent?
      unless @post.user_id == @post.parent.user_id
        @post.parent.user.create_notification("New response to your post #{@post.parent.title}",
                                              helpers.generic_share_link(@post))
      end
      @post.parent.update(last_activity: DateTime.now, last_activity_by: current_user)
    end

    ['p', '1', '2'].each do |key|
      Rails.cache.delete "community_user/#{current_user.community_user.id}/metric/#{key}"
    end

    do_draft_delete(URI(request.referer || '').path)

    redirect_to helpers.generic_show_link(@post)
  else
    render :new, status: :bad_request
  end
end

#deleteObject



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'app/controllers/posts_controller.rb', line 383

def delete
  unless check_your_privilege('flag_curate', @post, false)
    flash[:danger] = helpers.ability_err_msg(:flag_curate, 'delete this post')
    redirect_to post_path(@post)
    return
  end

  if @post.post_type.is_freely_editable && !current_user&.is_moderator
    flash[:danger] = helpers.i18ns('posts.cant_delete_community')
    redirect_to post_path(@post)
    return
  end

  if @post.children.any? { |a| !a.deleted? && a.score >= 0.5 } && !current_user&.is_moderator
    flash[:danger] = helpers.i18ns('posts.cant_delete_responded')
    redirect_to post_path(@post)
    return
  end

  if @post.deleted
    flash[:danger] = helpers.i18ns('posts.already_deleted')
    redirect_to post_path(@post)
    return
  end

  if @post.update(deleted: true, deleted_at: DateTime.now, deleted_by: current_user,
                  last_activity: DateTime.now, last_activity_by: current_user)
    PostHistory.post_deleted(@post, current_user)
    if @post.children.where(deleted: false).any?
      @post.children.where(deleted: false).update_all(deleted: true, deleted_at: DateTime.now,
                                                      deleted_by_id: current_user.id, last_activity: DateTime.now,
                                                      last_activity_by_id: current_user.id)
      histories = @post.children.map do |c|
        { post_history_type: PostHistoryType.find_by(name: 'post_deleted'), user: current_user, post: c,
          community: RequestContext.community }
      end
      PostHistory.create(histories)
    end
  else
    flash[:danger] = helpers.i18ns('posts.cant_delete_post')
  end

  redirect_to post_path(@post)
end

#delete_draftObject



622
623
624
625
# File 'app/controllers/posts_controller.rb', line 622

def delete_draft
  do_draft_delete(params[:path])
  render json: { status: 'success', success: true }
end

#do_update(post, user, body_rendered, **edit_post_params) ⇒ Boolean

Attempts to update a given post

Parameters:

  • post (Post)

    post the user is attempting to update

  • user (User)

    user attempting to update the post

  • body_rendered (String)

    new post body

  • edit_post_params (ActionController::Parameters)

    edit parameters

Returns:

  • (Boolean)

    status of the operation



169
170
171
172
173
174
175
# File 'app/controllers/posts_controller.rb', line 169

def do_update(post, user, body_rendered, **edit_post_params)
  post.update(edit_post_params.merge(body: body_rendered,
                                     last_edited_at: DateTime.now,
                                     last_edited_by: user,
                                     last_activity: DateTime.now,
                                     last_activity_by: user))
end

#do_update_network(post, posts, user, body_rendered, **edit_post_params) ⇒ Boolean

Attempts to update a given post network-wide. The update is manual to avoid skipping validations and fail early if at least one validation fails.

Parameters:

  • post (Post)

    post from which the network push is initiated

  • posts (ActiveRecord::Result)

    network posts to be updated

  • user (User)

    user attempting to push updates to network

  • body_rendered (String)

    new post body

  • edit_post_params (ActionController::Parameters)

    edit parameters

Returns:

  • (Boolean)

    status of the operation



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'app/controllers/posts_controller.rb', line 185

def do_update_network(post, posts, user, body_rendered, **edit_post_params)
  update_status = true

  posts.each do |network_post|
    network_post.update(edit_post_params.merge(body: body_rendered,
                                               last_edited_at: DateTime.now,
                                               last_edited_by_id: user.id,
                                               last_activity: DateTime.now,
                                               last_activity_by_id: user.id))

    if network_post.errors.any?
      post.errors.merge!(network_post.errors)
      update_status = false
    end

    next if update_status == true
  end

  update_status
end

#documentObject



466
467
468
469
470
471
472
473
474
475
# File 'app/controllers/posts_controller.rb', line 466

def document
  @post = Post.by_slug(params[:slug], current_user)

  if @post.nil?
    not_found
  end

  # Make sure we don't leak featured posts in the sidebar
  render layout: 'without_sidebar' if @prevent_sidebar
end

#editObject



161
# File 'app/controllers/posts_controller.rb', line 161

def edit; end

#featureObject



570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'app/controllers/posts_controller.rb', line 570

def feature
  return not_found(errors: ['no_privilege']) unless current_user.is_moderator

  data = {
    label: @post.parent.nil? ? @post.title : @post.parent.title,
    link: helpers.generic_show_link(@post),
    post: @post,
    active: true,
    community: RequestContext.community
  }
  @link = PinnedLink.create data

  attr = @link.attributes_print
  AuditLog.moderator_audit(event_type: 'pinned_link_create', related: @link, user: current_user,
                           comment: "<<PinnedLink #{attr}>>\n(using moderator tools on post)")
  flash[:success] = helpers.i18ns('posts.post_has_been_featured')
  render json: { status: 'success', success: true }
end

#help_centerObject



489
490
491
492
493
494
495
496
497
498
499
500
# File 'app/controllers/posts_controller.rb', line 489

def help_center
  @posts = Post.where(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id])
               .or(Post.unscoped.where(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id],
                                       community_id: nil))
               .where(Arel.sql("posts.help_category IS NULL OR posts.help_category != '$Disabled'"))
               .order(:help_ordering, :title)
               .group_by(&:post_type_id)
               .transform_values { |posts| posts.group_by { |p| p.help_category.presence } }

  # Make sure we don't leak featured posts in the sidebar
  render layout: 'without_sidebar' if @prevent_sidebar
end

#lockObject



537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
# File 'app/controllers/posts_controller.rb', line 537

def lock
  return not_found unless current_user.privilege? 'flag_curate'
  return not_found if @post.locked?

  length = params[:length].present? ? params[:length].to_i : nil
  if length
    if !current_user.is_moderator && length > 30
      length = 30
    end
    end_date = length.days.from_now
  elsif current_user.is_moderator
    end_date = nil
  else
    end_date = 7.days.from_now
  end

  @post.update locked: true, locked_by: current_user,
               locked_at: DateTime.now, locked_until: end_date
  render json: { status: 'success', success: true }
end

#newObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'app/controllers/posts_controller.rb', line 11

def new
  @post_type = PostType.find(params[:post_type])
  @category = params[:category].present? ? Category.find(params[:category]) : nil
  @parent = Post.where(id: params[:parent]).first
  @post = Post.new(category: @category, post_type: @post_type, parent: @parent)

  if @post_type.has_parent? && @parent.nil?
    flash[:danger] = helpers.i18ns('posts.type_requires_parent', type: @post_type.name)
    redirect_back fallback_location: root_path
    return
  end

  if @post_type.has_category? && @category.nil? && @parent.nil?
    flash[:danger] = helpers.i18ns('posts.type_requires_category', type: @post_type.name)
    redirect_back fallback_location: root_path
    return
  end

  if @post_type.system?
    check_permissions
    # return # uncomment if you add more code after this
  end
end

#reopenObject



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'app/controllers/posts_controller.rb', line 360

def reopen
  unless check_your_privilege('flag_close', nil, false)
    flash[:danger] = helpers.ability_err_msg(:flag_close, 'reopen this post')
    redirect_to post_path(@post)
    return
  end

  unless @post.closed
    flash[:danger] = helpers.i18ns('posts.already_opened')
    redirect_to post_path(@post)
    return
  end

  if @post.update(closed: false, closed_by: current_user, closed_at: DateTime.now,
                  last_activity: DateTime.now, last_activity_by: current_user,
                  close_reason: nil, duplicate_post: nil)
    PostHistory.question_reopened(@post, current_user)
  else
    flash[:danger] = helpers.i18ns('posts.cant_reopen_post')
  end
  redirect_to post_path(@post)
end

#restoreObject



428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# File 'app/controllers/posts_controller.rb', line 428

def restore
  unless check_your_privilege('flag_curate', @post, false)
    flash[:danger] = helpers.ability_err_msg(:flag_curate, 'restore this post')
    redirect_to post_path(@post)
    return
  end

  unless @post.deleted
    flash[:danger] = helpers.i18ns('posts.cant_restore_undeleted')
    redirect_to post_path(@post)
    return
  end

  if @post.deleted_by.is_moderator && !current_user.is_moderator
    flash[:danger] = helpers.i18ns('posts.cant_restore_deleted_by_moderator')
    redirect_to post_path(@post)
    return
  end

  deleted_at = @post.deleted_at
  if @post.update(deleted: false, deleted_at: nil, deleted_by: nil,
                  last_activity: DateTime.now, last_activity_by: current_user)
    PostHistory.post_undeleted(@post, current_user)
    restore_children = @post.children.where('deleted_at >= ?', deleted_at)
                            .where('deleted_at <= ?', deleted_at + 5.seconds)
    restore_children.update_all(deleted: false, last_activity: DateTime.now, last_activity_by_id: current_user.id)
    histories = restore_children.map do |c|
      { post_history_type: PostHistoryType.find_by(name: 'post_undeleted'), user: current_user, post: c,
        community: RequestContext.community }
    end
    PostHistory.create(histories)
  else
    flash[:danger] = helpers.i18ns('posts.cant_restore_post')
  end

  redirect_to post_path(@post)
end

#save_draftObject

saving by-field is kept for backwards compatibility with old drafts



590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
# File 'app/controllers/posts_controller.rb', line 590

def save_draft
  expiration_time = 86_400 * 7

  base_key = "saved_post.#{current_user.id}.#{params[:path]}"

  [:body, :comment, :excerpt, :license, :tag_name, :tags, :title].each do |key|
    next unless params.key?(key)

    key_name = [:body, :saved_at].include?(key) ? base_key : "#{base_key}.#{key}"

    if key == :tags
      valid_tags = params[key]&.select(&:present?)

      RequestContext.redis.del(key_name)

      if valid_tags.present?
        RequestContext.redis.sadd(key_name, valid_tags)
      end
    else
      RequestContext.redis.set(key_name, params[key])
    end

    RequestContext.redis.expire(key_name, expiration_time)
  end

  saved_at_key = "saved_post_at.#{current_user.id}.#{params[:path]}"
  RequestContext.redis.set(saved_at_key, DateTime.now.iso8601)
  RequestContext.redis.expire(saved_at_key, expiration_time)

  render json: { status: 'success', success: true, key: base_key }
end

#showObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'app/controllers/posts_controller.rb', line 123

def show
  if @post.parent_id.present?
    return redirect_to answer_post_path(@post.parent_id, answer: @post.id, anchor: "answer-#{@post.id}")
  end

  if @post.post_type_id == HelpDoc.post_type_id
    redirect_to help_path(@post.doc_slug)
  elsif @post.post_type_id == PolicyDoc.post_type_id
    redirect_to policy_path(@post.doc_slug)
  end

  if @post.deleted? && !current_user&.has_post_privilege?('flag_curate', @post)
    return not_found
  end

  @top_level_post_types = top_level_post_types
  @second_level_post_types = second_level_post_types

  if @post.category_id.present? && @post.category.min_view_trust_level.present? && \
     (!user_signed_in? || current_user.trust_level < @post.category.min_view_trust_level) && \
     @post.category.min_view_trust_level.positive?
    return not_found
  end

  # @post = @post.includes(:flags, flags: :post_flag_type)
  @children = if current_user&.privilege?('flag_curate')
                Post.where(parent_id: @post.id)
              else
                Post.where(parent_id: @post.id).undeleted
                    .or(Post.where(parent_id: @post.id, user_id: current_user&.id).where.not(user_id: nil))
              end.includes(:votes, :user, :comments, :license, :post_type, :flags, flags: :post_flag_type)
              .order(Post.arel_table[:id].not_eq(params[:answer]))
              .user_sort({ term: params[:sort], default: Arel.sql('deleted ASC, score DESC, RAND()') },
                         score: Arel.sql('deleted ASC, score DESC, RAND()'), active: :last_activity,
                         age: :created_at)
              .paginate(page: params[:page], per_page: 20)
end

#toggle_commentsObject



528
529
530
531
532
533
534
535
# File 'app/controllers/posts_controller.rb', line 528

def toggle_comments
  @post.update(comments_disabled: !@post.comments_disabled)
  if @post.comments_disabled && params[:delete_all_comments]
    @post.comments.update_all(deleted: true)
    @post.comment_threads.update_all(deleted: true, deleted_by_id: current_user.id, reply_count: 0)
  end
  render json: { status: 'success', success: true }
end

#unlockObject



558
559
560
561
562
563
564
565
566
567
568
# File 'app/controllers/posts_controller.rb', line 558

def unlock
  return not_found(errors: ['no_privilege']) unless current_user.privilege? 'flag_curate'
  return not_found(errors: ['not_locked']) unless @post.locked?
  if @post.locked_by.is_moderator && !current_user.is_moderator
    return not_found(errors: ['locked_by_mod'])
  end

  @post.update locked: false, locked_by: nil,
               locked_at: nil, locked_until: nil
  render json: { status: 'success', success: true }
end

#updateObject



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'app/controllers/posts_controller.rb', line 206

def update
  before = { body: @post.body_markdown, title: @post.title, tags: @post.tags.to_a }
  body_rendered = helpers.post_markdown(:post, :body_markdown)
  new_tags_cache = params[:post][:tags_cache]&.reject(&:empty?)

  if edit_post_params.to_h.all? { |k, v| @post.send(k) == v }
    flash[:danger] = helpers.i18ns('posts.no_edit_changes')
    return redirect_to post_path(@post)
  end

  if current_user.can_update(@post, @post_type)
    if current_user.can_push_to_network(@post_type) && params[:network_push] == 'true'
      # post network push & post histories creation must be atomic to prevent sync issues on error
      @post.transaction do
        posts = Post.unscoped.where(post_type_id: [PolicyDoc.post_type_id, HelpDoc.post_type_id],
                                    doc_slug: @post.doc_slug,
                                    body: @post.body)

        update_status = do_update_network(@post, posts, current_user, body_rendered, **edit_post_params)

        if update_status
          posts.each do |post|
            history_entry = PostHistory.post_edited(post, current_user, before: before[:body],
                                    after: @post.body_markdown, comment: params[:edit_comment],
                                    before_title: before[:title], after_title: @post.title,
                                    before_tags: before[:tags], after_tags: @post.tags)

            if history_entry&.errors&.any?
              @post.errors.merge!(history_entry.errors)
              raise ActiveRecord::Rollback
            end
          end

          flash[:success] = "#{helpers.pluralize(posts.to_a.size, 'post')} updated."
          redirect_to help_path(slug: @post.doc_slug)
        end

        next
      end
    else
      # post update & post history creation must be atomic to prevent sync issues on error
      @post.transaction do
        update_status = do_update(@post, current_user, body_rendered, **edit_post_params)

        if update_status
          history_entry = PostHistory.post_edited(@post, current_user, before: before[:body],
                                  after: @post.body_markdown, comment: params[:edit_comment],
                                  before_title: before[:title], after_title: @post.title,
                                  before_tags: before[:tags], after_tags: @post.tags)

          if history_entry&.errors&.any?
            @post.errors.merge!(history_entry.errors)
            raise ActiveRecord::Rollback
          end

          if params[:redact]
            PostHistory.redact(@post, current_user)
          end
          Rails.cache.delete "community_user/#{current_user.community_user.id}/metric/E"
          do_draft_delete(URI(request.referer || '').path)
          redirect_to post_path(@post)
        end

        next
      end
    end

    # this is only reached if we rollback the transaction or fail validations
    if @post.errors.any?
      render :edit, status: :bad_request
    end
  else
    new_user = !current_user.privilege?('unrestricted')
    rate_limit = SiteSetting["RL_#{new_user ? 'NewUser' : ''}SuggestedEdits"]
    recent_edits = SuggestedEdit.where(user: current_user, active: true).where('created_at > ?', 24.hours.ago).count
    if recent_edits >= rate_limit
      key = new_user ? 'rate_limit.new_user_suggested_edits' : 'rate_limit.suggested_edits'
      msg = helpers.i18ns key, count: rate_limit
      @post.errors.add :base, msg
      render :edit, status: :forbidden
    else
      data = {
        post: @post,
        user: current_user,
        body: body_rendered == @post.body ? nil : body_rendered,
        title: params[:post][:title] == @post.title ? nil : params[:post][:title],
        tags_cache: new_tags_cache == @post.tags_cache ? @post.tags_cache : new_tags_cache,
        body_markdown: params[:post][:body_markdown] == @post.body_markdown ? nil : params[:post][:body_markdown],
        comment: params[:edit_comment],
        active: true, accepted: false
      }
      edit = SuggestedEdit.new(data)
      if edit.save
        message = "Edit suggested on your #{@post_type.name.underscore.humanize.downcase}"
        if @post_type.has_parent
          message += " on '#{@post.parent.title}'"
        end
        @post.user.create_notification message, suggested_edit_url(edit, host: @post.community.host)
        do_draft_delete(URI(request.referer || '').path)
        redirect_to post_path(@post)
      else
        @post.errors.copy!(edit.errors)
        render :edit, status: :bad_request
      end
    end
  end
end

#uploadObject



477
478
479
480
481
482
483
484
485
486
487
# File 'app/controllers/posts_controller.rb', line 477

def upload
  content_types = Rails.application.config.active_storage.web_image_content_types
  extensions = content_types.map { |ct| ct.gsub('image/', '') }
  unless helpers.valid_image?(params[:file])
    render json: { error: "Images must be one of #{extensions.join(', ')}" }, status: :bad_request
    return
  end
  @blob = ActiveStorage::Blob.create_and_upload!(io: params[:file], filename: params[:file].original_filename,
                                                 content_type: params[:file].content_type)
  render json: { link: uploaded_url(@blob.key) }
end