array( 'avatar' => 'BBoss_User_Avatar', 'cover' => 'BBoss_User_Cover', ), 'group' => array( 'avatar' => 'BBoss_Group_Avatar', 'cover' => 'BBoss_Group_Cover', ), ); if ( isset( $class_map[ $object_type ][ $image_type ] ) ) { return __NAMESPACE__ . '\\' . $class_map[ $object_type ][ $image_type ]; } else { return false; } } /** * Create a new Buddy Boss item from the source id. * * @param int $source_id * @param array $options * * @return BBoss_Item|WP_Error */ public static function create_from_source_id( $source_id, $options = array() ) { $file_paths = static::get_local_files( $source_id ); if ( empty( $file_paths ) ) { return new WP_Error( 'exception', __( 'No local files found in ' . __FUNCTION__, 'amazon-s3-and-cloudfront' ) ); } $file_path = static::remove_size_from_filename( $file_paths[ Item::primary_object_key() ] ); $extra_info = array( 'objects' => array() ); foreach ( $file_paths as $key => $path ) { $extra_info['objects'][ $key ] = array( 'source_file' => wp_basename( $path ), 'is_private' => false, ); } return new static( null, null, null, null, false, $source_id, $file_path, null, $extra_info, self::CAN_USE_OBJECT_VERSIONING ); } /** * Get item's new public prefix path for current settings. * * @param bool $use_object_versioning * * @return string */ public function get_new_item_prefix( $use_object_versioning = true ) { /** @var Amazon_S3_And_CloudFront $as3cf */ global $as3cf; // Base prefix from settings $prefix = $as3cf->get_object_prefix(); $prefix .= AS3CF_Utils::trailingslash_prefix( $as3cf->get_dynamic_prefix( null, static::$can_use_yearmonth ) ); // Buddy Boss specific prefix $buddy_boss_prefix = sprintf( static::$prefix_pattern, $this->source_id() ); $prefix .= AS3CF_Utils::trailingslash_prefix( $buddy_boss_prefix ); return $prefix; } /** * Return all buddy boss file sizes from the source folder * * @param int $source_id * * @return array */ public static function get_local_files( $source_id ) { $basedir = bp_core_get_upload_dir( 'upload_path' ); // Get base path and apply filters if ( static::$is_cover ) { // Call filters indirectly via bp_attachments_cover_image_upload_dir() $args = array( 'object_id' => $source_id, 'object_directory' => str_replace( 'buddypress/', '', static::$folder ), ); $upload_dir = bp_attachments_cover_image_upload_dir( $args ); $image_path = $upload_dir['path']; } else { // Call apply_filters directly $image_path = trailingslashit( $basedir ) . trailingslashit( static::$folder ) . $source_id; $object_type = static::$is_group ? 'group' : 'user'; $image_path = apply_filters( 'bp_core_avatar_folder_dir', $image_path, $source_id, $object_type, static::$folder ); } $result = array(); if ( ! file_exists( $image_path ) ) { return $result; } $files = new DirectoryIterator( $image_path ); foreach ( $files as $file ) { if ( $file->isDot() ) { continue; } $base_name = $file->getFilename(); $file_name = substr( $file->getPathname(), strlen( $basedir ) ); $file_name = AS3CF_Utils::unleadingslashit( $file_name ); if ( false !== strpos( $base_name, '-bp-cover-image' ) ) { $result[ Item::primary_object_key() ] = $file_name; } if ( false !== strpos( $base_name, '-bpfull' ) ) { $result[ Item::primary_object_key() ] = $file_name; } if ( false !== strpos( $base_name, '-bpthumb' ) ) { $result['thumb'] = $file_name; } } return $result; } /** * Buddy Boss specific size removal from URL and convert it to a neutral * (mock) file name with the correct file extension * * @param string $file_name The file * * @return string */ public static function remove_size_from_filename( $file_name ) { $path_info = pathinfo( $file_name ); return trailingslashit( $path_info['dirname'] ) . 'bp.' . $path_info['extension']; } /** * Return size name based on the file name * * @param string $filename * * @return string | null */ public function get_object_key_from_filename( $filename ) { return BuddyBoss::get_object_key_from_filename( $filename ); } /** * Get an array of un-managed source_ids in descending order. * * While source id isn't strictly unique, it is by source type, which is always used in queries based on called class. * * @param int $upper_bound Returned source_ids should be lower than this, use null/0 for no upper bound. * @param int $limit Maximum number of source_ids to return. Required if not counting. * @param bool $count Just return a count of matching source_ids? Negates $limit, default false. * * @return array|int */ public static function get_missing_source_ids( $upper_bound, $limit, $count = false ) { global $wpdb; // Bail out with empty values if we are a group class and the groups component is not active if ( static::$is_group ) { $active_bp_components = apply_filters( 'bp_active_components', bp_get_option( 'bp-active-components' ) ); if ( empty( $active_bp_components['groups'] ) ) { return $count ? 0 : array(); } } $source_table = $wpdb->prefix . static::$source_table; $basedir = bp_core_get_upload_dir( 'upload_path' ); $dir = trailingslashit( $basedir ) . static::$folder . '/'; $missing_items = array(); $missing_count = 0; // Establish an upper bound if needed if ( empty( $upper_bound ) ) { $sql = "SELECT max(id) from $source_table"; $max_id = (int) $wpdb->get_var( $sql ); $upper_bound = $max_id + 1; } for ( $i = $upper_bound; $i >= 0; $i -= self::$chunk_size ) { $args = array(); $sql = " SELECT t.id as ID from $source_table as t LEFT OUTER JOIN " . static::items_table() . " as i ON (i.source_id = t.ID AND i.source_type=%s)"; $args[] = static::source_type(); $sql .= ' WHERE i.ID IS NULL AND t.id < %d'; $args[] = $upper_bound; $sql .= ' ORDER BY t.ID DESC LIMIT %d, %d'; $args[] = $upper_bound - $i; $args[] = self::$chunk_size; $sql = $wpdb->prepare( $sql, $args ); $items_without_managed_offload = array_map( 'intval', $wpdb->get_col( $sql ) ); foreach ( $items_without_managed_offload as $item_without_managed_offload ) { $target = $dir . $item_without_managed_offload; if ( is_dir( $target ) ) { if ( $count ) { $missing_count++; } else { $missing_items[] = $item_without_managed_offload; // If we have enough items, bail out if ( count( $missing_items ) >= $limit ) { break 2; } } } } } // Add custom default if available for offload. if ( ( $count || count( $missing_items ) < $limit ) && is_dir( $dir . '0' ) ) { if ( ! static::get_by_source_id( 0 ) && ! empty( static::get_local_files( 0 ) ) ) { if ( $count ) { $missing_count++; } else { $missing_items[] = 0; } } } if ( $count ) { return $missing_count; } else { return $missing_items; } } /** * Setter for item's path & original path values * * @param string $path */ public function set_path( $path ) { $path = static::remove_size_from_filename( $path ); parent::set_path( $path ); parent::set_original_path( $path ); } /** * Get absolute source file paths for offloaded files. * * @return array Associative array of object_key => path */ public function full_source_paths() { $basedir = bp_core_get_upload_dir( 'upload_path' ); $item_folder = dirname( $this->source_path() ); $objects = $this->objects(); $sizes = array(); foreach ( $objects as $size => $object ) { $sizes[ $size ] = trailingslashit( $basedir ) . trailingslashit( $item_folder ) . $object['source_file']; } return $sizes; } /** * Get the local URL for an item * * @param string|null $object_key * * @return string */ public function get_local_url( $object_key = null ) { if ( static::$is_cover ) { return $this->get_local_cover_url( $object_key ); } else { return $this->get_local_avatar_url( $object_key ); } } /** * Get the local URL for an avatar item * * @param string|null $object_key * * @return string */ protected function get_local_avatar_url( $object_key = null ) { $uploads = wp_upload_dir(); $url = trailingslashit( $uploads['baseurl'] ); $url .= $this->source_path( $object_key ); return $url; } /** * Get the local URL for a cover item * * @param string|null $object_key * * @return string */ protected function get_local_cover_url( $object_key = null ) { $uploads = wp_upload_dir(); $url = trailingslashit( $uploads['baseurl'] ); $url .= $this->source_path( $object_key ); return $url; } /** * Get the prefix pattern * * @return string */ public static function get_prefix_pattern() { return static::$prefix_pattern; } /** * Count total, offloaded and not offloaded items on current site. * * @return array Keys: * total: Total media count for site (current blog id) * offloaded: Count of offloaded media for site (current blog id) * not_offloaded: Difference between total and offloaded */ protected static function get_item_counts(): array { global $wpdb; $sql = 'SELECT count(id) FROM ' . static::items_table() . ' WHERE source_type = %s'; $sql = $wpdb->prepare( $sql, static::$source_type ); $offloaded_count = (int) $wpdb->get_var( $sql ); $missing_count = static::get_missing_source_ids( 0, 0, true ); if ( is_array( $missing_count ) ) { $missing_count = count( $missing_count ); } return array( 'total' => $offloaded_count + $missing_count, 'offloaded' => $offloaded_count, 'not_offloaded' => $missing_count, ); } }