Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
		| @@ -183,6 +183,9 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io | ||||
| # LDAP_BIND_DN= | ||||
| # LDAP_PASSWORD= | ||||
| # LDAP_UID=cn | ||||
| # LDAP_UID_CONVERSION_ENABLED=true | ||||
| # LDAP_UID_CONVERSION_SEARCH=., - | ||||
| # LDAP_UID_CONVERSION_REPLACE=_ | ||||
|  | ||||
| # PAM authentication (optional) | ||||
| # PAM authentication uses for the email generation the "email" pam variable | ||||
|   | ||||
| @@ -204,6 +204,9 @@ STREAMING_CLUSTER_NUM=1 | ||||
| # LDAP_PASSWORD= | ||||
| # LDAP_UID=cn | ||||
| # LDAP_SEARCH_FILTER=%{uid}=%{email} | ||||
| # LDAP_UID_CONVERSION_ENABLED=true | ||||
| # LDAP_UID_CONVERSION_SEARCH=., - | ||||
| # LDAP_UID_CONVERSION_REPLACE=_ | ||||
|  | ||||
| # PAM authentication (optional) | ||||
| # PAM authentication uses for the email generation the "email" pam variable | ||||
|   | ||||
| @@ -55,7 +55,8 @@ module Admin | ||||
|       params.permit( | ||||
|         :account_id, | ||||
|         :resolved, | ||||
|         :target_account_id | ||||
|         :target_account_id, | ||||
|         :by_target_domain | ||||
|       ) | ||||
|     end | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| module Admin::FilterHelper | ||||
|   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 | ||||
|   CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).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) { | ||||
|       this.setState({ revealed: false }); | ||||
|     } | ||||
|     if (!nextProps.children && !!this.props.children) { | ||||
|       this.activeElement.focus(); | ||||
|       this.activeElement = null; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   componentDidUpdate (prevProps) { | ||||
|     if (!this.props.children && !!prevProps.children) { | ||||
|       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) { | ||||
|       requestAnimationFrame(() => { | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import IconButton from 'mastodon/components/icon_button'; | ||||
| import RelativeTimestamp from 'mastodon/components/relative_timestamp'; | ||||
| import { HotKeys } from 'react-hotkeys'; | ||||
| import { autoPlayGif } from 'mastodon/initial_state'; | ||||
| import classNames from 'classnames'; | ||||
|  | ||||
| const messages = defineMessages({ | ||||
|   more: { id: 'status.more', defaultMessage: 'More' }, | ||||
| @@ -158,7 +159,7 @@ class Conversation extends ImmutablePureComponent { | ||||
|  | ||||
|     return ( | ||||
|       <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'> | ||||
|             <AvatarComposite accounts={accounts} size={48} /> | ||||
|           </div> | ||||
| @@ -166,7 +167,7 @@ class Conversation extends ImmutablePureComponent { | ||||
|           <div className='conversation__content'> | ||||
|             <div className='conversation__content__info'> | ||||
|               <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 className='conversation__content__names' ref={this.setNamesRef}> | ||||
|   | ||||
| @@ -6517,6 +6517,16 @@ noscript { | ||||
|     flex: 0 0 auto; | ||||
|     padding: 10px; | ||||
|     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 { | ||||
| @@ -6564,4 +6574,20 @@ noscript { | ||||
|       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 | ||||
|  | ||||
|   def audience_to | ||||
|     @object['to'] || @json['to'] | ||||
|   end | ||||
|  | ||||
|   def audience_cc | ||||
|     @object['cc'] || @json['cc'] | ||||
|   end | ||||
|  | ||||
|   def process_status | ||||
|     @tags     = [] | ||||
|     @mentions = [] | ||||
| @@ -75,7 +83,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity | ||||
|   end | ||||
|  | ||||
|   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] | ||||
|  | ||||
|       # 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 | ||||
|  | ||||
|   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 | ||||
|     elsif equals_or_includes?(@object['cc'], ActivityPub::TagManager::COLLECTIONS[:public]) | ||||
|     elsif equals_or_includes?(audience_cc, ActivityPub::TagManager::COLLECTIONS[:public]) | ||||
|       :unlisted | ||||
|     elsif equals_or_includes?(@object['to'], @account.followers_url) | ||||
|     elsif equals_or_includes?(audience_to, @account.followers_url) | ||||
|       :private | ||||
|     else | ||||
|       :direct | ||||
| @@ -304,7 +312,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity | ||||
|  | ||||
|   def audience_includes?(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 | ||||
|  | ||||
|   def replied_to_status | ||||
| @@ -415,7 +423,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity | ||||
|   def addresses_local_accounts? | ||||
|     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? | ||||
|  | ||||
|   | ||||
| @@ -14,10 +14,18 @@ module LdapAuthenticable | ||||
|     end | ||||
|  | ||||
|     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? | ||||
|         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! | ||||
|       end | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,8 @@ class ReportFilter | ||||
|  | ||||
|   def scope_for(key, value) | ||||
|     case key.to_sym | ||||
|     when :by_target_domain | ||||
|       Report.where(target_account: Account.where(domain: value)) | ||||
|     when :resolved | ||||
|       Report.resolved | ||||
|     when :account_id | ||||
|   | ||||
| @@ -8,6 +8,20 @@ | ||||
|       %li= filter_link_to t('admin.reports.unresolved'), resolved: nil | ||||
|       %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| | ||||
|   - target_account = reports.first.target_account | ||||
|   .report-card | ||||
|   | ||||
| @@ -5,8 +5,8 @@ | ||||
|   .filter-subset | ||||
|     %strong= t 'relationships.relationship' | ||||
|     %ul | ||||
|       %li= filter_link_to t('accounts.following', count: current_account.following_count), relationship: nil | ||||
|       %li= filter_link_to t('accounts.followers', count: current_account.followers_count), relationship: 'followed_by' | ||||
|       %li= filter_link_to t('relationships.following'), relationship: nil | ||||
|       %li= filter_link_to t('relationships.followers'), relationship: 'followed_by' | ||||
|       %li= filter_link_to t('relationships.mutual'), relationship: 'mutual' | ||||
|  | ||||
|   .filter-subset | ||||
|   | ||||
| @@ -61,6 +61,12 @@ module Devise | ||||
|   @@ldap_tls_no_verify = false | ||||
|   mattr_accessor :ldap_search_filter | ||||
|   @@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 | ||||
|     def valid? | ||||
| @@ -365,5 +371,8 @@ Devise.setup do |config| | ||||
|     config.ldap_uid            = ENV.fetch('LDAP_UID', 'cn') | ||||
|     config.ldap_tls_no_verify  = ENV['LDAP_TLS_NO_VERIFY'] == 'true' | ||||
|     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 | ||||
|   | ||||
| @@ -8,8 +8,20 @@ Doorkeeper.configure do | ||||
|   end | ||||
|  | ||||
|   resource_owner_from_credentials do |_routes| | ||||
|     user = User.find_by(email: request.params[:username]) | ||||
|     user if !user&.otp_required_for_login? && user&.valid_password?(request.params[:password]) | ||||
|     if Devise.ldap_authentication | ||||
|       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 | ||||
|  | ||||
|   # 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? | ||||
|       assign_to_self: Assign to me | ||||
|       assigned: Assigned moderator | ||||
|       by_target_domain: Domain of reported account | ||||
|       comment: | ||||
|         none: None | ||||
|       created_at: Reported | ||||
| @@ -938,6 +939,8 @@ en: | ||||
|   relationships: | ||||
|     activity: Account activity | ||||
|     dormant: Dormant | ||||
|     followers: Followers | ||||
|     following: Following | ||||
|     last_active: Last active | ||||
|     most_recent: Most recent | ||||
|     moved: Moved | ||||
|   | ||||
		Reference in New Issue
	
	Block a user