Skip to content

Multiple enhancements for new version #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.idea
vendor
composer.lock

.history/
60 changes: 50 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ An easy way to set a timezone for a user in your application and then show date/

## How does it work

This package listens for the `\Illuminate\Auth\Events\Login` event and will then automatically set a `timezone` on your `user` model (stored in the database).
This package listens for the `\Illuminate\Auth\Events\Login` event and will then automatically set a `timezone` on your `user` model (stored in the database). It decides whether to update user timezone or not according to user `detect_timezone` attribute, or, if not set, according to default value in config. For non-authorized routes, where auth user info is not accessible, package will use default timezone from its config.

This package uses the [torann/geoip](http://lyften.com/projects/laravel-geoip/doc/) package which looks up the users location based on their IP address. The package also returns information like the users currency and users timezone. [You can configure this package separately if you require](#custom-configuration).

## How to use
## How to use

You can show dates to your user in their timezone by using

Expand All @@ -34,24 +34,56 @@ Or use our nice blade directive

## Installation

Pull in the package using Composer
### Pull in the package using Composer

```
```bash
composer require jamesmills/laravel-timezone
```

Publish database migrations

```
### Default timezone attributes location

By default, timezone attributes placed into `users` table. If you wish to use package with this default, see instructions below.

#### Publish database migrations

```bash
php artisan vendor:publish --provider="JamesMills\LaravelTimezone\LaravelTimezoneServiceProvider" --tag=migrations
```

Run the database migrations. This will add a `timezone` column to your `users` table.
Run the database migrations. This will add `timezone` and `detect_timezone` columns to your `users` table. Note that migration will be placed to default Laravel migrations folder, so if you use custom folder, you should move migration file to appropriate location.

```
```bash
php artisan migrate
```

#### Update User model

Add `JamesMills\LaravelTimezone\Traits\HasTimezone` trait to your `user` model:

```php
use HasTimezone;
```

If you wish to work with `detect_timezone` attribute directly, you can add boolean cast for your `User` model:

```php
protected $casts = [
'detect_timezone' => 'boolean',
];
```

If you wish to set per-user timezone overwriting at user creation time, you can add `detect_timezone` attribute to your `User` model fillable property:

```php
protected $fillable = [
'detect_timezone',
];
```

### Custom timezone attributes location

If you wish to use different location for `timezone` and `detect_timezone` attributes, e.g. `Profile` model, you should override `HasTimezone` trait and use overriden one in your `User` model.

## Examples

### Showing date/time to the user in their timezone
Expand All @@ -72,6 +104,14 @@ If you wish you can set a custom format and also include a nice version of the t
// 2018-07-04 3:32 New York, America
```

If you wish to further work with converted Carbon instance, you can use toLocal method:

```php
{{ Timezone::toLocal($post->created_at)->diffForHumans() }}

// diff calculated relative to datetime with user-end timezone
```

### Using blade directive

Making your life easier one small step at a time
Expand Down Expand Up @@ -125,7 +165,7 @@ To override this configuration, you just need to change the `flash` property ins

### Overwrite existing timezones in the database

By default, the timezone will be overwritten at each login with the current user timezone. This behavior can be restricted to only update the timezone if it is blank by setting the `'overwrite' => false,` config option.
User timezone will be overwritten at each login with the current user timezone if `detect_timezone` is set to true for this user. If this attribute is not set, by default, the timezone will be overwritten. This behavior can be restricted to only update the timezone if it is blank by setting the `'overwrite' => false,` config option.

### Default Format

Expand Down
27 changes: 12 additions & 15 deletions src/LaravelTimezoneServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ class LaravelTimezoneServiceProvider extends ServiceProvider
*/
protected $defer = false;

private function registerEventListener(): void
{
Event::listen(
config('timezone.timezone_check.events', null) ?? [
\Illuminate\Auth\Events\Login::class,
\Laravel\Passport\Events\AccessTokenCreated::class,
],
config('timezone.timezone_check.listener', null) ?? UpdateUsersTimezone::class
);
}

/**
* Perform post-registration booting of services.
Expand All @@ -26,9 +36,9 @@ class LaravelTimezoneServiceProvider extends ServiceProvider
public function boot()
{
// Allow migrations publish
if (! class_exists('AddTimezoneColumnToUsersTable')) {
if (!class_exists('AddTimezoneColumnsToUsersTable')) {
$this->publishes([
__DIR__ . '/database/migrations/add_timezone_column_to_users_table.php.stub' => database_path('/migrations/' . date('Y_m_d_His') . '_add_timezone_column_to_users_table.php'),
__DIR__ . '/database/migrations/add_timezone_columns_to_users_table.php.stub' => database_path('/migrations/' . date('Y_m_d_His') . '_add_timezone_columns_to_users_table.php'),
], 'migrations');
}

Expand Down Expand Up @@ -76,17 +86,4 @@ public function register()
'timezone'
);
}

/**
*
*/
private function registerEventListener(): void
{
$events = [
\Illuminate\Auth\Events\Login::class,
\Laravel\Passport\Events\AccessTokenCreated::class,
];

Event::listen($events, UpdateUsersTimezone::class);
}
}
186 changes: 99 additions & 87 deletions src/Listeners/Auth/UpdateUsersTimezone.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,142 +4,154 @@

use Illuminate\Auth\Events\Login;
use Illuminate\Support\Facades\Auth;

use Laravel\Passport\Events\AccessTokenCreated;
use Torann\GeoIP\Location;
use JamesMills\LaravelTimezone\Traits\FlashesMessage;
use JamesMills\LaravelTimezone\Traits\RetrievesGeoIpTimezone;

class UpdateUsersTimezone
{
use RetrievesGeoIpTimezone;
use FlashesMessage;

/**
* Handle the event.
*
* @return void
*/
public function handle($event)
private function getFromLookup(): ?string
{
$user = null;

/**
* If the event is AccessTokenCreated,
* we logged the user and return,
* stopping the execution.
*
* The Auth::loginUsingId dispatches a Login event,
* making this listener be called again.
*/
if ($event instanceof AccessTokenCreated) {
Auth::loginUsingId($event->userId);
$result = null;

return;
}
foreach (config('timezone.lookup', []) as $type => $keys) {
if (empty($keys)) {
continue;
}

/**
* If the event is Login, we get the user from the web guard.
*/
if ($event instanceof Login) {
$user = Auth::user();
}
$result = $this->lookup($type, $keys);

/**
* If no user is found, we just return. Nothing to do here.
*/
if (is_null($user)) {
return;
if ($result === null) {
continue;
}
}

$ip = $this->getFromLookup();
$geoip_info = geoip()->getLocation($ip);
return $result;
}

if ($user->timezone != $geoip_info['timezone']) {
if (config('timezone.overwrite') == true || $user->timezone == null) {
$user->timezone = $geoip_info['timezone'];
$user->save();
private function lookup($type, $keys): ?string
{
$value = null;

$this->notify($geoip_info);
foreach ($keys as $key) {
if (!request()->$type->has($key)) {
continue;
}
$value = request()->$type->get($key);
}

return $value;
}

/**
* @param Location $geoip_info
*/
private function notify(Location $geoip_info)
protected function notify(array $info): void
{
if (config('timezone.flash') == 'off') {
if (config('timezone.flash', 'off') === 'off') {
return;
}

$message = 'We have set your timezone to ' . $geoip_info['timezone'];
if ($info['timezone'] === null) {
$key = config('timezone.messages.fail.key', 'error');
$message = config('timezone.messages.fail.message');
} else {
if ($info['default']) {
$key = config('timezone.messages.default.key', 'warning');
$message = config('timezone.messages.default.message');
} else {
$key = config('timezone.messages.success.key', 'info');
$message = config('timezone.messages.success.message');
}

if (config('timezone.flash') == 'laravel') {
request()->session()->flash('success', $message);
if ($message !== null) {
$message = sprintf($message, $info['timezone']);
}
}

if ($message === null) {
return;
}

if (config('timezone.flash') == 'laracasts') {
flash()->success($message);

if (config('timezone.flash') === 'laravel') {
$this->flashLaravelMessage($key, $message);
return;
}

if (config('timezone.flash') == 'mercuryseries') {
flashy()->success($message);

if (config('timezone.flash') === 'laracasts') {
$this->flashLaracastsMessage($key, $message);
return;
}

if (config('timezone.flash') == 'spatie') {
flash()->success($message);

if (config('timezone.flash') === 'mercuryseries') {
$this->flashMercuryseriesMessage($key, $message);
return;
}

if (config('timezone.flash') == 'mckenziearts') {
notify()->success($message);
if (config('timezone.flash') === 'spatie') {
$this->flashSpatieMessage($key, $message);
return;
}

if (config('timezone.flash') === 'mckenziearts') {
$this->flashMckenzieartsMessage($key, $message);
return;
}
}

/**
* @return mixed
*/
private function getFromLookup()
public function handle($event): void
{
$result = null;
$user = null;

foreach (config('timezone.lookup') as $type => $keys) {
if (empty($keys)) {
continue;
}
/**
* If the event is AccessTokenCreated,
* we logged the user and return,
* stopping the execution.
*
* The Auth::loginUsingId dispatches a Login event,
* making this listener be called again.
*/
if ($event instanceof AccessTokenCreated) {
Auth::loginUsingId($event->userId);

$result = $this->lookup($type, $keys);
return;
}

if (is_null($result)) {
continue;
}
/**
* If the event is Login, we get the user from the web guard.
*/
if ($event instanceof Login) {
$user = Auth::user();
}

return $result;
}
/**
* If no user is found or it has no timezone-related methods, return.
*/
if (
$user === null ||
!method_exists($user, 'getTimezone') ||
!method_exists($user, 'getDetectTimezone') ||
!method_exists($user, 'setTimezone') ||
!method_exists($user, 'setDetectTimezone')
) {
return;
}

/**
* @param $type
* @param $keys
* @return string|null
*/
private function lookup($type, $keys)
{
$value = null;
$overwrite = $user->getDetectTimezone() ?? config('timezone.overwrite', true);
$timezone = $user->getTimezone();

foreach ($keys as $key) {
if (!request()->$type->has($key)) {
continue;
if ($timezone === null || $overwrite === true) {
$info = $this->getGeoIpTimezone($this->getFromLookup());

if ($timezone === null || $timezone !== $info['timezone']) {
if ($info['timezone'] !== null) {
$user->setTimezone($info['timezone']);
$user->save();
}

$this->notify($info);
}
$value = request()->$type->get($key);
}

return $value;
}
}
Loading