Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
commit
99f1f48741
|
@ -183,6 +183,9 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||||
# LDAP_BIND_DN=
|
# LDAP_BIND_DN=
|
||||||
# LDAP_PASSWORD=
|
# LDAP_PASSWORD=
|
||||||
# LDAP_UID=cn
|
# LDAP_UID=cn
|
||||||
|
# LDAP_UID_CONVERSION_ENABLED=true
|
||||||
|
# LDAP_UID_CONVERSION_SEARCH=., -
|
||||||
|
# LDAP_UID_CONVERSION_REPLACE=_
|
||||||
|
|
||||||
# PAM authentication (optional)
|
# PAM authentication (optional)
|
||||||
# PAM authentication uses for the email generation the "email" pam variable
|
# PAM authentication uses for the email generation the "email" pam variable
|
||||||
|
|
|
@ -204,6 +204,9 @@ STREAMING_CLUSTER_NUM=1
|
||||||
# LDAP_PASSWORD=
|
# LDAP_PASSWORD=
|
||||||
# LDAP_UID=cn
|
# LDAP_UID=cn
|
||||||
# LDAP_SEARCH_FILTER=%{uid}=%{email}
|
# LDAP_SEARCH_FILTER=%{uid}=%{email}
|
||||||
|
# LDAP_UID_CONVERSION_ENABLED=true
|
||||||
|
# LDAP_UID_CONVERSION_SEARCH=., -
|
||||||
|
# LDAP_UID_CONVERSION_REPLACE=_
|
||||||
|
|
||||||
# PAM authentication (optional)
|
# PAM authentication (optional)
|
||||||
# PAM authentication uses for the email generation the "email" pam variable
|
# PAM authentication uses for the email generation the "email" pam variable
|
||||||
|
|
|
@ -55,7 +55,8 @@ module Admin
|
||||||
params.permit(
|
params.permit(
|
||||||
:account_id,
|
:account_id,
|
||||||
:resolved,
|
:resolved,
|
||||||
:target_account_id
|
:target_account_id,
|
||||||
|
:by_target_domain
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
module Admin::FilterHelper
|
module Admin::FilterHelper
|
||||||
ACCOUNT_FILTERS = %i(local remote by_domain active pending silenced suspended username display_name email ip staff).freeze
|
ACCOUNT_FILTERS = %i(local remote by_domain active pending silenced suspended username display_name email ip staff).freeze
|
||||||
REPORT_FILTERS = %i(resolved account_id target_account_id).freeze
|
REPORT_FILTERS = %i(resolved account_id target_account_id by_target_domain).freeze
|
||||||
INVITE_FILTER = %i(available expired).freeze
|
INVITE_FILTER = %i(available expired).freeze
|
||||||
CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
|
CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
|
||||||
TAGS_FILTERS = %i(directory reviewed unreviewed pending_review popular active name).freeze
|
TAGS_FILTERS = %i(directory reviewed unreviewed pending_review popular active name).freeze
|
||||||
|
|
|
@ -56,15 +56,21 @@ export default class ModalRoot extends React.PureComponent {
|
||||||
} else if (!nextProps.children) {
|
} else if (!nextProps.children) {
|
||||||
this.setState({ revealed: false });
|
this.setState({ revealed: false });
|
||||||
}
|
}
|
||||||
if (!nextProps.children && !!this.props.children) {
|
|
||||||
this.activeElement.focus();
|
|
||||||
this.activeElement = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
if (!this.props.children && !!prevProps.children) {
|
if (!this.props.children && !!prevProps.children) {
|
||||||
this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
|
this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
|
||||||
|
|
||||||
|
// Because of the wicg-inert polyfill, the activeElement may not be
|
||||||
|
// immediately selectable, we have to wait for observers to run, as
|
||||||
|
// described in https://github.com/WICG/inert#performance-and-gotchas
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
this.activeElement.focus();
|
||||||
|
this.activeElement = null;
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (this.props.children) {
|
if (this.props.children) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import IconButton from 'mastodon/components/icon_button';
|
||||||
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
import { autoPlayGif } from 'mastodon/initial_state';
|
import { autoPlayGif } from 'mastodon/initial_state';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
|
@ -158,7 +159,7 @@ class Conversation extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
<div className='conversation focusable muted' tabIndex='0'>
|
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'>
|
||||||
<div className='conversation__avatar'>
|
<div className='conversation__avatar'>
|
||||||
<AvatarComposite accounts={accounts} size={48} />
|
<AvatarComposite accounts={accounts} size={48} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -166,7 +167,7 @@ class Conversation extends ImmutablePureComponent {
|
||||||
<div className='conversation__content'>
|
<div className='conversation__content'>
|
||||||
<div className='conversation__content__info'>
|
<div className='conversation__content__info'>
|
||||||
<div className='conversation__content__relative-time'>
|
<div className='conversation__content__relative-time'>
|
||||||
<RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='conversation__content__names' ref={this.setNamesRef}>
|
<div className='conversation__content__names' ref={this.setNamesRef}>
|
||||||
|
|
|
@ -6517,6 +6517,16 @@ noscript {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__unread {
|
||||||
|
display: inline-block;
|
||||||
|
background: $highlight-text-color;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 0.625rem;
|
||||||
|
height: 0.625rem;
|
||||||
|
margin: -.1ex .15em .1ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
|
@ -6564,4 +6574,20 @@ noscript {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--unread {
|
||||||
|
background: lighten($ui-base-color, 2%);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: lighten($ui-base-color, 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation__content__info {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation__content__relative-time {
|
||||||
|
color: $primary-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,14 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def audience_to
|
||||||
|
@object['to'] || @json['to']
|
||||||
|
end
|
||||||
|
|
||||||
|
def audience_cc
|
||||||
|
@object['cc'] || @json['cc']
|
||||||
|
end
|
||||||
|
|
||||||
def process_status
|
def process_status
|
||||||
@tags = []
|
@tags = []
|
||||||
@mentions = []
|
@mentions = []
|
||||||
|
@ -75,7 +83,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_audience
|
def process_audience
|
||||||
(as_array(@object['to']) + as_array(@object['cc'])).uniq.each do |audience|
|
(as_array(audience_to) + as_array(audience_cc)).uniq.each do |audience|
|
||||||
next if audience == ActivityPub::TagManager::COLLECTIONS[:public]
|
next if audience == ActivityPub::TagManager::COLLECTIONS[:public]
|
||||||
|
|
||||||
# Unlike with tags, there is no point in resolving accounts we don't already
|
# Unlike with tags, there is no point in resolving accounts we don't already
|
||||||
|
@ -291,11 +299,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
end
|
end
|
||||||
|
|
||||||
def visibility_from_audience
|
def visibility_from_audience
|
||||||
if equals_or_includes?(@object['to'], ActivityPub::TagManager::COLLECTIONS[:public])
|
if equals_or_includes?(audience_to, ActivityPub::TagManager::COLLECTIONS[:public])
|
||||||
:public
|
:public
|
||||||
elsif equals_or_includes?(@object['cc'], ActivityPub::TagManager::COLLECTIONS[:public])
|
elsif equals_or_includes?(audience_cc, ActivityPub::TagManager::COLLECTIONS[:public])
|
||||||
:unlisted
|
:unlisted
|
||||||
elsif equals_or_includes?(@object['to'], @account.followers_url)
|
elsif equals_or_includes?(audience_to, @account.followers_url)
|
||||||
:private
|
:private
|
||||||
else
|
else
|
||||||
:direct
|
:direct
|
||||||
|
@ -304,7 +312,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
|
|
||||||
def audience_includes?(account)
|
def audience_includes?(account)
|
||||||
uri = ActivityPub::TagManager.instance.uri_for(account)
|
uri = ActivityPub::TagManager.instance.uri_for(account)
|
||||||
equals_or_includes?(@object['to'], uri) || equals_or_includes?(@object['cc'], uri)
|
equals_or_includes?(audience_to, uri) || equals_or_includes?(audience_cc, uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def replied_to_status
|
def replied_to_status
|
||||||
|
@ -415,7 +423,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
def addresses_local_accounts?
|
def addresses_local_accounts?
|
||||||
return true if @options[:delivered_to_account_id]
|
return true if @options[:delivered_to_account_id]
|
||||||
|
|
||||||
local_usernames = (as_array(@object['to']) + as_array(@object['cc'])).uniq.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
|
local_usernames = (as_array(audience_to) + as_array(audience_cc)).uniq.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
|
||||||
|
|
||||||
return false if local_usernames.empty?
|
return false if local_usernames.empty?
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,18 @@ module LdapAuthenticable
|
||||||
end
|
end
|
||||||
|
|
||||||
def ldap_get_user(attributes = {})
|
def ldap_get_user(attributes = {})
|
||||||
resource = joins(:account).find_by(accounts: { username: attributes[Devise.ldap_uid.to_sym].first })
|
safe_username = attributes[Devise.ldap_uid.to_sym].first
|
||||||
|
if Devise.ldap_uid_conversion_enabled
|
||||||
|
keys = Regexp.union(Devise.ldap_uid_conversion_search.chars)
|
||||||
|
replacement = Devise.ldap_uid_conversion_replace
|
||||||
|
|
||||||
|
safe_username = safe_username.gsub(keys, replacement)
|
||||||
|
end
|
||||||
|
|
||||||
|
resource = joins(:account).find_by(accounts: { username: safe_username })
|
||||||
|
|
||||||
if resource.blank?
|
if resource.blank?
|
||||||
resource = new(email: attributes[:mail].first, agreement: true, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first }, admin: false, external: true, confirmed_at: Time.now.utc)
|
resource = new(email: attributes[:mail].first, agreement: true, account_attributes: { username: safe_username }, admin: false, external: true, confirmed_at: Time.now.utc)
|
||||||
resource.save!
|
resource.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ class ReportFilter
|
||||||
|
|
||||||
def scope_for(key, value)
|
def scope_for(key, value)
|
||||||
case key.to_sym
|
case key.to_sym
|
||||||
|
when :by_target_domain
|
||||||
|
Report.where(target_account: Account.where(domain: value))
|
||||||
when :resolved
|
when :resolved
|
||||||
Report.resolved
|
Report.resolved
|
||||||
when :account_id
|
when :account_id
|
||||||
|
|
|
@ -8,6 +8,20 @@
|
||||||
%li= filter_link_to t('admin.reports.unresolved'), resolved: nil
|
%li= filter_link_to t('admin.reports.unresolved'), resolved: nil
|
||||||
%li= filter_link_to t('admin.reports.resolved'), resolved: '1'
|
%li= filter_link_to t('admin.reports.resolved'), resolved: '1'
|
||||||
|
|
||||||
|
= form_tag admin_reports_url, method: 'GET', class: 'simple_form' do
|
||||||
|
.fields-group
|
||||||
|
- Admin::FilterHelper::REPORT_FILTERS.each do |key|
|
||||||
|
- if params[key].present?
|
||||||
|
= hidden_field_tag key, params[key]
|
||||||
|
|
||||||
|
- %i(by_target_domain).each do |key|
|
||||||
|
.input.string.optional
|
||||||
|
= text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.reports.#{key}")
|
||||||
|
|
||||||
|
.actions
|
||||||
|
%button= t('admin.accounts.search')
|
||||||
|
= link_to t('admin.accounts.reset'), admin_reports_path, class: 'button negative'
|
||||||
|
|
||||||
- @reports.group_by(&:target_account_id).each do |target_account_id, reports|
|
- @reports.group_by(&:target_account_id).each do |target_account_id, reports|
|
||||||
- target_account = reports.first.target_account
|
- target_account = reports.first.target_account
|
||||||
.report-card
|
.report-card
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
.filter-subset
|
.filter-subset
|
||||||
%strong= t 'relationships.relationship'
|
%strong= t 'relationships.relationship'
|
||||||
%ul
|
%ul
|
||||||
%li= filter_link_to t('accounts.following', count: current_account.following_count), relationship: nil
|
%li= filter_link_to t('relationships.following'), relationship: nil
|
||||||
%li= filter_link_to t('accounts.followers', count: current_account.followers_count), relationship: 'followed_by'
|
%li= filter_link_to t('relationships.followers'), relationship: 'followed_by'
|
||||||
%li= filter_link_to t('relationships.mutual'), relationship: 'mutual'
|
%li= filter_link_to t('relationships.mutual'), relationship: 'mutual'
|
||||||
|
|
||||||
.filter-subset
|
.filter-subset
|
||||||
|
|
|
@ -61,6 +61,12 @@ module Devise
|
||||||
@@ldap_tls_no_verify = false
|
@@ldap_tls_no_verify = false
|
||||||
mattr_accessor :ldap_search_filter
|
mattr_accessor :ldap_search_filter
|
||||||
@@ldap_search_filter = nil
|
@@ldap_search_filter = nil
|
||||||
|
mattr_accessor :ldap_uid_conversion_enabled
|
||||||
|
@@ldap_uid_conversion_enabled = false
|
||||||
|
mattr_accessor :ldap_uid_conversion_search
|
||||||
|
@@ldap_uid_conversion_search = nil
|
||||||
|
mattr_accessor :ldap_uid_conversion_replace
|
||||||
|
@@ldap_uid_conversion_replace = nil
|
||||||
|
|
||||||
class Strategies::PamAuthenticatable
|
class Strategies::PamAuthenticatable
|
||||||
def valid?
|
def valid?
|
||||||
|
@ -365,5 +371,8 @@ Devise.setup do |config|
|
||||||
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
|
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
|
||||||
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
|
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
|
||||||
config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '%{uid}=%{email}')
|
config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '%{uid}=%{email}')
|
||||||
|
config.ldap_uid_conversion_enabled = ENV['LDAP_UID_CONVERSION_ENABLED'] == 'true'
|
||||||
|
config.ldap_uid_conversion_search = ENV.fetch('LDAP_UID_CONVERSION_SEARCH', '.,- ')
|
||||||
|
config.ldap_uid_conversion_replace = ENV.fetch('LDAP_UID_CONVERSION_REPLACE', '_')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,8 +8,20 @@ Doorkeeper.configure do
|
||||||
end
|
end
|
||||||
|
|
||||||
resource_owner_from_credentials do |_routes|
|
resource_owner_from_credentials do |_routes|
|
||||||
user = User.find_by(email: request.params[:username])
|
if Devise.ldap_authentication
|
||||||
user if !user&.otp_required_for_login? && user&.valid_password?(request.params[:password])
|
user = User.authenticate_with_ldap({ :email => request.params[:username], :password => request.params[:password] })
|
||||||
|
end
|
||||||
|
|
||||||
|
if Devise.pam_authentication
|
||||||
|
user ||= User.authenticate_with_ldap({ :email => request.params[:username], :password => request.params[:password] })
|
||||||
|
end
|
||||||
|
|
||||||
|
if user.nil?
|
||||||
|
user = User.find_by(email: request.params[:username])
|
||||||
|
user = nil unless user.valid_password?(request.params[:password])
|
||||||
|
end
|
||||||
|
|
||||||
|
user if !user&.otp_required_for_login?
|
||||||
end
|
end
|
||||||
|
|
||||||
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
|
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
|
||||||
|
|
|
@ -406,6 +406,7 @@ en:
|
||||||
are_you_sure: Are you sure?
|
are_you_sure: Are you sure?
|
||||||
assign_to_self: Assign to me
|
assign_to_self: Assign to me
|
||||||
assigned: Assigned moderator
|
assigned: Assigned moderator
|
||||||
|
by_target_domain: Domain of reported account
|
||||||
comment:
|
comment:
|
||||||
none: None
|
none: None
|
||||||
created_at: Reported
|
created_at: Reported
|
||||||
|
@ -938,6 +939,8 @@ en:
|
||||||
relationships:
|
relationships:
|
||||||
activity: Account activity
|
activity: Account activity
|
||||||
dormant: Dormant
|
dormant: Dormant
|
||||||
|
followers: Followers
|
||||||
|
following: Following
|
||||||
last_active: Last active
|
last_active: Last active
|
||||||
most_recent: Most recent
|
most_recent: Most recent
|
||||||
moved: Moved
|
moved: Moved
|
||||||
|
|
Reference in New Issue