Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions docs/building_admin_modules/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ There are 4 parts that are needed to get a working, filterable list of resources
The main route for your resource list page must support both GET and POST methods.

```php
$routes->match(['get', 'post'], 'users', 'UserController::list', ['as' => 'user-list']);
$routes->match(['GET', 'POST'], 'users', 'UserController::list', ['as' => 'user-list']);
```

The controller should then choose between sending either a full HTML page for a GET request, or just
sending back the table for POST requests. You might do it something like:
The controller should then choose between sending either a full HTML page for a regular GET request,
or just sending back the table for ajax requests issued via HTMX. You might do it something like:

```php
public function list()
Expand All @@ -109,7 +109,7 @@ public function list()
// Performs the actual filtering of the results.
$userModel->filter($this->request->getPost('filters'));

$view = $this->request->getMethod() == 'post'
$view = $this->request->hasHeader('HX-Request')
? $this->viewPrefix .'_table'
: $this->viewPrefix .'list';

Expand Down Expand Up @@ -151,14 +151,17 @@ Here's how this is used for the list of users within Bonfire.

<div class="row">
<!-- List Users -->
<div class="col" id="user-list">
<div class="col" id="content-list">
<?= $this->include('Bonfire\Users\Views\_table') ?>
</div>

<!-- Filters -->
<div class="col-auto" x-show="filtered" x-transition.duration.240ms>
<?= view_cell('Bonfire\Libraries\Cells\Filters::renderList', 'model=UserFilter target=#user-list') ?>
<?= view_cell('Bonfire\Libraries\Cells\Filters::renderList 'model=UserFilter target=#content-list') ?>
</div>
</div>
</div>
```

Note that the id of the eleent that contains the included table should be with `id="content-list"` as it this `id` is
the default target for content replacements by the pager and the filter.
8 changes: 8 additions & 0 deletions docs/intro/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,11 @@ php spark serve
That's all that's needed to get started. You can now visit [http://localhost:8080/admin](http://localhost:8080/admin) and login with your new user.

If you'd rather use a different server, like Apache or Nginx, you can follow the [CodeIgniter 4 guide](https://codeigniter.com/user_guide/installation/running.html) suggestions for a number of different server setups.

## Generate some data for testing

You can insert some records of users into your database by running this migration (each run will add 100 users randomly distributed through Users, Developers and Beta groups, randomly activated):

```bash
php spark db:seed Bonfire\\Users\\Database\\Seeds\\Seed100Users
```
6 changes: 4 additions & 2 deletions src/Users/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public function list()
/** @var UserFilter $userModel */
$userModel = model(UserFilter::class);

$userModel->filter($this->request->getPost('filters'));
$userModel->filter($this->request->getGet('filters'));

$view = $this->request->getMethod() === 'post'
$view = $this->request->hasHeader('HX-Request')
? $this->viewPrefix . '_table'
: $this->viewPrefix . 'list';

Expand Down Expand Up @@ -70,6 +70,8 @@ public function create()
$groups = setting('AuthGroups.groups');
asort($groups);

helper('form');

return $this->render($this->viewPrefix . 'form', [
'groups' => $groups,
]);
Expand Down
59 changes: 59 additions & 0 deletions src/Users/Database/Seeds/Seed100Users.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Bonfire\Users\Database\Seeds;

use CodeIgniter\Database\Seeder;
use Faker\Factory;

class Seed100Users extends Seeder
{
public function run()
{
$faker = Factory::create();
$groups = ['user', 'beta', 'developer'];

for ($i = 0; $i < 100; $i++) {
$timestamp = $faker->dateTimeBetween('-1 year', 'now')->format('Y-m-d H:i:s');
$firstName = $faker->firstName;
$lastName = $faker->lastName;
$username = substr($this->toAscii($firstName), 0, 5) . substr($this->toAscii($lastName), 0, 5);
$active = $faker->boolean;
$email = strtolower($this->toAscii($firstName) . '.' . $this->toAscii($lastName) . '@example.com');
$secret2 = $faker->sha256;

// Insert into users table
$this->db->table('users')->insert([
'first_name' => $firstName,
'last_name' => $lastName,
'username' => $username,
'active' => $active,
'created_at' => $timestamp,
'updated_at' => $timestamp,
'last_active' => $timestamp,
]);

// Get the last inserted ID
$userId = $this->db->insertID();

// Insert into auth_identities table
$this->db->table('auth_identities')->insert([
'user_id' => $userId,
'type' => 'email_password',
'secret' => $email,
'secret2' => $secret2,
]);

// Insert into auth_groups_users table
$this->db->table('auth_groups_users')->insert([
'user_id' => $userId,
'group' => $faker->randomElement($groups),
'created_at' => $timestamp,
]);
}
}

private function toAscii($str)
{
return iconv('UTF-8', 'ASCII//TRANSLIT', $str);
}
}
47 changes: 25 additions & 22 deletions src/Users/Views/_table.php
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
<div class="table-responsive">
<table class="table table-hover">
<?= $this->include('_table_head') ?>
<tbody>
<?php if (isset($users) && count($users)) : ?>
<?php foreach ($users as $user) : ?>
<tr>
<?php if (auth()->user()->can('users.delete')) : ?>
<td>
<input type="checkbox" name="selects[<?= $user->id ?>]" class="form-check">
</td>
<?php endif ?>
<?= view('Bonfire\Users\Views\_row_info', ['user' => $user]) ?>
</tr>
<?php endforeach ?>
<?php endif ?>
</tbody>
</table>
</div>
<form action="<?= site_url(ADMIN_AREA . '/users/delete-batch') ?>" method="post">
<?= csrf_field() ?>
<div class="table-responsive">
<table class="table table-hover">
<?= $this->include('_table_head') ?>
<tbody>
<?php if (isset($users) && count($users)) : ?>
<?php foreach ($users as $user) : ?>
<tr>
<?php if (auth()->user()->can('users.delete')) : ?>
<td>
<input type="checkbox" name="selects[<?= $user->id ?>]" class="form-check">
</td>
<?php endif ?>
<?= view('Bonfire\Users\Views\_row_info', ['user' => $user]) ?>
</tr>
<?php endforeach ?>
<?php endif ?>
</tbody>
</table>
</div>

<?php if (auth()->user()->can('users.delete')) : ?>
<input type="submit" value="Delete Selected" class="btn btn-sm btn-outline-danger" />
<?php endif ?>
<?php if (auth()->user()->can('users.delete')) : ?>
<input type="submit" value="Delete Selected" class="btn btn-sm btn-outline-danger" />
<?php endif ?>
</form>

<div class="text-center">
<?= $pager->links('default', 'bonfire_full') ?>
Expand Down
10 changes: 3 additions & 7 deletions src/Users/Views/list.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,13 @@

<div class="row">
<!-- List Users -->
<div class="col" id="user-list">
<form action="<?= site_url(ADMIN_AREA . '/users/delete-batch') ?>" method="post">
<?= csrf_field() ?>

<?= $this->include('Bonfire\Users\Views\_table') ?>
</form>
<div class="col" id="content-list">
<?= $this->include('Bonfire\Users\Views\_table') ?>
</div>

<!-- Filters -->
<div class="col-auto" x-show="filtered" x-transition.duration.240ms>
<?= view_cell('Bonfire\Core\Cells\Filters::renderList', 'model=UserFilter target=#user-list') ?>
<?= view_cell('Bonfire\Core\Cells\Filters::renderList', 'model=UserFilter target=#content-list') ?>
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/Views/_filter_list.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<section class="filters">
<?php if (isset($filters) && count($filters)): ?>
<form action="<?= current_url() ?>" method="post"
hx-post="<?= current_url() ?>"
<form action="<?= current_url() ?>" method="get"
hx-get="<?= current_url() ?>"
hx-trigger="change delay:400ms from:.filter-check"
hx-target="<?= $target ?>"
>
Expand Down
12 changes: 6 additions & 6 deletions src/Views/_pager_full.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,36 @@

<?php if ($pager->getPageCount() > 1) : ?>
<nav aria-label="<?= lang('Pager.pageNavigation') ?>" class="d-flex justify-content-center">
<ul class="pagination">
<ul class="pagination" hx-target="#content-list">
<?php if ($pager->hasPrevious()) : ?>
<li class="page-item">
<a href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>" class="page-link">
<a href="<?= $pager->getFirst() ?>" hx-get="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>" class="page-link">
<span aria-hidden="true"><?= lang('Pager.first') ?></span>
</a>
</li>
<li class="page-item">
<a href="<?= $pager->getPrevious() ?>" aria-label="<?= lang('Pager.previous') ?>" class="page-link">
<a href="<?= $pager->getPrevious() ?>" hx-get="<?= $pager->getPrevious() ?>" aria-label="<?= lang('Pager.previous') ?>" class="page-link">
<span aria-hidden="true"><?= lang('Pager.previous') ?></span>
</a>
</li>
<?php endif ?>

<?php foreach ($pager->links() as $link) : ?>
<li class="page-item <?= $link['active'] ? 'active' : '' ?>">
<a href="<?= $link['uri'] ?>" class="page-link">
<a href="<?= $link['uri'] ?>" hx-get="<?= $link['uri'] ?>" class="page-link">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>

<?php if ($pager->hasNext()) : ?>
<li class="page-item">
<a href="<?= $pager->getNext() ?>" aria-label="<?= lang('Pager.next') ?>" class="page-link">
<a href="<?= $pager->getNext() ?>" hx-get="<?= $pager->getNext() ?>" aria-label="<?= lang('Pager.next') ?>" class="page-link">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<li class="page-item">
<a href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>" class="page-link">
<a href="<?= $pager->getLast() ?>" hx-get="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>" class="page-link">
<span aria-hidden="true"><?= lang('Pager.last') ?></span>
</a>
</li>
Expand Down
23 changes: 16 additions & 7 deletions themes/Admin/js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,25 @@ function toggleSelectAll(checkbox) {
}
}

// Check if there is a .select-all element on the page
const selectAllElement = document.querySelector('.select-all');
// Function to attach the event listener
function attachSelectAllListener() {
const selectAllElement = document.querySelector('.select-all');

// Attach the event listener only if the element exists
if (selectAllElement) {
selectAllElement.addEventListener('click', function (e) {
toggleSelectAll(e.target);
});
// Attach the event listener only if the element exists
if (selectAllElement) {
selectAllElement.addEventListener('click', function (e) {
toggleSelectAll(e.target);
});
}
}

// Initial attachment
attachSelectAllListener();

// Re-attach the event listener after each htmx request
document.body.addEventListener('htmx:afterSettle', function () {
attachSelectAllListener();
});

// function for recycler get requests issued directly from select box
function sendRecyclerGetRequest(selectedValue) {
Expand Down
Loading