Skip to:
Content

BuddyPress.org

Ticket #8048: 8048.patch

File 8048.patch, 102.9 KB (added by imath, 6 years ago)
  • .gitignore

    diff --git .gitignore .gitignore
    index 769dc445e..f26f47fbd 100644
     
    11.DS_Store
    22phpunit.xml
    33.idea
     4.cache
    45
    56lib-cov
    67*.seed
  • .jshintignore

    diff --git .jshintignore .jshintignore
    index c7593c293..a5c26e46a 100644
     
    11// 3rd party libraries
    22src/bp-core/js/vendor/**/*
    33src/bp-messages/js/autocomplete/*
     4
     5// Blocks Scripts
     6src/**/js/blocks/*.js
  • Gruntfile.js

    diff --git Gruntfile.js Gruntfile.js
    index 590b38872..dc0160aa2 100644
    module.exports = function( grunt ) { 
    1717                        '**/*.js'
    1818                ],
    1919
     20                BP_EXCLUDED_JS = [
     21                        '!js/**/*.js',
     22                        '!**/js/blocks/*.js'
     23                ],
     24
    2025                BP_EXCLUDED_MISC = [
    2126                ],
    2227
    module.exports = function( grunt ) { 
    164169                                expand: true
    165170                        }
    166171                },
    167                 makepot: {
    168                         target: {
    169                                 options: {
    170                                         cwd: BUILD_DIR,
    171                                         domainPath: '.',
    172                                         mainFile: 'bp-loader.php',
    173                                         potFilename: 'buddypress.pot',
    174                                         processPot: function( pot ) {
    175                                                 pot.headers['report-msgid-bugs-to'] = 'https://buddypress.trac.wordpress.org';
    176                                                 pot.headers['last-translator'] = 'JOHN JAMES JACOBY <jjj@buddypress.org>';
    177                                                 pot.headers['language-team'] = 'ENGLISH <jjj@buddypress.org>';
    178                                                 return pot;
    179                                         },
    180                                         type: 'wp-plugin'
    181                                 }
    182                         }
    183                 },
    184172                imagemin: {
    185173                        core: {
    186174                                expand: true,
    module.exports = function( grunt ) { 
    201189                                                dest: BUILD_DIR,
    202190                                                dot: true,
    203191                                                expand: true,
    204                                                 src: ['**', '!**/.{svn,git}/**'].concat( BP_EXCLUDED_MISC )
     192                                                src: ['**', '!**/.{svn,git,cache}/**', '!js/**'].concat( BP_EXCLUDED_MISC )
    205193                                        },
    206194                                        {
    207195                                                dest: BUILD_DIR,
    module.exports = function( grunt ) { 
    245233                                extDot: 'last',
    246234                                expand: true,
    247235                                ext: '.min.js',
    248                                 src: BP_JS
     236                                src: BP_JS.concat( BP_EXCLUDED_JS, BP_EXCLUDED_MISC )
    249237                        }
    250238                },
    251239                stylelint: {
    module.exports = function( grunt ) { 
    332320                                command: 'svn export --force https://github.com/buddypress/BP-REST.git/trunk bp-rest',
    333321                                cwd: BUILD_DIR,
    334322                                stdout: false
     323                        },
     324                        makepot: {
     325                                command: 'wp i18n make-pot build build/buddypress.pot --headers=\'{"Project-Id-Version": "BuddyPress", "Report-Msgid-Bugs-To": "https://buddypress.trac.wordpress.org", "Last-Translator": "JOHN JAMES JACOBY <jjj@buddypress.org>", "Language-Team": "ENGLISH <jjj@buddypress.org>"}\'',
     326                                stdout: true
     327                        },
     328                        blocks_build: {
     329                                command: 'npm run build',
     330                                cwd: SOURCE_DIR,
     331                                stdout: true
     332                        },
     333                        blocks_minify: {
     334                                command: 'npm run minify',
     335                                cwd: SOURCE_DIR,
     336                                stdout: true
    335337                        }
    336338                },
    337339                jsvalidate:{
    module.exports = function( grunt ) { 
    342344                        },
    343345                        build: {
    344346                                files: {
    345                                         src: [BUILD_DIR + '/**/*.js']
     347                                        src: [BUILD_DIR + '/**/*.js'].concat( BP_EXCLUDED_JS, BP_EXCLUDED_MISC )
    346348                                }
    347349                        },
    348350                        src: {
    349351                                files: {
    350                                         src: [SOURCE_DIR + '/**/*.js'].concat( BP_EXCLUDED_MISC )
     352                                        src: [SOURCE_DIR + '/**/*.js'].concat( BP_EXCLUDED_JS, BP_EXCLUDED_MISC )
    351353                                }
    352354                        }
    353355                },
    module.exports = function( grunt ) { 
    367369         * Register tasks.
    368370         */
    369371        grunt.registerTask( 'src',     ['checkDependencies', 'jsvalidate:src', 'jshint', 'stylelint', 'sass', 'postcss', 'rtlcss'] );
     372        grunt.registerTask( 'makepot', ['exec:makepot'] );
    370373        grunt.registerTask( 'commit',  ['src', 'checktextdomain', 'imagemin', 'phplint', 'exec:phpcompat'] );
    371374        grunt.registerTask( 'bp_rest', [ 'exec:rest_api', 'copy:bp_rest_components', 'copy:bp_rest_core', 'clean:bp_rest' ] );
    372         grunt.registerTask( 'build',   ['commit', 'clean:all', 'copy:files', 'uglify', 'jsvalidate:build', 'cssmin', 'bp_rest', 'makepot', 'exec:bpdefault', 'exec:cli'] );
     375        grunt.registerTask( 'build',   ['commit', 'clean:all', 'copy:files', 'uglify:core', 'jsvalidate:build', 'exec:blocks_build', 'cssmin', 'bp_rest', 'makepot', 'exec:blocks_minify', 'exec:bpdefault', 'exec:cli'] );
    373376        grunt.registerTask( 'release', ['build'] );
    374377
    375378        // Testing tasks.
  • composer.json

    diff --git composer.json composer.json
    index df0c4dbdd..216b1d8a6 100644
     
    3131                "php": ">=5.3.0"
    3232        },
    3333        "require-dev": {
    34                 "phpcompatibility/phpcompatibility-wp": "*",
     34                "phpcompatibility/phpcompatibility-wp": "*",
    3535                "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3"
    3636        }
    3737}
  • package.json

    diff --git package.json package.json
    index 5e26d7be7..e54217bee 100644
     
    55        },
    66        "description": "BuddyPress adds community features to WordPress. Member Profiles, Activity Streams, Direct Messaging, Notifications, and more!",
    77        "devDependencies": {
     8                "@babel/core": "~7.8.7",
     9                "@wordpress/babel-preset-default": "~4.10.0",
    810                "@wordpress/browserslist-config": "~2.1.4",
    911                "autoprefixer": "~8.5.2",
    1012                "grunt": "~1.0.3",
     
    2628                "grunt-rtlcss": "~2.0.1",
    2729                "grunt-sass": "~2.0.0",
    2830                "grunt-stylelint": "~0.8.0",
    29                 "grunt-wp-i18n": "~1.0.2",
    3031                "matchdep": "~1.0.1",
     32                "parcel-bundler": "~1.12.4",
    3133                "postcss-scss": "~1.0.6",
    3234                "stylelint": "~7.10.1",
    3335                "stylelint-config-wordpress": "~11.0.0"
     
    3537        "engines": {
    3638                "node": ">=6.9.1"
    3739        },
     40        "scripts": {
     41                "start": "parcel watch src/js/bp-*/*s/blocks/*.js --out-dir src --no-source-maps",
     42                "build": "parcel build src/js/bp-*/*s/blocks/*.js --out-dir src --no-source-maps --no-minify",
     43                "minify": "parcel build src/js/bp-*/*s/blocks/*.js --out-dir build --no-source-maps"
     44        },
    3845        "keywords": [
    3946                "activity",
    4047                "community",
     
    5360                "url": "https://buddypress.svn.wordpress.org/trunk/"
    5461        },
    5562        "version": "6.0.0-alpha",
    56         "dependencies": {},
    5763        "browserslist": [
    5864                "extends @wordpress/browserslist-config"
    5965        ]
  • src/bp-core/bp-core-actions.php

    diff --git src/bp-core/bp-core-actions.php src/bp-core/bp-core-actions.php
    index fe50c8a58..89296dd79 100644
    add_action( 'bp_init', 'bp_setup_globals', 4 ); 
    7575add_action( 'bp_init', 'bp_setup_canonical_stack',   5  );
    7676add_action( 'bp_init', 'bp_setup_nav',               6  );
    7777add_action( 'bp_init', 'bp_setup_title',             8  );
     78add_action( 'bp_init', 'bp_blocks_init',             10 );
    7879add_action( 'bp_init', 'bp_core_load_admin_bar_css', 12 );
    7980add_action( 'bp_init', 'bp_add_rewrite_tags',        20 );
    8081add_action( 'bp_init', 'bp_add_rewrite_rules',       30 );
  • new file src/bp-core/bp-core-blocks.php

    diff --git src/bp-core/bp-core-blocks.php src/bp-core/bp-core-blocks.php
    new file mode 100644
    index 000000000..65b4bb3b8
    - +  
     1<?php
     2/**
     3 * Core BP Blocks functions.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 * @since 6.0.0
     8 */
     9
     10// Exit if accessed directly.
     11defined( 'ABSPATH' ) || exit;
     12
     13/**
     14 * Filters the Block Editor settings to gather BuddyPress ones into a `bp` key.
     15 *
     16 * @since 6.0.0
     17 *
     18 * @param array $editor_settings Default editor settings.
     19 * @return array The editor settings including BP blocks specific ones.
     20 */
     21function bp_blocks_editor_settings( $editor_settings = array() ) {
     22        /**
     23         * Filter here to include your BP Blocks specific settings.
     24         *
     25         * @since 6.0.0
     26         *
     27         * @param array $bp_editor_settings BP blocks specific editor settings.
     28         */
     29        $bp_editor_settings = (array) apply_filters( 'bp_blocks_editor_settings', array() );
     30
     31        if ( $bp_editor_settings ) {
     32                $editor_settings['bp'] = $bp_editor_settings;
     33        }
     34
     35        return $editor_settings;
     36}
     37add_filter( 'block_editor_settings', 'bp_blocks_editor_settings' );
     38
     39/**
     40 * Register a BuddyPress block type.
     41 *
     42 * @since 6.0.0
     43 *
     44 * @param array $args The registration arguments for the block type.
     45 * @return BP_Block   The BuddyPress block type object.
     46 */
     47function bp_register_block( $args = array() ) {
     48        return new BP_Block( $args );
     49}
  • src/bp-core/bp-core-dependency.php

    diff --git src/bp-core/bp-core-dependency.php src/bp-core/bp-core-dependency.php
    index 6ea67ed85..cb3a3f4f5 100644
    function bp_rest_api_init() { 
    267267        do_action( 'bp_rest_api_init' );
    268268}
    269269
     270/**
     271 * BP Blocks Init hook.
     272 *
     273 * @since 6.0.0
     274 */
     275function bp_blocks_init() {
     276        /**
     277         * Hook here to register your BuddyPress blocks.
     278         *
     279         * @since 6.0.0
     280         */
     281        do_action( 'bp_blocks_init' );
     282}
     283
    270284/**
    271285 * Fire the 'bp_customize_register' action when the Customizer has loaded,
    272286 * allowing scripts and styles to be initialized.
  • new file src/bp-core/classes/class-bp-block.php

    diff --git src/bp-core/classes/class-bp-block.php src/bp-core/classes/class-bp-block.php
    new file mode 100644
    index 000000000..722974c80
    - +  
     1<?php
     2/**
     3 * BP Block class.
     4 *
     5 * @package bp-blocks
     6 * @subpackage \inc\classes\class-bp-block
     7 *
     8 * @since 6.0.0
     9 */
     10
     11// Exit if accessed directly.
     12if ( ! defined( 'ABSPATH' ) ) {
     13        exit;
     14}
     15
     16/**
     17 * BP Block Class.
     18 *
     19 * @since 6.0.0
     20 */
     21class BP_Block {
     22        /**
     23         * WP Block Type object.
     24         *
     25         * @since 6.0.0
     26         * @var WP_Block_Type|WP_Error
     27         */
     28        public $block;
     29
     30        /**
     31         * The script types registered.
     32         *
     33         * @since 6.0.0
     34         * @var array
     35         */
     36        private $registered_scripts;
     37
     38        /**
     39         * The style types registered.
     40         *
     41         * @since 6.0.0
     42         * @var array
     43         */
     44        private $registered_styles;
     45
     46        /**
     47         * Construct the BuddyPress Block.
     48         *
     49         * @since 6.0.0
     50         *
     51         * @param array $args The registration arguments for the BP Block.
     52         */
     53        public function __construct( $args ) {
     54                if ( ! did_action( 'bp_blocks_init' ) ) {
     55                        _doing_it_wrong( __METHOD__, esc_html__( 'BP Blocks needs to be registered hooking `bp_blocks_init`', 'buddypress' ), '6.0.0' );
     56                }
     57
     58                $min     = bp_core_get_minified_asset_suffix();
     59                $wp_args = array_intersect_key(
     60                        $args,
     61                        array(
     62                                'name'            => '',
     63                                'render_callback' => '',
     64                                'attributes'      => '',
     65                                'editor_script'   => '',
     66                                'script'          => '',
     67                                'editor_style'    => '',
     68                                'style'           => '',
     69                        )
     70                );
     71
     72                if ( ! isset( $wp_args['name'] ) || ! $wp_args['name'] || ! isset( $wp_args['editor_script'] ) || ! $wp_args['editor_script'] ) {
     73                        $this->block = new WP_Error( 'missing_parameters', __( 'The `name` or `editor_script` required keys are missing.', 'buddypress' ) );
     74                } else {
     75                        // Get specific BP Blocks arguments.
     76                        $bp_args = array_intersect_key(
     77                                $args,
     78                                array(
     79                                        'editor_script_url'  => '',
     80                                        'editor_script_deps' => array(),
     81                                        'script_url'         => '',
     82                                        'script_deps'        => array(),
     83                                        'editor_style_url'   => '',
     84                                        'editor_style_deps'  => array(),
     85                                        'style_url'          => '',
     86                                        'style_deps'         => array(),
     87                                )
     88                        );
     89
     90                        // Register the scripts.
     91                        $version                  = bp_get_version();
     92                        $this->registered_scripts = array();
     93
     94                        foreach ( array( 'editor_script', 'script' ) as $script_handle_key ) {
     95                                if ( ! isset( $wp_args[ $script_handle_key ] ) || ! $wp_args[ $script_handle_key ] ) {
     96                                        continue;
     97                                }
     98
     99                                if ( ! isset( $bp_args[ $script_handle_key . '_url' ] ) || ! $bp_args[ $script_handle_key . '_url' ] ) {
     100                                        continue;
     101                                }
     102
     103                                $deps = array();
     104                                if ( isset( $bp_args[ $script_handle_key . '_deps' ] ) && is_array( $bp_args[ $script_handle_key . '_deps' ] ) ) {
     105                                        $deps = $bp_args[ $script_handle_key . '_deps' ];
     106                                }
     107
     108                                $this->registered_scripts[ $script_handle_key ] = wp_register_script(
     109                                        $wp_args[ $script_handle_key ],
     110                                        $bp_args[ $script_handle_key . '_url' ],
     111                                        $deps,
     112                                        $version,
     113                                        true
     114                                );
     115                        }
     116
     117                        if ( ! isset( $this->registered_scripts['editor_script'] ) || ! $this->registered_scripts['editor_script'] ) {
     118                                $this->block = new WP_Error( 'script_registration_error', __( 'The required `editor_script` could not be registered.', 'buddypress' ) );
     119                        } else {
     120                                // Register the styles.
     121                                $registered_styles = array();
     122
     123                                foreach ( array( 'editor_style', 'style' ) as $style_handle_key ) {
     124                                        if ( ! isset( $wp_args[ $style_handle_key ] ) || ! $wp_args[ $style_handle_key ] ) {
     125                                                continue;
     126                                        }
     127
     128                                        if ( ! isset( $bp_args[ $style_handle_key . '_url' ] ) || ! $bp_args[ $style_handle_key . '_url' ] ) {
     129                                                continue;
     130                                        }
     131
     132                                        if ( $min ) {
     133                                                $minified_css  = str_replace( '.css', $min . '.css', $bp_args[ $style_handle_key . '_url' ] );
     134                                                $css_file_path = str_replace( content_url(), WP_CONTENT_DIR, $minified_css );
     135
     136                                                if ( file_exists( $css_file_path ) ) {
     137                                                        $bp_args[ $style_handle_key . '_url' ] = $minified_css;
     138                                                }
     139                                        }
     140
     141                                        $deps = array();
     142                                        if ( isset( $bp_args[ $style_handle_key . '_deps' ] ) && is_array( $bp_args[ $style_handle_key . '_deps' ] ) ) {
     143                                                $deps = $bp_args[ $style_handle_key . '_deps' ];
     144                                        }
     145
     146                                        $this->registered_styles[ $style_handle_key ] = wp_register_style(
     147                                                $wp_args[ $style_handle_key ],
     148                                                $bp_args[ $style_handle_key . '_url' ],
     149                                                $deps,
     150                                                $version
     151                                        );
     152
     153                                        wp_style_add_data( $wp_args[ $style_handle_key ], 'rtl', 'replace' );
     154                                        if ( $min ) {
     155                                                wp_style_add_data( $wp_args[ $style_handle_key ], 'suffix', $min );
     156                                        }
     157                                }
     158
     159                                $name = $wp_args['name'];
     160                                unset( $wp_args['name'] );
     161
     162                                // Set the Block Type.
     163                                $this->block = new WP_Block_Type( $name, $wp_args );
     164
     165                                // Register the Block Type.
     166                                register_block_type( $this->block );
     167
     168                                // Load Block translations if found.
     169                                if ( $this->block->editor_script ) {
     170                                        /**
     171                                         * Filter here to use a custom directory to look for the JSON translation file into.
     172                                         *
     173                                         * @since 6.0.0
     174                                         *
     175                                         * @param string $value         Absolute path to the directory to look for the JSON translation file into.
     176                                         * @param string $editor_script The editor's script handle.
     177                                         * @param string $name          The block's name.
     178                                         */
     179                                        $translation_dir = apply_filters( 'bp_block_translation_dir', null, $this->block->editor_script, $name );
     180
     181                                        /**
     182                                         * Filter here to use a custom domain for the JSON translation file.
     183                                         *
     184                                         * @since 6.0.0
     185                                         *
     186                                         * @param string $value         The custom domain for the JSON translation file.
     187                                         * @param string $editor_script The editor's script handle.
     188                                         * @param string $name          The block's name.
     189                                         */
     190                                        $domain = apply_filters( 'bp_block_translation_dir', 'buddypress', $this->block->editor_script, $name );
     191
     192                                        // Try to load the translation.
     193                                        $translated = wp_set_script_translations( $this->block->editor_script, $domain, $translation_dir );
     194                                }
     195                        }
     196                }
     197        }
     198}
  • new file src/bp-core/js/blocks/bp-autocompleter.js

    diff --git src/bp-core/js/blocks/bp-autocompleter.js src/bp-core/js/blocks/bp-autocompleter.js
    new file mode 100644
    index 000000000..89b3e9c54
    - +  
     1// modules are defined as an array
     2// [ module function, map of requires ]
     3//
     4// map of requires is short require name -> numeric require
     5//
     6// anything defined in a previous bundle is accessed via the
     7// orig method which is the require for previous bundles
     8parcelRequire = (function (modules, cache, entry, globalName) {
     9  // Save the require from previous bundle to this closure if any
     10  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
     11  var nodeRequire = typeof require === 'function' && require;
     12
     13  function newRequire(name, jumped) {
     14    if (!cache[name]) {
     15      if (!modules[name]) {
     16        // if we cannot find the module within our internal map or
     17        // cache jump to the current global require ie. the last bundle
     18        // that was added to the page.
     19        var currentRequire = typeof parcelRequire === 'function' && parcelRequire;
     20        if (!jumped && currentRequire) {
     21          return currentRequire(name, true);
     22        }
     23
     24        // If there are other bundles on this page the require from the
     25        // previous one is saved to 'previousRequire'. Repeat this as
     26        // many times as there are bundles until the module is found or
     27        // we exhaust the require chain.
     28        if (previousRequire) {
     29          return previousRequire(name, true);
     30        }
     31
     32        // Try the node require function if it exists.
     33        if (nodeRequire && typeof name === 'string') {
     34          return nodeRequire(name);
     35        }
     36
     37        var err = new Error('Cannot find module \'' + name + '\'');
     38        err.code = 'MODULE_NOT_FOUND';
     39        throw err;
     40      }
     41
     42      localRequire.resolve = resolve;
     43      localRequire.cache = {};
     44
     45      var module = cache[name] = new newRequire.Module(name);
     46
     47      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
     48    }
     49
     50    return cache[name].exports;
     51
     52    function localRequire(x){
     53      return newRequire(localRequire.resolve(x));
     54    }
     55
     56    function resolve(x){
     57      return modules[name][1][x] || x;
     58    }
     59  }
     60
     61  function Module(moduleName) {
     62    this.id = moduleName;
     63    this.bundle = newRequire;
     64    this.exports = {};
     65  }
     66
     67  newRequire.isParcelRequire = true;
     68  newRequire.Module = Module;
     69  newRequire.modules = modules;
     70  newRequire.cache = cache;
     71  newRequire.parent = previousRequire;
     72  newRequire.register = function (id, exports) {
     73    modules[id] = [function (require, module) {
     74      module.exports = exports;
     75    }, {}];
     76  };
     77
     78  var error;
     79  for (var i = 0; i < entry.length; i++) {
     80    try {
     81      newRequire(entry[i]);
     82    } catch (e) {
     83      // Save first error but execute all entries
     84      if (!error) {
     85        error = e;
     86      }
     87    }
     88  }
     89
     90  if (entry.length) {
     91    // Expose entry point to Node, AMD or browser globals
     92    // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
     93    var mainExports = newRequire(entry[entry.length - 1]);
     94
     95    // CommonJS
     96    if (typeof exports === "object" && typeof module !== "undefined") {
     97      module.exports = mainExports;
     98
     99    // RequireJS
     100    } else if (typeof define === "function" && define.amd) {
     101     define(function () {
     102       return mainExports;
     103     });
     104
     105    // <script>
     106    } else if (globalName) {
     107      this[globalName] = mainExports;
     108    }
     109  }
     110
     111  // Override the current require with this new one
     112  parcelRequire = newRequire;
     113
     114  if (error) {
     115    // throw error from earlier, _after updating parcelRequire_
     116    throw error;
     117  }
     118
     119  return newRequire;
     120})({"kUj2":[function(require,module,exports) {
     121function _classCallCheck(instance, Constructor) {
     122  if (!(instance instanceof Constructor)) {
     123    throw new TypeError("Cannot call a class as a function");
     124  }
     125}
     126
     127module.exports = _classCallCheck;
     128},{}],"dMjH":[function(require,module,exports) {
     129function _defineProperties(target, props) {
     130  for (var i = 0; i < props.length; i++) {
     131    var descriptor = props[i];
     132    descriptor.enumerable = descriptor.enumerable || false;
     133    descriptor.configurable = true;
     134    if ("value" in descriptor) descriptor.writable = true;
     135    Object.defineProperty(target, descriptor.key, descriptor);
     136  }
     137}
     138
     139function _createClass(Constructor, protoProps, staticProps) {
     140  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
     141  if (staticProps) _defineProperties(Constructor, staticProps);
     142  return Constructor;
     143}
     144
     145module.exports = _createClass;
     146},{}],"FlpK":[function(require,module,exports) {
     147function _typeof(obj) {
     148  "@babel/helpers - typeof";
     149
     150  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
     151    module.exports = _typeof = function _typeof(obj) {
     152      return typeof obj;
     153    };
     154  } else {
     155    module.exports = _typeof = function _typeof(obj) {
     156      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
     157    };
     158  }
     159
     160  return _typeof(obj);
     161}
     162
     163module.exports = _typeof;
     164},{}],"oXBW":[function(require,module,exports) {
     165function _assertThisInitialized(self) {
     166  if (self === void 0) {
     167    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
     168  }
     169
     170  return self;
     171}
     172
     173module.exports = _assertThisInitialized;
     174},{}],"cbGp":[function(require,module,exports) {
     175var _typeof = require("../helpers/typeof");
     176
     177var assertThisInitialized = require("./assertThisInitialized");
     178
     179function _possibleConstructorReturn(self, call) {
     180  if (call && (_typeof(call) === "object" || typeof call === "function")) {
     181    return call;
     182  }
     183
     184  return assertThisInitialized(self);
     185}
     186
     187module.exports = _possibleConstructorReturn;
     188},{"../helpers/typeof":"FlpK","./assertThisInitialized":"oXBW"}],"XApn":[function(require,module,exports) {
     189function _getPrototypeOf(o) {
     190  module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
     191    return o.__proto__ || Object.getPrototypeOf(o);
     192  };
     193  return _getPrototypeOf(o);
     194}
     195
     196module.exports = _getPrototypeOf;
     197},{}],"Omxx":[function(require,module,exports) {
     198function _setPrototypeOf(o, p) {
     199  module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
     200    o.__proto__ = p;
     201    return o;
     202  };
     203
     204  return _setPrototypeOf(o, p);
     205}
     206
     207module.exports = _setPrototypeOf;
     208},{}],"PhTw":[function(require,module,exports) {
     209var setPrototypeOf = require("./setPrototypeOf");
     210
     211function _inherits(subClass, superClass) {
     212  if (typeof superClass !== "function" && superClass !== null) {
     213    throw new TypeError("Super expression must either be null or a function");
     214  }
     215
     216  subClass.prototype = Object.create(superClass && superClass.prototype, {
     217    constructor: {
     218      value: subClass,
     219      writable: true,
     220      configurable: true
     221    }
     222  });
     223  if (superClass) setPrototypeOf(subClass, superClass);
     224}
     225
     226module.exports = _inherits;
     227},{"./setPrototypeOf":"Omxx"}],"xHsb":[function(require,module,exports) {
     228"use strict";
     229
     230Object.defineProperty(exports, "__esModule", {
     231  value: true
     232});
     233exports.default = void 0;
     234
     235var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
     236
     237var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
     238
     239var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
     240
     241var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
     242
     243var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
     244
     245var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
     246
     247function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     248
     249/**
     250 * WordPress dependencies.
     251 */
     252var _wp$element = wp.element,
     253    Component = _wp$element.Component,
     254    Fragment = _wp$element.Fragment,
     255    createElement = _wp$element.createElement;
     256var Popover = wp.components.Popover;
     257var _wp = wp,
     258    apiFetch = _wp.apiFetch;
     259var __ = wp.i18n.__;
     260
     261var BPAutocompleter = /*#__PURE__*/function (_Component) {
     262  (0, _inherits2.default)(BPAutocompleter, _Component);
     263
     264  function BPAutocompleter() {
     265    var _this;
     266
     267    (0, _classCallCheck2.default)(this, BPAutocompleter);
     268    _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(BPAutocompleter).apply(this, arguments));
     269    _this.state = {
     270      search: '',
     271      items: [],
     272      error: ''
     273    };
     274    _this.searchItemName = _this.searchItemName.bind((0, _assertThisInitialized2.default)(_this));
     275    _this.selectItemName = _this.selectItemName.bind((0, _assertThisInitialized2.default)(_this));
     276    return _this;
     277  }
     278
     279  (0, _createClass2.default)(BPAutocompleter, [{
     280    key: "searchItemName",
     281    value: function searchItemName(value) {
     282      var _this2 = this;
     283
     284      var search = this.state.search;
     285      var _this$props = this.props,
     286          component = _this$props.component,
     287          objectStatus = _this$props.objectStatus;
     288      this.setState({
     289        search: value
     290      });
     291
     292      if (value.length < search.length) {
     293        this.setState({
     294          items: []
     295        });
     296      }
     297
     298      var path = '/buddypress/v1/' + component;
     299
     300      if (value) {
     301        path += '?search=' + encodeURIComponent(value);
     302      }
     303
     304      if (objectStatus) {
     305        path += '&status=' + objectStatus;
     306      }
     307
     308      apiFetch({
     309        path: path
     310      }).then(function (items) {
     311        _this2.setState({
     312          items: items
     313        });
     314      }, function (error) {
     315        _this2.setState({
     316          error: error.message
     317        });
     318      });
     319    }
     320  }, {
     321    key: "selectItemName",
     322    value: function selectItemName(event, itemID) {
     323      var onSelectItem = this.props.onSelectItem;
     324      event.preventDefault();
     325      this.setState({
     326        search: '',
     327        items: [],
     328        error: ''
     329      });
     330      return onSelectItem({
     331        itemID: itemID
     332      });
     333    }
     334  }, {
     335    key: "render",
     336    value: function render() {
     337      var _this3 = this;
     338
     339      var _this$state = this.state,
     340          search = _this$state.search,
     341          items = _this$state.items;
     342      var _this$props2 = this.props,
     343          ariaLabel = _this$props2.ariaLabel,
     344          placeholder = _this$props2.placeholder,
     345          useAvatar = _this$props2.useAvatar;
     346      var itemsList;
     347
     348      if (!ariaLabel) {
     349        ariaLabel = __('Item\'s name', 'buddypress');
     350      }
     351
     352      if (!placeholder) {
     353        placeholder = __('Enter Item\'s name here…', 'buddypress');
     354      }
     355
     356      if (items.length) {
     357        itemsList = items.map(function (item) {
     358          return createElement("button", {
     359            type: "button",
     360            key: 'editor-autocompleters__item-item-' + item.id,
     361            role: "option",
     362            "aria-selected": "true",
     363            className: "components-button components-autocomplete__result editor-autocompleters__user",
     364            onClick: function onClick(event) {
     365              return _this3.selectItemName(event, item.id);
     366            }
     367          }, useAvatar && createElement("img", {
     368            key: "avatar",
     369            className: "editor-autocompleters__user-avatar",
     370            alt: "",
     371            src: item.avatar_urls.thumb
     372          }), createElement("span", {
     373            key: "name",
     374            className: "editor-autocompleters__user-name"
     375          }, item.name), item.mention_name && createElement("span", {
     376            key: "slug",
     377            className: "editor-autocompleters__user-slug"
     378          }, item.mention_name));
     379        });
     380      }
     381
     382      return createElement(Fragment, null, createElement("input", {
     383        type: "text",
     384        value: search,
     385        className: "components-placeholder__input",
     386        "aria-label": ariaLabel,
     387        placeholder: placeholder,
     388        onChange: function onChange(event) {
     389          return _this3.searchItemName(event.target.value);
     390        }
     391      }), 0 !== items.length && createElement(Popover, {
     392        className: "components-autocomplete__popover",
     393        focusOnMount: false,
     394        position: "bottom left"
     395      }, createElement("div", {
     396        className: "components-autocomplete__results"
     397      }, itemsList)));
     398    }
     399  }]);
     400  return BPAutocompleter;
     401}(Component);
     402
     403var _default = BPAutocompleter;
     404exports.default = _default;
     405},{"@babel/runtime/helpers/classCallCheck":"kUj2","@babel/runtime/helpers/createClass":"dMjH","@babel/runtime/helpers/possibleConstructorReturn":"cbGp","@babel/runtime/helpers/getPrototypeOf":"XApn","@babel/runtime/helpers/assertThisInitialized":"oXBW","@babel/runtime/helpers/inherits":"PhTw"}]},{},["xHsb"], null)
     406 No newline at end of file
  • new file src/bp-groups/bp-groups-blocks.php

    diff --git src/bp-groups/bp-groups-blocks.php src/bp-groups/bp-groups-blocks.php
    new file mode 100644
    index 000000000..4f40e384e
    - +  
     1<?php
     2/**
     3 * BP Groups Blocks Functions.
     4 *
     5 * @package   bp-blocks
     6 * @subpackage \inc\bp-groups\bp-groups-blocks
     7 */
     8
     9// Exit if accessed directly.
     10if ( ! defined( 'ABSPATH' ) ) {
     11        exit;
     12}
     13
     14/**
     15 * Register Groups Blocks.
     16 *
     17 * @since 6.0.0
     18 */
     19function bp_groups_register_blocks() {
     20        bp_register_block(
     21                array(
     22                        'name'               => 'bp/group',
     23                        'editor_script'      => 'bp-group-block',
     24                        'editor_script_url'  => plugins_url( 'js/blocks/group.js', __FILE__ ),
     25                        'editor_script_deps' => array(
     26                                'wp-blocks',
     27                                'wp-element',
     28                                'wp-components',
     29                                'wp-i18n',
     30                                'wp-api-fetch',
     31                                'wp-editor',
     32                                'wp-compose',
     33                                'wp-data',
     34                                'wp-block-editor',
     35                        ),
     36                        'style'              => 'bp-group-block',
     37                        'style_url'          => plugins_url( 'css/blocks/group.css', __FILE__ ),
     38                        'render_callback'    => 'bp_groups_render_group_block',
     39                        'attributes'         => array(
     40                                'itemID'              => array(
     41                                        'type'    => 'integer',
     42                                        'default' => 0,
     43                                ),
     44                                'avatarSize'          => array(
     45                                        'type'    => 'string',
     46                                        'default' => 'full',
     47                                ),
     48                                'displayDescription'  => array(
     49                                        'type'    => 'boolean',
     50                                        'default' => true,
     51                                ),
     52                                'displayActionButton' => array(
     53                                        'type'    => 'boolean',
     54                                        'default' => true,
     55                                ),
     56                                'displayCoverImage'   => array(
     57                                        'type'    => 'boolean',
     58                                        'default' => true,
     59                                ),
     60                        ),
     61                )
     62        );
     63}
     64add_action( 'bp_blocks_init', 'bp_groups_register_blocks', 10 );
     65
     66/**
     67 * Add BP Groups blocks specific settings to the BP Blocks Editor ones.
     68 *
     69 * @since 6.0.0
     70 *
     71 * @param array $bp_editor_settings BP blocks editor settings.
     72 * @return array BP Groups blocks editor settings.
     73 */
     74function bp_groups_editor_settings( $bp_editor_settings = array() ) {
     75        $bp = buddypress();
     76
     77        return array_merge(
     78                $bp_editor_settings,
     79                array(
     80                        'groups' => array(
     81                                'isAvatarEnabled'     => $bp->avatar && $bp->avatar->show_avatars && ! bp_disable_group_avatar_uploads(),
     82                                'isCoverImageEnabled' => bp_is_active( 'groups', 'cover_image' ),
     83                        ),
     84                )
     85        );
     86}
     87add_filter( 'bp_blocks_editor_settings', 'bp_groups_editor_settings' );
     88
     89/**
     90 * Callback function to render the BP Group Block.
     91 *
     92 * @since 6.0.0
     93 *
     94 * @param array $attributes The block attributes.
     95 * @return string           HTML output.
     96 */
     97function bp_groups_render_group_block( $attributes = array() ) {
     98        $bp = buddypress();
     99
     100        $block_args = wp_parse_args(
     101                $attributes,
     102                array(
     103                        'itemID'              => 0,
     104                        'avatarSize'          => 'full',
     105                        'displayDescription'  => true,
     106                        'displayActionButton' => true,
     107                        'displayCoverImage'   => true,
     108                )
     109        );
     110
     111        if ( ! $block_args['itemID'] ) {
     112                return;
     113        }
     114
     115        // Set the group ID and container classes.
     116        $group_id          = (int) $block_args['itemID'];
     117        $container_classes = array( 'bp-block-group' );
     118
     119        // Group object.
     120        $group = groups_get_group( $group_id );
     121
     122        if ( ! $group->id ) {
     123                return;
     124        }
     125
     126        // Avatar variables.
     127        $avatar           = '';
     128        $avatar_container = '';
     129
     130        // Cover image variable.
     131        $cover_image     = '';
     132        $cover_style     = '';
     133        $cover_container = '';
     134
     135        // Group name/link/description variables.
     136        $group_name        = bp_get_group_name( $group );
     137        $group_link        = bp_get_group_permalink( $group );
     138        $group_description = '';
     139        $group_content     = '';
     140
     141        // Group action button.
     142        $action_button         = '';
     143        $display_action_button = (bool) $block_args['displayActionButton'];
     144
     145        if ( $bp->avatar && $bp->avatar->show_avatars && ! bp_disable_group_avatar_uploads() && in_array( $block_args['avatarSize'], array( 'thumb', 'full' ), true ) ) {
     146                $avatar = bp_core_fetch_avatar(
     147                        array(
     148                                'item_id' => $group->id,
     149                                'object'  => 'group',
     150                                'type'    => $block_args['avatarSize'],
     151                                'html'    => false,
     152                        )
     153                );
     154
     155                $container_classes[] = 'avatar-' . $block_args['avatarSize'];
     156        } else {
     157                $container_classes[] = 'avatar-none';
     158        }
     159
     160        if ( $avatar ) {
     161                $avatar_container = sprintf(
     162                        '<div class="item-header-avatar">
     163                                <a href="%1$s">
     164                                        <img src="%2$s" alt="%3$s" class="avatar">
     165                                </a>
     166                        </div>',
     167                        esc_url( $group_link ),
     168                        esc_url( $avatar ),
     169                        // Translators: %s is the group's name.
     170                        sprintf( esc_html__( 'Group Profile photo of %s', 'buddypress' ), $group_name )
     171                );
     172        }
     173
     174        $display_cover_image = (bool) $block_args['displayCoverImage'];
     175        if ( bp_is_active( 'groups', 'cover_image' ) && $display_cover_image ) {
     176                $cover_image = bp_attachments_get_attachment(
     177                        'url',
     178                        array(
     179                                'item_id'    => $group->id,
     180                                'object_dir' => 'groups',
     181                        )
     182                );
     183
     184                if ( $cover_image ) {
     185                        $cover_style = sprintf(
     186                                ' style="background-image: url( %s );"',
     187                                esc_url( $cover_image )
     188                        );
     189                }
     190
     191                $cover_container = sprintf(
     192                        '<div class="bp-group-cover-image"%s></div>',
     193                        $cover_style
     194                );
     195
     196                $container_classes[] = 'has-cover';
     197        }
     198
     199        $display_description = (bool) $block_args['displayDescription'];
     200        if ( $display_description ) {
     201                $group_description = bp_get_group_description( $group );
     202                $group_content     = sprintf(
     203                        '<div class="group-description-content">%s</div>',
     204                        $group_description
     205                );
     206
     207                $container_classes[] = 'has-description';
     208        }
     209
     210        if ( $display_action_button ) {
     211                $action_button = sprintf(
     212                        '<div class="bp-profile-button">
     213                                <a href="%1$s" class="button large primary button-primary" role="button">%2$s</a>
     214                        </div>',
     215                        esc_url( $group_link ),
     216                        esc_html__( 'Visit Group', 'buddypress' )
     217                );
     218        }
     219
     220        $output = sprintf(
     221                '<div class="%1$s">
     222                        %2$s
     223                        <div class="group-content">
     224                                %3$s
     225                                <div class="group-description">
     226                                        <strong><a href="%4$s">%5$s</a></strong>
     227                                        %6$s
     228                                        %7$s
     229                                </div>
     230                        </div>
     231                </div>',
     232                implode( ' ', array_map( 'sanitize_html_class', $container_classes ) ),
     233                $cover_container,
     234                $avatar_container,
     235                esc_url( $group_link ),
     236                esc_html( $group_name ),
     237                $group_content,
     238                $action_button
     239        );
     240
     241        // Compact all interesting parameters.
     242        $params = array_merge( $block_args, compact( 'group_name', 'group_link', 'group_description', 'avatar', 'cover_image' ) );
     243
     244        /**
     245         * Filter here to edit the output of the single group block.
     246         *
     247         * @since 6.0.0
     248         *
     249         * @param string          $output The HTML output of the block.
     250         * @param BP_Groups_Group $group  The group object.
     251         * @param array           $params The block extended parameters.
     252         */
     253        return apply_filters( 'bp_groups_render_group_block_output', $output, $group, $params );
     254}
  • src/bp-groups/classes/class-bp-groups-component.php

    diff --git src/bp-groups/classes/class-bp-groups-component.php src/bp-groups/classes/class-bp-groups-component.php
    index b53835ea0..644e1a612 100644
    class BP_Groups_Component extends BP_Component { 
    130130                        'functions',
    131131                        'notifications',
    132132                        'cssjs',
     133                        'blocks',
    133134                );
    134135
    135136                // Conditional includes.
  • new file src/bp-groups/css/blocks/group-rtl.css

    diff --git src/bp-groups/css/blocks/group-rtl.css src/bp-groups/css/blocks/group-rtl.css
    new file mode 100644
    index 000000000..dce932ac8
    - +  
     1/* CSS for the bp/group block */
     2.bp-block-group {
     3        position: relative;
     4}
     5
     6.bp-block-group .group-content {
     7        display: flex;
     8}
     9
     10.bp-block-group.has-cover .group-content,
     11.bp-block-group.has-cover .item-header-avatar,
     12.bp-block-group.has-cover .group-description {
     13        z-index: 2;
     14}
     15
     16.bp-block-group .group-description {
     17        width: 100%;
     18}
     19
     20.bp-block-group.has-cover .group-description {
     21        padding-top: 75px;
     22}
     23
     24.bp-block-group.avatar-full .group-description {
     25        padding-right: 35px;
     26}
     27
     28.bp-block-group.has-cover .bp-group-cover-image {
     29        background-color: #c5c5c5;
     30        background-position: center top;
     31        background-repeat: no-repeat;
     32        background-size: cover;
     33        border: 0;
     34        display: block;
     35        right: 0;
     36        margin: 0;
     37        padding: 0;
     38        position: absolute;
     39        top: 0;
     40        width: 100%;
     41        z-index: 1;
     42        height: 150px;
     43}
     44
     45.bp-block-group img.avatar {
     46        width: auto;
     47        height: auto;
     48}
     49
     50.bp-block-group.avatar-none .item-header-avatar {
     51        display: none;
     52}
     53
     54.bp-block-group.avatar-none.has-cover {
     55        min-height: 200px;
     56}
     57
     58.bp-block-group.avatar-full {
     59        min-height: 150px;
     60}
     61
     62.bp-block-group.avatar-full.has-cover {
     63        min-height: 300px;
     64}
     65
     66.bp-block-group.avatar-full .item-header-avatar {
     67        width: 180px;
     68}
     69
     70.bp-block-group.has-cover.avatar-full .item-header-avatar {
     71        width: 200px;
     72}
     73
     74.bp-block-group.has-cover.avatar-full img.avatar {
     75        border: solid 2px #fff;
     76        background: rgba(255, 255, 255, 0.8);
     77        margin-right: 20px;
     78}
     79
     80.bp-block-group.has-cover .group-content {
     81        padding-top: 75px;
     82}
     83
     84.bp-block-group.avatar-thumb .item-header-avatar img.avatar {
     85        margin-top: 15px;
     86}
     87
     88.bp-block-group.avatar-thumb:not(.has-description) .group-content {
     89        min-height: 50px;
     90        align-items: center;
     91}
     92
     93.bp-block-group .group-description-content {
     94        width: 100%;
     95        margin-bottom: 18px;
     96}
     97
     98.bp-block-group.avatar-thumb .item-header-avatar {
     99        width: 70px;
     100}
     101
     102.bp-block-group.avatar-thumb.has-cover .item-header-avatar {
     103        padding-top: 75px;
     104}
     105
     106.bp-block-group .bp-profile-button {
     107        width: 100%;
     108        overflow: hidden;
     109}
     110
     111.bp-block-group .bp-profile-button a.button {
     112        margin: 18px 0 0;
     113}
     114
     115.bp-block-group.has-description .bp-profile-button a.button {
     116        display: block;
     117        float: left;
     118}
  • new file src/bp-groups/css/blocks/group.css

    diff --git src/bp-groups/css/blocks/group.css src/bp-groups/css/blocks/group.css
    new file mode 100644
    index 000000000..502b70b69
    - +  
     1/* CSS for the bp/group block */
     2.bp-block-group {
     3        position: relative;
     4}
     5
     6.bp-block-group .group-content {
     7        display: flex;
     8}
     9
     10.bp-block-group.has-cover .group-content,
     11.bp-block-group.has-cover .item-header-avatar,
     12.bp-block-group.has-cover .group-description {
     13        z-index: 2;
     14}
     15
     16.bp-block-group .group-description {
     17        width: 100%;
     18}
     19
     20.bp-block-group.has-cover .group-description {
     21        padding-top: 75px;
     22}
     23
     24.bp-block-group.avatar-full .group-description {
     25        padding-left: 35px;
     26}
     27
     28.bp-block-group.has-cover .bp-group-cover-image {
     29        background-color: #c5c5c5;
     30        background-position: center top;
     31        background-repeat: no-repeat;
     32        background-size: cover;
     33        border: 0;
     34        display: block;
     35        left: 0;
     36        margin: 0;
     37        padding: 0;
     38        position: absolute;
     39        top: 0;
     40        width: 100%;
     41        z-index: 1;
     42        height: 150px;
     43}
     44
     45.bp-block-group img.avatar {
     46        width: auto;
     47        height: auto;
     48}
     49
     50.bp-block-group.avatar-none .item-header-avatar {
     51        display: none;
     52}
     53
     54.bp-block-group.avatar-none.has-cover {
     55        min-height: 200px;
     56}
     57
     58.bp-block-group.avatar-full {
     59        min-height: 150px;
     60}
     61
     62.bp-block-group.avatar-full.has-cover {
     63        min-height: 300px;
     64}
     65
     66.bp-block-group.avatar-full .item-header-avatar {
     67        width: 180px;
     68}
     69
     70.bp-block-group.has-cover.avatar-full .item-header-avatar {
     71        width: 200px;
     72}
     73
     74.bp-block-group.has-cover.avatar-full img.avatar {
     75        border: solid 2px #fff;
     76        background: rgba(255, 255, 255, 0.8);
     77        margin-left: 20px;
     78}
     79
     80.bp-block-group.has-cover .group-content {
     81        padding-top: 75px;
     82}
     83
     84.bp-block-group.avatar-thumb .item-header-avatar img.avatar {
     85        margin-top: 15px;
     86}
     87
     88.bp-block-group.avatar-thumb:not(.has-description) .group-content {
     89        min-height: 50px;
     90        align-items: center;
     91}
     92
     93.bp-block-group .group-description-content {
     94        width: 100%;
     95        margin-bottom: 18px;
     96}
     97
     98.bp-block-group.avatar-thumb .item-header-avatar {
     99        width: 70px;
     100}
     101
     102.bp-block-group.avatar-thumb.has-cover .item-header-avatar {
     103        padding-top: 75px;
     104}
     105
     106.bp-block-group .bp-profile-button {
     107        width: 100%;
     108        overflow: hidden;
     109}
     110
     111.bp-block-group .bp-profile-button a.button {
     112        margin: 18px 0 0;
     113}
     114
     115.bp-block-group.has-description .bp-profile-button a.button {
     116        display: block;
     117        float: right;
     118}
  • new file src/bp-groups/js/blocks/group.js

    diff --git src/bp-groups/js/blocks/group.js src/bp-groups/js/blocks/group.js
    new file mode 100644
    index 000000000..6a3718555
    - +  
     1// modules are defined as an array
     2// [ module function, map of requires ]
     3//
     4// map of requires is short require name -> numeric require
     5//
     6// anything defined in a previous bundle is accessed via the
     7// orig method which is the require for previous bundles
     8parcelRequire = (function (modules, cache, entry, globalName) {
     9  // Save the require from previous bundle to this closure if any
     10  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
     11  var nodeRequire = typeof require === 'function' && require;
     12
     13  function newRequire(name, jumped) {
     14    if (!cache[name]) {
     15      if (!modules[name]) {
     16        // if we cannot find the module within our internal map or
     17        // cache jump to the current global require ie. the last bundle
     18        // that was added to the page.
     19        var currentRequire = typeof parcelRequire === 'function' && parcelRequire;
     20        if (!jumped && currentRequire) {
     21          return currentRequire(name, true);
     22        }
     23
     24        // If there are other bundles on this page the require from the
     25        // previous one is saved to 'previousRequire'. Repeat this as
     26        // many times as there are bundles until the module is found or
     27        // we exhaust the require chain.
     28        if (previousRequire) {
     29          return previousRequire(name, true);
     30        }
     31
     32        // Try the node require function if it exists.
     33        if (nodeRequire && typeof name === 'string') {
     34          return nodeRequire(name);
     35        }
     36
     37        var err = new Error('Cannot find module \'' + name + '\'');
     38        err.code = 'MODULE_NOT_FOUND';
     39        throw err;
     40      }
     41
     42      localRequire.resolve = resolve;
     43      localRequire.cache = {};
     44
     45      var module = cache[name] = new newRequire.Module(name);
     46
     47      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
     48    }
     49
     50    return cache[name].exports;
     51
     52    function localRequire(x){
     53      return newRequire(localRequire.resolve(x));
     54    }
     55
     56    function resolve(x){
     57      return modules[name][1][x] || x;
     58    }
     59  }
     60
     61  function Module(moduleName) {
     62    this.id = moduleName;
     63    this.bundle = newRequire;
     64    this.exports = {};
     65  }
     66
     67  newRequire.isParcelRequire = true;
     68  newRequire.Module = Module;
     69  newRequire.modules = modules;
     70  newRequire.cache = cache;
     71  newRequire.parent = previousRequire;
     72  newRequire.register = function (id, exports) {
     73    modules[id] = [function (require, module) {
     74      module.exports = exports;
     75    }, {}];
     76  };
     77
     78  var error;
     79  for (var i = 0; i < entry.length; i++) {
     80    try {
     81      newRequire(entry[i]);
     82    } catch (e) {
     83      // Save first error but execute all entries
     84      if (!error) {
     85        error = e;
     86      }
     87    }
     88  }
     89
     90  if (entry.length) {
     91    // Expose entry point to Node, AMD or browser globals
     92    // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
     93    var mainExports = newRequire(entry[entry.length - 1]);
     94
     95    // CommonJS
     96    if (typeof exports === "object" && typeof module !== "undefined") {
     97      module.exports = mainExports;
     98
     99    // RequireJS
     100    } else if (typeof define === "function" && define.amd) {
     101     define(function () {
     102       return mainExports;
     103     });
     104
     105    // <script>
     106    } else if (globalName) {
     107      this[globalName] = mainExports;
     108    }
     109  }
     110
     111  // Override the current require with this new one
     112  parcelRequire = newRequire;
     113
     114  if (error) {
     115    // throw error from earlier, _after updating parcelRequire_
     116    throw error;
     117  }
     118
     119  return newRequire;
     120})({"kUj2":[function(require,module,exports) {
     121function _classCallCheck(instance, Constructor) {
     122  if (!(instance instanceof Constructor)) {
     123    throw new TypeError("Cannot call a class as a function");
     124  }
     125}
     126
     127module.exports = _classCallCheck;
     128},{}],"dMjH":[function(require,module,exports) {
     129function _defineProperties(target, props) {
     130  for (var i = 0; i < props.length; i++) {
     131    var descriptor = props[i];
     132    descriptor.enumerable = descriptor.enumerable || false;
     133    descriptor.configurable = true;
     134    if ("value" in descriptor) descriptor.writable = true;
     135    Object.defineProperty(target, descriptor.key, descriptor);
     136  }
     137}
     138
     139function _createClass(Constructor, protoProps, staticProps) {
     140  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
     141  if (staticProps) _defineProperties(Constructor, staticProps);
     142  return Constructor;
     143}
     144
     145module.exports = _createClass;
     146},{}],"FlpK":[function(require,module,exports) {
     147function _typeof(obj) {
     148  "@babel/helpers - typeof";
     149
     150  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
     151    module.exports = _typeof = function _typeof(obj) {
     152      return typeof obj;
     153    };
     154  } else {
     155    module.exports = _typeof = function _typeof(obj) {
     156      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
     157    };
     158  }
     159
     160  return _typeof(obj);
     161}
     162
     163module.exports = _typeof;
     164},{}],"oXBW":[function(require,module,exports) {
     165function _assertThisInitialized(self) {
     166  if (self === void 0) {
     167    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
     168  }
     169
     170  return self;
     171}
     172
     173module.exports = _assertThisInitialized;
     174},{}],"cbGp":[function(require,module,exports) {
     175var _typeof = require("../helpers/typeof");
     176
     177var assertThisInitialized = require("./assertThisInitialized");
     178
     179function _possibleConstructorReturn(self, call) {
     180  if (call && (_typeof(call) === "object" || typeof call === "function")) {
     181    return call;
     182  }
     183
     184  return assertThisInitialized(self);
     185}
     186
     187module.exports = _possibleConstructorReturn;
     188},{"../helpers/typeof":"FlpK","./assertThisInitialized":"oXBW"}],"XApn":[function(require,module,exports) {
     189function _getPrototypeOf(o) {
     190  module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
     191    return o.__proto__ || Object.getPrototypeOf(o);
     192  };
     193  return _getPrototypeOf(o);
     194}
     195
     196module.exports = _getPrototypeOf;
     197},{}],"Omxx":[function(require,module,exports) {
     198function _setPrototypeOf(o, p) {
     199  module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
     200    o.__proto__ = p;
     201    return o;
     202  };
     203
     204  return _setPrototypeOf(o, p);
     205}
     206
     207module.exports = _setPrototypeOf;
     208},{}],"PhTw":[function(require,module,exports) {
     209var setPrototypeOf = require("./setPrototypeOf");
     210
     211function _inherits(subClass, superClass) {
     212  if (typeof superClass !== "function" && superClass !== null) {
     213    throw new TypeError("Super expression must either be null or a function");
     214  }
     215
     216  subClass.prototype = Object.create(superClass && superClass.prototype, {
     217    constructor: {
     218      value: subClass,
     219      writable: true,
     220      configurable: true
     221    }
     222  });
     223  if (superClass) setPrototypeOf(subClass, superClass);
     224}
     225
     226module.exports = _inherits;
     227},{"./setPrototypeOf":"Omxx"}],"xHsb":[function(require,module,exports) {
     228"use strict";
     229
     230Object.defineProperty(exports, "__esModule", {
     231  value: true
     232});
     233exports.default = void 0;
     234
     235var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
     236
     237var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
     238
     239var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
     240
     241var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
     242
     243var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
     244
     245var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
     246
     247function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     248
     249/**
     250 * WordPress dependencies.
     251 */
     252var _wp$element = wp.element,
     253    Component = _wp$element.Component,
     254    Fragment = _wp$element.Fragment,
     255    createElement = _wp$element.createElement;
     256var Popover = wp.components.Popover;
     257var _wp = wp,
     258    apiFetch = _wp.apiFetch;
     259var __ = wp.i18n.__;
     260
     261var BPAutocompleter = /*#__PURE__*/function (_Component) {
     262  (0, _inherits2.default)(BPAutocompleter, _Component);
     263
     264  function BPAutocompleter() {
     265    var _this;
     266
     267    (0, _classCallCheck2.default)(this, BPAutocompleter);
     268    _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(BPAutocompleter).apply(this, arguments));
     269    _this.state = {
     270      search: '',
     271      items: [],
     272      error: ''
     273    };
     274    _this.searchItemName = _this.searchItemName.bind((0, _assertThisInitialized2.default)(_this));
     275    _this.selectItemName = _this.selectItemName.bind((0, _assertThisInitialized2.default)(_this));
     276    return _this;
     277  }
     278
     279  (0, _createClass2.default)(BPAutocompleter, [{
     280    key: "searchItemName",
     281    value: function searchItemName(value) {
     282      var _this2 = this;
     283
     284      var search = this.state.search;
     285      var _this$props = this.props,
     286          component = _this$props.component,
     287          objectStatus = _this$props.objectStatus;
     288      this.setState({
     289        search: value
     290      });
     291
     292      if (value.length < search.length) {
     293        this.setState({
     294          items: []
     295        });
     296      }
     297
     298      var path = '/buddypress/v1/' + component;
     299
     300      if (value) {
     301        path += '?search=' + encodeURIComponent(value);
     302      }
     303
     304      if (objectStatus) {
     305        path += '&status=' + objectStatus;
     306      }
     307
     308      apiFetch({
     309        path: path
     310      }).then(function (items) {
     311        _this2.setState({
     312          items: items
     313        });
     314      }, function (error) {
     315        _this2.setState({
     316          error: error.message
     317        });
     318      });
     319    }
     320  }, {
     321    key: "selectItemName",
     322    value: function selectItemName(event, itemID) {
     323      var onSelectItem = this.props.onSelectItem;
     324      event.preventDefault();
     325      this.setState({
     326        search: '',
     327        items: [],
     328        error: ''
     329      });
     330      return onSelectItem({
     331        itemID: itemID
     332      });
     333    }
     334  }, {
     335    key: "render",
     336    value: function render() {
     337      var _this3 = this;
     338
     339      var _this$state = this.state,
     340          search = _this$state.search,
     341          items = _this$state.items;
     342      var _this$props2 = this.props,
     343          ariaLabel = _this$props2.ariaLabel,
     344          placeholder = _this$props2.placeholder,
     345          useAvatar = _this$props2.useAvatar;
     346      var itemsList;
     347
     348      if (!ariaLabel) {
     349        ariaLabel = __('Item\'s name', 'buddypress');
     350      }
     351
     352      if (!placeholder) {
     353        placeholder = __('Enter Item\'s name here…', 'buddypress');
     354      }
     355
     356      if (items.length) {
     357        itemsList = items.map(function (item) {
     358          return createElement("button", {
     359            type: "button",
     360            key: 'editor-autocompleters__item-item-' + item.id,
     361            role: "option",
     362            "aria-selected": "true",
     363            className: "components-button components-autocomplete__result editor-autocompleters__user",
     364            onClick: function onClick(event) {
     365              return _this3.selectItemName(event, item.id);
     366            }
     367          }, useAvatar && createElement("img", {
     368            key: "avatar",
     369            className: "editor-autocompleters__user-avatar",
     370            alt: "",
     371            src: item.avatar_urls.thumb
     372          }), createElement("span", {
     373            key: "name",
     374            className: "editor-autocompleters__user-name"
     375          }, item.name), item.mention_name && createElement("span", {
     376            key: "slug",
     377            className: "editor-autocompleters__user-slug"
     378          }, item.mention_name));
     379        });
     380      }
     381
     382      return createElement(Fragment, null, createElement("input", {
     383        type: "text",
     384        value: search,
     385        className: "components-placeholder__input",
     386        "aria-label": ariaLabel,
     387        placeholder: placeholder,
     388        onChange: function onChange(event) {
     389          return _this3.searchItemName(event.target.value);
     390        }
     391      }), 0 !== items.length && createElement(Popover, {
     392        className: "components-autocomplete__popover",
     393        focusOnMount: false,
     394        position: "bottom left"
     395      }, createElement("div", {
     396        className: "components-autocomplete__results"
     397      }, itemsList)));
     398    }
     399  }]);
     400  return BPAutocompleter;
     401}(Component);
     402
     403var _default = BPAutocompleter;
     404exports.default = _default;
     405},{"@babel/runtime/helpers/classCallCheck":"kUj2","@babel/runtime/helpers/createClass":"dMjH","@babel/runtime/helpers/possibleConstructorReturn":"cbGp","@babel/runtime/helpers/getPrototypeOf":"XApn","@babel/runtime/helpers/assertThisInitialized":"oXBW","@babel/runtime/helpers/inherits":"PhTw"}],"pvse":[function(require,module,exports) {
     406"use strict";
     407
     408var _bpAutocompleter = _interopRequireDefault(require("../../../bp-core/js/blocks/bp-autocompleter"));
     409
     410function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     411
     412/**
     413 * WordPress dependencies.
     414 */
     415var registerBlockType = wp.blocks.registerBlockType;
     416var _wp$element = wp.element,
     417    createElement = _wp$element.createElement,
     418    Fragment = _wp$element.Fragment;
     419var _wp$components = wp.components,
     420    Placeholder = _wp$components.Placeholder,
     421    Disabled = _wp$components.Disabled,
     422    PanelBody = _wp$components.PanelBody,
     423    SelectControl = _wp$components.SelectControl,
     424    ToggleControl = _wp$components.ToggleControl,
     425    Toolbar = _wp$components.Toolbar,
     426    ToolbarButton = _wp$components.ToolbarButton;
     427var _wp$blockEditor = wp.blockEditor,
     428    InspectorControls = _wp$blockEditor.InspectorControls,
     429    BlockControls = _wp$blockEditor.BlockControls;
     430var withSelect = wp.data.withSelect;
     431var compose = wp.compose.compose;
     432var ServerSideRender = wp.editor.ServerSideRender;
     433var __ = wp.i18n.__;
     434/**
     435 * Internal dependencies.
     436 */
     437
     438var AVATAR_SIZES = [{
     439  label: __('None', 'buddypress'),
     440  value: 'none'
     441}, {
     442  label: __('Thumb', 'buddypress'),
     443  value: 'thumb'
     444}, {
     445  label: __('Full', 'buddypress'),
     446  value: 'full'
     447}];
     448
     449var editGroup = function editGroup(_ref) {
     450  var attributes = _ref.attributes,
     451      setAttributes = _ref.setAttributes,
     452      bpSettings = _ref.bpSettings;
     453  var isAvatarEnabled = bpSettings.isAvatarEnabled,
     454      isCoverImageEnabled = bpSettings.isCoverImageEnabled;
     455  var avatarSize = attributes.avatarSize,
     456      displayDescription = attributes.displayDescription,
     457      displayActionButton = attributes.displayActionButton,
     458      displayCoverImage = attributes.displayCoverImage;
     459
     460  if (!attributes.itemID) {
     461    return createElement(Placeholder, {
     462      icon: "buddicons-groups",
     463      label: __('BuddyPress Group', 'buddypress'),
     464      instructions: __('Start typing the name of the group you want to feature into this post.', 'buddypress')
     465    }, createElement(_bpAutocompleter.default, {
     466      component: "groups",
     467      objectStatus: "public",
     468      ariaLabel: __('Group\'s name', 'buddypress'),
     469      placeholder: __('Enter Group\'s name here…', 'buddypress'),
     470      onSelectItem: setAttributes,
     471      useAvatar: isAvatarEnabled
     472    }));
     473  }
     474
     475  return createElement(Fragment, null, createElement(BlockControls, null, createElement(Toolbar, null, createElement(ToolbarButton, {
     476    icon: "edit",
     477    title: __('Select another group', 'buddypress'),
     478    onClick: function onClick() {
     479      setAttributes({
     480        itemID: 0
     481      });
     482    }
     483  }))), createElement(InspectorControls, null, createElement(PanelBody, {
     484    title: __('Group\'s home button settings', 'buddypress'),
     485    initialOpen: true
     486  }, createElement(ToggleControl, {
     487    label: __('Display Group\'s home button', 'buddypress'),
     488    checked: !!displayActionButton,
     489    onChange: function onChange() {
     490      setAttributes({
     491        displayActionButton: !displayActionButton
     492      });
     493    },
     494    help: displayActionButton ? __('Include a link to the group\'s home page under their name.', 'buddypress') : __('Toggle to display a link to the group\'s home page under their name.', 'buddypress')
     495  })), createElement(PanelBody, {
     496    title: __('Description settings', 'buddypress'),
     497    initialOpen: false
     498  }, createElement(ToggleControl, {
     499    label: __('Display group\'s description', 'buddypress'),
     500    checked: !!displayDescription,
     501    onChange: function onChange() {
     502      setAttributes({
     503        displayDescription: !displayDescription
     504      });
     505    },
     506    help: displayDescription ? __('Include the group\'s description under their name.', 'buddypress') : __('Toggle to display the group\'s description under their name.', 'buddypress')
     507  })), isAvatarEnabled && createElement(PanelBody, {
     508    title: __('Avatar settings', 'buddypress'),
     509    initialOpen: false
     510  }, createElement(SelectControl, {
     511    label: __('Size', 'buddypress'),
     512    value: avatarSize,
     513    options: AVATAR_SIZES,
     514    onChange: function onChange(option) {
     515      setAttributes({
     516        avatarSize: option
     517      });
     518    }
     519  })), isCoverImageEnabled && createElement(PanelBody, {
     520    title: __('Cover image settings', 'buddypress'),
     521    initialOpen: false
     522  }, createElement(ToggleControl, {
     523    label: __('Display Cover Image', 'buddypress'),
     524    checked: !!displayCoverImage,
     525    onChange: function onChange() {
     526      setAttributes({
     527        displayCoverImage: !displayCoverImage
     528      });
     529    },
     530    help: displayCoverImage ? __('Include the group\'s cover image over their name.', 'buddypress') : __('Toggle to display the group\'s cover image over their name.', 'buddypress')
     531  }))), createElement(Disabled, null, createElement(ServerSideRender, {
     532    block: "bp/group",
     533    attributes: attributes
     534  })));
     535};
     536
     537var editGroupBlock = compose([withSelect(function (select) {
     538  var editorSettings = select('core/editor').getEditorSettings();
     539  return {
     540    bpSettings: editorSettings.bp.groups || {}
     541  };
     542})])(editGroup);
     543registerBlockType('bp/group', {
     544  title: __('Group', 'buddypress'),
     545  description: __('BuddyPress Group.', 'buddypress'),
     546  icon: 'buddicons-groups',
     547  category: 'buddypress',
     548  attributes: {
     549    itemID: {
     550      type: 'integer',
     551      default: 0
     552    },
     553    avatarSize: {
     554      type: 'string',
     555      default: 'full'
     556    },
     557    displayDescription: {
     558      type: 'boolean',
     559      default: true
     560    },
     561    displayActionButton: {
     562      type: 'boolean',
     563      default: true
     564    },
     565    displayCoverImage: {
     566      type: 'boolean',
     567      default: true
     568    }
     569  },
     570  edit: editGroupBlock
     571});
     572},{"../../../bp-core/js/blocks/bp-autocompleter":"xHsb"}]},{},["pvse"], null)
     573 No newline at end of file
  • new file src/bp-members/bp-members-blocks.php

    diff --git src/bp-members/bp-members-blocks.php src/bp-members/bp-members-blocks.php
    new file mode 100644
    index 000000000..1c2d2fb27
    - +  
     1<?php
     2/**
     3 * BP Members Blocks Functions.
     4 *
     5 * @package   bp-blocks
     6 * @subpackage \inc\bp-members\bp-members-blocks
     7 */
     8
     9// Exit if accessed directly.
     10if ( ! defined( 'ABSPATH' ) ) {
     11        exit;
     12}
     13
     14/**
     15 * Register Members Blocks.
     16 *
     17 * @since 6.0.0
     18 */
     19function bp_members_register_blocks() {
     20        bp_register_block(
     21                array(
     22                        'name'               => 'bp/member',
     23                        'editor_script'      => 'bp-member-block',
     24                        'editor_script_url'  => plugins_url( 'js/blocks/member.js', __FILE__ ),
     25                        'editor_script_deps' => array(
     26                                'wp-blocks',
     27                                'wp-element',
     28                                'wp-components',
     29                                'wp-i18n',
     30                                'wp-api-fetch',
     31                                'wp-editor',
     32                                'wp-compose',
     33                                'wp-data',
     34                                'wp-block-editor',
     35                        ),
     36                        'style'              => 'bp-member-block',
     37                        'style_url'          => plugins_url( 'css/blocks/member.css', __FILE__ ),
     38                        'render_callback'    => 'bp_members_render_member_block',
     39                        'attributes'         => array(
     40                                'itemID'              => array(
     41                                        'type'    => 'integer',
     42                                        'default' => 0,
     43                                ),
     44                                'avatarSize'          => array(
     45                                        'type'    => 'string',
     46                                        'default' => 'full',
     47                                ),
     48                                'displayMentionSlug'  => array(
     49                                        'type'    => 'boolean',
     50                                        'default' => true,
     51                                ),
     52                                'displayActionButton' => array(
     53                                        'type'    => 'boolean',
     54                                        'default' => true,
     55                                ),
     56                                'displayCoverImage'   => array(
     57                                        'type'    => 'boolean',
     58                                        'default' => true,
     59                                ),
     60                        ),
     61                )
     62        );
     63}
     64add_action( 'bp_blocks_init', 'bp_members_register_blocks', 10 );
     65
     66/**
     67 * Add BP Members blocks specific settings to the BP Blocks Editor ones.
     68 *
     69 * @since 6.0.0
     70 *
     71 * @param array $bp_editor_settings BP blocks editor settings.
     72 * @return array BP Members blocks editor settings.
     73 */
     74function bp_members_editor_settings( $bp_editor_settings = array() ) {
     75        $bp = buddypress();
     76
     77        return array_merge(
     78                $bp_editor_settings,
     79                array(
     80                        'members' => array(
     81                                'isMentionEnabled'    => bp_is_active( 'activity' ) && bp_activity_do_mentions(),
     82                                'isAvatarEnabled'     => $bp->avatar && $bp->avatar->show_avatars,
     83                                'isCoverImageEnabled' => bp_is_active( 'members', 'cover_image' ),
     84                        ),
     85                )
     86        );
     87}
     88add_filter( 'bp_blocks_editor_settings', 'bp_members_editor_settings' );
     89
     90/**
     91 * Callback function to render the BP Member Block.
     92 *
     93 * @since 6.0.0
     94 *
     95 * @param array $attributes The block attributes.
     96 * @return string           HTML output.
     97 */
     98function bp_members_render_member_block( $attributes = array() ) {
     99        $bp = buddypress();
     100
     101        $block_args = wp_parse_args(
     102                $attributes,
     103                array(
     104                        'itemID'              => 0,
     105                        'avatarSize'          => 'full',
     106                        'displayMentionSlug'  => true,
     107                        'displayActionButton' => true,
     108                        'displayCoverImage'   => true,
     109                )
     110        );
     111
     112        if ( ! $block_args['itemID'] ) {
     113                return;
     114        }
     115
     116        // Set the member ID and container classes.
     117        $member_id         = (int) $block_args['itemID'];
     118        $container_classes = array( 'bp-block-member' );
     119
     120        // Mention variables.
     121        $username   = bp_core_get_username( $member_id );
     122        $at_mention = '';
     123
     124        // Avatar variables.
     125        $avatar           = '';
     126        $avatar_container = '';
     127
     128        // Cover image variable.
     129        $cover_image     = '';
     130        $cover_style     = '';
     131        $cover_container = '';
     132
     133        // Member name variables.
     134        $display_name = bp_core_get_user_displayname( $member_id );
     135        $member_link  = bp_core_get_user_domain( $member_id );
     136
     137        // Member action button.
     138        $action_button         = '';
     139        $display_action_button = (bool) $block_args['displayActionButton'];
     140
     141        if ( $bp->avatar && $bp->avatar->show_avatars && in_array( $block_args['avatarSize'], array( 'thumb', 'full' ), true ) ) {
     142                $avatar = bp_core_fetch_avatar(
     143                        array(
     144                                'item_id' => $member_id,
     145                                'object'  => 'user',
     146                                'type'    => $block_args['avatarSize'],
     147                                'html'    => false,
     148                        )
     149                );
     150
     151                $container_classes[] = 'avatar-' . $block_args['avatarSize'];
     152        } else {
     153                $container_classes[] = 'avatar-none';
     154        }
     155
     156        if ( $avatar ) {
     157                $avatar_container = sprintf(
     158                        '<div class="item-header-avatar">
     159                                <a href="%1$s">
     160                                        <img src="%2$s" alt="%3$s" class="avatar">
     161                                </a>
     162                        </div>',
     163                        esc_url( $member_link ),
     164                        esc_url( $avatar ),
     165                        // Translators: %s is the member's display name.
     166                        sprintf( esc_html__( 'Profile photo of %s', 'buddypress' ), $display_name )
     167                );
     168        }
     169
     170        $display_cover_image = (bool) $block_args['displayCoverImage'];
     171        if ( bp_is_active( 'members', 'cover_image' ) && $display_cover_image ) {
     172                $cover_image = bp_attachments_get_attachment(
     173                        'url',
     174                        array(
     175                                'item_id' => $member_id,
     176                        )
     177                );
     178
     179                if ( $cover_image ) {
     180                        $cover_style = sprintf(
     181                                ' style="background-image: url( %s );"',
     182                                esc_url( $cover_image )
     183                        );
     184                }
     185
     186                $cover_container = sprintf(
     187                        '<div class="bp-member-cover-image"%s></div>',
     188                        $cover_style
     189                );
     190
     191                $container_classes[] = 'has-cover';
     192        }
     193
     194        $display_mention_slug = (bool) $block_args['displayMentionSlug'];
     195        if ( bp_is_active( 'activity' ) && bp_activity_do_mentions() && $display_mention_slug ) {
     196                $at_mention = sprintf(
     197                        '<span class="user-nicename">@%s</span>',
     198                        esc_html( $username )
     199                );
     200        }
     201
     202        if ( $display_action_button ) {
     203                $action_button = sprintf(
     204                        '<div class="bp-profile-button">
     205                                <a href="%1$s" class="button large primary button-primary" role="button">%2$s</a>
     206                        </div>',
     207                        esc_url( $member_link ),
     208                        esc_html__( 'View Profile', 'buddypress' )
     209                );
     210        }
     211
     212        $output = sprintf(
     213                '<div class="%1$s">
     214                        %2$s
     215                        <div class="member-content">
     216                                %3$s
     217                                <div class="member-description">
     218                                        <strong><a href="%4$s">%5$s</a></strong>
     219                                        %6$s
     220                                        %7$s
     221                                </div>
     222                        </div>
     223                </div>',
     224                implode( ' ', array_map( 'sanitize_html_class', $container_classes ) ),
     225                $cover_container,
     226                $avatar_container,
     227                esc_url( $member_link ),
     228                esc_html( $display_name ),
     229                $at_mention,
     230                $action_button
     231        );
     232
     233        // Compact all interesting parameters.
     234        $params = array_merge( $block_args, compact( 'username', 'display_name', 'member_link', 'avatar', 'cover_image' ) );
     235
     236        /**
     237         * Filter here to edit the output of the single member block.
     238         *
     239         * @since 6.0.0
     240         *
     241         * @param string          $output The HTML output of the block.
     242         * @param array           $params The block extended parameters.
     243         */
     244        return apply_filters( 'bp_members_render_member_block_output', $output, $params );
     245}
  • src/bp-members/classes/class-bp-members-component.php

    diff --git src/bp-members/classes/class-bp-members-component.php src/bp-members/classes/class-bp-members-component.php
    index 29576b52f..af46a4689 100644
    class BP_Members_Component extends BP_Component { 
    6161                        'template',
    6262                        'adminbar',
    6363                        'functions',
     64                        'blocks',
    6465                        'widgets',
    6566                        'cache',
    6667                );
  • new file src/bp-members/css/blocks/member-rtl.css

    diff --git src/bp-members/css/blocks/member-rtl.css src/bp-members/css/blocks/member-rtl.css
    new file mode 100644
    index 000000000..ff9de024c
    - +  
     1/* CSS for the bp/member block */
     2.bp-block-member {
     3        position: relative;
     4}
     5
     6.bp-block-member .member-content {
     7        display: flex;
     8}
     9
     10.bp-block-member.has-cover .member-content,
     11.bp-block-member.has-cover .item-header-avatar,
     12.bp-block-member.has-cover .member-description {
     13        z-index: 2;
     14}
     15
     16.bp-block-member.has-cover .member-description {
     17        padding-top: 75px;
     18}
     19
     20.bp-block-member.has-cover .bp-member-cover-image {
     21        background-color: #c5c5c5;
     22        background-position: center top;
     23        background-repeat: no-repeat;
     24        background-size: cover;
     25        border: 0;
     26        display: block;
     27        right: 0;
     28        margin: 0;
     29        padding: 0;
     30        position: absolute;
     31        top: 0;
     32        width: 100%;
     33        z-index: 1;
     34        height: 150px;
     35}
     36
     37.bp-block-member img.avatar {
     38        width: auto;
     39        height: auto;
     40}
     41
     42.bp-block-member.avatar-none .item-header-avatar {
     43        display: none;
     44}
     45
     46.bp-block-member.avatar-none.has-cover {
     47        min-height: 200px;
     48}
     49
     50.bp-block-member.avatar-full {
     51        min-height: 150px;
     52}
     53
     54.bp-block-member.avatar-full.has-cover {
     55        min-height: 300px;
     56}
     57
     58.bp-block-member.avatar-full .item-header-avatar {
     59        width: 180px;
     60}
     61
     62.bp-block-member.has-cover.avatar-full .item-header-avatar {
     63        width: 200px;
     64}
     65
     66.bp-block-member.has-cover.avatar-full img.avatar {
     67        border: solid 2px #fff;
     68        background: rgba(255, 255, 255, 0.8);
     69        margin-right: 20px;
     70}
     71
     72.bp-block-member.has-cover .member-content {
     73        padding-top: 75px;
     74}
     75
     76.bp-block-member.avatar-thumb .member-content {
     77        min-height: 50px;
     78        align-items: center;
     79}
     80
     81.bp-block-member.avatar-thumb .item-header-avatar {
     82        width: 70px;
     83}
     84
     85.bp-block-member.avatar-thumb.has-cover .item-header-avatar {
     86        padding-top: 75px;
     87}
     88
     89.bp-block-member .user-nicename {
     90        display: block;
     91}
     92
     93.bp-block-member .user-nicename a,
     94.entry .entry-content .bp-block-member .user-nicename a {
     95        color: currentColor;
     96        text-decoration: none;
     97        border: none;
     98}
     99
     100.bp-block-member .bp-profile-button {
     101        width: 100%;
     102}
     103
     104.bp-block-member .bp-profile-button a.button {
     105        position: absolute;
     106        bottom: 10px;
     107        left: 0;
     108        display: inline-block;
     109        margin: 18px 0 0;
     110}
  • new file src/bp-members/css/blocks/member.css

    diff --git src/bp-members/css/blocks/member.css src/bp-members/css/blocks/member.css
    new file mode 100644
    index 000000000..b03ea8687
    - +  
     1/* CSS for the bp/member block */
     2.bp-block-member {
     3        position: relative;
     4}
     5
     6.bp-block-member .member-content {
     7        display: flex;
     8}
     9
     10.bp-block-member.has-cover .member-content,
     11.bp-block-member.has-cover .item-header-avatar,
     12.bp-block-member.has-cover .member-description {
     13        z-index: 2;
     14}
     15
     16.bp-block-member.has-cover .member-description {
     17        padding-top: 75px;
     18}
     19
     20.bp-block-member.has-cover .bp-member-cover-image {
     21        background-color: #c5c5c5;
     22        background-position: center top;
     23        background-repeat: no-repeat;
     24        background-size: cover;
     25        border: 0;
     26        display: block;
     27        left: 0;
     28        margin: 0;
     29        padding: 0;
     30        position: absolute;
     31        top: 0;
     32        width: 100%;
     33        z-index: 1;
     34        height: 150px;
     35}
     36
     37.bp-block-member img.avatar {
     38        width: auto;
     39        height: auto;
     40}
     41
     42.bp-block-member.avatar-none .item-header-avatar {
     43        display: none;
     44}
     45
     46.bp-block-member.avatar-none.has-cover {
     47        min-height: 200px;
     48}
     49
     50.bp-block-member.avatar-full {
     51        min-height: 150px;
     52}
     53
     54.bp-block-member.avatar-full.has-cover {
     55        min-height: 300px;
     56}
     57
     58.bp-block-member.avatar-full .item-header-avatar {
     59        width: 180px;
     60}
     61
     62.bp-block-member.has-cover.avatar-full .item-header-avatar {
     63        width: 200px;
     64}
     65
     66.bp-block-member.has-cover.avatar-full img.avatar {
     67        border: solid 2px #fff;
     68        background: rgba(255, 255, 255, 0.8);
     69        margin-left: 20px;
     70}
     71
     72.bp-block-member.has-cover .member-content {
     73        padding-top: 75px;
     74}
     75
     76.bp-block-member.avatar-thumb .member-content {
     77        min-height: 50px;
     78        align-items: center;
     79}
     80
     81.bp-block-member.avatar-thumb .item-header-avatar {
     82        width: 70px;
     83}
     84
     85.bp-block-member.avatar-thumb.has-cover .item-header-avatar {
     86        padding-top: 75px;
     87}
     88
     89.bp-block-member .user-nicename {
     90        display: block;
     91}
     92
     93.bp-block-member .user-nicename a,
     94.entry .entry-content .bp-block-member .user-nicename a {
     95        color: currentColor;
     96        text-decoration: none;
     97        border: none;
     98}
     99
     100.bp-block-member .bp-profile-button {
     101        width: 100%;
     102}
     103
     104.bp-block-member .bp-profile-button a.button {
     105        position: absolute;
     106        bottom: 10px;
     107        right: 0;
     108        display: inline-block;
     109        margin: 18px 0 0;
     110}
  • new file src/bp-members/js/blocks/member.js

    diff --git src/bp-members/js/blocks/member.js src/bp-members/js/blocks/member.js
    new file mode 100644
    index 000000000..2bbb30326
    - +  
     1// modules are defined as an array
     2// [ module function, map of requires ]
     3//
     4// map of requires is short require name -> numeric require
     5//
     6// anything defined in a previous bundle is accessed via the
     7// orig method which is the require for previous bundles
     8parcelRequire = (function (modules, cache, entry, globalName) {
     9  // Save the require from previous bundle to this closure if any
     10  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
     11  var nodeRequire = typeof require === 'function' && require;
     12
     13  function newRequire(name, jumped) {
     14    if (!cache[name]) {
     15      if (!modules[name]) {
     16        // if we cannot find the module within our internal map or
     17        // cache jump to the current global require ie. the last bundle
     18        // that was added to the page.
     19        var currentRequire = typeof parcelRequire === 'function' && parcelRequire;
     20        if (!jumped && currentRequire) {
     21          return currentRequire(name, true);
     22        }
     23
     24        // If there are other bundles on this page the require from the
     25        // previous one is saved to 'previousRequire'. Repeat this as
     26        // many times as there are bundles until the module is found or
     27        // we exhaust the require chain.
     28        if (previousRequire) {
     29          return previousRequire(name, true);
     30        }
     31
     32        // Try the node require function if it exists.
     33        if (nodeRequire && typeof name === 'string') {
     34          return nodeRequire(name);
     35        }
     36
     37        var err = new Error('Cannot find module \'' + name + '\'');
     38        err.code = 'MODULE_NOT_FOUND';
     39        throw err;
     40      }
     41
     42      localRequire.resolve = resolve;
     43      localRequire.cache = {};
     44
     45      var module = cache[name] = new newRequire.Module(name);
     46
     47      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
     48    }
     49
     50    return cache[name].exports;
     51
     52    function localRequire(x){
     53      return newRequire(localRequire.resolve(x));
     54    }
     55
     56    function resolve(x){
     57      return modules[name][1][x] || x;
     58    }
     59  }
     60
     61  function Module(moduleName) {
     62    this.id = moduleName;
     63    this.bundle = newRequire;
     64    this.exports = {};
     65  }
     66
     67  newRequire.isParcelRequire = true;
     68  newRequire.Module = Module;
     69  newRequire.modules = modules;
     70  newRequire.cache = cache;
     71  newRequire.parent = previousRequire;
     72  newRequire.register = function (id, exports) {
     73    modules[id] = [function (require, module) {
     74      module.exports = exports;
     75    }, {}];
     76  };
     77
     78  var error;
     79  for (var i = 0; i < entry.length; i++) {
     80    try {
     81      newRequire(entry[i]);
     82    } catch (e) {
     83      // Save first error but execute all entries
     84      if (!error) {
     85        error = e;
     86      }
     87    }
     88  }
     89
     90  if (entry.length) {
     91    // Expose entry point to Node, AMD or browser globals
     92    // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
     93    var mainExports = newRequire(entry[entry.length - 1]);
     94
     95    // CommonJS
     96    if (typeof exports === "object" && typeof module !== "undefined") {
     97      module.exports = mainExports;
     98
     99    // RequireJS
     100    } else if (typeof define === "function" && define.amd) {
     101     define(function () {
     102       return mainExports;
     103     });
     104
     105    // <script>
     106    } else if (globalName) {
     107      this[globalName] = mainExports;
     108    }
     109  }
     110
     111  // Override the current require with this new one
     112  parcelRequire = newRequire;
     113
     114  if (error) {
     115    // throw error from earlier, _after updating parcelRequire_
     116    throw error;
     117  }
     118
     119  return newRequire;
     120})({"kUj2":[function(require,module,exports) {
     121function _classCallCheck(instance, Constructor) {
     122  if (!(instance instanceof Constructor)) {
     123    throw new TypeError("Cannot call a class as a function");
     124  }
     125}
     126
     127module.exports = _classCallCheck;
     128},{}],"dMjH":[function(require,module,exports) {
     129function _defineProperties(target, props) {
     130  for (var i = 0; i < props.length; i++) {
     131    var descriptor = props[i];
     132    descriptor.enumerable = descriptor.enumerable || false;
     133    descriptor.configurable = true;
     134    if ("value" in descriptor) descriptor.writable = true;
     135    Object.defineProperty(target, descriptor.key, descriptor);
     136  }
     137}
     138
     139function _createClass(Constructor, protoProps, staticProps) {
     140  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
     141  if (staticProps) _defineProperties(Constructor, staticProps);
     142  return Constructor;
     143}
     144
     145module.exports = _createClass;
     146},{}],"FlpK":[function(require,module,exports) {
     147function _typeof(obj) {
     148  "@babel/helpers - typeof";
     149
     150  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
     151    module.exports = _typeof = function _typeof(obj) {
     152      return typeof obj;
     153    };
     154  } else {
     155    module.exports = _typeof = function _typeof(obj) {
     156      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
     157    };
     158  }
     159
     160  return _typeof(obj);
     161}
     162
     163module.exports = _typeof;
     164},{}],"oXBW":[function(require,module,exports) {
     165function _assertThisInitialized(self) {
     166  if (self === void 0) {
     167    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
     168  }
     169
     170  return self;
     171}
     172
     173module.exports = _assertThisInitialized;
     174},{}],"cbGp":[function(require,module,exports) {
     175var _typeof = require("../helpers/typeof");
     176
     177var assertThisInitialized = require("./assertThisInitialized");
     178
     179function _possibleConstructorReturn(self, call) {
     180  if (call && (_typeof(call) === "object" || typeof call === "function")) {
     181    return call;
     182  }
     183
     184  return assertThisInitialized(self);
     185}
     186
     187module.exports = _possibleConstructorReturn;
     188},{"../helpers/typeof":"FlpK","./assertThisInitialized":"oXBW"}],"XApn":[function(require,module,exports) {
     189function _getPrototypeOf(o) {
     190  module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
     191    return o.__proto__ || Object.getPrototypeOf(o);
     192  };
     193  return _getPrototypeOf(o);
     194}
     195
     196module.exports = _getPrototypeOf;
     197},{}],"Omxx":[function(require,module,exports) {
     198function _setPrototypeOf(o, p) {
     199  module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
     200    o.__proto__ = p;
     201    return o;
     202  };
     203
     204  return _setPrototypeOf(o, p);
     205}
     206
     207module.exports = _setPrototypeOf;
     208},{}],"PhTw":[function(require,module,exports) {
     209var setPrototypeOf = require("./setPrototypeOf");
     210
     211function _inherits(subClass, superClass) {
     212  if (typeof superClass !== "function" && superClass !== null) {
     213    throw new TypeError("Super expression must either be null or a function");
     214  }
     215
     216  subClass.prototype = Object.create(superClass && superClass.prototype, {
     217    constructor: {
     218      value: subClass,
     219      writable: true,
     220      configurable: true
     221    }
     222  });
     223  if (superClass) setPrototypeOf(subClass, superClass);
     224}
     225
     226module.exports = _inherits;
     227},{"./setPrototypeOf":"Omxx"}],"xHsb":[function(require,module,exports) {
     228"use strict";
     229
     230Object.defineProperty(exports, "__esModule", {
     231  value: true
     232});
     233exports.default = void 0;
     234
     235var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
     236
     237var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
     238
     239var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
     240
     241var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
     242
     243var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
     244
     245var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
     246
     247function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     248
     249/**
     250 * WordPress dependencies.
     251 */
     252var _wp$element = wp.element,
     253    Component = _wp$element.Component,
     254    Fragment = _wp$element.Fragment,
     255    createElement = _wp$element.createElement;
     256var Popover = wp.components.Popover;
     257var _wp = wp,
     258    apiFetch = _wp.apiFetch;
     259var __ = wp.i18n.__;
     260
     261var BPAutocompleter = /*#__PURE__*/function (_Component) {
     262  (0, _inherits2.default)(BPAutocompleter, _Component);
     263
     264  function BPAutocompleter() {
     265    var _this;
     266
     267    (0, _classCallCheck2.default)(this, BPAutocompleter);
     268    _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(BPAutocompleter).apply(this, arguments));
     269    _this.state = {
     270      search: '',
     271      items: [],
     272      error: ''
     273    };
     274    _this.searchItemName = _this.searchItemName.bind((0, _assertThisInitialized2.default)(_this));
     275    _this.selectItemName = _this.selectItemName.bind((0, _assertThisInitialized2.default)(_this));
     276    return _this;
     277  }
     278
     279  (0, _createClass2.default)(BPAutocompleter, [{
     280    key: "searchItemName",
     281    value: function searchItemName(value) {
     282      var _this2 = this;
     283
     284      var search = this.state.search;
     285      var _this$props = this.props,
     286          component = _this$props.component,
     287          objectStatus = _this$props.objectStatus;
     288      this.setState({
     289        search: value
     290      });
     291
     292      if (value.length < search.length) {
     293        this.setState({
     294          items: []
     295        });
     296      }
     297
     298      var path = '/buddypress/v1/' + component;
     299
     300      if (value) {
     301        path += '?search=' + encodeURIComponent(value);
     302      }
     303
     304      if (objectStatus) {
     305        path += '&status=' + objectStatus;
     306      }
     307
     308      apiFetch({
     309        path: path
     310      }).then(function (items) {
     311        _this2.setState({
     312          items: items
     313        });
     314      }, function (error) {
     315        _this2.setState({
     316          error: error.message
     317        });
     318      });
     319    }
     320  }, {
     321    key: "selectItemName",
     322    value: function selectItemName(event, itemID) {
     323      var onSelectItem = this.props.onSelectItem;
     324      event.preventDefault();
     325      this.setState({
     326        search: '',
     327        items: [],
     328        error: ''
     329      });
     330      return onSelectItem({
     331        itemID: itemID
     332      });
     333    }
     334  }, {
     335    key: "render",
     336    value: function render() {
     337      var _this3 = this;
     338
     339      var _this$state = this.state,
     340          search = _this$state.search,
     341          items = _this$state.items;
     342      var _this$props2 = this.props,
     343          ariaLabel = _this$props2.ariaLabel,
     344          placeholder = _this$props2.placeholder,
     345          useAvatar = _this$props2.useAvatar;
     346      var itemsList;
     347
     348      if (!ariaLabel) {
     349        ariaLabel = __('Item\'s name', 'buddypress');
     350      }
     351
     352      if (!placeholder) {
     353        placeholder = __('Enter Item\'s name here…', 'buddypress');
     354      }
     355
     356      if (items.length) {
     357        itemsList = items.map(function (item) {
     358          return createElement("button", {
     359            type: "button",
     360            key: 'editor-autocompleters__item-item-' + item.id,
     361            role: "option",
     362            "aria-selected": "true",
     363            className: "components-button components-autocomplete__result editor-autocompleters__user",
     364            onClick: function onClick(event) {
     365              return _this3.selectItemName(event, item.id);
     366            }
     367          }, useAvatar && createElement("img", {
     368            key: "avatar",
     369            className: "editor-autocompleters__user-avatar",
     370            alt: "",
     371            src: item.avatar_urls.thumb
     372          }), createElement("span", {
     373            key: "name",
     374            className: "editor-autocompleters__user-name"
     375          }, item.name), item.mention_name && createElement("span", {
     376            key: "slug",
     377            className: "editor-autocompleters__user-slug"
     378          }, item.mention_name));
     379        });
     380      }
     381
     382      return createElement(Fragment, null, createElement("input", {
     383        type: "text",
     384        value: search,
     385        className: "components-placeholder__input",
     386        "aria-label": ariaLabel,
     387        placeholder: placeholder,
     388        onChange: function onChange(event) {
     389          return _this3.searchItemName(event.target.value);
     390        }
     391      }), 0 !== items.length && createElement(Popover, {
     392        className: "components-autocomplete__popover",
     393        focusOnMount: false,
     394        position: "bottom left"
     395      }, createElement("div", {
     396        className: "components-autocomplete__results"
     397      }, itemsList)));
     398    }
     399  }]);
     400  return BPAutocompleter;
     401}(Component);
     402
     403var _default = BPAutocompleter;
     404exports.default = _default;
     405},{"@babel/runtime/helpers/classCallCheck":"kUj2","@babel/runtime/helpers/createClass":"dMjH","@babel/runtime/helpers/possibleConstructorReturn":"cbGp","@babel/runtime/helpers/getPrototypeOf":"XApn","@babel/runtime/helpers/assertThisInitialized":"oXBW","@babel/runtime/helpers/inherits":"PhTw"}],"TmUL":[function(require,module,exports) {
     406"use strict";
     407
     408var _bpAutocompleter = _interopRequireDefault(require("../../../bp-core/js/blocks/bp-autocompleter"));
     409
     410function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
     411
     412/**
     413 * WordPress dependencies.
     414 */
     415var registerBlockType = wp.blocks.registerBlockType;
     416var _wp$element = wp.element,
     417    createElement = _wp$element.createElement,
     418    Fragment = _wp$element.Fragment;
     419var _wp$components = wp.components,
     420    Placeholder = _wp$components.Placeholder,
     421    Disabled = _wp$components.Disabled,
     422    PanelBody = _wp$components.PanelBody,
     423    SelectControl = _wp$components.SelectControl,
     424    ToggleControl = _wp$components.ToggleControl,
     425    Toolbar = _wp$components.Toolbar,
     426    ToolbarButton = _wp$components.ToolbarButton;
     427var _wp$blockEditor = wp.blockEditor,
     428    InspectorControls = _wp$blockEditor.InspectorControls,
     429    BlockControls = _wp$blockEditor.BlockControls;
     430var withSelect = wp.data.withSelect;
     431var compose = wp.compose.compose;
     432var ServerSideRender = wp.editor.ServerSideRender;
     433var __ = wp.i18n.__;
     434/**
     435 * Internal dependencies.
     436 */
     437
     438var AVATAR_SIZES = [{
     439  label: __('None', 'buddypress'),
     440  value: 'none'
     441}, {
     442  label: __('Thumb', 'buddypress'),
     443  value: 'thumb'
     444}, {
     445  label: __('Full', 'buddypress'),
     446  value: 'full'
     447}];
     448
     449var editMember = function editMember(_ref) {
     450  var attributes = _ref.attributes,
     451      setAttributes = _ref.setAttributes,
     452      bpSettings = _ref.bpSettings;
     453  var isAvatarEnabled = bpSettings.isAvatarEnabled,
     454      isMentionEnabled = bpSettings.isMentionEnabled,
     455      isCoverImageEnabled = bpSettings.isCoverImageEnabled;
     456  var avatarSize = attributes.avatarSize,
     457      displayMentionSlug = attributes.displayMentionSlug,
     458      displayActionButton = attributes.displayActionButton,
     459      displayCoverImage = attributes.displayCoverImage;
     460
     461  if (!attributes.itemID) {
     462    return createElement(Placeholder, {
     463      icon: "admin-users",
     464      label: __('BuddyPress Member', 'buddypress'),
     465      instructions: __('Start typing the name of the member you want to feature into this post.', 'buddypress')
     466    }, createElement(_bpAutocompleter.default, {
     467      component: "members",
     468      ariaLabel: __('Member\'s username', 'buddypress'),
     469      placeholder: __('Enter Member\'s username here…', 'buddypress'),
     470      onSelectItem: setAttributes,
     471      useAvatar: isAvatarEnabled
     472    }));
     473  }
     474
     475  return createElement(Fragment, null, createElement(BlockControls, null, createElement(Toolbar, null, createElement(ToolbarButton, {
     476    icon: "edit",
     477    title: __('Select another member', 'buddypress'),
     478    onClick: function onClick() {
     479      setAttributes({
     480        itemID: 0
     481      });
     482    }
     483  }))), createElement(InspectorControls, null, createElement(PanelBody, {
     484    title: __('Profile button settings', 'buddypress'),
     485    initialOpen: true
     486  }, createElement(ToggleControl, {
     487    label: __('Display Profile button', 'buddypress'),
     488    checked: !!displayActionButton,
     489    onChange: function onChange() {
     490      setAttributes({
     491        displayActionButton: !displayActionButton
     492      });
     493    },
     494    help: displayActionButton ? __('Include a link to the user\'s profile page under their display name.', 'buddypress') : __('Toggle to display a link to the user\'s profile page under their display name.', 'buddypress')
     495  })), isAvatarEnabled && createElement(PanelBody, {
     496    title: __('Avatar settings', 'buddypress'),
     497    initialOpen: false
     498  }, createElement(SelectControl, {
     499    label: __('Size', 'buddypress'),
     500    value: avatarSize,
     501    options: AVATAR_SIZES,
     502    onChange: function onChange(option) {
     503      setAttributes({
     504        avatarSize: option
     505      });
     506    }
     507  })), isCoverImageEnabled && createElement(PanelBody, {
     508    title: __('Cover image settings', 'buddypress'),
     509    initialOpen: false
     510  }, createElement(ToggleControl, {
     511    label: __('Display Cover Image', 'buddypress'),
     512    checked: !!displayCoverImage,
     513    onChange: function onChange() {
     514      setAttributes({
     515        displayCoverImage: !displayCoverImage
     516      });
     517    },
     518    help: displayCoverImage ? __('Include the user\'s cover image over their display name.', 'buddypress') : __('Toggle to display the user\'s cover image over their display name.', 'buddypress')
     519  })), isMentionEnabled && createElement(PanelBody, {
     520    title: __('Mention settings', 'buddypress'),
     521    initialOpen: false
     522  }, createElement(ToggleControl, {
     523    label: __('Display Mention slug', 'buddypress'),
     524    checked: !!displayMentionSlug,
     525    onChange: function onChange() {
     526      setAttributes({
     527        displayMentionSlug: !displayMentionSlug
     528      });
     529    },
     530    help: displayMentionSlug ? __('Include the user\'s mention name under their display name.', 'buddypress') : __('Toggle to display the user\'s mention name under their display name.', 'buddypress')
     531  }))), createElement(Disabled, null, createElement(ServerSideRender, {
     532    block: "bp/member",
     533    attributes: attributes
     534  })));
     535};
     536
     537var editMemberBlock = compose([withSelect(function (select) {
     538  var editorSettings = select('core/editor').getEditorSettings();
     539  return {
     540    bpSettings: editorSettings.bp.members || {}
     541  };
     542})])(editMember);
     543registerBlockType('bp/member', {
     544  title: __('Member', 'buddypress'),
     545  description: __('BuddyPress Member.', 'buddypress'),
     546  icon: 'admin-users',
     547  category: 'buddypress',
     548  attributes: {
     549    itemID: {
     550      type: 'integer',
     551      default: 0
     552    },
     553    avatarSize: {
     554      type: 'string',
     555      default: 'full'
     556    },
     557    displayMentionSlug: {
     558      type: 'boolean',
     559      default: true
     560    },
     561    displayActionButton: {
     562      type: 'boolean',
     563      default: true
     564    },
     565    displayCoverImage: {
     566      type: 'boolean',
     567      default: true
     568    }
     569  },
     570  edit: editMemberBlock
     571});
     572},{"../../../bp-core/js/blocks/bp-autocompleter":"xHsb"}]},{},["TmUL"], null)
     573 No newline at end of file
  • src/class-buddypress.php

    diff --git src/class-buddypress.php src/class-buddypress.php
    index a5d449ebd..e4e559eae 100644
    class BuddyPress { 
    488488                require( $this->plugin_dir . 'bp-core/bp-core-loader.php'           );
    489489                require( $this->plugin_dir . 'bp-core/bp-core-customizer-email.php' );
    490490                require( $this->plugin_dir . 'bp-core/bp-core-rest-api.php'         );
     491                require( $this->plugin_dir . 'bp-core/bp-core-blocks.php'           );
    491492
    492493                // Maybe load deprecated functionality (this double negative is proof positive!).
    493494                if ( ! bp_get_option( '_bp_ignore_deprecated_code', ! $this->load_deprecated ) ) {
    class BuddyPress { 
    554555                        'BP_Attachment_Cover_Image'                  => 'core',
    555556                        'BP_Attachment'                              => 'core',
    556557                        'BP_Button'                                  => 'core',
     558                        'BP_Block'                                   => 'core',
    557559                        'BP_Component'                               => 'core',
    558560                        'BP_Customizer_Control_Range'                => 'core',
    559561                        'BP_Date_Query'                              => 'core',
  • new file src/js/bp-core/js/blocks/.babelrc

    diff --git src/js/bp-core/js/blocks/.babelrc src/js/bp-core/js/blocks/.babelrc
    new file mode 100644
    index 000000000..ab258a69c
    - +  
     1{
     2        "presets": ["@wordpress/default"]
     3}
  • new file src/js/bp-core/js/blocks/bp-autocompleter.js

    diff --git src/js/bp-core/js/blocks/bp-autocompleter.js src/js/bp-core/js/blocks/bp-autocompleter.js
    new file mode 100644
    index 000000000..6c8ca3d04
    - +  
     1/**
     2 * WordPress dependencies.
     3 */
     4const { Component, Fragment, createElement } = wp.element;
     5const { Popover } = wp.components;
     6const { apiFetch } = wp;
     7const { __ } = wp.i18n;
     8
     9class BPAutocompleter extends Component {
     10        constructor() {
     11                super( ...arguments );
     12
     13                this.state = {
     14                        search: '',
     15                        items: [],
     16                        error: '',
     17                };
     18
     19                this.searchItemName = this.searchItemName.bind( this );
     20                this.selectItemName = this.selectItemName.bind( this );
     21        }
     22
     23        searchItemName( value ) {
     24                const { search } = this.state;
     25                const { component, objectStatus } = this.props;
     26                this.setState( { search: value } );
     27
     28                if ( value.length < search.length ) {
     29                        this.setState( { items: [] } );
     30                }
     31
     32                let path= '/buddypress/v1/' + component;
     33
     34                if ( value ) {
     35                        path += '?search=' + encodeURIComponent( value );
     36                }
     37
     38                if ( objectStatus ) {
     39                        path += '&status=' + objectStatus;
     40                }
     41
     42                apiFetch( { path:  path } ).then( items => {
     43                        this.setState( { items: items } );
     44                }, error => {
     45                        this.setState( { error: error.message } );
     46                } );
     47        }
     48
     49        selectItemName( event, itemID ) {
     50                const { onSelectItem } = this.props;
     51                event.preventDefault();
     52
     53                this.setState( {
     54                        search: '',
     55                        items: [],
     56                        error: '',
     57                } );
     58
     59                return onSelectItem( { itemID: itemID } );
     60        }
     61
     62        render() {
     63                const { search, items } = this.state;
     64                let { ariaLabel, placeholder, useAvatar } = this.props;
     65                let itemsList;
     66
     67                if ( ! ariaLabel ) {
     68                        ariaLabel = __( 'Item\'s name', 'buddypress' );
     69                }
     70
     71                if ( ! placeholder ) {
     72                        placeholder = __( 'Enter Item\'s name here…', 'buddypress' );
     73                }
     74
     75                if ( items.length ) {
     76                        itemsList = items.map( ( item ) => {
     77                                return (
     78                                        <button
     79                                                type="button" key={ 'editor-autocompleters__item-item-' + item.id }
     80                                                role="option"
     81                                                aria-selected="true"
     82                                                className="components-button components-autocomplete__result editor-autocompleters__user"
     83                                                onClick={ ( event ) => this.selectItemName( event, item.id ) }
     84                                        >
     85                                                { useAvatar && (
     86                                                        <img key="avatar" className="editor-autocompleters__user-avatar" alt="" src={ item.avatar_urls.thumb } />
     87                                                ) }
     88                                                <span key="name" className="editor-autocompleters__user-name">{ item.name }</span>
     89
     90                                                { item.mention_name && (
     91                                                        <span key="slug" className="editor-autocompleters__user-slug">{ item.mention_name }</span>
     92                                                ) }
     93                                        </button>
     94                                );
     95                        } );
     96                }
     97
     98                return (
     99                        <Fragment>
     100                                <input
     101                                        type="text"
     102                                        value={ search }
     103                                        className="components-placeholder__input"
     104                                        aria-label={ ariaLabel }
     105                                        placeholder={ placeholder }
     106                                        onChange={ ( event ) => this.searchItemName( event.target.value ) }
     107                                />
     108                                { 0 !== items.length &&
     109                                        <Popover
     110                                                className="components-autocomplete__popover"
     111                                                focusOnMount={ false }
     112                                                position="bottom left"
     113                                        >
     114                                                <div className="components-autocomplete__results">
     115                                                        { itemsList }
     116                                                </div>
     117                                        </Popover>
     118                                }
     119                        </Fragment>
     120                );
     121        }
     122}
     123
     124export default BPAutocompleter;
  • new file src/js/bp-groups/js/blocks/.babelrc

    diff --git src/js/bp-groups/js/blocks/.babelrc src/js/bp-groups/js/blocks/.babelrc
    new file mode 100644
    index 000000000..ab258a69c
    - +  
     1{
     2        "presets": ["@wordpress/default"]
     3}
  • new file src/js/bp-groups/js/blocks/group.js

    diff --git src/js/bp-groups/js/blocks/group.js src/js/bp-groups/js/blocks/group.js
    new file mode 100644
    index 000000000..5fa40674e
    - +  
     1/**
     2 * WordPress dependencies.
     3 */
     4const { registerBlockType } = wp.blocks;
     5const { createElement, Fragment } = wp.element;
     6const { Placeholder, Disabled, PanelBody, SelectControl, ToggleControl, Toolbar, ToolbarButton } = wp.components;
     7const { InspectorControls, BlockControls } = wp.blockEditor;
     8const { withSelect } = wp.data;
     9const { compose } = wp.compose;
     10const { ServerSideRender } = wp.editor;
     11const { __ } = wp.i18n;
     12
     13/**
     14 * Internal dependencies.
     15 */
     16import BPAutocompleter from '../../../bp-core/js/blocks/bp-autocompleter';
     17
     18const AVATAR_SIZES = [
     19        {
     20                label: __( 'None', 'buddypress' ),
     21                value: 'none',
     22        },
     23        {
     24                label: __( 'Thumb', 'buddypress' ),
     25                value: 'thumb',
     26        },
     27        {
     28                label: __( 'Full', 'buddypress' ),
     29                value: 'full',
     30        },
     31];
     32
     33const editGroup = ( { attributes, setAttributes, bpSettings } ) => {
     34        const { isAvatarEnabled, isCoverImageEnabled } = bpSettings;
     35        const { avatarSize, displayDescription, displayActionButton, displayCoverImage } = attributes;
     36
     37        if ( ! attributes.itemID ) {
     38                return (
     39                        <Placeholder
     40                                icon="buddicons-groups"
     41                                label={ __( 'BuddyPress Group', 'buddypress' ) }
     42                                instructions={ __( 'Start typing the name of the group you want to feature into this post.', 'buddypress' ) }
     43                        >
     44                                <BPAutocompleter
     45                                        component="groups"
     46                                        objectStatus="public"
     47                                        ariaLabel={ __( 'Group\'s name', 'buddypress' ) }
     48                                        placeholder={ __( 'Enter Group\'s name here…', 'buddypress' ) }
     49                                        onSelectItem={ setAttributes }
     50                                        useAvatar={ isAvatarEnabled }
     51                                />
     52                        </Placeholder>
     53                );
     54        }
     55
     56        return (
     57                <Fragment>
     58                        <BlockControls>
     59                                <Toolbar>
     60                                        <ToolbarButton
     61                                                icon="edit"
     62                                                title={ __( 'Select another group', 'buddypress' ) }
     63                                                onClick={ () =>{
     64                                                        setAttributes( { itemID: 0 } );
     65                                                } }
     66                                        />
     67                                </Toolbar>
     68                        </BlockControls>
     69                        <InspectorControls>
     70                                <PanelBody title={ __( 'Group\'s home button settings', 'buddypress' ) } initialOpen={ true }>
     71                                        <ToggleControl
     72                                                label={ __( 'Display Group\'s home button', 'buddypress' ) }
     73                                                checked={ !! displayActionButton }
     74                                                onChange={ () => {
     75                                                        setAttributes( { displayActionButton: ! displayActionButton } );
     76                                                } }
     77                                                help={
     78                                                        displayActionButton
     79                                                                ? __( 'Include a link to the group\'s home page under their name.', 'buddypress' )
     80                                                                : __( 'Toggle to display a link to the group\'s home page under their name.', 'buddypress' )
     81                                                }
     82                                        />
     83                                </PanelBody>
     84                                <PanelBody title={ __( 'Description settings', 'buddypress' ) } initialOpen={ false }>
     85                                        <ToggleControl
     86                                                label={ __( 'Display group\'s description', 'buddypress' ) }
     87                                                checked={ !! displayDescription }
     88                                                onChange={ () => {
     89                                                        setAttributes( { displayDescription: ! displayDescription } );
     90                                                } }
     91                                                help={
     92                                                        displayDescription
     93                                                                ? __( 'Include the group\'s description under their name.', 'buddypress' )
     94                                                                : __( 'Toggle to display the group\'s description under their name.', 'buddypress' )
     95                                                }
     96                                        />
     97                                </PanelBody>
     98                                { isAvatarEnabled && (
     99                                        <PanelBody title={ __( 'Avatar settings', 'buddypress' ) } initialOpen={ false }>
     100                                                <SelectControl
     101                                                        label={ __( 'Size', 'buddypress' ) }
     102                                                        value={ avatarSize }
     103                                                        options={ AVATAR_SIZES }
     104                                                        onChange={ ( option ) => {
     105                                                                setAttributes( { avatarSize: option } );
     106                                                        } }
     107                                                />
     108                                        </PanelBody>
     109                                ) }
     110                                { isCoverImageEnabled && (
     111                                        <PanelBody title={ __( 'Cover image settings', 'buddypress' ) } initialOpen={ false }>
     112                                                <ToggleControl
     113                                                        label={ __( 'Display Cover Image', 'buddypress' ) }
     114                                                        checked={ !! displayCoverImage }
     115                                                        onChange={ () => {
     116                                                                setAttributes( { displayCoverImage: ! displayCoverImage } );
     117                                                        } }
     118                                                        help={
     119                                                                displayCoverImage
     120                                                                        ? __( 'Include the group\'s cover image over their name.', 'buddypress' )
     121                                                                        : __( 'Toggle to display the group\'s cover image over their name.', 'buddypress' )
     122                                                        }
     123                                                />
     124                                        </PanelBody>
     125                                ) }
     126                        </InspectorControls>
     127                        <Disabled>
     128                                <ServerSideRender block="bp/group" attributes={ attributes } />
     129                        </Disabled>
     130                </Fragment>
     131        );
     132};
     133
     134const editGroupBlock = compose( [
     135        withSelect( ( select ) => {
     136                const editorSettings = select( 'core/editor' ).getEditorSettings();
     137                return {
     138                        bpSettings: editorSettings.bp.groups || {},
     139                };
     140        } ),
     141] )( editGroup );
     142
     143registerBlockType( 'bp/group', {
     144        title: __( 'Group', 'buddypress' ),
     145
     146        description: __( 'BuddyPress Group.', 'buddypress' ),
     147
     148        icon: 'buddicons-groups',
     149
     150        category: 'buddypress',
     151
     152        attributes: {
     153                itemID: {
     154                        type: 'integer',
     155                        default: 0,
     156                },
     157                avatarSize: {
     158                        type: 'string',
     159                        default: 'full',
     160                },
     161                displayDescription: {
     162                        type: 'boolean',
     163                        default: true,
     164                },
     165                displayActionButton: {
     166                        type: 'boolean',
     167                        default: true,
     168                },
     169                displayCoverImage: {
     170                        type: 'boolean',
     171                        default: true,
     172                },
     173        },
     174
     175        edit: editGroupBlock,
     176} );
  • new file src/js/bp-members/js/blocks/.babelrc

    diff --git src/js/bp-members/js/blocks/.babelrc src/js/bp-members/js/blocks/.babelrc
    new file mode 100644
    index 000000000..ab258a69c
    - +  
     1{
     2        "presets": ["@wordpress/default"]
     3}
  • new file src/js/bp-members/js/blocks/member.js

    diff --git src/js/bp-members/js/blocks/member.js src/js/bp-members/js/blocks/member.js
    new file mode 100644
    index 000000000..179a61dbb
    - +  
     1/**
     2 * WordPress dependencies.
     3 */
     4const { registerBlockType } = wp.blocks;
     5const { createElement, Fragment } = wp.element;
     6const { Placeholder, Disabled, PanelBody, SelectControl, ToggleControl, Toolbar, ToolbarButton } = wp.components;
     7const { InspectorControls, BlockControls } = wp.blockEditor;
     8const { withSelect } = wp.data;
     9const { compose } = wp.compose;
     10const { ServerSideRender } = wp.editor;
     11const { __ } = wp.i18n;
     12
     13/**
     14 * Internal dependencies.
     15 */
     16import BPAutocompleter from '../../../bp-core/js/blocks/bp-autocompleter';
     17
     18const AVATAR_SIZES = [
     19        {
     20                label: __( 'None', 'buddypress' ),
     21                value: 'none',
     22        },
     23        {
     24                label: __( 'Thumb', 'buddypress' ),
     25                value: 'thumb',
     26        },
     27        {
     28                label: __( 'Full', 'buddypress' ),
     29                value: 'full',
     30        },
     31];
     32
     33const editMember = ( { attributes, setAttributes, bpSettings } ) => {
     34        const { isAvatarEnabled, isMentionEnabled, isCoverImageEnabled } = bpSettings;
     35        const { avatarSize, displayMentionSlug, displayActionButton, displayCoverImage } = attributes;
     36
     37        if ( ! attributes.itemID ) {
     38                return (
     39                        <Placeholder
     40                                icon="admin-users"
     41                                label={ __( 'BuddyPress Member', 'buddypress' ) }
     42                                instructions={ __( 'Start typing the name of the member you want to feature into this post.', 'buddypress' ) }
     43                        >
     44                                <BPAutocompleter
     45                                        component="members"
     46                                        ariaLabel={ __( 'Member\'s username', 'buddypress' ) }
     47                                        placeholder={ __( 'Enter Member\'s username here…', 'buddypress' ) }
     48                                        onSelectItem={ setAttributes }
     49                                        useAvatar={ isAvatarEnabled }
     50                                />
     51                        </Placeholder>
     52                );
     53        }
     54
     55        return (
     56                <Fragment>
     57                        <BlockControls>
     58                                <Toolbar>
     59                                        <ToolbarButton
     60                                                icon="edit"
     61                                                title={ __( 'Select another member', 'buddypress' ) }
     62                                                onClick={ () =>{
     63                                                        setAttributes( { itemID: 0 } );
     64                                                } }
     65                                        />
     66                                </Toolbar>
     67                        </BlockControls>
     68                        <InspectorControls>
     69                                <PanelBody title={ __( 'Profile button settings', 'buddypress' ) } initialOpen={ true }>
     70                                        <ToggleControl
     71                                                label={ __( 'Display Profile button', 'buddypress' ) }
     72                                                checked={ !! displayActionButton }
     73                                                onChange={ () => {
     74                                                        setAttributes( { displayActionButton: ! displayActionButton } );
     75                                                } }
     76                                                help={
     77                                                        displayActionButton
     78                                                                ? __( 'Include a link to the user\'s profile page under their display name.', 'buddypress' )
     79                                                                : __( 'Toggle to display a link to the user\'s profile page under their display name.', 'buddypress' )
     80                                                }
     81                                        />
     82                                </PanelBody>
     83                                { isAvatarEnabled && (
     84                                        <PanelBody title={ __( 'Avatar settings', 'buddypress' ) } initialOpen={ false }>
     85                                                <SelectControl
     86                                                        label={ __( 'Size', 'buddypress' ) }
     87                                                        value={ avatarSize }
     88                                                        options={ AVATAR_SIZES }
     89                                                        onChange={ ( option ) => {
     90                                                                setAttributes( { avatarSize: option } );
     91                                                        } }
     92                                                />
     93                                        </PanelBody>
     94                                ) }
     95                                { isCoverImageEnabled && (
     96                                        <PanelBody title={ __( 'Cover image settings', 'buddypress' ) } initialOpen={ false }>
     97                                                <ToggleControl
     98                                                        label={ __( 'Display Cover Image', 'buddypress' ) }
     99                                                        checked={ !! displayCoverImage }
     100                                                        onChange={ () => {
     101                                                                setAttributes( { displayCoverImage: ! displayCoverImage } );
     102                                                        } }
     103                                                        help={
     104                                                                displayCoverImage
     105                                                                        ? __( 'Include the user\'s cover image over their display name.', 'buddypress' )
     106                                                                        : __( 'Toggle to display the user\'s cover image over their display name.', 'buddypress' )
     107                                                        }
     108                                                />
     109                                        </PanelBody>
     110                                ) }
     111                                { isMentionEnabled && (
     112                                        <PanelBody title={ __( 'Mention settings', 'buddypress' ) } initialOpen={ false }>
     113                                                <ToggleControl
     114                                                        label={ __( 'Display Mention slug', 'buddypress' ) }
     115                                                        checked={ !! displayMentionSlug }
     116                                                        onChange={ () => {
     117                                                                setAttributes( { displayMentionSlug: ! displayMentionSlug } );
     118                                                        } }
     119                                                        help={
     120                                                                displayMentionSlug
     121                                                                        ? __( 'Include the user\'s mention name under their display name.', 'buddypress' )
     122                                                                        : __( 'Toggle to display the user\'s mention name under their display name.', 'buddypress' )
     123                                                        }
     124                                                />
     125                                        </PanelBody>
     126                                ) }
     127                        </InspectorControls>
     128                        <Disabled>
     129                                <ServerSideRender block="bp/member" attributes={ attributes } />
     130                        </Disabled>
     131                </Fragment>
     132        );
     133};
     134
     135const editMemberBlock = compose( [
     136        withSelect( ( select ) => {
     137                const editorSettings = select( 'core/editor' ).getEditorSettings();
     138                return {
     139                        bpSettings: editorSettings.bp.members || {},
     140                };
     141        } ),
     142] )( editMember );
     143
     144registerBlockType( 'bp/member', {
     145        title: __( 'Member', 'buddypress' ),
     146
     147        description: __( 'BuddyPress Member.', 'buddypress' ),
     148
     149        icon: 'admin-users',
     150
     151        category: 'buddypress',
     152
     153        attributes: {
     154                itemID: {
     155                        type: 'integer',
     156                        default: 0,
     157                },
     158                avatarSize: {
     159                        type: 'string',
     160                        default: 'full',
     161                },
     162                displayMentionSlug: {
     163                        type: 'boolean',
     164                        default: true,
     165                },
     166                displayActionButton: {
     167                        type: 'boolean',
     168                        default: true,
     169                },
     170                displayCoverImage: {
     171                        type: 'boolean',
     172                        default: true,
     173                },
     174        },
     175
     176        edit: editMemberBlock,
     177} );