mirror of
https://github.com/astuto/astuto.git
synced 2025-12-15 19:27:52 +01:00
Various improvements (#332)
* Fix locale fallbacks * Make header links relative * Improve like button style * Other small improvements...
This commit is contained in:
committed by
GitHub
parent
a292b133b0
commit
747483cfa3
@@ -4,27 +4,15 @@
|
||||
.flex-column,
|
||||
.mr-3;
|
||||
|
||||
$like_button_size: 11px;
|
||||
|
||||
.likeButton {
|
||||
@extend .mb-2;
|
||||
|
||||
border-left: $like_button_size solid transparent;
|
||||
border-right: $like_button_size solid transparent;
|
||||
border-bottom: $like_button_size solid rgba(35,35,35,.2);
|
||||
|
||||
&:hover {
|
||||
border-bottom-color: var(--primary-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.likeButton.liked {
|
||||
border-bottom-color: var(--primary-color);
|
||||
.likeButton:hover {
|
||||
cursor: pointer;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.likeCountLabel {
|
||||
@extend .mt-2;
|
||||
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def load_tenant_data
|
||||
# Set default locale
|
||||
I18n.locale = I18n.default_locale
|
||||
|
||||
current_tenant = get_tenant_from_request(request)
|
||||
return unless current_tenant
|
||||
|
||||
@@ -38,7 +41,7 @@ class ApplicationController < ActionController::Base
|
||||
@tenant_billing = TenantBilling.first_or_create
|
||||
@boards = Board.select(:id, :name, :slug).order(order: :asc)
|
||||
|
||||
# Setup locale
|
||||
# Set tenant locale
|
||||
I18n.locale = @tenant.locale
|
||||
end
|
||||
|
||||
|
||||
@@ -172,6 +172,7 @@ class BillingController < ApplicationController
|
||||
@tenant_setting = @tenant.tenant_setting
|
||||
@tenant_billing = @tenant.tenant_billing
|
||||
@boards = Board.select(:id, :name, :slug).order(order: :asc)
|
||||
@header_full_urls = true
|
||||
I18n.locale = @tenant.locale
|
||||
|
||||
# needed because signing out from 'billing' subdomain cause authenticity token error
|
||||
|
||||
@@ -7,7 +7,7 @@ class TenantsController < ApplicationController
|
||||
|
||||
def new
|
||||
@page_title = "Create your feedback space"
|
||||
@o_auths = OAuth.unscoped.where(tenant_id: nil, is_enabled: true)
|
||||
@o_auths = OAuth.unscoped.where(tenant_id: nil, is_enabled: true).order(created_at: :asc)
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -102,7 +102,7 @@ class TenantsController < ApplicationController
|
||||
# Given a new_subdomain
|
||||
# Returns true if it is available, false otherwise
|
||||
def is_available
|
||||
subdomain = params[:new_subdomain]
|
||||
subdomain = params[:new_subdomain].downcase
|
||||
|
||||
return unless subdomain.present?
|
||||
return if RESERVED_SUBDOMAINS.include?(subdomain)
|
||||
|
||||
@@ -44,7 +44,7 @@ const Return = ({ tenantBilling, homeUrl, billingUrl }: Props) => {
|
||||
if (status === 'complete') {
|
||||
return (
|
||||
<Box customClass="billingContainer">
|
||||
<h2>Success</h2>
|
||||
<h2>Success!</h2>
|
||||
<p>Thank you for choosing Astuto! Your subscription will be activated shortly.</p>
|
||||
|
||||
<ActionLink onClick={() => window.location.href = homeUrl} icon={<BackIcon />}>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { LikeIcon, SolidLikeIcon } from '../common/Icons';
|
||||
|
||||
interface Props {
|
||||
postId: number;
|
||||
@@ -35,6 +36,7 @@ const LikeButtonP = ({
|
||||
className={`likeButton${liked ? ' liked' : ''}`}
|
||||
hidden={!showLikeButton}
|
||||
>
|
||||
{ liked ? <SolidLikeIcon /> : <LikeIcon />}
|
||||
</div>
|
||||
{ showLikeCount && <span className="likeCountLabel">{likeCount}</span> }
|
||||
</div>
|
||||
|
||||
@@ -119,6 +119,7 @@ const OAuthForm = ({
|
||||
<label htmlFor="logo">{ getLabel('o_auth', 'logo') }</label>
|
||||
<input
|
||||
{...register('logo')}
|
||||
placeholder='https://example.com/logo.png'
|
||||
id="logo"
|
||||
className="formControl"
|
||||
/>
|
||||
|
||||
@@ -133,6 +133,7 @@ const GeneralSiteSettingsP = ({
|
||||
<label htmlFor="siteLogo">{ getLabel('tenant', 'site_logo') }</label>
|
||||
<input
|
||||
{...register('siteLogo')}
|
||||
placeholder='https://example.com/logo.png'
|
||||
id="siteLogo"
|
||||
className="formControl"
|
||||
/>
|
||||
|
||||
@@ -45,6 +45,9 @@ const TenantSignUpForm = ({
|
||||
|
||||
<div className="formRow">
|
||||
<div className="input-group">
|
||||
<div className="input-group-prepend">
|
||||
<div className="input-group-text">https://</div>
|
||||
</div>
|
||||
<input
|
||||
{...register('subdomain', {
|
||||
required: true,
|
||||
@@ -57,7 +60,7 @@ const TenantSignUpForm = ({
|
||||
},
|
||||
},
|
||||
})}
|
||||
placeholder={getLabel('tenant', 'subdomain')}
|
||||
placeholder={getLabel('tenant', 'subdomain').toLowerCase()}
|
||||
id="tenantSubdomain"
|
||||
className="formControl"
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { MdContentCopy, MdDone, MdOutlineArrowBack } from 'react-icons/md';
|
||||
import { GrTest, GrClearOption } from 'react-icons/gr';
|
||||
import { MdOutlineLibraryBooks } from "react-icons/md";
|
||||
import { MdVerified } from "react-icons/md";
|
||||
import { BiLike, BiSolidLike } from "react-icons/bi";
|
||||
|
||||
export const EditIcon = () => <FiEdit />;
|
||||
|
||||
@@ -38,4 +39,8 @@ export const StaffIcon = () => (
|
||||
</span>
|
||||
);
|
||||
|
||||
export const ClearIcon = () => <GrClearOption />;
|
||||
export const ClearIcon = () => <GrClearOption />;
|
||||
|
||||
export const LikeIcon = ({size = 32}) => <BiLike size={size} />;
|
||||
|
||||
export const SolidLikeIcon = ({size = 32}) => <BiSolidLike size={size} />;
|
||||
@@ -14,7 +14,7 @@ export interface IOAuth {
|
||||
|
||||
callbackUrl?: string;
|
||||
tenantId?: number;
|
||||
defaultOAuthIsEnabled: boolean;
|
||||
defaultOAuthIsEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface IOAuthJSON {
|
||||
@@ -33,7 +33,7 @@ export interface IOAuthJSON {
|
||||
|
||||
callback_url?: string;
|
||||
tenant_id?: string;
|
||||
default_o_auth_is_enabled: boolean;
|
||||
default_o_auth_is_enabled?: boolean;
|
||||
}
|
||||
|
||||
export const oAuthJSON2JS = (oAuthJSON: IOAuthJSON): IOAuth => ({
|
||||
|
||||
@@ -2,6 +2,8 @@ import I18n from "i18n-js"
|
||||
|
||||
I18n.translations = <%= I18n::JS.filtered_translations.to_json %>
|
||||
I18n.locale = LOCALE
|
||||
I18n.defaultLocale = "en"
|
||||
I18n.fallbacks = <%= Rails.env.production? ? true : false %>
|
||||
|
||||
I18n.pluralization["zh-CN"] = function(count) { return ["other"] }
|
||||
I18n.pluralization["vi"] = function(count) { return ["other"] }
|
||||
|
||||
@@ -6,7 +6,13 @@ class ApplicationMailer < ActionMailer::Base
|
||||
|
||||
def set_mail_from_for_multitenancy
|
||||
if Rails.application.multi_tenancy?
|
||||
from = "#{Current.tenant_or_raise!.site_name} <#{ENV.fetch('EMAIL_MAIL_FROM', 'notifications@astuto.io')}>"
|
||||
from = "#{Current.tenant.site_name} <#{ENV.fetch('EMAIL_MAIL_FROM', 'notifications@astuto.io')}>"
|
||||
|
||||
# Set a specific 'from' for the Devise::Mailer#confirmation_instructions method on tenant signup
|
||||
if self.class.name == "Devise::Mailer" && action_name == "confirmation_instructions" && Current.tenant.users.count == 1
|
||||
from = "Astuto <#{ENV.fetch('EMAIL_MAIL_FROM', 'notifications@astuto.io')}>"
|
||||
end
|
||||
|
||||
mail.from = from
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class TenantMailer < ApplicationMailer
|
||||
layout :choose_layout
|
||||
skip_after_action :set_mail_from_for_multitenancy
|
||||
before_action :set_locale_to_english
|
||||
|
||||
def trial_start(tenant:)
|
||||
@tenant = tenant
|
||||
@@ -20,7 +21,7 @@ class TenantMailer < ApplicationMailer
|
||||
@tenant = tenant
|
||||
Current.tenant = tenant
|
||||
|
||||
@trial_ends_at = tenant.tenant_billing.trial_ends_at
|
||||
@trial_ends_at = tenant.tenant_billing.trial_ends_at.to_date
|
||||
|
||||
mail(
|
||||
from: email_from_riccardo,
|
||||
@@ -62,7 +63,7 @@ class TenantMailer < ApplicationMailer
|
||||
|
||||
@tenant = tenant
|
||||
Current.tenant = tenant
|
||||
@subscription_ends_at = Current.tenant.tenant_billing.subscription_ends_at
|
||||
@subscription_ends_at = Current.tenant.tenant_billing.subscription_ends_at.to_date
|
||||
|
||||
mail(
|
||||
from: email_from_astuto,
|
||||
@@ -97,4 +98,8 @@ class TenantMailer < ApplicationMailer
|
||||
def choose_layout
|
||||
action_name == 'trial_mid' || action_name == 'trial_end' ? 'mailer_no_style' : 'mailer'
|
||||
end
|
||||
|
||||
def set_locale_to_english
|
||||
I18n.locale = :en
|
||||
end
|
||||
end
|
||||
@@ -65,6 +65,14 @@ class User < ApplicationRecord
|
||||
"https://secure.gravatar.com/avatar/#{gravatar_id}"
|
||||
end
|
||||
|
||||
def full_name_or_email
|
||||
if full_name.present? && full_name != I18n.t('defaults.user_full_name') && full_name != I18n.t('defaults.user_full_name', locale: 'en')
|
||||
full_name
|
||||
else
|
||||
email
|
||||
end
|
||||
end
|
||||
|
||||
def owner?
|
||||
role == 'owner'
|
||||
end
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<p><%= t('mailers.devise.welcome_greeting', email: @email, site_name: Current.tenant_or_raise!.site_name) %></p>
|
||||
<p>
|
||||
<%= t('mailers.devise.welcome_greeting', email: @email, site_name: Current.tenant.users.count == 1 ? "Astuto" : Current.tenant.site_name) %>
|
||||
</p>
|
||||
|
||||
<p><%= t('mailers.devise.confirmation_instructions.body') %></p>
|
||||
<p>
|
||||
<%= t('mailers.devise.confirmation_instructions.body') %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= link_to t('mailers.devise.confirmation_instructions.action'),
|
||||
|
||||
@@ -66,3 +66,20 @@
|
||||
method: :delete,
|
||||
class: "btn btn-danger btn-block" %>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<% if Rails.application.multi_tenancy? && user_signed_in? && current_user.owner? %>
|
||||
<div class="edit_user">
|
||||
<h3>Delete feedback space</h3>
|
||||
|
||||
<p>
|
||||
Deleting the feedback space will also delete all the feedback and users associated with it. This action cannot be undone.
|
||||
</p>
|
||||
<p>
|
||||
If you want to delete the feedback space, please send an email to <a href="mailto:info@astuto.io">info@astuto.io</a> from the email address associated with this feedback space.
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<br />
|
||||
@@ -6,14 +6,14 @@
|
||||
<ul class="dropdown-menu boards-dropdown" aria-labelledby="navbarDropdown">
|
||||
<% boards.each do |board| %>
|
||||
<li>
|
||||
<%= link_to board.name, get_url_for(method(:board_url), resource: board), class: 'dropdown-item' %>
|
||||
<%= link_to board.name, @header_full_urls ? get_url_for(method(:board_url), resource: board) : board_path(board), class: 'dropdown-item' %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<% boards.each do |board| %>
|
||||
<li class="nav-item<%= board.id == @board.id ? ' active' : '' unless @board.nil? %>">
|
||||
<%= link_to board.name, get_url_for(method(:board_url), resource: board), class: 'nav-link' %>
|
||||
<%= link_to board.name, @header_full_urls ? get_url_for(method(:board_url), resource: board) : board_path(board), class: 'nav-link' %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -1,7 +1,7 @@
|
||||
<nav class="header">
|
||||
<div class="container">
|
||||
<%=
|
||||
link_to get_url_for(method(:root_url)), class: 'brand' do
|
||||
link_to @header_full_urls ? get_url_for(method(:root_url)) : root_path, class: 'brand' do
|
||||
app_name = content_tag :span, @tenant.site_name
|
||||
logo = image_tag(@tenant.site_logo ? @tenant.site_logo : "", class: 'logo', skip_pipeline: true)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<ul class="boardsNav">
|
||||
<% if @tenant_setting.show_roadmap_in_header %>
|
||||
<li class="nav-item<%= (current_page?(roadmap_path) or (@tenant_setting.root_board_id == 0 and current_page?(root_path))) ? ' active' : '' %>">
|
||||
<%= link_to t('roadmap.title'), get_url_for(method(:roadmap_url)), class: 'nav-link' %>
|
||||
<%= link_to t('roadmap.title'), @header_full_urls ? get_url_for(method(:roadmap_url)) : roadmap_path, class: 'nav-link' %>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
@@ -47,14 +47,21 @@
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<% if current_user.moderator? %>
|
||||
<%=
|
||||
link_to t('header.menu.site_settings'),
|
||||
current_user.admin? ? get_url_for(method(:site_settings_general_url)) : get_url_for(method(:site_settings_users_url)),
|
||||
class: 'dropdown-item'
|
||||
%>
|
||||
|
||||
<% if current_user.admin? or current_user.owner? %>
|
||||
<%=
|
||||
link_to t('header.menu.site_settings'),
|
||||
@header_full_urls ? get_url_for(method(:site_settings_general_url)) : site_settings_general_path,
|
||||
class: 'dropdown-item'
|
||||
%>
|
||||
<% else %>
|
||||
<%=
|
||||
link_to t('header.menu.site_settings'),
|
||||
@header_full_urls ? get_url_for(method(:site_settings_users_url)) : site_settings_users_path,
|
||||
class: 'dropdown-item'
|
||||
%>
|
||||
<% end %>
|
||||
<% if current_user.owner? and Rails.application.multi_tenancy? %>
|
||||
<%= link_to get_url_for(method(:request_billing_page_url)), class: 'dropdown-item', data: {turbolinks: false} do %>
|
||||
<%= link_to @header_full_urls ? get_url_for(method(:request_billing_page_url)) : request_billing_page_path, class: 'dropdown-item', data: {turbolinks: false} do %>
|
||||
<%= t('billing.title') %>
|
||||
<% if not Current.tenant.tenant_billing.has_active_subscription? %>
|
||||
<span style="color: red; font-size: 18px;">⚠️</span>
|
||||
@@ -65,11 +72,11 @@
|
||||
<div class="dropdown-divider"></div>
|
||||
<% end %>
|
||||
|
||||
<%= link_to t('header.menu.profile_settings'), get_url_for(method(:edit_user_registration_url)), class: 'dropdown-item' %>
|
||||
<%= link_to t('header.menu.profile_settings'), @header_full_urls ? get_url_for(method(:edit_user_registration_url)) : edit_user_registration_path, class: 'dropdown-item' %>
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<% unless @disable_sign_out %>
|
||||
<%= button_to t('header.menu.sign_out'), get_url_for(method(:destroy_user_session_url)), method: :delete, class: 'dropdown-item' %>
|
||||
<%= button_to t('header.menu.sign_out'), destroy_user_session_path, method: :delete, class: 'dropdown-item' %>
|
||||
<% else %>
|
||||
<small style="font-size: 12px; color: grey; padding: 0.25rem 1.5rem;">
|
||||
Sign out disabled
|
||||
@@ -79,7 +86,7 @@
|
||||
</li>
|
||||
<% else %>
|
||||
<li class="nav-item">
|
||||
<%= link_to t('header.log_in'), get_url_for(method(:new_user_session_url)), class: 'nav-link' %>
|
||||
<%= link_to t('header.log_in'), @header_full_urls ? get_url_for(method(:new_user_session_url)) : new_user_session_path, class: 'nav-link' %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Hello <%= @tenant.owner.full_name %>!
|
||||
Hello <%= @tenant.owner.full_name_or_email %>!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -7,7 +7,7 @@ We're sorry to see you go. <b>Your subscription has been cancelled and won't be
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Your current subscription will remain active until <%= @subscription_ends_at.strftime('%B %d, %Y') %>.
|
||||
Your current subscription will remain active until <%= I18n.localize @subscription_ends_at, format: :long %>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Hello <%= @tenant.owner.full_name %>!
|
||||
Hello <%= @tenant.owner.full_name_or_email %>!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Hello <%= @tenant.owner.full_name %>!
|
||||
Hello <%= @tenant.owner.full_name_or_email %>!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Hi <%= @tenant.owner.full_name %>,
|
||||
Hi <%= @tenant.owner.full_name_or_email %>,
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Hi <%= @tenant.owner.full_name %>,
|
||||
Hi <%= @tenant.owner.full_name_or_email %>,
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -15,7 +15,7 @@ How's your Astuto trial going? If you have any questions or encounter any issues
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Also remember that your trial period will expire on <%= @trial_ends_at.strftime('%B %d, %Y') %>.
|
||||
Also remember that your trial period will expire on <%= I18n.localize @trial_ends_at, format: :long %>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Hello <%= @tenant.owner.full_name %>,
|
||||
Hello <%= @tenant.owner.full_name_or_email %>,
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name %>
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name_or_email %>
|
||||
|
||||
<p>
|
||||
<%= t('mailers.user.notify_comment_owner.body_html', user: @comment.user.full_name, post: @comment.post.title) %>:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name %>
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name_or_email %>
|
||||
|
||||
<p>
|
||||
<%= t('mailers.user.notify_follower_of_post_status_change.body_html', post: @post.title) %>:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name %>
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name_or_email %>
|
||||
|
||||
<p>
|
||||
<%= t('mailers.user.notify_follower_of_post_update.body_html', user: @comment.user.full_name, post: @comment.post.title) %>:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name %>
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name_or_email %>
|
||||
|
||||
<p>
|
||||
<%= t('mailers.user.notify_post_owner.body_html', user: @comment.user.full_name, post: @comment.post.title) %>:
|
||||
|
||||
@@ -11,6 +11,9 @@ module App
|
||||
# Initialize configuration defaults for originally generated Rails version.
|
||||
config.load_defaults 6.0
|
||||
|
||||
# Set default locale
|
||||
config.i18n.default_locale = :en
|
||||
|
||||
# Settings in config/environments/* take precedence over those specified here.
|
||||
# Application configuration can go into files in config/initializers
|
||||
# -- all .rb files in that directory are automatically loaded after loading
|
||||
|
||||
@@ -3,8 +3,6 @@ I18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml')]
|
||||
|
||||
I18n.available_locales = [:en, :it, :de, :fr, :es, 'zh-CN', :ru, :vi]
|
||||
|
||||
I18n.default_locale = :en
|
||||
|
||||
# Custom pluralization rules
|
||||
# Those must be mirrored in app/javascript/translations/index.js.erb
|
||||
I18n::Backend::Simple.include(I18n::Backend::Pluralization)
|
||||
|
||||
@@ -19,7 +19,7 @@ task notify_tenants_trial_period: [:environment] do
|
||||
|
||||
tenants_mid_trial.each do |tenant|
|
||||
puts "Delivering trial_mid email for #{tenant.site_name}..."
|
||||
TenantMailer.trial_mid(tenant: tenant).deliver_later
|
||||
TenantMailer.trial_mid(tenant: tenant).deliver_now
|
||||
end
|
||||
|
||||
# Notify tenants end of trial
|
||||
@@ -28,7 +28,7 @@ task notify_tenants_trial_period: [:environment] do
|
||||
|
||||
tenants_end_trial.each do |tenant|
|
||||
puts "Delivering trial_end email for #{tenant.site_name}..."
|
||||
TenantMailer.trial_end(tenant: tenant).deliver_later
|
||||
TenantMailer.trial_end(tenant: tenant).deliver_now
|
||||
end
|
||||
rescue Exception => e
|
||||
error_subject = "Scheduled Task 'notify_tenants_trial_period.rake' Failed"
|
||||
|
||||
Reference in New Issue
Block a user