Skip to content

Commit 067b0e0

Browse files
committed
Merge branch 'main' into fix-bulk-actions
2 parents bb961bf + 2744e6d commit 067b0e0

File tree

7 files changed

+204
-46
lines changed

7 files changed

+204
-46
lines changed

src/app/Console/Commands/AddCustomRouteContent.php

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
class AddCustomRouteContent extends Command
1010
{
11+
use \Backpack\CRUD\app\Console\Commands\Traits\PrettyCommandOutput;
12+
1113
/**
1214
* The name and signature of the console command.
1315
*
@@ -45,32 +47,40 @@ public function handle()
4547
$disk = Storage::disk($disk_name);
4648
$code = $this->argument('code');
4749

48-
if ($disk->exists($path)) {
49-
$old_file_path = $disk->path($path);
50+
$this->progressBlock("Adding route to $path");
5051

51-
// insert the given code before the file's last line
52-
$file_lines = file($old_file_path, FILE_IGNORE_NEW_LINES);
52+
// Validate file exists
53+
if (! $disk->exists($path)) {
54+
Artisan::call('vendor:publish', ['--provider' => \Backpack\CRUD\BackpackServiceProvider::class, '--tag' => 'custom_routes']);
55+
$this->handle();
5356

54-
// if the code already exists in the file, abort
55-
if ($this->getLastLineNumberThatContains($code, $file_lines)) {
56-
return $this->comment('Route already existed.');
57-
}
57+
return;
58+
}
5859

59-
$end_line_number = $this->customRoutesFileEndLine($file_lines);
60-
$file_lines[$end_line_number + 1] = $file_lines[$end_line_number];
61-
$file_lines[$end_line_number] = ' '.$code;
62-
$new_file_content = implode(PHP_EOL, $file_lines);
60+
// insert the given code before the file's last line
61+
$old_file_path = $disk->path($path);
62+
$file_lines = file($old_file_path, FILE_IGNORE_NEW_LINES);
6363

64-
if ($disk->put($path, $new_file_content)) {
65-
$this->info('Successfully added code to '.$path);
66-
} else {
67-
$this->error('Could not write to file: '.$path);
68-
}
69-
} else {
70-
Artisan::call('vendor:publish', ['--provider' => 'Backpack\CRUD\BackpackServiceProvider', '--tag' => 'custom_routes']);
64+
// if the code already exists in the file, abort
65+
if ($this->getLastLineNumberThatContains($code, $file_lines)) {
66+
$this->closeProgressBlock('Already existed', 'yellow');
7167

72-
$this->handle();
68+
return;
7369
}
70+
71+
$end_line_number = $this->customRoutesFileEndLine($file_lines);
72+
$file_lines[$end_line_number + 1] = $file_lines[$end_line_number];
73+
$file_lines[$end_line_number] = ' '.$code;
74+
$new_file_content = implode(PHP_EOL, $file_lines);
75+
76+
if (! $disk->put($path, $new_file_content)) {
77+
$this->errorProgressBlock();
78+
$this->note('Could not write to file.', 'red');
79+
80+
return;
81+
}
82+
83+
$this->closeProgressBlock();
7484
}
7585

7686
private function customRoutesFileEndLine($file_lines)

src/app/Console/Commands/AddSidebarContent.php

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
class AddSidebarContent extends Command
99
{
10+
use \Backpack\CRUD\app\Console\Commands\Traits\PrettyCommandOutput;
11+
1012
/**
1113
* The name and signature of the console command.
1214
*
@@ -44,22 +46,34 @@ public function handle()
4446
$disk = Storage::disk($disk_name);
4547
$code = $this->argument('code');
4648

47-
if ($disk->exists($path)) {
48-
$contents = $disk->get($path);
49-
$file_lines = file($disk->path($path), FILE_IGNORE_NEW_LINES);
50-
51-
if ($this->getLastLineNumberThatContains($code, $file_lines)) {
52-
return $this->comment('Sidebar item already existed.');
53-
}
54-
55-
if ($disk->put($path, $contents.PHP_EOL.$code)) {
56-
$this->info('Successfully added code to sidebar_content file.');
57-
} else {
58-
$this->error('Could not write to sidebar_content file.');
59-
}
60-
} else {
61-
$this->error('The sidebar_content file does not exist. Make sure Backpack is properly installed.');
49+
$this->progressBlock("Adding sidebar entry to $path");
50+
51+
// Validate file exists
52+
if (! $disk->exists($path)) {
53+
$this->errorProgressBlock();
54+
$this->note('The sidebar_content file does not exist. Make sure Backpack is properly installed.', 'red');
55+
56+
return;
6257
}
58+
59+
$contents = $disk->get($path);
60+
$file_lines = file($disk->path($path), FILE_IGNORE_NEW_LINES);
61+
62+
// Validate the entry already exists
63+
if ($this->getLastLineNumberThatContains($code, $file_lines)) {
64+
$this->closeProgressBlock('Already existed', 'yellow');
65+
66+
return;
67+
}
68+
69+
if (! $disk->put($path, $contents.PHP_EOL.$code)) {
70+
$this->errorProgressBlock();
71+
$this->note('Could not write to sidebar_content file.', 'red');
72+
73+
return;
74+
}
75+
76+
$this->closeProgressBlock();
6377
}
6478

6579
/**

src/app/Library/CrudPanel/Traits/Filters.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function clearFilters()
4545
* Add a filter to the CRUD table view.
4646
*
4747
* @param array $options Name, type, label, etc.
48-
* @param bool|array|\Closure $values The HTML for the filter.
48+
* @param bool|string|array|\Closure $values The HTML for the filter.
4949
* @param bool|\Closure $filterLogic Query modification (filtering) logic when filter is active.
5050
* @param bool|\Closure $fallbackLogic Query modification (filtering) logic when filter is not active.
5151
*/
@@ -62,7 +62,7 @@ public function addFilter($options, $values = false, $filterLogic = false, $fall
6262
* The filter will NOT get applied.
6363
*
6464
* @param array $options Name, type, label, etc.
65-
* @param bool|array|\Closure $values The HTML for the filter.
65+
* @param bool|string|array|\Closure $values The HTML for the filter.
6666
* @param bool|\Closure $filterLogic Query modification (filtering) logic when filter is active.
6767
* @param bool|\Closure $fallbackLogic Query modification (filtering) logic when filter is not active.
6868
*/

src/config/backpack/base.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
// Content of the HTML meta robots tag to prevent indexing and link following
3737
'meta_robots_content' => 'noindex, nofollow',
3838

39+
// ---------
40+
// DASHBOARD
41+
// ---------
42+
43+
// Show "Getting Started with Backpack" info block?
44+
'show_getting_started' => env('APP_ENV') == 'local',
45+
3946
// ------
4047
// STYLES
4148
// ------
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
@extends(backpack_view('blank'))
22

33
@php
4-
$widgets['before_content'][] = [
5-
'type' => 'jumbotron',
6-
'heading' => trans('backpack::base.welcome'),
7-
'content' => trans('backpack::base.use_sidebar'),
8-
'button_link' => backpack_url('logout'),
9-
'button_text' => trans('backpack::base.logout'),
10-
];
4+
if (config('backpack.base.show_getting_started')) {
5+
$widgets['before_content'][] = [
6+
'type' => 'view',
7+
'view' => 'backpack::inc.getting_started',
8+
];
9+
} else {
10+
$widgets['before_content'][] = [
11+
'type' => 'jumbotron',
12+
'heading' => trans('backpack::base.welcome'),
13+
'content' => trans('backpack::base.use_sidebar'),
14+
'button_link' => backpack_url('logout'),
15+
'button_text' => trans('backpack::base.logout'),
16+
];
17+
}
1118
@endphp
1219

1320
@section('content')
14-
@endsection
21+
@endsection
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<div class="card">
2+
<div class="card-body">
3+
4+
<h3>Getting Started</h3>
5+
<p>If it's your first time using Backpack, we heavily recommend you follow the steps below:</p>
6+
7+
<div id="accordion" role="tablist">
8+
<div class="card mb-1">
9+
<div class="card-header bg-light" id="headingOne" role="tab">
10+
<h5 class="mb-0"><a data-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne" class="collapsed text-dark"><span class="badge badge-warning mr-2 mt-n2">1</span>Create your first CRUD <small class="float-right mt-2">1-5 min</small></a></h5>
11+
</div>
12+
<div class="collapse" id="collapseOne" role="tabpanel" aria-labelledby="headingOne" data-parent="#accordion" style="">
13+
<div class="card-body">
14+
<p>You've already got a model, <code class="text-primary bg-light p-1 rounded">App\Models\User</code>... all Laravel projects do. So <strong>let's create a page to administer users</strong>. We want the admin to Create, Read, Update and Delete them. In Backpack, we call that a <a href="https://backpackforlaravel.com/docs/5.x/crud-basics" target="blank">CRUD</a>. And you can easily generate one for an existing Eloquent model, by running:</p>
15+
<p>
16+
<code class="text-primary bg-light p-1 rounded">php artisan backpack:crud user</code>
17+
</p>
18+
<p>Go ahead, run it in your terminal. You can now click on the new sidebar item (or <a href="{{ backpack_url('user') }}">here</a>) and you'll be able to see the entries in the <code class="text-primary bg-light p-1 rounded">users</code> table. Even though generated CRUDs work out-of-the-box, they might not be <i>exactly</i> what you need. But that's where Backpack shines, in how easy it is to customize.</p>
19+
20+
<p>To dig a little deeper, <a href="#" data-toggle="collapse" data-target="#customizeUsersCRUD" aria-expanded="true" aria-controls="customizeUsersCRUD">let's make a few changes to the Users CRUD <i class="la la-angle-double-right"></i></a></p>
21+
22+
<div class="collapse" id="customizeUsersCRUD">
23+
<p><strong>1. When Listing, let's remove the "password" column</strong> - no point in showing the hash. To do that, go to <code class="text-primary bg-light p-1 rounded">UserCrudController::setupListOperation()</code> and remove the line saying <code class="text-primary bg-light p-1 rounded">CRUD::column('password');</code> - easy-peasy, right?</p>
24+
<p><strong>2. On Create & Update, let's add validation to forms</strong>. There are <a href="https://backpackforlaravel.com/docs/5.x/crud-operation-create#validation" target="_blank">multiple ways to add validation (A, B, C)</a>. Let's change the standard <a href="https://backpackforlaravel.com/docs/5.x/crud-operation-create#validating-fields-using-formrequests" target="_blank">validation using FormRequests</a> (A), to a simpler validation using <a href="https://backpackforlaravel.com/docs/5.x/crud-operation-create#validating-fields-using-field-attributes" target="_blank">field attributes</a> (C):</p>
25+
<ul>
26+
<li>to remove the current validation:<br>
27+
<ul>
28+
<li>inside <code class="text-primary bg-light p-1 rounded">UserCrudController</code>, remove <code class="text-primary bg-light p-1 rounded">use App\Http\Requests\UserRequest;</code> from the top;</li>
29+
<li>inside <code class="text-primary bg-light p-1 rounded">UserCrudController</code>, remove <code class="text-primary bg-light p-1 rounded">CRUD::setValidation(UserRequest::class);</code> from <code class="text-primary bg-light p-1 rounded">setupCreateOperation()</code>;</li>
30+
<li>delete the <code class="text-primary bg-light p-1 rounded">App\Http\Requests\UserRequest;</code> file;</li>
31+
</ul>
32+
</li>
33+
<li>a quick way to add validation is to go to <code class="text-primary bg-light p-1 rounded">setupCreateOperation()</code> and specify validation rules directly on the fields:
34+
<p>
35+
<pre class="language-php rounded"><code class="language-php p-1">
36+
CRUD::field('name')->validationRules('required|min:5');
37+
CRUD::field('email')->validationRules('required|email|unique:users,email');
38+
CRUD::field('password')->validationRules('required');
39+
</code></pre>
40+
</p>
41+
</li>
42+
</ul>
43+
<p><strong>3. On Create, let's hash the password.</strong> Currently, if we create a new User, it'll work. But if you look in the database... you'll notice the password is stored in plain text. We don't want that - we want it hashed. There are <a href="https://backpackforlaravel.com/docs/5.x/crud-operation-create#use-events-in-your-setup-method" target="_blank">multiple ways to achieve this too</a>. Let's use Model Events inside <code class="text-primary bg-light p-1 rounded">setupCreateOperation()</code>. Here's how our method could look, when we also tap into the <code class="text-primary bg-light p-1 rounded">creating</code> event, to hash the password:</p>
44+
<p>
45+
<pre class="language-php rounded"><code class="language-php p-1">
46+
protected function setupCreateOperation()
47+
{
48+
CRUD::field('name')->validationRules('required|min:5');
49+
CRUD::field('email')->validationRules('required|email|unique:users,email');
50+
CRUD::field('password')->validationRules('required');
51+
52+
\App\Models\User::creating(function ($entry) {
53+
$entry->password = \Hash::make($entry->password);
54+
});
55+
}
56+
</code></pre>
57+
</p>
58+
<p><strong>4. On Update, let's not require the password</strong>. It should only be needed if an admin wants to change it, right? That means the validation rules will be different for "password". But then again... the rules will also be different for "email" (because on Update, we need to pass the ID to the unique rule in Laravel). Since 2/3 rules are different, let's just delete what was inside <code class="text-primary bg-light p-1 rounded">setupUpdateOperation()</code> and code it from scratch:</p>
59+
<p>
60+
<pre class="language-php rounded"><code class="language-php p-1">
61+
protected function setupUpdateOperation()
62+
{
63+
CRUD::field('name')->validationRules('required|min:5');
64+
CRUD::field('email')->validationRules('required|email|unique:users,email,'.CRUD::getCurrentEntryId());
65+
CRUD::field('password')->hint('Type a password to change it.');
66+
67+
\App\Models\User::updating(function ($entry) {
68+
if (request('password') == null) {
69+
$entry->password = $entry->getOriginal('password');
70+
} else {
71+
$entry->password = \Hash::make(request('password'));
72+
}
73+
});
74+
}
75+
</code></pre>
76+
</p>
77+
<p>
78+
That's it. You have a working Users CRUD. Plus, you've already learned some advanced techniques, like <a href="https://backpackforlaravel.com/docs/5.x/crud-operation-create#use-events-in-your-setup-method" target="_blank">using Model events inside CrudController</a> and <a href="https://backpackforlaravel.com/docs/5.x/crud-operation-create#b-validating-fields-using-a-rules-array" target="_blank">using field-attribute-validation instead of form-request-validation</a>. Of course, this only scratches the surface of what Backpack can do. To really understand how it works, and how you can best use Backpack's features, <strong>we heavily recommend you move on to the next step, and learn the basics.</strong>
79+
</p>
80+
</div>
81+
</div>
82+
</div>
83+
</div>
84+
<div class="card mb-1">
85+
<div class="card-header bg-light" id="headingTwo" role="tab">
86+
<h5 class="mb-0"><a class="collapsed text-dark" data-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"><span class="badge badge-warning mr-2 mt-n2">2</span>Learn the basics <small class="float-right mt-2">20-30 min</small></a></h5>
87+
</div>
88+
<div class="collapse" id="collapseTwo" role="tabpanel" aria-labelledby="headingTwo" data-parent="#accordion" style="">
89+
<div class="card-body">
90+
<p>So you've created your first CRUD? Excellent. Now it's time to understand <i>how it works</i> and <i>what else you can do</i>. Time to learn the basics - how to build and customize admin panels using Backpack. Please follow one of the courses below, depending on how you prefer to learn:</p>
91+
<ul>
92+
<li><strong><a target="_blank" href="https://backpackforlaravel.com/docs/5.x/getting-started-videos">Video Course</a></strong> - 31 minutes</li>
93+
<li><strong><a target="_blank" href="https://backpackforlaravel.com/docs/5.x/getting-started-basics">Text Course</a></strong> - 20 minutes</li>
94+
<li><strong><a target="_blank" href="https://backpackforlaravel.com/getting-started-emails">Email Course</a></strong> - 1 email per day, for 4 days, 5 minutes each</li>
95+
</ul>
96+
</div>
97+
</div>
98+
</div>
99+
<div class="card mb-1">
100+
<div class="card-header bg-light" id="headingThree" role="tab">
101+
<h5 class="mb-0"><a class="collapsed text-dark" data-toggle="collapse" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree"><span class="badge badge-warning mr-2 mt-n2">3</span>Hide this notice <small class="float-right mt-2">1 min</small></a></h5>
102+
</div>
103+
<div class="collapse" id="collapseThree" role="tabpanel" aria-labelledby="headingThree" data-parent="#accordion" style="">
104+
<div class="card-body">Go to your <code class="text-primary bg-light p-1 rounded">config/backpack/base.php</code> and change <code class="text-primary bg-light p-1 rounded">show_getting_started</code> to <code class="text-primary bg-light p-1 rounded">false</code>.</div>
105+
</div>
106+
</div>
107+
</div>
108+
109+
<p class="mt-3 mb-0"><small>* this card is only visible on <i>localhost</i>. Follow the last step to hide it from <i>localhost</i> too.</small></p>
110+
</div>
111+
</div>
112+
113+
@push('after_styles')
114+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/base16/dracula.min.css">
115+
@endpush
116+
117+
@push('after_scripts')
118+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
119+
<script>hljs.highlightAll();</script>
120+
@endpush

src/resources/views/crud/fields/datetime.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
$timestamp = strtotime(old_empty_or_null($field['name'], '') ?? $field['value'] ?? $field['default'] ?? '');
1111
12-
$value = $timestamp ? strftime('%Y-%m-%dT%H:%M:%S', $timestamp) : '';
12+
$value = $timestamp ? date('Y-m-d\TH:i:s', $timestamp) : '';
1313
@endphp
1414

1515
@include('crud::fields.inc.wrapper_start')

0 commit comments

Comments
 (0)