diff --git a/resources/views/table/wp-users/books-column.blade.php b/resources/views/table/wp-users/books-column.blade.php
new file mode 100644
index 00000000..c8567e79
--- /dev/null
+++ b/resources/views/table/wp-users/books-column.blade.php
@@ -0,0 +1,16 @@
+@foreach ($books as $book)
+
+
+ {{ $book->domain . $book->path }}
+
+
+
+ {{ __('Dashboard', 'pressbooks-multi-institution') }} |
+
+
+ {{ __('View', 'pressbooks-multi-institution') }}
+
+
+
+
+@endforeach
diff --git a/src/Actions/AssignUserToInstitution.php b/src/Actions/AssignUserToInstitution.php
index a44e55bc..1f28d8b7 100644
--- a/src/Actions/AssignUserToInstitution.php
+++ b/src/Actions/AssignUserToInstitution.php
@@ -4,6 +4,9 @@
use Illuminate\Support\Str;
use PressbooksMultiInstitution\Models\Institution;
+use WP_User;
+
+use function PressbooksMultiInstitution\Support\get_institution_by_manager;
class AssignUserToInstitution
{
@@ -15,6 +18,14 @@ public function handle(int $userId): bool
return false;
}
+ $institution = get_institution_by_manager();
+
+ return $institution === 0 ?
+ $this->assignByUserDomain($user) : $this->assignUserByInstitution($user, $institution);
+ }
+
+ private function assignByUserDomain(WP_User $user): bool
+ {
$email = Str::of($user->user_email);
$domain = (string) $email->after('@')->trim();
@@ -27,7 +38,18 @@ public function handle(int $userId): bool
}
$institution->users()->create([
- 'user_id' => $userId,
+ 'user_id' => $user->ID,
+ ]);
+
+ return true;
+ }
+
+ private function assignUserByInstitution(WP_User $user, int $institution): bool
+ {
+ $institution = Institution::find($institution);
+
+ $institution->users()->create([
+ 'user_id' => $user->ID,
]);
return true;
diff --git a/src/Bootstrap.php b/src/Bootstrap.php
index ab659da5..41d00c54 100644
--- a/src/Bootstrap.php
+++ b/src/Bootstrap.php
@@ -11,6 +11,7 @@
use PressbooksMultiInstitution\Services\MenuManager;
use PressbooksMultiInstitution\Services\PermissionsManager;
use PressbooksMultiInstitution\Views\BookList;
+use PressbooksMultiInstitution\Views\WpUserList;
use PressbooksMultiInstitution\Views\UserList;
/**
@@ -40,6 +41,7 @@ public function setUp(): void
Container::getInstance()->singleton(BookList::class, fn () => new BookList(app('db')));
Container::getInstance()->singleton(UserList::class, fn () => new UserList(app('db')));
+ Container::getInstance()->singleton(WpUserList::class, fn () => new WpUserList);
}
private function registerActions(): void
diff --git a/src/Services/MenuManager.php b/src/Services/MenuManager.php
index 56b94b88..1118a880 100644
--- a/src/Services/MenuManager.php
+++ b/src/Services/MenuManager.php
@@ -86,21 +86,35 @@ public function handleMenus(): void
if (!is_main_site() || !is_super_admin()) {
unset($submenu['index.php'][0]);
}
+
if (get_institution_by_manager() !== 0) {
- remove_menu_page($this->getContextSlug('customize.php', true));
- remove_menu_page($this->getContextSlug('edit.php?post_type=page', true));
- // Remove the default dashboard page and point to the institutional dashboard
- foreach ($menu as &$item) {
- if ($item[2] == network_admin_url('index.php')) {
- $item[2] = network_site_url('wp-admin/index.php?page=pb_institutional_manager');
- break;
- }
- }
- remove_menu_page($this->getContextSlug('admin.php?page=pb_network_integrations', false));
- remove_menu_page('settings.php');
- remove_menu_page('pb_network_integrations');
- remove_menu_page($this->slug);
+ $this->removeMenuItems($menu);
+
add_action('admin_bar_menu', [$this, 'modifyAdminBarMenus'], 1000);
+
+ if (count($submenu['users.php']) > 1) {
+ // remove submenu items for users menu
+ $submenu['users.php'] = array_slice($submenu['users.php'], 0, 1);
+ }
+ }
+ }
+
+ private function removeMenuItems(array &$menu): void
+ {
+ remove_menu_page($this->getContextSlug('customize.php', true));
+ remove_menu_page($this->getContextSlug('edit.php?post_type=page', true));
+ remove_menu_page($this->getContextSlug('admin.php?page=pb_network_integrations', false));
+ remove_menu_page('settings.php');
+ remove_menu_page('pb_network_integrations');
+ remove_menu_page($this->slug);
+ remove_menu_page('h5p');
+
+ // Remove the default dashboard page and point to the institutional dashboard
+ foreach ($menu as &$item) {
+ if ($item[2] == network_admin_url('index.php')) {
+ $item[2] = network_site_url('wp-admin/index.php?page=pb_institutional_manager');
+ break;
+ }
}
}
diff --git a/src/Services/PermissionsManager.php b/src/Services/PermissionsManager.php
index 2b429e1a..083ed5d4 100644
--- a/src/Services/PermissionsManager.php
+++ b/src/Services/PermissionsManager.php
@@ -9,8 +9,10 @@
use PressbooksMultiInstitution\Views\BookList;
use PressbooksMultiInstitution\Views\UserList;
+use PressbooksMultiInstitution\Views\WpUserList;
+
use function Pressbooks\Admin\NetworkManagers\_restricted_users;
-use function PressbooksMultiInstitution\Support\get_allowed_book_pages;
+use function PressbooksMultiInstitution\Support\get_restricted_book_pages;
use function PressbooksMultiInstitution\Support\get_allowed_pages;
use function PressbooksMultiInstitution\Support\get_institution_books;
use function PressbooksMultiInstitution\Support\get_institution_by_manager;
@@ -39,6 +41,7 @@ public function setupFilters(): void
Container::get(TableViews::class)->init();
Container::get(BookList::class)->init();
Container::get(UserList::class)->init();
+ Container::get(WpUserList::class)->init();
do_action('pb_institutional_filters_created', $institution, $institutionalManagers, $institutionalUsers);
}
@@ -65,7 +68,7 @@ public function handlePagesPermissions($institution, $institutionalManagers, $in
*/
add_filter('can_edit_network', function ($canEdit) use ($allowedBooks) {
- if (is_network_admin() && !in_array($_REQUEST['id'], $allowedBooks)) {
+ if (is_network_admin() && isset($_REQUEST['id']) && !in_array($_REQUEST['id'], $allowedBooks)) {
$canEdit = false;
}
return $canEdit;
@@ -157,7 +160,7 @@ private function currentUserHasAccess(string $currentPageParam, array $allowedBo
global $pagenow;
$allowedPages = get_allowed_pages();
- $bookPages = get_allowed_book_pages();
+ $restrictedBookPages = get_restricted_book_pages();
$isAccessAllowed = false;
@@ -181,18 +184,21 @@ private function currentUserHasAccess(string $currentPageParam, array $allowedBo
// Check if the current page is a book page and if the user has access to it
$userBooks = array_slice(array_keys(get_blogs_of_user(get_current_user_id())), 1); // remove the main site
- if ((in_array($currentBlogId, $userBooks) || in_array($currentBlogId, $allowedBooks)) && !in_array($pagenow, $bookPages)) {
+ if (
+ (in_array($currentBlogId, $userBooks) || in_array($currentBlogId, $allowedBooks)) &&
+ !in_array($pagenow, $restrictedBookPages)
+ ) {
$isAccessAllowed = true;
}
$institutionalUsers = apply_filters('pb_institutional_users', []);
if ($currentPageParam === 'pb_network_analytics_userlist' || $pagenow === 'users.php' || $pagenow === 'user-edit.php') {
- if (isset($_REQUEST['user_id']) && in_array($_REQUEST['user_id'], $institutionalUsers)) {
- $isAccessAllowed = true;
- }
+ $isAccessAllowed = true;
+
+ $userId = $_REQUEST['id'] ?? $_REQUEST['user_id'] ?? null;
- if (isset($_REQUEST['id']) && !in_array($_REQUEST['id'], $institutionalUsers)) {
+ if ($userId && ! in_array($userId, $institutionalUsers)) {
$isAccessAllowed = false;
}
}
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
index ab3a334a..b5093f90 100644
--- a/src/Support/helpers.php
+++ b/src/Support/helpers.php
@@ -67,13 +67,14 @@ function get_allowed_pages(): array
'plugins.php',
'media-new.php',
'users.php',
+ 'user-new.php',
'export-personal-data.php',
'erase-personal-data.php',
'options-privacy.php'
];
}
-function get_allowed_book_pages(): array
+function get_restricted_book_pages(): array
{
return [
'site-info.php',
diff --git a/src/Views/WpUserList.php b/src/Views/WpUserList.php
new file mode 100644
index 00000000..bfb1dd64
--- /dev/null
+++ b/src/Views/WpUserList.php
@@ -0,0 +1,103 @@
+ InstitutionUser::query()
+ ->where('user_id', $userId)
+ ->first()
+ ?->institution
+ ->name ?? __('Unassigned', 'pressbooks-multi-institution'),
+ 'books' => $this->getBooksColumnValue($userId),
+ default => $value,
+ };
+ }
+
+ private function getBooksColumnValue(int $userId): string
+ {
+ $blogs = get_blogs_of_user($userId);
+
+ unset($blogs[get_main_site_id()]);
+
+ return app('Blade')->render('PressbooksMultiInstitution::table.wp-users.books-column', [
+ 'books' => $blogs,
+ ]);
+ }
+
+ public function manageTableColumns(array $columns): array
+ {
+ unset($columns['blogs']);
+
+ return array_slice($columns, 0, 4, true) +
+ ['institution' => __('Institution', 'pressbooks-multi-institution')] +
+ array_slice($columns, 4, null, true) +
+ ['books' => __('Books', 'pressbooks-multi-institution')];
+ }
+
+ public function makeInstitutionColumnSortable(array $columns): array
+ {
+ $columns['institution'] = 'institution';
+ return $columns;
+ }
+
+ public function modifyUserQuery(WP_User_Query $query): void
+ {
+ global $pagenow;
+ if (! is_super_admin() || ! is_main_site() || $pagenow !== 'users.php') {
+ return;
+ }
+
+ global $wpdb;
+ $query->query_from .= " LEFT JOIN {$wpdb->base_prefix}institutions_users AS iu ON {$wpdb->users}.ID = iu.user_id";
+ $query->query_from .= " LEFT JOIN {$wpdb->base_prefix}institutions AS i ON iu.institution_id = i.id";
+
+ $institution = get_institution_by_manager();
+ if ($institution !== 0) {
+ $query->query_where .= $wpdb->prepare(" AND iu.institution_id = %d", $institution);
+ }
+
+ $order = (isset($_GET['order']) && $_GET['order'] === 'asc') ? 'ASC' : 'DESC';
+
+ $query->query_orderby = "ORDER BY i.name " . $order;
+ }
+
+ public function removeSuperAdminFilter(array $views): array
+ {
+ $institution = get_institution_by_manager();
+
+ if($institution === 0) {
+ return $views;
+ }
+
+ unset($views['super']);
+
+ $totalUsers = Institution::find($institution)->users()->count();
+ $views['all'] = " " .
+ __('All', 'pressbooks-multi-institution') . " ({$totalUsers})";
+
+ return $views;
+ }
+}
diff --git a/tests/Feature/Views/WpUserListTest.php b/tests/Feature/Views/WpUserListTest.php
new file mode 100644
index 00000000..3d1ed361
--- /dev/null
+++ b/tests/Feature/Views/WpUserListTest.php
@@ -0,0 +1,175 @@
+assertFalse(has_filter('wpmu_users_columns'));
+ $this->assertFalse(has_filter('manage_users_custom_column'));
+ $this->assertFalse(has_filter('manage_users-network_sortable_columns'));
+ $this->assertFalse(has_action('pre_user_query'));
+ $this->assertFalse(has_filter('views_users-network'));
+
+ Container::get(WpUserList::class)->init();
+
+ $this->assertTrue(has_filter('wpmu_users_columns'));
+ $this->assertTrue(has_filter('manage_users_custom_column'));
+ $this->assertTrue(has_filter('manage_users-network_sortable_columns'));
+ $this->assertTrue(has_action('pre_user_query'));
+ $this->assertTrue(has_filter('views_users-network'));
+ }
+
+ /**
+ * @test
+ */
+ public function it_displays_custom_columns_value(): void
+ {
+ $this->createInstitutionsUsers(1, 1);
+
+ $institution = Institution::query()->first();
+ $userId = $institution->users()->first()->user_id;
+
+ $bookId = $this->newBook();
+
+ // assign $userId to $bookId
+ add_user_to_blog($bookId, $userId, 'administrator');
+
+ $wpUserList = Container::get(WpUserList::class);
+
+ $this->assertEquals(
+ $institution->name,
+ $wpUserList->displayCustomColumns('', 'institution', $userId)
+ );
+
+ $this->assertEquals(
+ 'Unassigned',
+ $wpUserList->displayCustomColumns('', 'institution', 99)
+ );
+
+ $this->assertStringContainsString(
+ '',
+ $wpUserList->displayCustomColumns('', 'books', $userId)
+ );
+
+ $this->assertStringContainsString(
+ 'Dashboard',
+ $wpUserList->displayCustomColumns('', 'books', $userId)
+ );
+
+ $this->assertStringContainsString(
+ 'View',
+ $wpUserList->displayCustomColumns('', 'books', $userId)
+ );
+ }
+
+ /**
+ * @test
+ */
+ public function it_adds_institution_column(): void
+ {
+ $columns = ['name' => 'Name', 'email' => 'Email', 'registered' => 'Registered'];
+ $expected = ['name' => 'Name', 'email' => 'Email', 'institution' => 'Institution', 'registered' => 'Registered', 'books' => 'Books'];
+
+ $this->assertEquals(
+ $expected,
+ Container::get(WpUserList::class)->manageTableColumns($columns)
+ );
+ }
+
+ /**
+ * @test
+ */
+ public function it_makes_institution_column_sortable(): void
+ {
+ $columns = ['name' => 'name', 'email' => 'email', 'registered' => 'registered'];
+ $expected = ['name' => 'name', 'email' => 'email', 'registered' => 'registered', 'institution' => 'institution'];
+
+ $this->assertEquals(
+ $expected,
+ Container::get(WpUserList::class)->makeInstitutionColumnSortable($columns)
+ );
+ }
+
+ /**
+ * @test
+ */
+ public function it_modifies_user_query(): void
+ {
+ $query = new \WP_User_Query(['blog_id' => 1]);
+
+ global $pagenow;
+ $pagenow = 'users.php';
+ $userId = $this->newSuperAdmin();
+ wp_set_current_user($userId);
+ $_GET['order'] = 'desc';
+
+ Container::get(WpUserList::class)->modifyUserQuery($query);
+
+ $this->assertStringContainsString('LEFT JOIN', $query->query_from);
+ $this->assertStringContainsString('institutions_users', $query->query_from);
+ $this->assertStringContainsString('institutions', $query->query_from);
+ $this->assertStringContainsString('ORDER BY i.name DESC', $query->query_orderby);
+ }
+
+ /**
+ * @test
+ */
+ public function it_add_where_conditions_to_query_for_ims(): void
+ {
+ $query = new \WP_User_Query(['blog_id' => 1]);
+
+ $this->createInstitutionsUsers(1, 3);
+
+ $institution = Institution::query()->first();
+ $userId = $this->newInstitutionalManager($institution);
+
+ grant_super_admin($userId);
+ wp_set_current_user($userId);
+
+ global $pagenow;
+ $pagenow = 'users.php';
+
+ Container::get(WpUserList::class)->modifyUserQuery($query);
+
+ $this->assertStringContainsString('LEFT JOIN', $query->query_from);
+ $this->assertStringContainsString('institutions_users', $query->query_from);
+ $this->assertStringContainsString('institutions', $query->query_from);
+ $this->assertStringContainsString('AND iu.institution_id = ' . $institution->id, $query->query_where);
+ }
+
+ /**
+ * @test
+ */
+ public function it_removes_super_admin_filter(): void
+ {
+ $this->createInstitutionsUsers(1, 3);
+
+ $institution = Institution::query()->first();
+ $userId = $this->newInstitutionalManager($institution);
+ wp_set_current_user($userId);
+
+ $views = ['all' => 'All', 'super' => 'Super'];
+ $totalUsers = $institution->users()->count();
+
+ $this->assertEquals(
+ ['all' => " All ({$totalUsers})"],
+ Container::get(WpUserList::class)->removeSuperAdminFilter($views)
+ );
+ }
+}