commit
c6b7b98489
@ -0,0 +1,44 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
module Admin |
||||
class TagsController < BaseController |
||||
before_action :set_tags, only: :index |
||||
before_action :set_tag, except: :index |
||||
before_action :set_filter_params |
||||
|
||||
def index |
||||
authorize :tag, :index? |
||||
end |
||||
|
||||
def hide |
||||
authorize @tag, :hide? |
||||
@tag.account_tag_stat.update!(hidden: true) |
||||
redirect_to admin_tags_path(@filter_params) |
||||
end |
||||
|
||||
def unhide |
||||
authorize @tag, :unhide? |
||||
@tag.account_tag_stat.update!(hidden: true) |
||||
redirect_to admin_tags_path(@filter_params) |
||||
end |
||||
|
||||
private |
||||
|
||||
def set_tags |
||||
@tags = Tag.discoverable |
||||
@tags.merge!(Tag.hidden) if filter_params[:hidden] |
||||
end |
||||
|
||||
def set_tag |
||||
@tag = Tag.find(params[:id]) |
||||
end |
||||
|
||||
def set_filter_params |
||||
@filter_params = filter_params.to_hash.symbolize_keys |
||||
end |
||||
|
||||
def filter_params |
||||
params.permit(:hidden) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,53 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class DirectoriesController < ApplicationController |
||||
layout 'public' |
||||
|
||||
before_action :set_instance_presenter |
||||
before_action :set_tag, only: :show |
||||
before_action :set_tags |
||||
before_action :set_accounts |
||||
before_action :set_pack |
||||
|
||||
def index |
||||
render :index |
||||
end |
||||
|
||||
def show |
||||
render :index |
||||
end |
||||
|
||||
private |
||||
|
||||
def set_pack |
||||
use_pack 'share' |
||||
end |
||||
|
||||
def set_tag |
||||
@tag = Tag.discoverable.find_by!(name: params[:id].downcase) |
||||
end |
||||
|
||||
def set_tags |
||||
@tags = Tag.discoverable.limit(30) |
||||
end |
||||
|
||||
def set_accounts |
||||
@accounts = Account.searchable.discoverable.page(params[:page]).per(50).tap do |query| |
||||
query.merge!(Account.tagged_with(@tag.id)) if @tag |
||||
|
||||
if popular_requested? |
||||
query.merge!(Account.popular) |
||||
else |
||||
query.merge!(Account.by_recent_status) |
||||
end |
||||
end |
||||
end |
||||
|
||||
def set_instance_presenter |
||||
@instance_presenter = InstancePresenter.new |
||||
end |
||||
|
||||
def popular_requested? |
||||
request.path.ends_with?('/popular') |
||||
end |
||||
end |
@ -0,0 +1,24 @@ |
||||
# frozen_string_literal: true |
||||
# == Schema Information |
||||
# |
||||
# Table name: account_tag_stats |
||||
# |
||||
# id :bigint(8) not null, primary key |
||||
# tag_id :bigint(8) not null |
||||
# accounts_count :bigint(8) default(0), not null |
||||
# hidden :boolean default(FALSE), not null |
||||
# created_at :datetime not null |
||||
# updated_at :datetime not null |
||||
# |
||||
|
||||
class AccountTagStat < ApplicationRecord |
||||
belongs_to :tag, inverse_of: :account_tag_stat |
||||
|
||||
def increment_count!(key) |
||||
update(key => public_send(key) + 1) |
||||
end |
||||
|
||||
def decrement_count!(key) |
||||
update(key => [public_send(key) - 1, 0].max) |
||||
end |
||||
end |
@ -0,0 +1,15 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class TagPolicy < ApplicationPolicy |
||||
def index? |
||||
staff? |
||||
end |
||||
|
||||
def hide? |
||||
staff? |
||||
end |
||||
|
||||
def unhide? |
||||
staff? |
||||
end |
||||
end |
@ -0,0 +1,12 @@ |
||||
%tr |
||||
%td |
||||
= link_to explore_hashtag_path(tag) do |
||||
= fa_icon 'hashtag' |
||||
= tag.name |
||||
%td |
||||
= t('directories.people', count: tag.accounts_count) |
||||
%td |
||||
- if tag.hidden? |
||||
= table_link_to 'eye', t('admin.tags.unhide'), unhide_admin_tag_path(tag.id, **@filter_params), method: :post |
||||
- else |
||||
= table_link_to 'eye-slash', t('admin.tags.hide'), hide_admin_tag_path(tag.id, **@filter_params), method: :post |
@ -0,0 +1,19 @@ |
||||
- content_for :page_title do |
||||
= t('admin.tags.title') |
||||
|
||||
.filters |
||||
.filter-subset |
||||
%strong= t('admin.reports.status') |
||||
%ul |
||||
%li= filter_link_to t('admin.tags.visible'), hidden: nil |
||||
%li= filter_link_to t('admin.tags.hidden'), hidden: '1' |
||||
|
||||
.table-wrapper |
||||
%table.table |
||||
%thead |
||||
%tr |
||||
%th= t('admin.tags.name') |
||||
%th= t('admin.tags.accounts') |
||||
%th |
||||
%tbody |
||||
= render @tags |
@ -0,0 +1,61 @@ |
||||
- content_for :page_title do |
||||
= t('directories.explore_mastodon', title: site_title) |
||||
|
||||
- content_for :header_tags do |
||||
%meta{ name: 'description', content: t('directories.explanation') } |
||||
|
||||
= opengraph 'og:site_name', t('about.hosted_on', domain: site_hostname) |
||||
= opengraph 'og:type', 'website' |
||||
= opengraph 'og:title', t('directories.explore_mastodon', title: site_title) |
||||
= opengraph 'og:description', t('directories.explanation') |
||||
= opengraph 'og:image', File.join(root_url, 'android-chrome-192x192.png') |
||||
|
||||
.page-header |
||||
%h1= t('directories.explore_mastodon', title: site_title) |
||||
%p= t('directories.explanation') |
||||
|
||||
.grid |
||||
.column-0 |
||||
.account__section-headline |
||||
= active_link_to t('directories.most_recently_active'), @tag ? explore_hashtag_path(@tag) : explore_path |
||||
= active_link_to t('directories.most_popular'), @tag ? explore_hashtag_popular_path(@tag) : explore_popular_path |
||||
|
||||
- if @accounts.empty? |
||||
= nothing_here |
||||
- else |
||||
.directory |
||||
%table.accounts-table |
||||
%tbody |
||||
- @accounts.each do |account| |
||||
%tr |
||||
%td= account_link_to account |
||||
%td.accounts-table__count |
||||
= number_to_human account.statuses_count, strip_insignificant_zeros: true |
||||
%small= t('accounts.posts', count: account.statuses_count).downcase |
||||
%td.accounts-table__count |
||||
= number_to_human account.followers_count, strip_insignificant_zeros: true |
||||
%small= t('accounts.followers', count: account.followers_count).downcase |
||||
%td.accounts-table__count |
||||
- if account.last_status_at.present? |
||||
%time.time-ago{ datetime: account.last_status_at.iso8601, title: l(account.last_status_at) }= l account.last_status_at |
||||
- else |
||||
\- |
||||
%small= t('accounts.last_active') |
||||
|
||||
= paginate @accounts |
||||
|
||||
.column-1 |
||||
- if @tags.empty? |
||||
.nothing-here.nothing-here--flexible |
||||
- else |
||||
- @tags.each do |tag| |
||||
.directory__tag{ class: tag.id == @tag&.id ? 'active' : nil } |
||||
= link_to explore_hashtag_path(tag) do |
||||
%h4 |
||||
= fa_icon 'hashtag' |
||||
= tag.name |
||||
%small= t('directories.people', count: tag.accounts_count) |
||||
|
||||
.avatar-stack |
||||
- tag.cached_sample_accounts.each do |account| |
||||
= image_tag current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url, width: 48, height: 48, alt: '', class: 'account__avatar' |
@ -0,0 +1,8 @@ |
||||
class CreateAccountsTagsJoinTable < ActiveRecord::Migration[5.2] |
||||
def change |
||||
create_join_table :accounts, :tags do |t| |
||||
t.index [:account_id, :tag_id] |
||||
t.index [:tag_id, :account_id], unique: true |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,5 @@ |
||||
class AddDiscoverableToAccounts < ActiveRecord::Migration[5.2] |
||||
def change |
||||
add_column :accounts, :discoverable, :boolean |
||||
end |
||||
end |
@ -0,0 +1,5 @@ |
||||
class AddLastStatusAtToAccountStats < ActiveRecord::Migration[5.2] |
||||
def change |
||||
add_column :account_stats, :last_status_at, :datetime |
||||
end |
||||
end |
@ -0,0 +1,11 @@ |
||||
class CreateAccountTagStats < ActiveRecord::Migration[5.2] |
||||
def change |
||||
create_table :account_tag_stats do |t| |
||||
t.belongs_to :tag, null: false, foreign_key: { on_delete: :cascade }, index: { unique: true } |
||||
t.bigint :accounts_count, default: 0, null: false |
||||
t.boolean :hidden, default: false, null: false |
||||
|
||||
t.timestamps |
||||
end |
||||
end |
||||
end |
@ -1,7 +1,29 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe ActivityPub::InboxesController, type: :controller do |
||||
describe 'POST #create' do |
||||
pending |
||||
context 'if signed_request_account' do |
||||
it 'returns 202' do |
||||
allow(controller).to receive(:signed_request_account) do |
||||
Fabricate(:account) |
||||
end |
||||
|
||||
post :create |
||||
expect(response).to have_http_status(202) |
||||
end |
||||
end |
||||
|
||||
context 'not signed_request_account' do |
||||
it 'returns 401' do |
||||
allow(controller).to receive(:signed_request_account) do |
||||
false |
||||
end |
||||
|
||||
post :create |
||||
expect(response).to have_http_status(401) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
@ -0,0 +1,3 @@ |
||||
Fabricator(:account_tag_stat) do |
||||
accounts_count "" |
||||
end |
@ -1,15 +1,55 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
# Specs in this file have access to a helper object that includes |
||||
# the Admin::AccountModerationNotesHelper. For example: |
||||
# |
||||
# describe Admin::AccountModerationNotesHelper do |
||||
# describe "string concat" do |
||||
# it "concats two strings with spaces" do |
||||
# expect(helper.concat_strings("this","that")).to eq("this that") |
||||
# end |
||||
# end |
||||
# end |
||||
RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do |
||||
pending "add some examples to (or delete) #{__FILE__}" |
||||
include StreamEntriesHelper |
||||
|
||||
describe '#admin_account_link_to' do |
||||
context 'account is nil' do |
||||
let(:account) { nil } |
||||
|
||||
it 'returns nil' do |
||||
expect(helper.admin_account_link_to(account)).to be_nil |
||||
end |
||||
end |
||||
|
||||
context 'with account' do |
||||
let(:account) { Fabricate(:account) } |
||||
|
||||
it 'calls #link_to' do |
||||
expect(helper).to receive(:link_to).with( |
||||
admin_account_path(account.id), |
||||
class: name_tag_classes(account), |
||||
title: account.acct |
||||
) |
||||
|
||||
helper.admin_account_link_to(account) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#admin_account_inline_link_to' do |
||||
context 'account is nil' do |
||||
let(:account) { nil } |
||||
|
||||
it 'returns nil' do |
||||
expect(helper.admin_account_inline_link_to(account)).to be_nil |
||||
end |
||||
end |
||||
|
||||
context 'with account' do |
||||
let(:account) { Fabricate(:account) } |
||||
|
||||
it 'calls #link_to' do |
||||
expect(helper).to receive(:link_to).with( |
||||
admin_account_path(account.id), |
||||
class: name_tag_classes(account, true), |
||||
title: account.acct |
||||
) |
||||
|
||||
helper.admin_account_inline_link_to(account) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
@ -1,5 +0,0 @@ |
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe AccountPin, type: :model do |
||||
pending "add some examples to (or delete) #{__FILE__}" |
||||
end |
@ -0,0 +1,38 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe AccountTagStat, type: :model do |
||||
key = 'accounts_count' |
||||
let(:account_tag_stat) { Fabricate(:tag).account_tag_stat } |
||||
|
||||
describe '#increment_count!' do |
||||
it 'calls #update' do |
||||
args = { key => account_tag_stat.public_send(key) + 1 } |
||||
expect(account_tag_stat).to receive(:update).with(args) |
||||
account_tag_stat.increment_count!(key) |
||||
end |
||||
|
||||
it 'increments value by 1' do |
||||
expect do |
||||
account_tag_stat.increment_count!(key) |
||||
end.to change { account_tag_stat.accounts_count }.by(1) |
||||
end |
||||
end |
||||
|
||||
describe '#decrement_count!' do |
||||
it 'calls #update' do |
||||
args = { key => [account_tag_stat.public_send(key) - 1, 0].max } |
||||
expect(account_tag_stat).to receive(:update).with(args) |
||||
account_tag_stat.decrement_count!(key) |
||||
end |
||||
|
||||
it 'decrements value by 1' do |
||||
account_tag_stat.update(key => 1) |
||||
|
||||
expect do |
||||
account_tag_stat.decrement_count!(key) |
||||
end.to change { account_tag_stat.accounts_count }.by(-1) |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue