clean up routes

- replace `match` with `get`/`post`/`patch`/`delete`
- format routes.rb
- rename the `show_user_{profile,question,answer}` routes to
  `profile`, `question`, `answer` so `url_for` (used by Rails Admin)
  works fine for these things
- also add `to_param` to the `User` model so that `url_for(some_user)`
  uses the user name
This commit is contained in:
Georg Gadinger 2022-07-23 12:06:05 +02:00
parent 37f4326d3b
commit 66efa5d4f4
28 changed files with 160 additions and 155 deletions

View File

@ -19,7 +19,7 @@ module ApplicationHelper::GraphMethods
"og:title": user.profile.safe_name, "og:title": user.profile.safe_name,
"og:type": "profile", "og:type": "profile",
"og:image": full_profile_picture_url(user), "og:image": full_profile_picture_url(user),
"og:url": show_user_profile_url(user.screen_name), "og:url": user_url(user),
"og:description": user.profile.description, "og:description": user.profile.description,
"og:site_name": APP_CONFIG["site_name"], "og:site_name": APP_CONFIG["site_name"],
"profile:username": user.screen_name "profile:username": user.screen_name
@ -43,7 +43,7 @@ module ApplicationHelper::GraphMethods
"og:title": "#{answer.user.profile.safe_name} answered: #{answer.question.content}", "og:title": "#{answer.user.profile.safe_name} answered: #{answer.question.content}",
"og:type": "article", "og:type": "article",
"og:image": full_profile_picture_url(answer.user), "og:image": full_profile_picture_url(answer.user),
"og:url": show_user_answer_url(answer.user.screen_name, answer.id), "og:url": answer_url(answer.user.screen_name, answer.id),
"og:description": answer.content, "og:description": answer.content,
"og:site_name": APP_CONFIG["site_name"] "og:site_name": APP_CONFIG["site_name"]
}) })

View File

@ -4,13 +4,13 @@ module ModerationHelper
target = report.target target = report.target
case report.type case report.type
when 'Reports::Answer' when 'Reports::Answer'
show_user_answer_path target.user.screen_name, target.id answer_path target.user.screen_name, target.id
when 'Reports::Comment' when 'Reports::Comment'
show_user_answer_path target.answer.user.screen_name, target.answer.id answer_path target.answer.user.screen_name, target.answer.id
when 'Reports::Question' when 'Reports::Question'
show_user_question_path 'user', target.id question_path 'user', target.id
when 'Reports::User' when 'Reports::User'
show_user_profile_path target.screen_name user_path target
else else
'#' '#'
end end

View File

@ -12,7 +12,7 @@ module SocialHelper::TumblrMethods
end end
def tumblr_body(answer) def tumblr_body(answer)
answer_url = show_user_answer_url( answer_url = answer_url(
id: answer.id, id: answer.id,
username: answer.user.screen_name, username: answer.user.screen_name,
host: APP_CONFIG['hostname'], host: APP_CONFIG['hostname'],
@ -23,7 +23,7 @@ module SocialHelper::TumblrMethods
end end
def tumblr_share_url(answer) def tumblr_share_url(answer)
answer_url = show_user_answer_url( answer_url = answer_url(
id: answer.id, id: answer.id,
username: answer.user.screen_name, username: answer.user.screen_name,
host: APP_CONFIG['hostname'], host: APP_CONFIG['hostname'],
@ -32,4 +32,4 @@ module SocialHelper::TumblrMethods
"https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape(answer_url)}&caption=&content=#{CGI.escape(tumblr_body(answer))}" "https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape(answer_url)}&caption=&content=#{CGI.escape(tumblr_body(answer))}"
end end
end end

View File

@ -8,7 +8,7 @@ module SocialHelper::TwitterMethods
original_question_length = question_content.length original_question_length = question_content.length
answer_content = twitter_markdown answer.content answer_content = twitter_markdown answer.content
original_answer_length = answer_content.length original_answer_length = answer_content.length
answer_url = show_user_answer_url( answer_url = answer_url(
id: answer.id, id: answer.id,
username: answer.user.screen_name, username: answer.user.screen_name,
host: APP_CONFIG['hostname'], host: APP_CONFIG['hostname'],
@ -42,4 +42,4 @@ module SocialHelper::TwitterMethods
def twitter_share_url(answer) def twitter_share_url(answer)
"https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}" "https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}"
end end
end end

View File

@ -10,7 +10,7 @@ module UserHelper
return anonymous_name(context_user) if anonymous?(user, author_identifier.present?) return anonymous_name(context_user) if anonymous?(user, author_identifier.present?)
if url if url
return show_user_profile_path(user.screen_name) if link_only return user_path(user) if link_only
return profile_link(user) return profile_link(user)
end end
@ -24,7 +24,7 @@ module UserHelper
private private
def profile_link(user) def profile_link(user)
link_to(user.profile.safe_name, show_user_profile_path(user.screen_name), class: ("user--banned" if user.banned?).to_s) link_to(user.profile.safe_name, user_path(user), class: ("user--banned" if user.banned?).to_s)
end end
def should_unmask?(author_identifier) def should_unmask?(author_identifier)

View File

@ -81,6 +81,9 @@ class User < ApplicationRecord
Profile.create(user_id: id) if Profile.where(user_id: id).count.zero? Profile.create(user_id: id) if Profile.where(user_id: id).count.zero?
end end
# use the screen name as parameter for url helpers
def to_param = screen_name
def login=(login) def login=(login)
@login = login @login = login
end end

View File

@ -10,7 +10,7 @@ class FlavoredMarkdown < Redcarpet::Render::HTML
def wrap_mentions(text) def wrap_mentions(text)
text.gsub(/(^|\s)(@[a-zA-Z0-9_]{1,16})/) do text.gsub(/(^|\s)(@[a-zA-Z0-9_]{1,16})/) do
"#{$1}[#{$2}](#{show_user_profile_path $2.tr('@', '')})" "#{$1}[#{$2}](#{user_path $2.tr('@', '')})"
end end
end end

View File

@ -7,7 +7,7 @@
%div{ style: "height: 0; width: 0" }= render "modal/comment_smiles", comment: comment %div{ style: "height: 0; width: 0" }= render "modal/comment_smiles", comment: comment
.media .media
.pull-left .pull-left
%a{ href: show_user_profile_path(username: comment.user.screen_name) } %a{ href: user_path(comment.user) }
%img.comment__user-avatar.avatar-sm{ src: comment.user.profile_picture.url(:medium) } %img.comment__user-avatar.avatar-sm{ src: comment.user.profile_picture.url(:medium) }
.media-body .media-body
%h6.media-heading.comment__user %h6.media-heading.comment__user

View File

@ -1,7 +1,7 @@
.card-header .card-header
.media .media
- unless a.question.author_is_anonymous - unless a.question.author_is_anonymous
%a.pull-left{ href: show_user_profile_path(a.question.user.screen_name) } %a.pull-left{ href: user_path(a.question.user) }
%img.answerbox__question-user-avatar.avatar-md{ src: a.question.user.profile_picture.url(:medium) } %img.answerbox__question-user-avatar.avatar-md{ src: a.question.user.profile_picture.url(:medium) }
.media-body .media-body
- if user_signed_in? - if user_signed_in?
@ -28,7 +28,7 @@
= raw t('views.answerbox.asked', user: user_screen_name(a.question.user, context_user: a.user, author_identifier: a.question.author_is_anonymous ? a.question.author_identifier: nil), time: time_tooltip(a.question)) = raw t('views.answerbox.asked', user: user_screen_name(a.question.user, context_user: a.user, author_identifier: a.question.author_is_anonymous ? a.question.author_identifier: nil), time: time_tooltip(a.question))
- if !a.question.author_is_anonymous && !a.question.direct - if !a.question.author_is_anonymous && !a.question.direct
· ·
%a{ href: show_user_question_path(a.question.user.screen_name, a.question.id) } %a{ href: question_path(a.question.user.screen_name, a.question.id) }
= pluralize(a.question.answer_count, t('views.general.answer')) = pluralize(a.question.answer_count, t('views.general.answer'))
.answerbox__question-text .answerbox__question-text
= question_markdown a.question.content = question_markdown a.question.content

View File

@ -6,7 +6,7 @@
= t 'views.answerbox.no_smile' = t 'views.answerbox.no_smile'
- else - else
- a.smiles.all.each do |smile| - a.smiles.all.each do |smile|
%a{ href: show_user_profile_path(smile.user.screen_name), %a{ href: user_path(smile.user),
title: user_screen_name(smile.user, url: false), title: user_screen_name(smile.user, url: false),
data: { toggle: :tooltip, placement: :top, smile_id: smile.id } } data: { toggle: :tooltip, placement: :top, smile_id: smile.id } }
%img.avatar-xs{ src: smile.user.profile_picture.url(:medium) } %img.avatar-xs{ src: smile.user.profile_picture.url(:medium) }

View File

@ -9,7 +9,7 @@
- if a.content.length > 640 - if a.content.length > 640
[...] [...]
%p %p
%a.btn.btn-primary{ href: show_user_answer_path(a.user.screen_name, a.id) } %a.btn.btn-primary{ href: answer_path(a.user.screen_name, a.id) }
= t 'views.answerbox.read' = t 'views.answerbox.read'
- else - else
.answerbox__answer-text .answerbox__answer-text
@ -19,20 +19,20 @@
.col-sm-6.text-left.text-muted .col-sm-6.text-left.text-muted
.media .media
.pull-left .pull-left
%a{ href: show_user_profile_path(a.user.screen_name) } %a{ href: user_path(a.user) }
%img.answerbox__answer-user-avatar.avatar-sm{ src: a.user.profile_picture.url(:medium) } %img.answerbox__answer-user-avatar.avatar-sm{ src: a.user.profile_picture.url(:medium) }
.media-body .media-body
%h6.media-heading.answerbox__answer-user %h6.media-heading.answerbox__answer-user
= raw t('views.answerbox.answered', hide: hidespan(t('views.answerbox.hide'), 'd-none d-sm-inline'), user: user_screen_name(a.user)) = raw t('views.answerbox.answered', hide: hidespan(t('views.answerbox.hide'), 'd-none d-sm-inline'), user: user_screen_name(a.user))
.answerbox__answer-date .answerbox__answer-date
= link_to(raw(t('views.answerbox.time', time: time_tooltip(a))), show_user_answer_path(a.user.screen_name, a.id)) = link_to(raw(t('views.answerbox.time', time: time_tooltip(a))), answer_path(a.user.screen_name, a.id))
.col-md-6.d-flex.d-md-block.answerbox__actions .col-md-6.d-flex.d-md-block.answerbox__actions
= render 'answerbox/actions', a: a, display_all: display_all = render 'answerbox/actions', a: a, display_all: display_all
- else - else
.row .row
.col-md-6.text-left.text-muted .col-md-6.text-left.text-muted
%i.fa.fa-clock-o %i.fa.fa-clock-o
= link_to(raw(t('views.answerbox.time', time: time_tooltip(a))), show_user_answer_path(a.user.screen_name, a.id), class: 'answerbox__permalink') = link_to(raw(t('views.answerbox.time', time: time_tooltip(a))), answer_path(a.user.screen_name, a.id), class: 'answerbox__permalink')
.col-md-6.d-md-flex.answerbox__actions .col-md-6.d-md-flex.answerbox__actions
= render 'answerbox/actions', a: a, display_all: display_all = render 'answerbox/actions', a: a, display_all: display_all
.card-footer{ id: "ab-comments-section-#{a.id}", class: display_all.nil? ? 'd-none' : nil } .card-footer{ id: "ab-comments-section-#{a.id}", class: display_all.nil? ? 'd-none' : nil }

View File

@ -2,15 +2,15 @@
.card-body .card-body
.media .media
.pull-left .pull-left
%a{ href: show_user_profile_path(u.screen_name) } %a{ href: user_path(u) }
%img.avatar-md.mr-2{ src: u.profile_picture.url(:medium) } %img.avatar-md.mr-2{ src: u.profile_picture.url(:medium) }
.media-body .media-body
%h6.media-heading.answerbox__question-user %h6.media-heading.answerbox__question-user
- if u.profile.display_name.blank? - if u.profile.display_name.blank?
%a{ href: show_user_profile_path(u.screen_name) } %a{ href: user_path(u) }
= u.screen_name = u.screen_name
- else - else
%a{ href: show_user_profile_path(u.screen_name) } %a{ href: user_path(u) }
= u.profile.display_name = u.profile.display_name
%span.text-muted= u.screen_name %span.text-muted= u.screen_name
%p.answerbox__question-text %p.answerbox__question-text

View File

@ -2,7 +2,7 @@
.card-header .card-header
.media .media
- unless i.question.author_is_anonymous - unless i.question.author_is_anonymous
%a.pull-left{ href: show_user_profile_path(i.question.user.screen_name) } %a.pull-left{ href: user_path(i.question.user) }
%img.answerbox__question-user-avatar.avatar-md{ src: i.question.user.profile_picture.url(:medium) } %img.answerbox__question-user-avatar.avatar-md{ src: i.question.user.profile_picture.url(:medium) }
.media-body .media-body
%h6.text-muted.media-heading.answerbox__question-user %h6.text-muted.media-heading.answerbox__question-user
@ -11,7 +11,7 @@
= t(".asked_html", user: user_screen_name(i.question.user, context_user: i.user, author_identifier: i.question.author_is_anonymous ? i.question.author_identifier : nil), time: time_tooltip(i.question)) = t(".asked_html", user: user_screen_name(i.question.user, context_user: i.user, author_identifier: i.question.author_is_anonymous ? i.question.author_identifier : nil), time: time_tooltip(i.question))
- if !i.question.author_is_anonymous && i.question.answer_count.positive? - if !i.question.author_is_anonymous && i.question.answer_count.positive?
· ·
%a{ href: show_user_question_path(i.question.user.screen_name, i.question.id) } %a{ href: question_path(i.question.user.screen_name, i.question.id) }
= t(".answers", count: i.question.answer_count) = t(".answers", count: i.question.answer_count)
.answerbox__question-text= question_markdown i.question.content .answerbox__question-text= question_markdown i.question.content
- if i.question.user_id != current_user.id || current_user.has_role?(:administrator) - if i.question.user_id != current_user.id || current_user.has_role?(:administrator)

View File

@ -6,11 +6,11 @@
.card-header= t(".share.heading") .card-header= t(".share.heading")
.card-body .card-body
%a.btn.btn-block.btn-primary{ target: "_blank", %a.btn.btn-block.btn-primary{ target: "_blank",
href: "https://twitter.com/intent/tweet?text=Ask%20me%20anything%21&url=#{show_user_profile_url(current_user.screen_name)}" } href: "https://twitter.com/intent/tweet?text=Ask%20me%20anything%21&url=#{user_url(current_user)}" }
%i.fa.fa-fw.fa-twitter %i.fa.fa-fw.fa-twitter
= t(".share.button", service: "Twitter") = t(".share.button", service: "Twitter")
%a.btn.btn-block.btn-primary{ target: "_blank", %a.btn.btn-block.btn-primary{ target: "_blank",
href: "https://www.tumblr.com/share/link?url=#{show_user_profile_url(current_user.screen_name)}&name=Ask%20me%20anything%21" } href: "https://www.tumblr.com/share/link?url=#{user_url(current_user)}&name=Ask%20me%20anything%21" }
%i.fa.fa-fw.fa-tumblr %i.fa.fa-fw.fa-tumblr
= t(".share.button", service: "Tumblr") = t(".share.button", service: "Tumblr")
.card .card

View File

@ -13,6 +13,6 @@
%ul.smiles__user-list %ul.smiles__user-list
- comment.smiles.all.each do |smile| - comment.smiles.all.each do |smile|
%li.smiles__user-list-entry %li.smiles__user-list-entry
%a{ href: show_user_profile_path(smile.user.screen_name) } %a{ href: user_path(smile.user) }
%img{ src: smile.user.profile_picture.url(:small), alt: user_screen_name(smile.user, url: false) } %img{ src: smile.user.profile_picture.url(:small), alt: user_screen_name(smile.user, url: false) }
%span= user_screen_name(smile.user, url: false) %span= user_screen_name(smile.user, url: false)

View File

@ -26,7 +26,7 @@
%a.btn.btn-primary{ href: content_url(report) } %a.btn.btn-primary{ href: content_url(report) }
= t('views.moderation.moderationbox.view', content: report.type.sub('Reports::', '')) = t('views.moderation.moderationbox.view', content: report.type.sub('Reports::', ''))
- if report.target.respond_to?(:user) && report.target.user - if report.target.respond_to?(:user) && report.target.user
%a.btn.btn-primary{ href: show_user_profile_path(report.target.user.screen_name) } %a.btn.btn-primary{ href: user_path(report.target.user) }
= t('views.moderation.moderationbox.view', content: t('views.general.user')) = t('views.moderation.moderationbox.view', content: t('views.general.user'))
.col-md-4.col-sm-4.col-xs-4.text-right .col-md-4.col-sm-4.col-xs-4.text-right
%button.btn.btn-default.btn-sm{ type: :button, name: 'mod-delete-report', data: { id: report.id } } %button.btn.btn-default.btn-sm{ type: :button, name: 'mod-delete-report', data: { id: report.id } }

View File

@ -2,7 +2,7 @@
.container .container
.card-body .card-body
.media .media
%a.pull-left{ href: show_user_profile_path(user.screen_name) } %a.pull-left{ href: user_path(user) }
%img.answerbox__question-user-avatar.avatar-md{ src: user.profile_picture.url(:medium) } %img.answerbox__question-user-avatar.avatar-md{ src: user.profile_picture.url(:medium) }
.media-body .media-body
= t(".title_html", screen_name: user.screen_name, user_id: user.id) = t(".title_html", screen_name: user.screen_name, user_id: user.id)

View File

@ -1,6 +1,6 @@
.dropdown-menu.profile-dropdown{ id: "rs-#{size}-nav-profile" } .dropdown-menu.profile-dropdown{ id: "rs-#{size}-nav-profile" }
%h6.dropdown-header.d-none.d-sm-block= current_user.screen_name %h6.dropdown-header.d-none.d-sm-block= current_user.screen_name
%a.dropdown-item{ href: show_user_profile_path(current_user.screen_name) } %a.dropdown-item{ href: user_path(current_user) }
%i.fa.fa-fw.fa-user %i.fa.fa-fw.fa-user
= t("views.navigation.show") = t("views.navigation.show")
%a.dropdown-item{ href: edit_user_registration_path } %a.dropdown-item{ href: edit_user_registration_path }

View File

@ -6,7 +6,7 @@
%img.avatar-xs{ src: notification.target.user.profile_picture.url(:small) } %img.avatar-xs{ src: notification.target.user.profile_picture.url(:small) }
= t(".heading_html", = t(".heading_html",
user: user_screen_name(notification.target.user), user: user_screen_name(notification.target.user),
question: link_to(t(".link_text"), show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.id)), question: link_to(t(".link_text"), answer_path(username: notification.target.user.screen_name, id: notification.target.id)),
time: time_tooltip(notification.target)) time: time_tooltip(notification.target))
.list-group .list-group
.list-group-item .list-group-item

View File

@ -7,17 +7,17 @@
- if notification.target.answer.user == current_user - if notification.target.answer.user == current_user
= t(".heading_html", = t(".heading_html",
user: user_screen_name(notification.target.user), user: user_screen_name(notification.target.user),
answer: link_to(t(".active.link_text"), show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.answer.id)), answer: link_to(t(".active.link_text"), answer_path(username: notification.target.user.screen_name, id: notification.target.answer.id)),
time: time_tooltip(notification.target)) time: time_tooltip(notification.target))
- elsif notification.target.user == notification.target.answer.user - elsif notification.target.user == notification.target.answer.user
= t(".heading_html", = t(".heading_html",
user: user_screen_name(notification.target.user), user: user_screen_name(notification.target.user),
answer: link_to(t(".passive.link_text"), show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.answer.id)), answer: link_to(t(".passive.link_text"), answer_path(username: notification.target.user.screen_name, id: notification.target.answer.id)),
time: time_tooltip(notification.target)) time: time_tooltip(notification.target))
- else - else
= t(".heading_html", = t(".heading_html",
user: user_screen_name(notification.target.user), user: user_screen_name(notification.target.user),
answer: link_to(t(".other.link_text_html", user: user_screen_name(notification.target.answer.user)), show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.answer.id)), answer: link_to(t(".other.link_text_html", user: user_screen_name(notification.target.answer.user)), answer_path(username: notification.target.user.screen_name, id: notification.target.answer.id)),
time: time_tooltip(notification.target)) time: time_tooltip(notification.target))
.list-group .list-group
.list-group-item .list-group-item

View File

@ -7,12 +7,12 @@
- if notification.target.parent_type == 'Answer' - if notification.target.parent_type == 'Answer'
= t(".heading_html", = t(".heading_html",
user: user_screen_name(notification.target.user), user: user_screen_name(notification.target.user),
type: link_to(t(".#{notification.target.parent_type.downcase}.link_text"), show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.parent.id)), type: link_to(t(".#{notification.target.parent_type.downcase}.link_text"), answer_path(username: notification.target.user.screen_name, id: notification.target.parent.id)),
time: time_tooltip(notification.target)) time: time_tooltip(notification.target))
- elsif notification.target.parent_type == 'Comment' - elsif notification.target.parent_type == 'Comment'
= t(".heading_html", = t(".heading_html",
user: user_screen_name(notification.target.user), user: user_screen_name(notification.target.user),
type: link_to(t(".#{notification.target.parent_type.downcase}.link_text"), show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.parent.answer.id)), type: link_to(t(".#{notification.target.parent_type.downcase}.link_text"), answer_path(username: notification.target.user.screen_name, id: notification.target.parent.answer.id)),
time: time_tooltip(notification.target)) time: time_tooltip(notification.target))
.list-group .list-group
.list-group-item .list-group-item

View File

@ -3,7 +3,7 @@
.card-body .card-body
.media .media
- unless question.author_is_anonymous - unless question.author_is_anonymous
%a.pull-left{ href: unless hidden then show_user_profile_path(question.user.screen_name) end } %a.pull-left{ href: unless hidden then user_path(question.user) end }
%img.answerbox__question-user-avatar.avatar-md{ src: question.user.profile_picture.url(:medium) } %img.answerbox__question-user-avatar.avatar-md{ src: question.user.profile_picture.url(:medium) }
.media-body .media-body
- if user_signed_in? - if user_signed_in?

View File

@ -25,7 +25,7 @@
= raw t('views.answerbox.asked', user: user_screen_name(q.user), time: time_tooltip(q)) = raw t('views.answerbox.asked', user: user_screen_name(q.user), time: time_tooltip(q))
- if q.answer_count > 1 - if q.answer_count > 1
· ·
%a{ href: show_user_question_path(q.user.screen_name, q.id) } %a{ href: question_path(q.user.screen_name, q.id) }
= pluralize(q.answer_count, t('views.general.answer')) = pluralize(q.answer_count, t('views.general.answer'))
%p.answerbox__question-text %p.answerbox__question-text
= q.content = q.content

View File

@ -15,7 +15,7 @@
- if @list.members.empty? - if @list.members.empty?
%p.text-muted No members yet. %p.text-muted No members yet.
- @list.members.each do |member| - @list.members.each do |member|
%a{ href: show_user_profile_path(member.user.screen_name), title: member.user.screen_name, data: { toggle: :tooltip, placement: :top } } %a{ href: user_path(member.user), title: member.user.screen_name, data: { toggle: :tooltip, placement: :top } }
%img.avatar-xs{ src: member.user.profile_picture.url(:medium) } %img.avatar-xs{ src: member.user.profile_picture.url(:medium) }
= render 'shared/links' = render 'shared/links'

View File

@ -3,7 +3,7 @@
%img.userbox__header{ src: user.profile_header.url(:mobile) } %img.userbox__header{ src: user.profile_header.url(:mobile) }
.card-body .card-body
%img.userbox__avatar{ src: user.profile_picture.url(:small) } %img.userbox__avatar{ src: user.profile_picture.url(:small) }
%a.profile__name{ href: show_user_profile_path(user.screen_name) } %a.profile__name{ href: user_path(user) }
- unless user.profile.display_name.blank? - unless user.profile.display_name.blank?
.profile__display-name .profile__display-name
= user.profile.display_name = user.profile.display_name

View File

@ -1,6 +1,6 @@
.card .card
.list-group.list-group-horizontal-sm.text-center .list-group.list-group-horizontal-sm.text-center
= list_group_item 'Answers', show_user_profile_path(user.screen_name), badge: user.answered_count = list_group_item 'Answers', user_path(user), badge: user.answered_count
= list_group_item 'Questions', show_user_questions_path(user.screen_name), badge: user.asked_count = list_group_item 'Questions', show_user_questions_path(user.screen_name), badge: user.asked_count
= list_group_item 'Followers', show_user_followers_path(user.screen_name), badge: user.followers.count = list_group_item 'Followers', show_user_followers_path(user.screen_name), badge: user.followers.count
= list_group_item 'Following', show_user_followings_path(user.screen_name), badge: user.followings.count = list_group_item 'Following', show_user_followings_path(user.screen_name), badge: user.followings.count

View File

@ -1,169 +1,171 @@
require 'sidekiq/web' # frozen_string_literal: true
require "sidekiq/web"
Rails.application.routes.draw do Rails.application.routes.draw do
start = Time.now start = Time.zone.now
# Routes only accessible by admins (admin panels, sidekiq, pghero) # Routes only accessible by admins (admin panels, sidekiq, pghero)
authenticate :user, ->(user) { user.has_role?(:administrator) } do authenticate :user, ->(user) { user.has_role?(:administrator) } do
# Admin panel # Admin panel
mount RailsAdmin::Engine => "/justask_admin", as: "rails_admin" mount RailsAdmin::Engine => "/justask_admin", :as => "rails_admin"
mount Sidekiq::Web, at: "/sidekiq" mount Sidekiq::Web, at: "/sidekiq"
mount PgHero::Engine, at: "/pghero", as: "pghero" mount PgHero::Engine, at: "/pghero", as: "pghero"
match "/admin/announcements", to: "announcement#index", via: :get, as: :announcement_index get "/admin/announcements", to: "announcement#index", as: :announcement_index
match "/admin/announcements", to: "announcement#create", via: :post, as: :announcement_create post "/admin/announcements", to: "announcement#create", as: :announcement_create
match "/admin/announcements/new", to: "announcement#new", via: :get, as: :announcement_new get "/admin/announcements/new", to: "announcement#new", as: :announcement_new
match "/admin/announcements/:id/edit", to: "announcement#edit", via: :get, as: :announcement_edit get "/admin/announcements/:id/edit", to: "announcement#edit", as: :announcement_edit
match "/admin/announcements/:id", to: "announcement#update", via: :patch, as: :announcement_update patch "/admin/announcements/:id", to: "announcement#update", as: :announcement_update
match "/admin/announcements/:id", to: "announcement#destroy", via: :delete, as: :announcement_destroy delete "/admin/announcements/:id", to: "announcement#destroy", as: :announcement_destroy
end end
# Routes only accessible by moderators (moderation panel) # Routes only accessible by moderators (moderation panel)
authenticate :user, ->(user) { user.mod? } do authenticate :user, ->(user) { user.mod? } do
match '/moderation/unmask', to: 'moderation#toggle_unmask', via: :post, as: :moderation_toggle_unmask post "/moderation/unmask", to: "moderation#toggle_unmask", as: :moderation_toggle_unmask
match '/moderation(/:type)', to: 'moderation#index', via: :get, as: :moderation, defaults: {type: 'all'} get "/moderation(/:type)", to: "moderation#index", as: :moderation, defaults: { type: "all" }
match '/moderation/inbox/:user', to: 'moderation/inbox#index', via: :get, as: :mod_inbox_index get "/moderation/inbox/:user", to: "moderation/inbox#index", as: :mod_inbox_index
namespace :ajax do namespace :ajax do
match '/mod/destroy_report', to: 'moderation#destroy_report', via: :post, as: :mod_destroy_report post "/mod/destroy_report", to: "moderation#destroy_report", as: :mod_destroy_report
match '/mod/privilege', to: 'moderation#privilege', via: :post, as: :mod_privilege post "/mod/privilege", to: "moderation#privilege", as: :mod_privilege
match '/mod/ban', to: 'moderation#ban', via: :post, as: :mod_ban post "/mod/ban", to: "moderation#ban", as: :mod_ban
end end
end end
unauthenticated :user do unauthenticated :user do
root to: 'about#index' root to: "about#index"
end end
authenticate :user do authenticate :user do
root to: 'timeline#index', as: :timeline root to: "timeline#index", as: :timeline
end end
match '/about', to: 'about#about', via: 'get' get "/about", to: "about#about"
match '/privacy', to: 'about#privacy_policy', via: 'get', as: :privacy_policy get "/privacy", to: "about#privacy_policy", as: :privacy_policy
match '/terms', to: 'about#terms', via: 'get', as: :terms get "/terms", to: "about#terms", as: :terms
match '/linkfilter', to: 'link_filter#index', via: 'get', as: :linkfilter get "/linkfilter", to: "link_filter#index", as: :linkfilter
match '/manifest.json', to: 'manifests#show', via: 'get', as: :webapp_manifest get "/manifest.json", to: "manifests#show", as: :webapp_manifest
# Devise routes # Devise routes
devise_for :users, path: 'user', skip: [:sessions, :registrations] devise_for :users, path: "user", skip: %i[sessions registrations]
as :user do as :user do
# :sessions # :sessions
get 'sign_in' => 'user/sessions#new', as: :new_user_session get "sign_in" => "user/sessions#new", :as => :new_user_session
post 'sign_in' => 'user/sessions#create', as: :user_session post "sign_in" => "user/sessions#create", :as => :user_session
delete 'sign_out' => 'devise/sessions#destroy', as: :destroy_user_session delete "sign_out" => "devise/sessions#destroy", :as => :destroy_user_session
# :registrations # :registrations
get 'settings/delete_account' => 'devise/registrations#cancel', as: :cancel_user_registration get "settings/delete_account" => "devise/registrations#cancel", :as => :cancel_user_registration
post '/user/create' => 'user/registrations#create', as: :user_registration post "/user/create" => "user/registrations#create", :as => :user_registration
get '/sign_up' => 'devise/registrations#new', as: :new_user_registration get "/sign_up" => "devise/registrations#new", :as => :new_user_registration
get '/settings/account' => 'devise/registrations#edit', as: :edit_user_registration get "/settings/account" => "devise/registrations#edit", :as => :edit_user_registration
patch '/settings/account' => 'devise/registrations#update', as: :update_user_registration patch "/settings/account" => "devise/registrations#update", :as => :update_user_registration
put '/settings/account' => 'devise/registrations#update' put "/settings/account" => "devise/registrations#update"
delete '/settings/account' => 'user/registrations#destroy' delete "/settings/account" => "user/registrations#destroy"
end end
namespace :settings do namespace :settings do
get :theme, to: redirect('/settings/theme/edit') get :theme, to: redirect("/settings/theme/edit")
resource :theme, controller: :theme, only: %i[edit update destroy] resource :theme, controller: :theme, only: %i[edit update destroy]
get :profile, to: redirect('/settings/profile/edit') get :profile, to: redirect("/settings/profile/edit")
resource :profile, controller: :profile, only: %i[edit update] resource :profile, controller: :profile, only: %i[edit update]
resource :profile_picture, controller: :profile_picture, only: %i[update] resource :profile_picture, controller: :profile_picture, only: %i[update]
get :privacy, to: redirect('/settings/privacy/edit') get :privacy, to: redirect("/settings/privacy/edit")
resource :privacy, controller: :privacy, only: %i[edit update] resource :privacy, controller: :privacy, only: %i[edit update]
get :export, to: 'export#index' get :export, to: "export#index"
post :export, to: 'export#create' post :export, to: "export#create"
get :muted, to: 'mutes#index' get :muted, to: "mutes#index"
get :blocks, to: 'blocks#index' get :blocks, to: "blocks#index"
get :data, to: 'data#index' get :data, to: "data#index"
namespace :two_factor_authentication do namespace :two_factor_authentication do
get :otp_authentication, to: 'otp_authentication#index' get :otp_authentication, to: "otp_authentication#index"
patch :otp_authentication, to: 'otp_authentication#update' patch :otp_authentication, to: "otp_authentication#update"
delete :otp_authentication, to: 'otp_authentication#destroy' delete :otp_authentication, to: "otp_authentication#destroy"
match 'otp_authentication/reset', to: 'otp_authentication#reset', via: :delete delete "otp_authentication/reset", to: "otp_authentication#reset"
end end
end end
resolve('Theme') { [:settings_theme] } # to make link_to/form_for work nicely when passing a `Theme` object to it, see also: https://api.rubyonrails.org/v6.1.5.1/classes/ActionDispatch/Routing/Mapper/CustomUrls.html#method-i-resolve resolve("Theme") { [:settings_theme] } # to make link_to/form_for work nicely when passing a `Theme` object to it, see also: https://api.rubyonrails.org/v6.1.5.1/classes/ActionDispatch/Routing/Mapper/CustomUrls.html#method-i-resolve
resolve('Profile') { [:settings_profile] } resolve("Profile") { [:settings_profile] }
# resources :services, only: [:index, :destroy] # resources :services, only: [:index, :destroy]
match '/settings/services', to: 'services#index', via: 'get', as: :services get "/settings/services", to: "services#index", as: :services
match '/settings/services/:id', to: 'services#update', via: 'patch', as: :update_service patch "/settings/services/:id", to: "services#update", as: :update_service
match '/settings/services/:id', to: 'services#destroy', via: 'delete', as: :service delete "/settings/services/:id", to: "services#destroy", as: :service
controller :services do controller :services do
scope "/auth", as: "auth" do scope "/auth", as: "auth" do
get ':provider/callback' => :create get ":provider/callback" => :create
get :failure get :failure
end end
end end
namespace :ajax do namespace :ajax do
match '/ask', to: 'question#create', via: :post, as: :ask post "/ask", to: "question#create", as: :ask
match '/destroy_question', to: 'question#destroy', via: :post, as: :destroy_question post "/destroy_question", to: "question#destroy", as: :destroy_question
match '/generate_question', to: 'inbox#create', via: :post, as: :generate_question post "/generate_question", to: "inbox#create", as: :generate_question
match '/delete_inbox', to: 'inbox#remove', via: :post, as: :delete_inbox post "/delete_inbox", to: "inbox#remove", as: :delete_inbox
match '/delete_all_inbox', to: 'inbox#remove_all', via: :post, as: :delete_all_inbox post "/delete_all_inbox", to: "inbox#remove_all", as: :delete_all_inbox
match '/delete_all_inbox/:author', to: 'inbox#remove_all_author', via: :post, as: :delete_all_author post "/delete_all_inbox/:author", to: "inbox#remove_all_author", as: :delete_all_author
match '/answer', to: 'answer#create', via: :post, as: :answer post "/answer", to: "answer#create", as: :answer
match '/destroy_answer', to: 'answer#destroy', via: :post, as: :destroy_answer post "/destroy_answer", to: "answer#destroy", as: :destroy_answer
match '/create_relationship', to: 'relationship#create', via: :post, as: :create_relationship post "/create_relationship", to: "relationship#create", as: :create_relationship
match '/destroy_relationship', to: 'relationship#destroy', via: :post, as: :destroy_relationship post "/destroy_relationship", to: "relationship#destroy", as: :destroy_relationship
match '/create_smile', to: 'smile#create', via: :post, as: :create_smile post "/create_smile", to: "smile#create", as: :create_smile
match '/destroy_smile', to: 'smile#destroy', via: :post, as: :destroy_smile post "/destroy_smile", to: "smile#destroy", as: :destroy_smile
match '/create_comment_smile', to: 'smile#create_comment', via: :post, as: :create_comment_smile post "/create_comment_smile", to: "smile#create_comment", as: :create_comment_smile
match '/destroy_comment_smile', to: 'smile#destroy_comment', via: :post, as: :destroy_comment_smile post "/destroy_comment_smile", to: "smile#destroy_comment", as: :destroy_comment_smile
match '/create_comment', to: 'comment#create', via: :post, as: :create_comment post "/create_comment", to: "comment#create", as: :create_comment
match '/destroy_comment', to: 'comment#destroy', via: :post, as: :destroy_comment post "/destroy_comment", to: "comment#destroy", as: :destroy_comment
match '/report', to: 'report#create', via: :post, as: :report post "/report", to: "report#create", as: :report
match '/create_list', to: 'list#create', via: :post, as: :create_list post "/create_list", to: "list#create", as: :create_list
match '/destroy_list', to: 'list#destroy', via: :post, as: :destroy_list post "/destroy_list", to: "list#destroy", as: :destroy_list
match '/list_membership', to: 'list#membership', via: :post, as: :list_membership post "/list_membership", to: "list#membership", as: :list_membership
match '/subscribe', to: 'subscription#subscribe', via: :post, as: :subscribe_answer post "/subscribe", to: "subscription#subscribe", as: :subscribe_answer
match '/unsubscribe', to: 'subscription#unsubscribe', via: :post, as: :unsubscribe_answer post "/unsubscribe", to: "subscription#unsubscribe", as: :unsubscribe_answer
match '/mute', to: 'mute_rule#create', via: :post, as: :create_mute_rule post "/mute", to: "mute_rule#create", as: :create_mute_rule
match '/mute/:id', to: 'mute_rule#update', via: :post, as: :update_mute_rule post "/mute/:id", to: "mute_rule#update", as: :update_mute_rule
match '/mute/:id', to: 'mute_rule#destroy', via: :delete, as: :delete_mute_rule delete "/mute/:id", to: "mute_rule#destroy", as: :delete_mute_rule
match '/block_anon', to: 'anonymous_block#create', via: :post, as: :block_anon post "/block_anon", to: "anonymous_block#create", as: :block_anon
match '/block_anon/:id', to: 'anonymous_block#destroy', via: :delete, as: :unblock_anon delete "/block_anon/:id", to: "anonymous_block#destroy", as: :unblock_anon
end end
match '/discover', to: 'discover#index', via: :get, as: :discover get "/discover", to: "discover#index", as: :discover
match '/public', to: 'timeline#public', via: :get, as: :public_timeline if APP_CONFIG.dig(:features, :public, :enabled) get "/public", to: "timeline#public", as: :public_timeline if APP_CONFIG.dig(:features, :public, :enabled)
match '/list/:list_name', to: 'timeline#list', via: :get, as: :list_timeline get "/list/:list_name", to: "timeline#list", as: :list_timeline
match '/notifications(/:type)', to: 'notifications#index', via: :get, as: :notifications, defaults: {type: 'new'} get "/notifications(/:type)", to: "notifications#index", as: :notifications, defaults: { type: "new" }
match '/inbox', to: 'inbox#show', via: 'get' get "/inbox", to: "inbox#show"
match '/inbox/:author', to: 'inbox#show', via: 'get' get "/inbox/:author", to: "inbox#show"
match '/user/:username(/p/:page)', to: 'user#show', via: 'get', defaults: {page: 1} get "/user/:username(/p/:page)", to: "user#show", defaults: { page: 1 }
match '/@:username(/p/:page)', to: 'user#show', via: 'get', as: :show_user_profile, defaults: {page: 1} get "/@:username(/p/:page)", to: "user#show", as: :user, defaults: { page: 1 }
match '/@:username/a/:id', to: 'answer#show', via: 'get', as: :show_user_answer get "/@:username/a/:id", to: "answer#show", via: "get", as: :answer
match '/@:username/q/:id', to: 'question#show', via: 'get', as: :show_user_question get "/@:username/q/:id", to: "question#show", via: "get", as: :question
match '/@:username/followers(/p/:page)', to: 'user#followers', via: 'get', as: :show_user_followers, defaults: {page: 1} get "/@:username/followers(/p/:page)", to: "user#followers", as: :show_user_followers, defaults: { page: 1 }
match '/@:username/followings(/p/:page)', to: 'user#followings', via: 'get', as: :show_user_followings, defaults: {page: 1} get "/@:username/followings(/p/:page)", to: "user#followings", as: :show_user_followings, defaults: { page: 1 }
match '/@:username/friends(/p/:page)', to: redirect('/@%{username}/followings/p/%{page}'), via: 'get', defaults: {page: 1} get "/@:username/friends(/p/:page)", to: redirect("/@%{username}/followings/p/%{page}"), defaults: { page: 1 }
match '/@:username/questions(/p/:page)', to: 'user#questions', via: 'get', as: :show_user_questions, defaults: {page: 1} get "/@:username/questions(/p/:page)", to: "user#questions", as: :show_user_questions, defaults: { page: 1 }
match '/:username(/p/:page)', to: 'user#show', via: 'get', as: :show_user_profile_alt, defaults: {page: 1} get "/:username(/p/:page)", to: "user#show", as: :user_alt, defaults: { page: 1 }
match '/:username/a/:id', to: 'answer#show', via: 'get', as: :show_user_answer_alt get "/:username/a/:id", to: "answer#show", as: :answer_alt
match '/:username/q/:id', to: 'question#show', via: 'get', as: :show_user_question_alt get "/:username/q/:id", to: "question#show", as: :question_alt
match '/:username/followers(/p/:page)', to: 'user#followers', via: 'get', as: :show_user_followers_alt, defaults: {page: 1} get "/:username/followers(/p/:page)", to: "user#followers", as: :show_user_followers_alt, defaults: { page: 1 }
match '/:username/followings(/p/:page)', to: 'user#followings', via: 'get', as: :show_user_followings_alt, defaults: {page: 1} get "/:username/followings(/p/:page)", to: "user#followings", as: :show_user_followings_alt, defaults: { page: 1 }
match '/:username/friends(/p/:page)', to: redirect('/%{username}/followings/p/%{page}'), via: 'get', defaults: {page: 1} get "/:username/friends(/p/:page)", to: redirect("/%{username}/followings/p/%{page}"), defaults: { page: 1 }
match '/:username/questions(/p/:page)', to: 'user#questions', via: 'get', as: :show_user_questions_alt, defaults: {page: 1} get "/:username/questions(/p/:page)", to: "user#questions", as: :show_user_questions_alt, defaults: { page: 1 }
match '/feedback/consent', to: 'feedback#consent', via: 'get', as: 'feedback_consent' get "/feedback/consent", to: "feedback#consent", as: "feedback_consent"
match '/feedback/consent/update', to: 'feedback#update', via: 'post', as: 'feedback_consent_update' post "/feedback/consent/update", to: "feedback#update", as: "feedback_consent_update"
match '/feedback/bugs(/*any)', to: 'feedback#bugs', via: 'get', as: 'feedback_bugs' get "/feedback/bugs(/*any)", to: "feedback#bugs", as: "feedback_bugs"
match '/feedback/feature_requests(/*any)', to: 'feedback#features', via: 'get', as: 'feedback_features' get "/feedback/feature_requests(/*any)", to: "feedback#features", as: "feedback_features"
get '/.well-known/change-password', to: redirect('/settings/account') get "/.well-known/change-password", to: redirect("/settings/account")
puts 'processing time of routes.rb: ' + "#{(Time.now - start).round(3).to_s.ljust(5, '0')}s".light_green puts "processing time of routes.rb: #{"#{(Time.zone.now - start).round(3).to_s.ljust(5, '0')}s".light_green}"
end end

View File

@ -37,7 +37,7 @@ describe UserHelper, type: :helper do
context "user is not banned" do context "user is not banned" do
it "unmasks the author" do it "unmasks the author" do
expect(subject).to eq(link_to(user.profile.safe_name, show_user_profile_path(user.screen_name), class: "")) expect(subject).to eq(link_to(user.profile.safe_name, user_path(user), class: ""))
end end
end end
@ -47,7 +47,7 @@ describe UserHelper, type: :helper do
end end
it "unmasks the author" do it "unmasks the author" do
expect(subject).to eq(link_to(user.profile.safe_name, show_user_profile_path(user.screen_name), class: "user--banned")) expect(subject).to eq(link_to(user.profile.safe_name, user_path(user), class: "user--banned"))
end end
end end
end end
@ -114,7 +114,7 @@ describe UserHelper, type: :helper do
let(:link_only) { true } let(:link_only) { true }
it "returns the url to the user's profile" do it "returns the url to the user's profile" do
expect(subject).to eq(show_user_profile_path(user.screen_name)) expect(subject).to eq(user_path(user))
end end
end end
@ -123,7 +123,7 @@ describe UserHelper, type: :helper do
context "user is not banned" do context "user is not banned" do
it "returns a link tag to the user's profile" do it "returns a link tag to the user's profile" do
expect(subject).to eq(link_to(user.profile.safe_name, show_user_profile_path(user.screen_name), class: "")) expect(subject).to eq(link_to(user.profile.safe_name, user_path(user), class: ""))
end end
end end
@ -133,7 +133,7 @@ describe UserHelper, type: :helper do
end end
it "returns a link tag to the user's profile" do it "returns a link tag to the user's profile" do
expect(subject).to eq(link_to(user.profile.safe_name, show_user_profile_path(user.screen_name), class: "user--banned")) expect(subject).to eq(link_to(user.profile.safe_name, user_path(user), class: "user--banned"))
end end
end end
end end