diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml
index f55d1fa8..5b1c7c38 100644
--- a/.github/workflows/php-cs-fixer.yml
+++ b/.github/workflows/php-cs-fixer.yml
@@ -20,4 +20,4 @@ jobs:
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
- commit_message: Fix styling
+ commit_message: "fix: apply code styling rules"
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index b184d0b2..28b0602f 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -13,7 +13,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, windows-latest]
- php: [7.4, 8.0]
+ php: [8.0]
laravel: [8.*]
stability: [prefer-lowest, prefer-stable]
include:
diff --git a/.php_cs.dist.php b/.php_cs.dist.php
index 3de28fd4..b524e240 100644
--- a/.php_cs.dist.php
+++ b/.php_cs.dist.php
@@ -3,6 +3,7 @@
$finder = Symfony\Component\Finder\Finder::create()
->in([
__DIR__ . '/src',
+ __DIR__ . '/stubs',
__DIR__ . '/tests',
])
->name('*.php')
diff --git a/README.md b/README.md
index 9ad68b71..5a6a1a2b 100644
--- a/README.md
+++ b/README.md
@@ -21,28 +21,38 @@ Hearth is a simple starter kit for the Laravel framework. It provides a few thin
## Installation
-You can install the package via composer:
+You may use Composer to install Hearth into your new Laravel project:
```bash
composer require fluid-project/hearth
```
-After installing the Hearth package, you can use the `hearth:install` command to
+_Note: attempting to install Hearth into an existing Laravel application will result in unexpected behaviour._
+
+After installing the Hearth package, you can use the `hearth:install` Artisan command to
install the Hearth scaffolding within your Laravel application:
```bash
php artisan hearth:install
```
-After installing Hearth, you will need to install and build your NPM dependencies
-and run your database migrations:
+After installing Hearth, you will need to install and build your NPM dependencies, run your database migrations and link
+public storage:
```bash
npm install
npm run dev
php artisan migrate
+php artisan storage:link
```
+### Emails
+
+In order to test emails (for example, using Mailhog with [Laravel Sail](https://laravel.com/docs/8.x/sail#previewing-emails)),
+you must update your Laravel application's `.env` file's `MAIL_FROM_ADDRESS` environment variable with a
+properly-formatted email address. For local development, this might be `noreply@hearth.test` (assuming your local
+ application is accessible at `http://hearth.test`).
+
## Usage
TODO.
@@ -73,3 +83,13 @@ Please review [our security policy](../../security/policy) on how to report secu
## License
The BSD 3-Clause License. Please see [License File](LICENSE.md) for more information.
+
+## Third Party Software in Hearth
+
+Hearth is based on other publicly available software, categorized by license:
+
+### MIT License
+
+- [Laravel Breeze](https://github.com/laravel/breeze)
+- [Laravel Jetstream](https://github.com/laravel/jetstream)
+- [Laravel Package Skeleton](https://github.com/spatie/package-skeleton-laravel)
diff --git a/composer.json b/composer.json
index 30ebd943..7e9de9c5 100644
--- a/composer.json
+++ b/composer.json
@@ -16,12 +16,16 @@
}
],
"require": {
- "php": "^7.4|^8.0",
+ "php": "^8.0",
"chinleung/laravel-locales": "^1.2",
"chinleung/laravel-multilingual-routes": "^2.7",
+ "commerceguys/intl": "^1.1",
"illuminate/contracts": "^8.37",
"laravel/fortify": "^1.7",
- "spatie/laravel-package-tools": "^1.4.3"
+ "spatie/laravel-flash": "^1.8",
+ "spatie/laravel-google-fonts": "^1.0",
+ "spatie/laravel-package-tools": "^1.9",
+ "spatie/laravel-sluggable": "^3.1"
},
"require-dev": {
"brianium/paratest": "^6.2",
diff --git a/config/hearth.php b/config/hearth.php
index 1e70ae14..d77e8fb4 100644
--- a/config/hearth.php
+++ b/config/hearth.php
@@ -1,5 +1,6 @@
faker->name;
+ return [
+ 'name' => $name,
+ 'slug' => Str::slug($name),
+ 'email' => $this->faker->unique()->safeEmail,
+ 'email_verified_at' => now(),
+ 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+ 'remember_token' => Str::random(10),
+ 'locale' => 'en',
+ ];
+ }
+}
diff --git a/database/migrations/create_hearth_table.php.stub b/database/migrations/create_hearth_table.php.stub
deleted file mode 100644
index 7491b59f..00000000
--- a/database/migrations/create_hearth_table.php.stub
+++ /dev/null
@@ -1,19 +0,0 @@
-id();
-
- // add fields
-
- $table->timestamps();
- });
- }
-};
diff --git a/database/migrations/create_users_table.php.stub b/database/migrations/create_users_table.php.stub
new file mode 100644
index 00000000..e6b41747
--- /dev/null
+++ b/database/migrations/create_users_table.php.stub
@@ -0,0 +1,38 @@
+id();
+ $table->string('name');
+ $table->string('slug');
+ $table->string('email')->unique();
+ $table->timestamp('email_verified_at')->nullable();
+ $table->string('password');
+ $table->string('locale')->default('en');
+ $table->rememberToken();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('users');
+ }
+}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index ccdd25d6..da767ac6 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -19,7 +19,7 @@
verbose="true"
>
-
+ tests
diff --git a/resources/lang/en/alert.php b/resources/lang/en/alert.php
new file mode 100644
index 00000000..92d263fc
--- /dev/null
+++ b/resources/lang/en/alert.php
@@ -0,0 +1,8 @@
+ 'Error',
+ 'warning' => 'Warning',
+ 'success' => 'Success',
+ 'notice' => 'Notice'
+];
diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php
new file mode 100644
index 00000000..813cebce
--- /dev/null
+++ b/resources/lang/en/auth.php
@@ -0,0 +1,42 @@
+ 'Sign in',
+ 'sign_out' => 'Sign out',
+ 'create_account' => 'Create an account',
+ 'label_password' => 'Password',
+ 'label_password_confirmation' => 'Confirm password',
+ 'existing_account_prompt' => 'Do you already have an account?',
+ 'create_your_account' => 'Create your account',
+ 'label_current_password' => 'Current password',
+ 'change_password' => 'Change password',
+ 'action_confirm' => 'Confirm',
+ 'password_change_succeeded' => 'Your password has been changed.',
+ 'label_remember_me' => 'Remember me',
+ 'error_intro' => 'Sorry, something went wrong.',
+ 'failed' => 'These credentials do not match our records.',
+ 'wrong_password' => 'The provided password is incorrect.',
+ 'throttle' => 'Too many log in attempts. Please try again in :seconds seconds.',
+ 'confirm_intro' => 'For your security, please confirm your password before continuing.',
+ 'forget_prompt' => 'Forgot your password?',
+ 'forgot_intro' => 'Forgot your password? Enter your email address and we will email you a password reset link that will let you choose a new one.',
+ 'forgot_submit' => 'Send password reset link',
+ 'reset_submit' => 'Reset password',
+ 'verification_required' => 'Email verification required',
+ 'verification_intro' => 'Please verify your email address by clicking on the link we emailed to you. If you didn’t receive the email, we will gladly send you another.',
+ 'verification_sent' => 'A new verification link has been sent to the email address you provided.',
+ 'resend_verification_email' => 'Resend verification email',
+ 'verification_succeeded' => 'Your email address has been verified.'
+];
diff --git a/resources/lang/en/dashboard.php b/resources/lang/en/dashboard.php
new file mode 100644
index 00000000..0c9545f5
--- /dev/null
+++ b/resources/lang/en/dashboard.php
@@ -0,0 +1,6 @@
+ 'Dashboard',
+ 'welcome' => 'Welcome, :name!'
+];
diff --git a/resources/lang/en/errors.php b/resources/lang/en/errors.php
new file mode 100644
index 00000000..6d75dd8b
--- /dev/null
+++ b/resources/lang/en/errors.php
@@ -0,0 +1,31 @@
+ 'Not authorized',
+ 'error_401_message' => 'You are not authorized to visit this page. You may need to log in.',
+ 'error_403_title' => 'Access forbidden',
+ 'error_403_message' => 'You do not have permission to visit this page.',
+ 'error_404_title' => 'Not found',
+ 'error_404_message' => 'The page you are trying to visit could not be found.',
+ 'error_419_title' => 'Page expired',
+ 'error_419_message' => 'The page has expired. Please try again.',
+ 'error_429_title' => 'Too many requests',
+ 'error_429_message' => 'You have sent too many requests to this page. Please wait a few moments and try again.',
+ 'error_500_title' => 'Server error',
+ 'error_500_message' => 'Sorry, it looks like something isn’t working properly on our end.',
+ 'error_503_title' => 'Service unavailable',
+ 'error_503_message' => 'Sorry, it looks like the website can’t respond to your request right now. Please wait a few moments and try again.',
+ 'return_home' => 'Return to home page',
+];
diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php
new file mode 100644
index 00000000..29592f83
--- /dev/null
+++ b/resources/lang/en/forms.php
@@ -0,0 +1,8 @@
+ 'Save changes',
+ 'label_email' => 'Email address',
+ 'errors_found' => 'Errors found',
+ 'errors_found_message' => 'Sorry, some errors were found in your submission. Please correct these errors and try again.'
+];
diff --git a/resources/lang/en/mail.php b/resources/lang/en/mail.php
new file mode 100644
index 00000000..050d0d27
--- /dev/null
+++ b/resources/lang/en/mail.php
@@ -0,0 +1,8 @@
+ 'Sorry!',
+ 'greeting' => 'Hello!',
+ 'salutation' => 'Regards',
+ 'link_guidance' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:'
+];
diff --git a/resources/lang/en/passwords.php b/resources/lang/en/passwords.php
new file mode 100644
index 00000000..2345a56b
--- /dev/null
+++ b/resources/lang/en/passwords.php
@@ -0,0 +1,22 @@
+ 'Your password has been reset!',
+ 'sent' => 'We have emailed your password reset link!',
+ 'throttled' => 'Please wait before retrying.',
+ 'token' => 'This password reset token is invalid.',
+ 'user' => "We can't find a user with that email address.",
+
+];
diff --git a/resources/lang/en/routes.php b/resources/lang/en/routes.php
new file mode 100644
index 00000000..ed44e4c7
--- /dev/null
+++ b/resources/lang/en/routes.php
@@ -0,0 +1,9 @@
+ 'dashboard',
+ 'logout' => 'logout',
+ 'login' => 'login',
+ 'register' => 'register',
+ 'verification.verify' => '/verify-email/{id}/{hash}'
+];
diff --git a/resources/lang/en/user.php b/resources/lang/en/user.php
new file mode 100644
index 00000000..582612b9
--- /dev/null
+++ b/resources/lang/en/user.php
@@ -0,0 +1,13 @@
+ 'Settings',
+ 'account' => 'Account',
+ 'label_name' => 'Full name',
+ 'label_locale' => 'Preferred language',
+ 'delete_account' => 'Delete account',
+ 'delete_account_intro' => 'Your account will be deleted and cannot be recovered. If you still want to delete your account, please enter your current password to proceed.',
+ 'action_delete_account' => 'Delete my account',
+ 'settings_update_succeeded' => 'Your settings have been saved.',
+ 'destroy_succeeded' => 'Your account has been deleted.',
+];
diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php
new file mode 100644
index 00000000..a664bdf1
--- /dev/null
+++ b/resources/lang/en/validation.php
@@ -0,0 +1,162 @@
+ 'The :attribute must be accepted.',
+ 'active_url' => 'The :attribute is not a valid URL.',
+ 'after' => 'The :attribute must be a date after :date.',
+ 'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
+ 'alpha' => 'The :attribute may only contain letters.',
+ 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
+ 'alpha_num' => 'The :attribute may only contain letters and numbers.',
+ 'array' => 'The :attribute must be an array.',
+ 'before' => 'The :attribute must be a date before :date.',
+ 'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
+ 'between' => [
+ 'numeric' => 'The :attribute must be between :min and :max.',
+ 'file' => 'The :attribute must be between :min and :max kilobytes.',
+ 'string' => 'The :attribute must be between :min and :max characters.',
+ 'array' => 'The :attribute must have between :min and :max items.',
+ ],
+ 'boolean' => 'The :attribute field must be true or false.',
+ 'confirmed' => 'The :attribute confirmation does not match.',
+ 'date' => 'The :attribute is not a valid date.',
+ 'date_equals' => 'The :attribute must be a date equal to :date.',
+ 'date_format' => 'The :attribute does not match the format :format.',
+ 'different' => 'The :attribute and :other must be different.',
+ 'digits' => 'The :attribute must be :digits digits.',
+ 'digits_between' => 'The :attribute must be between :min and :max digits.',
+ 'dimensions' => 'The :attribute has invalid image dimensions.',
+ 'distinct' => 'The :attribute field has a duplicate value.',
+ 'email' => 'The :attribute must be a valid email address.',
+ 'ends_with' => 'The :attribute must end with one of the following: :values.',
+ 'exists' => 'The selected :attribute is invalid.',
+ 'file' => 'The :attribute must be a file.',
+ 'filled' => 'The :attribute field must have a value.',
+ 'gt' => [
+ 'numeric' => 'The :attribute must be greater than :value.',
+ 'file' => 'The :attribute must be greater than :value kilobytes.',
+ 'string' => 'The :attribute must be greater than :value characters.',
+ 'array' => 'The :attribute must have more than :value items.',
+ ],
+ 'gte' => [
+ 'numeric' => 'The :attribute must be greater than or equal :value.',
+ 'file' => 'The :attribute must be greater than or equal :value kilobytes.',
+ 'string' => 'The :attribute must be greater than or equal :value characters.',
+ 'array' => 'The :attribute must have :value items or more.',
+ ],
+ 'image' => 'The :attribute must be an image.',
+ 'in' => 'The selected :attribute is invalid.',
+ 'in_array' => 'The :attribute field does not exist in :other.',
+ 'integer' => 'The :attribute must be an integer.',
+ 'ip' => 'The :attribute must be a valid IP address.',
+ 'ipv4' => 'The :attribute must be a valid IPv4 address.',
+ 'ipv6' => 'The :attribute must be a valid IPv6 address.',
+ 'json' => 'The :attribute must be a valid JSON string.',
+ 'lt' => [
+ 'numeric' => 'The :attribute must be less than :value.',
+ 'file' => 'The :attribute must be less than :value kilobytes.',
+ 'string' => 'The :attribute must be less than :value characters.',
+ 'array' => 'The :attribute must have less than :value items.',
+ ],
+ 'lte' => [
+ 'numeric' => 'The :attribute must be less than or equal :value.',
+ 'file' => 'The :attribute must be less than or equal :value kilobytes.',
+ 'string' => 'The :attribute must be less than or equal :value characters.',
+ 'array' => 'The :attribute must not have more than :value items.',
+ ],
+ 'max' => [
+ 'numeric' => 'The :attribute may not be greater than :max.',
+ 'file' => 'The :attribute may not be greater than :max kilobytes.',
+ 'string' => 'The :attribute may not be greater than :max characters.',
+ 'array' => 'The :attribute may not have more than :max items.',
+ ],
+ 'mimes' => 'The :attribute must be a file of type: :values.',
+ 'mimetypes' => 'The :attribute must be a file of type: :values.',
+ 'min' => [
+ 'numeric' => 'The :attribute must be at least :min.',
+ 'file' => 'The :attribute must be at least :min kilobytes.',
+ 'string' => 'The :attribute must be at least :min characters.',
+ 'array' => 'The :attribute must have at least :min items.',
+ ],
+ 'multiple_of' => 'The :attribute must be a multiple of :value.',
+ 'not_in' => 'The selected :attribute is invalid.',
+ 'not_regex' => 'The :attribute format is invalid.',
+ 'numeric' => 'The :attribute must be a number.',
+ 'password' => 'The password is incorrect.',
+ 'present' => 'The :attribute field must be present.',
+ 'regex' => 'The :attribute format is invalid.',
+ 'required' => 'The :attribute field is required.',
+ 'required_if' => 'The :attribute field is required when :other is :value.',
+ 'required_unless' => 'The :attribute field is required unless :other is in :values.',
+ 'required_with' => 'The :attribute field is required when :values is present.',
+ 'required_with_all' => 'The :attribute field is required when :values are present.',
+ 'required_without' => 'The :attribute field is required when :values is not present.',
+ 'required_without_all' => 'The :attribute field is required when none of :values are present.',
+ 'same' => 'The :attribute and :other must match.',
+ 'size' => [
+ 'numeric' => 'The :attribute must be :size.',
+ 'file' => 'The :attribute must be :size kilobytes.',
+ 'string' => 'The :attribute must be :size characters.',
+ 'array' => 'The :attribute must contain :size items.',
+ ],
+ 'starts_with' => 'The :attribute must start with one of the following: :values.',
+ 'string' => 'The :attribute must be a string.',
+ 'timezone' => 'The :attribute must be a valid zone.',
+ 'unique' => 'The :attribute has already been taken.',
+ 'uploaded' => 'The :attribute failed to upload.',
+ 'url' => 'The :attribute format is invalid.',
+ 'uuid' => 'The :attribute must be a valid UUID.',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify custom validation messages for attributes using the
+ | convention "attribute.rule" to name the lines. This makes it quick to
+ | specify a specific custom language line for a given attribute rule.
+ |
+ */
+
+ 'custom' => [
+ 'password' => [
+ 'length-uppercase' => 'The :attribute must be at least :length characters and contain at least one uppercase character.',
+ 'length-numeric' => 'The :attribute must be at least :length characters and contain at least one number.',
+ 'length-specialcharacter' => 'The :attribute must be at least :length characters and contain at least one special character.',
+ 'length-uppercase-numeric' => 'The :attribute must be at least :length characters and contain at least one uppercase character and one number.',
+ 'length-uppercase-specialcharacter' => 'The :attribute must be at least :length characters and contain at least one uppercase character and one special character.',
+ 'length-uppercase-numeric-specialcharacter' => 'The :attribute must be at least :length characters and contain at least one uppercase character, one number, and one special character.',
+ 'length-numeric-specialcharacter' => 'The :attribute must be at least :length characters and contain at least one special character and one number.',
+ 'length' => 'The :attribute must be at least :length characters.'
+ ],
+ 'organization_user' => [
+ 'not_last_admin' => 'There must be at least one administrator in this organization.'
+ ]
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Attributes
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used to swap our attribute placeholder
+ | with something more reader friendly such as "E-Mail Address" instead
+ | of "email". This simply helps us make our message more expressive.
+ |
+ */
+
+ 'attributes' => [],
+
+];
diff --git a/resources/lang/en/welcome.php b/resources/lang/en/welcome.php
new file mode 100644
index 00000000..75c08436
--- /dev/null
+++ b/resources/lang/en/welcome.php
@@ -0,0 +1,6 @@
+ 'Welcome to Hearth!',
+ 'details' => 'Make yourself at home.',
+];
diff --git a/resources/lang/fr/alert.php b/resources/lang/fr/alert.php
new file mode 100644
index 00000000..0b67a5fe
--- /dev/null
+++ b/resources/lang/fr/alert.php
@@ -0,0 +1,3 @@
+
+