initial
This commit is contained in:
32
app/Console/Kernel.php
Normal file
32
app/Console/Kernel.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule The schedule.
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__ . '/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
50
app/Enum/Enum.php
Normal file
50
app/Enum/Enum.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enum;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
class Enum
|
||||
{
|
||||
/**
|
||||
* Caches reflections of enum subclasses.
|
||||
*
|
||||
* @var array<class-string<static>, ReflectionClass<static>>
|
||||
*/
|
||||
public static $reflectionCache = [];
|
||||
|
||||
|
||||
/**
|
||||
* Returns a reflection of the enum subclass.
|
||||
*
|
||||
* @return ReflectionClass<static>
|
||||
*/
|
||||
public static function getReflection(): ReflectionClass
|
||||
{
|
||||
$class = static::class;
|
||||
|
||||
return static::$reflectionCache[$class] ??= new ReflectionClass($class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the constants in the enum subclass
|
||||
*
|
||||
* @return array<static>
|
||||
*/
|
||||
public static function getConstants(): array
|
||||
{
|
||||
return static::getReflection()->getConstants();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the constants values in the enum subclass
|
||||
*
|
||||
* @return array<static>
|
||||
*/
|
||||
public static function getConstantValues(): array
|
||||
{
|
||||
return array_values(static::getReflection()->getConstants());
|
||||
}
|
||||
}
|
||||
165
app/Enum/HttpResponseCodes.php
Normal file
165
app/Enum/HttpResponseCodes.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enum;
|
||||
|
||||
class HttpResponseCodes extends Enum
|
||||
{
|
||||
public const HTTP_CONTINUE = 100;
|
||||
public const HTTP_SWITCHING_PROTOCOLS = 101;
|
||||
public const HTTP_PROCESSING = 102;
|
||||
|
||||
public const HTTP_OK = 200;
|
||||
public const HTTP_CREATED = 201;
|
||||
public const HTTP_ACCEPTED = 202;
|
||||
public const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
|
||||
public const HTTP_NO_CONTENT = 204;
|
||||
public const HTTP_RESET_CONTENT = 205;
|
||||
public const HTTP_PARTIAL_CONTENT = 206;
|
||||
public const HTTP_MULTI_STATUS = 207;
|
||||
|
||||
public const HTTP_ALREADY_REPORTED = 208;
|
||||
|
||||
public const HTTP_IM_USED = 226;
|
||||
|
||||
public const HTTP_MULTIPLE_CHOICES = 300;
|
||||
public const HTTP_MOVED_PERMANENTLY = 301;
|
||||
public const HTTP_FOUND = 302;
|
||||
public const HTTP_SEE_OTHER = 303;
|
||||
public const HTTP_NOT_MODIFIED = 304;
|
||||
public const HTTP_USE_PROXY = 305;
|
||||
public const HTTP_RESERVED = 306;
|
||||
public const HTTP_TEMPORARY_REDIRECT = 307;
|
||||
public const HTTP_PERMANENTLY_REDIRECT = 308;
|
||||
|
||||
public const HTTP_BAD_REQUEST = 400;
|
||||
public const HTTP_UNAUTHORIZED = 401;
|
||||
public const HTTP_PAYMENT_REQUIRED = 402;
|
||||
public const HTTP_FORBIDDEN = 403;
|
||||
public const HTTP_NOT_FOUND = 404;
|
||||
public const HTTP_METHOD_NOT_ALLOWED = 405;
|
||||
public const HTTP_NOT_ACCEPTABLE = 406;
|
||||
public const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
|
||||
public const HTTP_REQUEST_TIMEOUT = 408;
|
||||
public const HTTP_CONFLICT = 409;
|
||||
public const HTTP_GONE = 410;
|
||||
public const HTTP_LENGTH_REQUIRED = 411;
|
||||
public const HTTP_PRECONDITION_FAILED = 412;
|
||||
public const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
|
||||
public const HTTP_REQUEST_URI_TOO_LONG = 414;
|
||||
public const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
public const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
|
||||
public const HTTP_EXPECTATION_FAILED = 417;
|
||||
public const HTTP_I_AM_A_TEAPOT = 418;
|
||||
|
||||
public const HTTP_MISDIRECTED_REQUEST = 421;
|
||||
public const HTTP_UNPROCESSABLE_ENTITY = 422;
|
||||
public const HTTP_LOCKED = 423;
|
||||
public const HTTP_FAILED_DEPENDENCY = 424;
|
||||
public const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425;
|
||||
public const HTTP_UPGRADE_REQUIRED = 426;
|
||||
public const HTTP_PRECONDITION_REQUIRED = 428;
|
||||
public const HTTP_TOO_MANY_REQUESTS = 429;
|
||||
public const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
|
||||
public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
|
||||
|
||||
public const HTTP_INTERNAL_SERVER_ERROR = 500;
|
||||
public const HTTP_NOT_IMPLEMENTED = 501;
|
||||
public const HTTP_BAD_GATEWAY = 502;
|
||||
public const HTTP_SERVICE_UNAVAILABLE = 503;
|
||||
public const HTTP_GATEWAY_TIMEOUT = 504;
|
||||
public const HTTP_VERSION_NOT_SUPPORTED = 505;
|
||||
public const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506;
|
||||
public const HTTP_INSUFFICIENT_STORAGE = 507;
|
||||
public const HTTP_LOOP_DETECTED = 508;
|
||||
public const HTTP_NOT_EXTENDED = 510;
|
||||
public const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;
|
||||
|
||||
/**
|
||||
* HTTP Response Messages
|
||||
* @var string[]
|
||||
*/
|
||||
public static $statusTexts = [
|
||||
100 => 'Continue.',
|
||||
101 => 'Switching Protocols.',
|
||||
102 => 'Processing.',
|
||||
// RFC2518
|
||||
200 => 'OK.',
|
||||
201 => 'Created.',
|
||||
202 => 'Accepted.',
|
||||
203 => 'Non-Authoritative Information.',
|
||||
204 => 'No Content.',
|
||||
205 => 'Reset Content.',
|
||||
206 => 'Partial Content.',
|
||||
207 => 'Multi-Status.',
|
||||
// RFC4918
|
||||
208 => 'Already Reported.',
|
||||
// RFC5842
|
||||
226 => 'IM Used.',
|
||||
// RFC3229
|
||||
300 => 'Multiple Choices.',
|
||||
301 => 'Moved Permanently.',
|
||||
302 => 'Found.',
|
||||
303 => 'See Other.',
|
||||
304 => 'Not Modified.',
|
||||
305 => 'Use Proxy.',
|
||||
307 => 'Temporary Redirect.',
|
||||
308 => 'Permanent Redirect.',
|
||||
// RFC7238
|
||||
400 => 'Bad Request.',
|
||||
401 => 'Unauthorized.',
|
||||
402 => 'Payment Required.',
|
||||
403 => 'Forbidden.',
|
||||
404 => 'Not Found.',
|
||||
405 => 'Method Not Allowed.',
|
||||
406 => 'Not Acceptable.',
|
||||
407 => 'Proxy Authentication Required.',
|
||||
408 => 'Request Timeout.',
|
||||
409 => 'Conflict.',
|
||||
410 => 'Gone.',
|
||||
411 => 'Length Required.',
|
||||
412 => 'Precondition Failed.',
|
||||
413 => 'Payload Too Large.',
|
||||
414 => 'URI Too Long.',
|
||||
415 => 'Unsupported Media Type.',
|
||||
416 => 'Range Not Satisfiable.',
|
||||
417 => 'Expectation Failed.',
|
||||
418 => 'I\'m a teapot.',
|
||||
// RFC2324
|
||||
421 => 'Misdirected Request.',
|
||||
// RFC7540
|
||||
422 => 'Unprocessable Entity.',
|
||||
// RFC4918
|
||||
423 => 'Locked.',
|
||||
// RFC4918
|
||||
424 => 'Failed Dependency.',
|
||||
// RFC4918
|
||||
425 => 'Reserved for WebDAV advanced collections expired proposal.',
|
||||
// RFC2817
|
||||
426 => 'Upgrade Required.',
|
||||
// RFC2817
|
||||
428 => 'Precondition Required.',
|
||||
// RFC6585
|
||||
429 => 'Too Many Requests.',
|
||||
// RFC6585
|
||||
431 => 'Request Header Fields Too Large.',
|
||||
// RFC6585
|
||||
451 => 'Unavailable For Legal Reasons.',
|
||||
// RFC7725
|
||||
500 => 'Internal Server Error.',
|
||||
501 => 'Not Implemented.',
|
||||
502 => 'Bad Gateway.',
|
||||
503 => 'Service Unavailable.',
|
||||
504 => 'Gateway Timeout.',
|
||||
505 => 'HTTP Version Not Supported.',
|
||||
506 => 'Variant Also Negotiates.',
|
||||
// RFC2295
|
||||
507 => 'Insufficient Storage.',
|
||||
// RFC4918
|
||||
508 => 'Loop Detected.',
|
||||
// RFC5842
|
||||
510 => 'Not Extended.',
|
||||
// RFC2774
|
||||
511 => 'Network Authentication Required.',
|
||||
// RFC6585
|
||||
];
|
||||
}
|
||||
85
app/Exceptions/Handler.php
Normal file
85
app/Exceptions/Handler.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Throwable;
|
||||
use PDOException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of exception types with their corresponding custom log levels.
|
||||
*
|
||||
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||
*/
|
||||
protected $levels = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array<int, class-string<\Throwable>>
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// $this->renderable(function (HttpException $e, $request) {
|
||||
// if ($request->is('api/*')) {
|
||||
// $message = $e->getMessage();
|
||||
// if ($message === '') {
|
||||
// $message = HttpResponseCodes::$statusTexts[$e->getStatusCode()];
|
||||
// }
|
||||
|
||||
// return response()->json([
|
||||
// 'message' => $message
|
||||
// ], $e->getStatusCode());
|
||||
// }
|
||||
// });
|
||||
|
||||
$this->renderable(function (NotFoundHttpException $e, $request) {
|
||||
if ($request->is('api/*') === true) {
|
||||
return response()->json([
|
||||
'message' => 'Resource not found'
|
||||
], 404);
|
||||
}
|
||||
});
|
||||
|
||||
$this->renderable(function (PDOException $e, $request) {
|
||||
if ($request->is('api/*') === true) {
|
||||
return response()->json([
|
||||
'message' => 'The server is currently unavailable'
|
||||
], 503);
|
||||
}
|
||||
});
|
||||
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
29
app/Filters/AuditFilter.php
Normal file
29
app/Filters/AuditFilter.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class AuditFilter
|
||||
{
|
||||
// public static function filter(Collection $collection): array
|
||||
// {
|
||||
// $collection->transform(function ($item, $key) {
|
||||
// $row = $item->toArray();
|
||||
|
||||
// unset($row['user_type']);
|
||||
// unset($row['auditable_type']);
|
||||
|
||||
// if (array_key_exists('password', $row['old_values'])) {
|
||||
// $row['old_values']['password'] = '###';
|
||||
// }
|
||||
// if (array_key_exists('password', $row['new_values'])) {
|
||||
// $row['new_values']['password'] = '###';
|
||||
// }
|
||||
|
||||
// return $row;
|
||||
// });
|
||||
|
||||
// return $collection->toArray();
|
||||
// }
|
||||
}
|
||||
70
app/Filters/EventFilter.php
Normal file
70
app/Filters/EventFilter.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\Event;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
|
||||
class EventFilter extends FilterAbstract
|
||||
{
|
||||
/**
|
||||
* Class name of Model
|
||||
* @var string
|
||||
*/
|
||||
protected $class = '\App\Models\Event';
|
||||
|
||||
/**
|
||||
* Default column sorting (prefix with - for descending)
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $defaultSort = 'start_at';
|
||||
|
||||
/**
|
||||
* Filter columns for q param
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $q = [
|
||||
'_' => ['title','content'],
|
||||
'location' => ['location','address'],
|
||||
];
|
||||
|
||||
// protected $q = [
|
||||
// 'title',
|
||||
// 'content'
|
||||
// ];
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the user can view the media model
|
||||
*
|
||||
* @param Event $event The event instance.
|
||||
* @param mixed $user The current logged in user.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function viewable(Event $event, mixed $user)
|
||||
{
|
||||
return (strcasecmp($event->status, 'draft') !== 0 && $event->publish_at <= now())
|
||||
|| $user?->hasPermission('admin/events') === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the prebuild query to limit results
|
||||
*
|
||||
* @param EloquentBuilder $builder The builder instance.
|
||||
* @param mixed $user The current logged in user.
|
||||
* @return EloquentBuilder|null
|
||||
*/
|
||||
protected function prebuild(Builder $builder, mixed $user)
|
||||
{
|
||||
if (
|
||||
$user?->hasPermission('admin/events') !== true
|
||||
) {
|
||||
return $builder
|
||||
->where('status', '!=', 'draft')
|
||||
->where('publish_at', '>=', now());
|
||||
}
|
||||
}
|
||||
}
|
||||
589
app/Filters/FilterAbstract.php
Normal file
589
app/Filters/FilterAbstract.php
Normal file
@@ -0,0 +1,589 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use ReflectionClass;
|
||||
use RuntimeException;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Schema;
|
||||
|
||||
abstract class FilterAbstract
|
||||
{
|
||||
/**
|
||||
* The model class to filter
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $class;
|
||||
|
||||
/**
|
||||
* The filter request
|
||||
*
|
||||
* @var \Illuminate\Http\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The models table
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = '';
|
||||
|
||||
/**
|
||||
* Array of columns that can be filtered by the api
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $filterable = null;
|
||||
|
||||
/**
|
||||
* Default column sorting (prefix with - for descending)
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $defaultSort = 'id';
|
||||
|
||||
/**
|
||||
* Default collection result limit
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $defaultLimit = 50;
|
||||
|
||||
/**
|
||||
* Found records from query
|
||||
* @var integer
|
||||
*/
|
||||
protected $foundTotal = 0;
|
||||
|
||||
/**
|
||||
* Maximum collection result limit
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $maxLimit = 100;
|
||||
|
||||
/**
|
||||
* Only return these attributes in the results
|
||||
* (minus any excludes)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $only = [];
|
||||
|
||||
/**
|
||||
* Exclude these attributes from the results
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $exclude = [];
|
||||
|
||||
/**
|
||||
* Filter columns for q param
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $q = [];
|
||||
|
||||
|
||||
/**
|
||||
* Filter constructor.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request Request object.
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only include the specified attributes in the results.
|
||||
*
|
||||
* @param string|array $only Only return these attributes.
|
||||
* @return void
|
||||
*/
|
||||
public function only(mixed $only)
|
||||
{
|
||||
if (is_array($only) === true) {
|
||||
$this->only = $only;
|
||||
} else {
|
||||
$this->only = [$only];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude the specified attributes in the results.
|
||||
*
|
||||
* @param string|array $exclude Attributes to exclude.
|
||||
* @return void
|
||||
*/
|
||||
public function exclude(mixed $exclude)
|
||||
{
|
||||
if (is_array($exclude) === true) {
|
||||
$this->exclude = $exclude;
|
||||
} else {
|
||||
$this->exclude = [$exclude];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the model is viewable by the user
|
||||
*
|
||||
* @param mixed $model Model instance.
|
||||
* @param mixed $user Current user.
|
||||
* @return boolean
|
||||
*/
|
||||
// protected function viewable(mixed $model, mixed $user)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Prepend action to the builder to limit the results
|
||||
*
|
||||
* @param Builder $builder Builder instance.
|
||||
* @param mixed $user Current user.
|
||||
* @return Builder|null
|
||||
*/
|
||||
// protected function prebuild(Builder $builder, mixed $user)
|
||||
// {
|
||||
// return $builder;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Return an array of attributes visible in the results
|
||||
*
|
||||
* @param array $attributes Attributes currently visible.
|
||||
* @param User|null $user Current logged in user or null.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function seeAttributes(array $attributes, mixed $user)
|
||||
{
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all the requested filters if available.
|
||||
*
|
||||
* @param Model $model Model object to filter. If null create query.
|
||||
* @return Builder|Model
|
||||
*/
|
||||
public function filter(Model $model = null)
|
||||
{
|
||||
$this->foundTotal = 0;
|
||||
|
||||
$builder = $this->class::query();
|
||||
|
||||
/* Get the related model */
|
||||
$classModel = $model;
|
||||
if ($model === null) {
|
||||
$classModel = $builder->getModel();
|
||||
}
|
||||
|
||||
/* Get table name */
|
||||
if ($this->table === '') {
|
||||
if ($model === null) {
|
||||
$this->table = $classModel->getTable();
|
||||
} else {
|
||||
$this->table = $model->getTable();
|
||||
}
|
||||
}
|
||||
|
||||
/* Run query prebuilder or viewable */
|
||||
if ($model === null) {
|
||||
if (method_exists($this, 'prebuild') === true) {
|
||||
$prebuilder = $this->prebuild($builder, $this->request->user());
|
||||
if ($prebuilder instanceof Builder) {
|
||||
$builder = $prebuilder;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (method_exists($this, 'viewable') === true) {
|
||||
if ($this->viewable($model, $this->request->user()) === false) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get attributes from table or use 'only' */
|
||||
$attributes = [];
|
||||
if (is_array($this->only) === true && count($this->only) > 0) {
|
||||
$attributes = $this->only;
|
||||
} else {
|
||||
$attributes = Schema::getColumnListing($this->table);
|
||||
}
|
||||
|
||||
/* Run attribute modifiers*/
|
||||
$modifiedAttribs = $this->seeAttributes($attributes, $this->request->user());
|
||||
if (is_array($modifiedAttribs) === true) {
|
||||
$attributes = $modifiedAttribs;
|
||||
}
|
||||
|
||||
foreach ($attributes as $key => $column) {
|
||||
$method = 'see' . Str::studly($column) . 'Attribute';
|
||||
if (
|
||||
method_exists($this, $method) === true &&
|
||||
$this->$method($this->request->user()) === false
|
||||
) {
|
||||
unset($attributes[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($this->exclude) === true && count($this->exclude) > 0) {
|
||||
$attributes = array_diff($attributes, $this->exclude);
|
||||
}
|
||||
|
||||
/* Setup attributes and appends */
|
||||
// $attributesAppends = array_merge($attributes, $classModel->getAppends());
|
||||
|
||||
/* Apply ?fields= request to attributes */
|
||||
if ($this->request->has('fields') === true) {
|
||||
$attributes = array_intersect($attributes, explode(',', $this->request->fields));
|
||||
}
|
||||
|
||||
/* Hide remaining attributes in model (if present) and return */
|
||||
if ($model !== null) {
|
||||
// TODO: Also show $this->request->fields that are appends
|
||||
|
||||
$model->makeHidden(array_diff(Schema::getColumnListing($this->table), $attributes));
|
||||
return $model;
|
||||
}
|
||||
|
||||
/* Are there attributes left? */
|
||||
if (count($attributes) === 0) {
|
||||
$this->foundTotal = 0;
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
/* apply select! */
|
||||
$builder->select($attributes);
|
||||
|
||||
/* Setup filterables if not present */
|
||||
if ($this->filterable === null) {
|
||||
$this->filterable = $attributes;
|
||||
}
|
||||
|
||||
/* Filter values */
|
||||
$filterRequest = array_filter($this->request->only(array_intersect($attributes, $this->filterable)));
|
||||
$this->builderArrayFilter($builder, $filterRequest);
|
||||
|
||||
if (is_array($this->q) === true && count($this->q) > 0) {
|
||||
$qQueries = [];
|
||||
foreach ($this->q as $key => $value) {
|
||||
if (is_array($value) === true) {
|
||||
$qKey = $key === '_' ? '' : $key;
|
||||
foreach ($value as $subvalue) {
|
||||
$qQueries[$key][$subvalue] = $this->request->get("q" . $qKey);
|
||||
}
|
||||
} elseif ($this->request->has("q") === true) {
|
||||
$qQueries['_'][$value] = $this->request->get("q");
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($qQueries as $key => $value) {
|
||||
$builder->where(function ($query) use ($value) {
|
||||
$this->builderArrayFilter($query, $value, 'or');
|
||||
});
|
||||
}
|
||||
}//end if
|
||||
|
||||
/* Apply sorting */
|
||||
$sortList = $this->defaultSort;
|
||||
if ($this->request->has('sort') === true) {
|
||||
$sortList = explode(',', $this->request->sort);
|
||||
}
|
||||
|
||||
/* Transform sort list to array */
|
||||
if (is_array($sortList) === false) {
|
||||
if (strlen($sortList) > 0) {
|
||||
$sortList = [$sortList];
|
||||
} else {
|
||||
$sortList = [];
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove non-viewable attributes from sort list */
|
||||
if (count($sortList) > 0) {
|
||||
$sortList = array_intersect($attributes, $sortList);
|
||||
}
|
||||
|
||||
/* Do we have any sort element left? */
|
||||
if (count($sortList) > 0) {
|
||||
foreach ($sortList as $sortAttribute) {
|
||||
$prefix = substr($sortAttribute, 0, 1);
|
||||
$direction = 'asc';
|
||||
|
||||
if (in_array($prefix, ['-', '+']) === true) {
|
||||
$sortAttribute = substr($sortAttribute, 1);
|
||||
if ($prefix === '-') {
|
||||
$direction = 'desc';
|
||||
}
|
||||
}
|
||||
|
||||
$builder->orderBy($sortAttribute, $direction);
|
||||
}//end foreach
|
||||
}//end if
|
||||
|
||||
/* save found count */
|
||||
$this->foundTotal = $builder->count();
|
||||
|
||||
/* Apply result limit */
|
||||
$limit = $this->defaultLimit;
|
||||
if ($this->request->has('limit') === true) {
|
||||
$limit = intval($this->request->limit);
|
||||
}
|
||||
if ($limit < 1) {
|
||||
$limit = 1;
|
||||
}
|
||||
if ($limit > $this->maxLimit && $this->maxLimit !== 0) {
|
||||
$limit = $this->maxLimit;
|
||||
}
|
||||
|
||||
$builder->limit($limit);
|
||||
|
||||
/* Apply page offset */
|
||||
if ($this->request->has('page') === true) {
|
||||
$page = intval($this->request->page);
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$builder->offset((intval($this->request->page) - 1) * $limit);
|
||||
}
|
||||
|
||||
/* run spot run */
|
||||
$collection = $builder->get();
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter content based on the filterRequest
|
||||
* @param mixed $builder Builder object
|
||||
* @param array $filterRequest Filter key/value
|
||||
* @param string $defaultBoolean Default where boolean
|
||||
* @return void
|
||||
*/
|
||||
protected function builderArrayFilter(mixed $builder, array $filterRequest, string $defaultBoolean = 'and')
|
||||
{
|
||||
foreach ($filterRequest as $filterAttribute => $filterValue) {
|
||||
$tags = [];
|
||||
$boolean = $defaultBoolean;
|
||||
|
||||
$matches = preg_split('/(?<!\\\\)"/', $filterValue, -1, PREG_SPLIT_OFFSET_CAPTURE);
|
||||
foreach ($matches as $idx => $match_info) {
|
||||
if (($idx % 2) === true) {
|
||||
if (substr($filterValue, ($match_info[1] - 2), 1) === ',') {
|
||||
$tags[] = ['operator' => '', 'tag' => stripslashes(trim($match_info[0]))];
|
||||
} else {
|
||||
$tags[(count($tags) - 1)]['tag'] .= stripslashes(trim($match_info[0]));
|
||||
}
|
||||
} else {
|
||||
$innerTags = [$match_info[0]];
|
||||
if (strpos($match_info[0], ',') !== false) {
|
||||
$innerTags = preg_split('/(?<!\\\\),/', $match_info[0]);
|
||||
}
|
||||
|
||||
foreach ($innerTags as $tag) {
|
||||
$tag = stripslashes(trim($tag));
|
||||
if (strlen($tag) > 0) {
|
||||
$operator = '=';
|
||||
|
||||
$single = substr($tag, 0, 1);
|
||||
$double = substr($tag . ' ', 0, 2); // add empty space incase len $tag < 2
|
||||
|
||||
// check for operators at start
|
||||
if (in_array($double, ['!=', '<>', '><', '>=', '<=', '=>', '=<']) === true) {
|
||||
if ($double === '<>' || $double === '><') {
|
||||
$double = '!=';
|
||||
} elseif ($double === '=>') {
|
||||
$double = '>=';
|
||||
} elseif ($double === '=<') {
|
||||
$double == '>=';
|
||||
}
|
||||
|
||||
$operator = $double;
|
||||
$tag = substr($tag, 2);
|
||||
} else {
|
||||
if (in_array($single, ['=', '!', '>', '<', '~', '%']) === true) {
|
||||
if ($single === '=') {
|
||||
$single = '=='; // a single '=' is actually a double '=='
|
||||
}
|
||||
|
||||
$operator = $single;
|
||||
$tag = substr($tag, 1);
|
||||
}
|
||||
}//end if
|
||||
|
||||
$tags[] = ['operator' => $operator, 'tag' => $tag];
|
||||
}//end if
|
||||
}//end foreach
|
||||
}//end if
|
||||
}//end foreach
|
||||
|
||||
if (count($tags) > 1) {
|
||||
$boolean = 'or';
|
||||
}
|
||||
|
||||
foreach ($tags as $tag_data) {
|
||||
$operator = $tag_data['operator'];
|
||||
$value = $tag_data['tag'];
|
||||
$table = $this->table;
|
||||
$column = $filterAttribute;
|
||||
|
||||
if (($dotPos = strpos($filterAttribute, '.')) !== false) {
|
||||
$table = substr($filterAttribute, 0, $dotPos);
|
||||
$column = substr($filterAttribute, ($dotPos + 1));
|
||||
}
|
||||
|
||||
$columnType = DB::getSchemaBuilder()->getColumnType($table, $column);
|
||||
|
||||
if (
|
||||
in_array($columnType, ['tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint',
|
||||
'decimal', 'float', 'double', 'real', 'double precision'
|
||||
]) === true
|
||||
) {
|
||||
if (in_array($operator, ['=', '>', '<', '>=', '<=', '%', '!']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columnType = 'numeric';
|
||||
} elseif (in_array($columnType, ['date', 'time', 'datetime', 'timestamp', 'year']) === true) {
|
||||
if (in_array($operator, ['=', '>', '<', '>=', '<=', '!']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columnType = 'datetime';
|
||||
} elseif (
|
||||
in_array($columnType, ['string', 'char', 'varchar', 'timeblob', 'blob', 'mediumblob',
|
||||
'longblob', 'tinytext', 'text', 'mediumtext', 'longtext', 'enum'
|
||||
]) === true
|
||||
) {
|
||||
if (in_array($operator, ['=', '==', '!', '!=', '~']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$columnType = 'text';
|
||||
|
||||
if ($value === "''" || $value === '""') {
|
||||
$value = '';
|
||||
} elseif (strcasecmp($value, 'null') !== 0) {
|
||||
if ($operator === '!') {
|
||||
$operator = 'NOT LIKE';
|
||||
$value = '%' . $value . '%';
|
||||
} elseif ($operator === '=') {
|
||||
$operator = 'LIKE';
|
||||
$value = '%' . $value . '%';
|
||||
} elseif ($operator === '~') {
|
||||
$operator = 'SOUNDS LIKE';
|
||||
} elseif ($operator === '==') {
|
||||
$operator = '=';
|
||||
}
|
||||
}
|
||||
} elseif ($columnType === 'boolean') {
|
||||
if (in_array($operator, ['=', '!']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strtolower($value) === 'true') {
|
||||
$value = 1;
|
||||
} elseif (strtolower($value) === 'false') {
|
||||
$value = 0;
|
||||
}
|
||||
}//end if
|
||||
|
||||
$betweenSeperator = strpos($value, '<>');
|
||||
if (
|
||||
$operator === '=' && $betweenSeperator !== false && in_array($columnType, ['numeric',
|
||||
'datetime'
|
||||
]) === true
|
||||
) {
|
||||
$value = explode('<>', $value);
|
||||
$operator = '<>';
|
||||
}
|
||||
|
||||
if ($operator !== '') {
|
||||
$this->builderWhere($builder, $table, $column, $operator, $value, $boolean);
|
||||
}
|
||||
}//end foreach
|
||||
}//end foreach
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a where statement into the builder, taking the filter map into consideration
|
||||
*
|
||||
* @param Builder $builder Builder instance.
|
||||
* @param string $table Table name.
|
||||
* @param string $column Column name.
|
||||
* @param string $operator Where operator.
|
||||
* @param mixed $value Value to test.
|
||||
* @param string $boolean Use Or comparison.
|
||||
* @return void
|
||||
* @throws RuntimeException Error applying statement.
|
||||
* @throws InvalidArgumentException Error applying statement.
|
||||
*/
|
||||
protected function builderWhere(
|
||||
Builder &$builder,
|
||||
string $table,
|
||||
string $column,
|
||||
string $operator,
|
||||
mixed $value,
|
||||
string $boolean
|
||||
) {
|
||||
if (
|
||||
(is_string($value) === true && $operator !== '<>') || (is_array($value) === true && count($value) === 2 &&
|
||||
$operator === '<>')
|
||||
) {
|
||||
if ($table !== '' && $table !== $this->table) {
|
||||
$builder->whereHas($table, function ($query) use ($column, $operator, $value, $boolean) {
|
||||
if ($operator !== '<>') {
|
||||
if (strcasecmp($value, 'null') === 0) {
|
||||
if ($operator === '!') {
|
||||
$query->whereNotNull($column, $boolean);
|
||||
} else {
|
||||
$query->whereNull($column, $boolean);
|
||||
}
|
||||
} else {
|
||||
$query->where($column, $operator, $value, $boolean);
|
||||
}
|
||||
} else {
|
||||
$query->whereBetween($column, $value, $boolean);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if ($operator !== '<>') {
|
||||
if (strcasecmp($value, 'null') === 0) {
|
||||
if ($operator === '!') {
|
||||
$builder->whereNotNull($column, $boolean);
|
||||
} else {
|
||||
$builder->whereNull($column, $boolean);
|
||||
}
|
||||
} else {
|
||||
$builder->where($column, $operator, $value, $boolean);
|
||||
}
|
||||
} else {
|
||||
$builder->whereBetween($column, $value, $boolean);
|
||||
}
|
||||
}//end if
|
||||
}//end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the found total of items
|
||||
* @return integer
|
||||
*/
|
||||
public function foundTotal()
|
||||
{
|
||||
return $this->foundTotal;
|
||||
}
|
||||
}
|
||||
58
app/Filters/MediaFilter.php
Normal file
58
app/Filters/MediaFilter.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\Media;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
|
||||
class MediaFilter extends FilterAbstract
|
||||
{
|
||||
/**
|
||||
* Class name of Model
|
||||
* @var string
|
||||
*/
|
||||
protected $class = '\App\Models\Media';
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the user can view the media model
|
||||
*
|
||||
* @param Media $media The media instance.
|
||||
* @param mixed $user The current logged in user.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function viewable(Media $media, mixed $user)
|
||||
{
|
||||
if (empty($media->permission) === false) {
|
||||
return ($user?->hasPermission('admin/media') || $user?->hasPermission($media->permission));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the prebuild query to limit results
|
||||
*
|
||||
* @param EloquentBuilder $builder The builder instance.
|
||||
* @param mixed $user The current logged in user.
|
||||
* @return EloquentBuilder|null
|
||||
*/
|
||||
protected function prebuild(Builder $builder, mixed $user)
|
||||
{
|
||||
if ($user === null) {
|
||||
return $builder->whereNull('permission');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the permission attribute in the results
|
||||
*
|
||||
* @param User|null $user Current logged in user or null.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function seePermissionAttribute(mixed $user)
|
||||
{
|
||||
return ($user?->hasPermission('admin/media'));
|
||||
}
|
||||
}
|
||||
47
app/Filters/PostFilter.php
Normal file
47
app/Filters/PostFilter.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\Post;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
|
||||
class PostFilter extends FilterAbstract
|
||||
{
|
||||
/**
|
||||
* Class name of Model
|
||||
* @var string
|
||||
*/
|
||||
protected $class = '\App\Models\Post';
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the user can view the media model
|
||||
*
|
||||
* @param Post $post The post instance.
|
||||
* @param mixed $user The current logged in user.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function viewable(Post $post, mixed $user)
|
||||
{
|
||||
if ($user?->hasPermission('admin/posts') !== true) {
|
||||
return ($post->publish_at <= now());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the prebuild query to limit results
|
||||
*
|
||||
* @param EloquentBuilder $builder The builder instance.
|
||||
* @param mixed $user The current logged in user.
|
||||
* @return EloquentBuilder|null
|
||||
*/
|
||||
protected function prebuild(Builder $builder, mixed $user)
|
||||
{
|
||||
if ($user?->hasPermission('admin/posts') !== true) {
|
||||
return $builder->where('publish_at', '<=', Carbon::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
30
app/Filters/UserFilter.php
Normal file
30
app/Filters/UserFilter.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class UserFilter extends FilterAbstract
|
||||
{
|
||||
/**
|
||||
* The model class to filter
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $class = '\App\Models\User';
|
||||
|
||||
|
||||
/**
|
||||
* Return an array of attributes visible in the results
|
||||
*
|
||||
* @param array $attributes Attributes currently visible.
|
||||
* @param User|null $user Current logged in user or null.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function seeAttributes(array $attributes, mixed $user)
|
||||
{
|
||||
if ($user?->hasPermission('admin/users') !== true) {
|
||||
return ['id', 'username'];
|
||||
}
|
||||
}
|
||||
}
|
||||
172
app/Http/Controllers/Api/ApiController.php
Normal file
172
app/Http/Controllers/Api/ApiController.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ApiController extends Controller
|
||||
{
|
||||
/**
|
||||
* Resource name
|
||||
* @var string
|
||||
*/
|
||||
protected $resourceName = '';
|
||||
|
||||
|
||||
/**
|
||||
* Return generic json response with the given data.
|
||||
*
|
||||
* @param array $data Response data.
|
||||
* @param integer $respondCode Response status code.
|
||||
* @param array $headers Response headers.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondJson(array $data, int $respondCode = HttpResponseCodes::HTTP_OK, array $headers = [])
|
||||
{
|
||||
return response()->json($data, $respondCode, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return forbidden message
|
||||
*
|
||||
* @param string $message Response message.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondForbidden(string $message = 'You do not have permission to access the resource.')
|
||||
{
|
||||
return response()->json(['message' => $message], HttpResponseCodes::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return forbidden message
|
||||
*
|
||||
* @param string $message Response message.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondNotFound(string $message = 'The resource was not found.')
|
||||
{
|
||||
return response()->json(['message' => $message], HttpResponseCodes::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return too large message
|
||||
*
|
||||
* @param string $message Response message.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondTooLarge(string $message = 'The request entity is too large.')
|
||||
{
|
||||
return response()->json(['message' => $message], HttpResponseCodes::HTTP_REQUEST_ENTITY_TOO_LARGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return no content
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondNoContent()
|
||||
{
|
||||
return response()->json([], HttpResponseCodes::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return created
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondCreated()
|
||||
{
|
||||
return response()->json([], HttpResponseCodes::HTTP_CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return single error message
|
||||
*
|
||||
* @param string $message Error message.
|
||||
* @param integer $responseCode Resource code.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondError(string $message, int $responseCode = HttpResponseCodes::HTTP_UNPROCESSABLE_ENTITY)
|
||||
{
|
||||
return response()->json([
|
||||
'message' => $message
|
||||
], $responseCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return formatted errors
|
||||
*
|
||||
* @param array $errors Error messages.
|
||||
* @param integer $responseCode Resource code.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function respondWithErrors(array $errors, int $responseCode = HttpResponseCodes::HTTP_UNPROCESSABLE_ENTITY)
|
||||
{
|
||||
$keys = array_keys($errors);
|
||||
$error = $errors[$keys[0]];
|
||||
|
||||
if (count($keys) > 1) {
|
||||
$additional_errors = (count($keys) - 1);
|
||||
$error .= sprintf(' (and %d more %s', $additional_errors, Str::plural('error', $additional_errors));
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => $error,
|
||||
'errors' => $errors
|
||||
], $responseCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return resource data
|
||||
*
|
||||
* @param array|Model|Collection $data Resource data.
|
||||
* @param array|null $appendData Data to append to response.
|
||||
* @param integer $respondCode Resource code.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function respondAsResource(
|
||||
mixed $data,
|
||||
mixed $appendData = null,
|
||||
int $respondCode = HttpResponseCodes::HTTP_OK
|
||||
) {
|
||||
if ($data === null || ($data instanceof Collection && $data->count() === 0)) {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
|
||||
$resourceName = $this->resourceName;
|
||||
|
||||
if ($this->resourceName === '') {
|
||||
$resourceName = get_class($this);
|
||||
$resourceName = substr($resourceName, (strrpos($resourceName, '\\') + 1));
|
||||
$resourceName = substr($resourceName, 0, strpos($resourceName, 'Controller'));
|
||||
$resourceName = strtolower($resourceName);
|
||||
}
|
||||
|
||||
$is_multiple = true;
|
||||
|
||||
$dataArray = [];
|
||||
if ($data instanceof Collection) {
|
||||
$dataArray = $data->toArray();
|
||||
} elseif (is_array($data) === true) {
|
||||
$dataArray = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
$is_multiple = false;
|
||||
$dataArray = $data->toArray();
|
||||
}
|
||||
|
||||
$resource = [];
|
||||
if ($is_multiple === true) {
|
||||
$resource = [Str::plural($resourceName) => $dataArray];
|
||||
} else {
|
||||
$resource = [Str::singular($resourceName) => $dataArray];
|
||||
}
|
||||
|
||||
if ($appendData !== null) {
|
||||
$resource += $appendData;
|
||||
}
|
||||
|
||||
return response()->json($resource, $respondCode);
|
||||
}
|
||||
}
|
||||
101
app/Http/Controllers/Api/AuthController.php
Normal file
101
app/Http/Controllers/Api/AuthController.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Http\Requests\AuthLoginRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class AuthController extends ApiController
|
||||
{
|
||||
/**
|
||||
* Resource name
|
||||
* @var string
|
||||
*/
|
||||
protected $resourceName = 'user';
|
||||
|
||||
|
||||
/**
|
||||
* ApplicationController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// $this->middleware('auth:sanctum')
|
||||
// ->only(['me']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Current User details
|
||||
*
|
||||
* @param Request $request Current request data.
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function me(Request $request)
|
||||
{
|
||||
$user = $request->user()->makeVisible(['permissions']);
|
||||
return $this->respondAsResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login user with supplied creditials
|
||||
*
|
||||
* @param App\Http\Controllers\Api\AuthLoginRequest $request Created request data.
|
||||
* @return JsonResponse|void
|
||||
*/
|
||||
public function login(AuthLoginRequest $request)
|
||||
{
|
||||
$user = User::where('username', '=', $request->input('username'))->first();
|
||||
|
||||
if ($user !== null && Hash::check($request->input('password'), $user->password) === true) {
|
||||
if ($user->email_verified_at === null) {
|
||||
return $this->respondWithErrors([
|
||||
'username' => 'Email address has not been verified.'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($user->disabled === true) {
|
||||
return $this->respondWithErrors([
|
||||
'username' => 'Account has been disabled.'
|
||||
]);
|
||||
}
|
||||
|
||||
$token = $user->createToken('user_token')->plainTextToken;
|
||||
|
||||
$user->logins()->create([
|
||||
'token' => $token,
|
||||
'login' => now(),
|
||||
'ip_address' => $request->ip(),
|
||||
'user_agent' => $request->userAgent()
|
||||
]);
|
||||
|
||||
return $this->respondAsResource(
|
||||
$user->makeVisible(['permissions']),
|
||||
['token' => $token]
|
||||
);
|
||||
}//end if
|
||||
|
||||
return $this->respondWithErrors([
|
||||
'username' => 'Invalid username or password',
|
||||
'password' => 'Invalid username or password',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout current user
|
||||
*
|
||||
* @param Request $request Current request data.
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$user->logins()->where('token', $user->currentAccessToken())->update(['logout' => now()]);
|
||||
$user->currentAccessToken()->delete();
|
||||
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
}
|
||||
30
app/Http/Controllers/Api/ContactController.php
Normal file
30
app/Http/Controllers/Api/ContactController.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Requests\ContactSendRequest;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Mail\Contact;
|
||||
|
||||
class ContactController extends ApiController
|
||||
{
|
||||
/**
|
||||
* Send the request to the site admin by email
|
||||
*
|
||||
* @param \App\Http\Requests\User\ContactSendRequest $request Request data.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function send(ContactSendRequest $request)
|
||||
{
|
||||
dispatch((new SendEmailJob(
|
||||
config('contact.contact_address'),
|
||||
new Contact(
|
||||
$request->input('name'),
|
||||
$request->input('email'),
|
||||
$request->input('content')
|
||||
)
|
||||
)))->onQueue('mail');
|
||||
|
||||
return $this->respondCreated();
|
||||
}
|
||||
}
|
||||
88
app/Http/Controllers/Api/EventController.php
Normal file
88
app/Http/Controllers/Api/EventController.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\EventFilter;
|
||||
use App\Http\Requests\EventRequest;
|
||||
use App\Models\Event;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EventController extends ApiController
|
||||
{
|
||||
/**
|
||||
* ApplicationController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum')
|
||||
->only(['store','update','destroy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param EventFilter $filter The event filter.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(EventFilter $filter)
|
||||
{
|
||||
return $this->respondAsResource(
|
||||
$filter->filter(),
|
||||
['total' => $filter->foundTotal()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param EventRequest $request The event store request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(EventRequest $request)
|
||||
{
|
||||
$event = Event::create($request->all());
|
||||
return $this->respondAsResource(
|
||||
(new EventFilter($request))->filter($event),
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param EventFilter $filter The event filter.
|
||||
* @param \App\Models\Event $event The specified event.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(EventFilter $filter, Event $event)
|
||||
{
|
||||
return $this->respondAsResource($filter->filter($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param EventRequest $request The event update request.
|
||||
* @param \App\Models\Event $event The specified event.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(EventRequest $request, Event $event)
|
||||
{
|
||||
$event->update($request->all());
|
||||
return $this->respondAsResource((new EventFilter($request))->filter($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Event $event The specified event.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Event $event)
|
||||
{
|
||||
$event->delete();
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
}
|
||||
247
app/Http/Controllers/Api/MediaController.php
Normal file
247
app/Http/Controllers/Api/MediaController.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\MediaFilter;
|
||||
use App\Http\Requests\MediaStoreRequest;
|
||||
use App\Http\Requests\MediaUpdateRequest;
|
||||
use App\Models\Media;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Sanctum\PersonalAccessToken;
|
||||
|
||||
class MediaController extends ApiController
|
||||
{
|
||||
/**
|
||||
* ApplicationController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum')
|
||||
->only(['store','update','destroy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \App\Filters\MediaFilter $filter Created filter object.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(MediaFilter $filter)
|
||||
{
|
||||
return $this->respondAsResource(
|
||||
$filter->filter(),
|
||||
['total' => $filter->foundTotal()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param MediaFilter $filter The request filter.
|
||||
* @param Media $medium The request media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(MediaFilter $filter, Media $medium)
|
||||
{
|
||||
return $this->respondAsResource($filter->filter($medium));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new media resource
|
||||
*
|
||||
* @param MediaStoreRequest $request The uploaded media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(MediaStoreRequest $request)
|
||||
{
|
||||
$file = $request->file('file');
|
||||
if ($file === null) {
|
||||
return $this->respondError(['file' => 'An error occurred uploading the file to the server.']);
|
||||
}
|
||||
|
||||
if ($file->isValid() !== true) {
|
||||
switch ($file->getError()) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
return $this->respondTooLarge();
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
return $this->respondError(['file' => 'The file upload was interrupted.']);
|
||||
default:
|
||||
return $this->respondError(['file' => 'An error occurred uploading the file to the server.']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($file->getSize() > Media::maxUploadSize()) {
|
||||
return $this->respondTooLarge();
|
||||
}
|
||||
|
||||
$title = $file->getClientOriginalName();
|
||||
$mime = $file->getMimeType();
|
||||
$fileInfo = Media::store($file, empty($request->input('permission')));
|
||||
if ($fileInfo === null) {
|
||||
return $this->respondError(
|
||||
['file' => 'The file could not be stored on the server'],
|
||||
HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
$request->merge([
|
||||
'title' => $title,
|
||||
'mime' => $mime,
|
||||
'name' => $fileInfo['name'],
|
||||
'size' => filesize($fileInfo['path'])
|
||||
]);
|
||||
|
||||
$media = $request->user()->media()->create($request->all());
|
||||
return $this->respondAsResource((new MediaFilter($request))->filter($media));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the media resource in storage.
|
||||
*
|
||||
* @param MediaUpdateRequest $request The update request.
|
||||
* @param \App\Models\Media $medium The specified media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(MediaUpdateRequest $request, Media $medium)
|
||||
{
|
||||
if ((new MediaFilter($request))->filter($medium) === null) {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
|
||||
$file = $request->file('file');
|
||||
if ($file !== null) {
|
||||
if ($file->getSize() > Media::maxUploadSize()) {
|
||||
return $this->respondTooLarge();
|
||||
}
|
||||
|
||||
$oldPath = $medium->path();
|
||||
$fileInfo = Media::store($file, empty($request->input('permission')));
|
||||
if ($fileInfo === null) {
|
||||
return $this->respondError(
|
||||
['file' => 'The file could not be stored on the server'],
|
||||
HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
if (file_exists($oldPath) === true) {
|
||||
unlink($oldPath);
|
||||
}
|
||||
|
||||
$request->merge([
|
||||
'title' => $file->getClientOriginalName(),
|
||||
'mime' => $file->getMimeType(),
|
||||
'name' => $fileInfo['name'],
|
||||
'size' => filesize($fileInfo['path'])
|
||||
]);
|
||||
}//end if
|
||||
|
||||
$medium->update($request->all());
|
||||
return $this->respondWithTransformer($file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param Request $request Request instance.
|
||||
* @param \App\Models\Media $medium Specified media file.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request, Media $medium)
|
||||
{
|
||||
if ((new MediaFilter($request))->filter($medium) !== null) {
|
||||
if (file_exists($medium->path()) === true) {
|
||||
unlink($medium->path());
|
||||
}
|
||||
|
||||
$medium->delete();
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param Request $request Request instance.
|
||||
* @param \App\Models\Media $medium Specified media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function download(Request $request, Media $medium)
|
||||
{
|
||||
$respondJson = in_array('application/json', explode(',', $request->header('Accept', 'application/json')));
|
||||
|
||||
$headers = [];
|
||||
$path = $medium->path();
|
||||
|
||||
/* File exists */
|
||||
if (file_exists($path) === false) {
|
||||
if ($respondJson === false) {
|
||||
return redirect('/not-found');
|
||||
} else {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
$updated_at = Carbon::parse(filemtime($path));
|
||||
|
||||
$headerPragma = 'no-cache';
|
||||
$headerCacheControl = 'max-age=0, must-revalidate';
|
||||
$headerExpires = $updated_at->toRfc2822String();
|
||||
|
||||
if (empty($medium->permission) === true) {
|
||||
if ($request->user() === null && $request->has('token') === true) {
|
||||
$accessToken = PersonalAccessToken::findToken(urldecode($request->input('token')));
|
||||
|
||||
if (
|
||||
$accessToken !== null && (config('sanctum.expiration') === null ||
|
||||
$accessToken->created_at->lte(now()->subMinutes(config('sanctum.expiration'))) === false)
|
||||
) {
|
||||
$user = $accessToken->tokenable;
|
||||
}
|
||||
}
|
||||
if ($request->user() === null || $user->hasPermission($medium->permission) === false) {
|
||||
if ($respondJson === false) {
|
||||
return redirect('/login?redirect=' . $request->path());
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$headerPragma = 'public';
|
||||
$headerExpires = $updated_at->addMonth()->toRfc2822String();
|
||||
}//end if
|
||||
|
||||
$headerEtag = md5($updated_at->format('U'));
|
||||
$headerLastModified = $updated_at->toRfc2822String();
|
||||
|
||||
$headers = [
|
||||
'Cache-Control' => $headerCacheControl,
|
||||
'Content-Disposition' => sprintf('inline; filename="%s"', basename($path)),
|
||||
'Etag' => $headerEtag,
|
||||
'Expires' => $headerExpires,
|
||||
'Last-Modified' => $headerLastModified,
|
||||
'Pragma' => $headerPragma,
|
||||
];
|
||||
|
||||
$server = request()->server;
|
||||
|
||||
$requestModifiedSince = $server->has('HTTP_IF_MODIFIED_SINCE') &&
|
||||
$server->get('HTTP_IF_MODIFIED_SINCE') === $headerLastModified;
|
||||
|
||||
$requestNoneMatch = $server->has('HTTP_IF_NONE_MATCH') &&
|
||||
$server->get('HTTP_IF_NONE_MATCH') === $headerEtag;
|
||||
|
||||
if ($requestModifiedSince === true || $requestNoneMatch === true) {
|
||||
return response()->make('', 304, $headers);
|
||||
}
|
||||
|
||||
return response()->file($path, $headers);
|
||||
}
|
||||
}
|
||||
93
app/Http/Controllers/Api/PostController.php
Normal file
93
app/Http/Controllers/Api/PostController.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\PostFilter;
|
||||
use App\Http\Requests\PostStoreRequest;
|
||||
use App\Http\Requests\PostUpdateRequest;
|
||||
use App\Models\Post;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PostController extends ApiController
|
||||
{
|
||||
/**
|
||||
* ApplicationController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum')
|
||||
->only([
|
||||
'store',
|
||||
'update',
|
||||
'delete'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \App\Filters\PostFilter $filter Post filter request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(PostFilter $filter)
|
||||
{
|
||||
return $this->respondAsResource(
|
||||
$filter->filter(),
|
||||
['total' => $filter->foundTotal()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param PostFilter $filter The filter request.
|
||||
* @param \App\Models\Post $post The post model.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(PostFilter $filter, Post $post)
|
||||
{
|
||||
return $this->respondAsResource($filter->filter($post));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param PostStoreRequest $request The post store request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(PostStoreRequest $request)
|
||||
{
|
||||
$post = Post::create($request->all());
|
||||
return $this->respondAsResource(
|
||||
(new PostFilter($request))->filter($post),
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param PostUpdateRequest $request The post update request.
|
||||
* @param \App\Models\Post $post The specified post.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(PostUpdateRequest $request, Post $post)
|
||||
{
|
||||
$post->update($request->all());
|
||||
return $this->respondAsResource((new PostFilter($request))->filter($post));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Post $post The specified post.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Post $post)
|
||||
{
|
||||
$post->delete();
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
}
|
||||
126
app/Http/Controllers/Api/SubscriptionController.php
Normal file
126
app/Http/Controllers/Api/SubscriptionController.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\UserFilter;
|
||||
use App\Http\Requests\UserUpdateRequest;
|
||||
use App\Http\Requests\UserStoreRequest;
|
||||
use App\Http\Requests\UserForgotPasswordRequest;
|
||||
use App\Http\Requests\UserForgotUsernameRequest;
|
||||
use App\Http\Requests\UserRegisterRequest;
|
||||
use App\Http\Requests\UserResendVerifyEmailRequest;
|
||||
use App\Http\Requests\UserResetPasswordRequest;
|
||||
use App\Http\Requests\UserVerifyEmailRequest;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Mail\ChangedEmail;
|
||||
use App\Mail\ChangedPassword;
|
||||
use App\Mail\ChangeEmailVerify;
|
||||
use App\Mail\ForgotUsername;
|
||||
use App\Mail\ForgotPassword;
|
||||
use App\Mail\EmailVerify;
|
||||
use App\Models\User;
|
||||
use App\Models\UserCode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class SubscriptionController extends ApiController
|
||||
{
|
||||
/**
|
||||
* ApplicationController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum')
|
||||
->except([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \App\Filters\UserFilter $filter Filter object.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(UserFilter $filter)
|
||||
{
|
||||
$collection = $filter->filter();
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
['total' => $filter->foundTotal()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created user in the database.
|
||||
*
|
||||
* @param UserStoreRequest $request The user update request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(UserStoreRequest $request)
|
||||
{
|
||||
if ($request->user()->hasPermission('admin/user') !== true) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$user = User::create($request->all());
|
||||
return $this->respondAsResource((new UserFilter($request))->filter($user), [], HttpResponseCodes::HTTP_CREATED);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified user.
|
||||
*
|
||||
* @param UserFilter $filter The user filter.
|
||||
* @param User $user The user model.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(UserFilter $filter, User $user)
|
||||
{
|
||||
return $this->respondAsResource($filter->filter($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UserUpdateRequest $request The user update request.
|
||||
* @param User $user The specified user.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(UserUpdateRequest $request, User $user)
|
||||
{
|
||||
$input = [];
|
||||
$updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
||||
|
||||
if ($request->user()->hasPermission('admin/user') === true) {
|
||||
$updatable = array_merge($updatable, ['email_verified_at']);
|
||||
} elseif ($request->user()->is($user) !== true) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$input = $request->only($updatable);
|
||||
if (array_key_exists('password', $input) === true) {
|
||||
$input['password'] = Hash::make($request->input('password'));
|
||||
}
|
||||
|
||||
$user->update($input);
|
||||
|
||||
return $this->respondAsResource((new UserFilter($request))->filter($user));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the user from the database.
|
||||
*
|
||||
* @param User $user The specified user.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if ($user->hasPermission('admin/user') === false) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
}
|
||||
337
app/Http/Controllers/Api/UserController.php
Normal file
337
app/Http/Controllers/Api/UserController.php
Normal file
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\UserFilter;
|
||||
use App\Http\Requests\UserUpdateRequest;
|
||||
use App\Http\Requests\UserStoreRequest;
|
||||
use App\Http\Requests\UserForgotPasswordRequest;
|
||||
use App\Http\Requests\UserForgotUsernameRequest;
|
||||
use App\Http\Requests\UserRegisterRequest;
|
||||
use App\Http\Requests\UserResendVerifyEmailRequest;
|
||||
use App\Http\Requests\UserResetPasswordRequest;
|
||||
use App\Http\Requests\UserVerifyEmailRequest;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Mail\ChangedEmail;
|
||||
use App\Mail\ChangedPassword;
|
||||
use App\Mail\ChangeEmailVerify;
|
||||
use App\Mail\ForgotUsername;
|
||||
use App\Mail\ForgotPassword;
|
||||
use App\Mail\EmailVerify;
|
||||
use App\Models\User;
|
||||
use App\Models\UserCode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class UserController extends ApiController
|
||||
{
|
||||
/**
|
||||
* ApplicationController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:sanctum')
|
||||
->except([
|
||||
'index',
|
||||
'show',
|
||||
'register',
|
||||
'exists',
|
||||
'forgotPassword',
|
||||
'forgotUsername',
|
||||
'resetPassword',
|
||||
'verifyEmail',
|
||||
'resendVerifyEmailCode'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \App\Filters\UserFilter $filter Filter object.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(UserFilter $filter)
|
||||
{
|
||||
$collection = $filter->filter();
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
['total' => $filter->foundTotal()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created user in the database.
|
||||
*
|
||||
* @param UserStoreRequest $request The user update request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(UserStoreRequest $request)
|
||||
{
|
||||
if ($request->user()->hasPermission('admin/user') !== true) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$user = User::create($request->all());
|
||||
return $this->respondAsResource((new UserFilter($request))->filter($user), [], HttpResponseCodes::HTTP_CREATED);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified user.
|
||||
*
|
||||
* @param UserFilter $filter The user filter.
|
||||
* @param User $user The user model.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(UserFilter $filter, User $user)
|
||||
{
|
||||
return $this->respondAsResource($filter->filter($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UserUpdateRequest $request The user update request.
|
||||
* @param User $user The specified user.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(UserUpdateRequest $request, User $user)
|
||||
{
|
||||
$input = [];
|
||||
$updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
||||
|
||||
if ($request->user()->hasPermission('admin/user') === true) {
|
||||
$updatable = array_merge($updatable, ['email_verified_at']);
|
||||
} elseif ($request->user()->is($user) !== true) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$input = $request->only($updatable);
|
||||
if (array_key_exists('password', $input) === true) {
|
||||
$input['password'] = Hash::make($request->input('password'));
|
||||
}
|
||||
|
||||
$user->update($input);
|
||||
|
||||
return $this->respondAsResource((new UserFilter($request))->filter($user));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the user from the database.
|
||||
*
|
||||
* @param User $user The specified user.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if ($user->hasPermission('admin/user') === false) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new user
|
||||
*
|
||||
* @param UserRegisterRequest $request The register user request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function register(UserRegisterRequest $request)
|
||||
{
|
||||
try {
|
||||
$user = User::create([
|
||||
'first_name' => $request->input('first_name'),
|
||||
'last_name' => $request->input('last_name'),
|
||||
'username' => $request->input('username'),
|
||||
'email' => $request->input('email'),
|
||||
'phone' => $request->input('phone'),
|
||||
'password' => Hash::make($request->input('password'))
|
||||
]);
|
||||
|
||||
$code = $user->codes()->create([
|
||||
'action' => 'verify-email',
|
||||
]);
|
||||
|
||||
dispatch((new SendEmailJob($user->email, new EmailVerify($user, $code->code))))->onQueue('mail');
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Check your email for a welcome code.'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => 'A server error occurred. Please try again later' . $e
|
||||
], 500);
|
||||
}//end try
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an email with all the usernames registered at that address
|
||||
*
|
||||
* @param UserForgotUsernameRequest $request The forgot username request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function forgotUsername(UserForgotUsernameRequest $request)
|
||||
{
|
||||
$users = User::where('email', $request->input('email'))->whereNotNull('email_verified_at')->get();
|
||||
if ($users->count() > 0) {
|
||||
dispatch((new SendEmailJob(
|
||||
$users->first()->email,
|
||||
new ForgotUsername($users->pluck('username')->toArray())
|
||||
)))->onQueue('mail');
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondJson(['message' => 'Username send to the email address if registered']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new reset password code
|
||||
*
|
||||
* @param UserForgotPasswordRequest $request The reset password request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function forgotPassword(UserForgotPasswordRequest $request)
|
||||
{
|
||||
$user = User::where('username', $request->input('username'))->first();
|
||||
if ($user !== null) {
|
||||
$user->codes()->where('action', 'reset-password')->delete();
|
||||
$code = $user->codes()->create([
|
||||
'action' => 'reset-password'
|
||||
]);
|
||||
|
||||
dispatch((new SendEmailJob($user->email, new ForgotPassword($user, $code->code))))->onQueue('mail');
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a user password
|
||||
*
|
||||
* @param UserResetPasswordRequest $request The reset password request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function resetPassword(UserResetPasswordRequest $request)
|
||||
{
|
||||
UserCode::clearExpired();
|
||||
|
||||
$code = UserCode::where('code', $request->input('code'))->where('action', 'reset-password')->first();
|
||||
if ($code !== null) {
|
||||
$user = $code->user()->first();
|
||||
|
||||
$code->delete();
|
||||
$user->codes()->where('action', 'verify-email')->delete();
|
||||
|
||||
$user->password = Hash::make($request->input('password'));
|
||||
|
||||
if ($user->email_verified_at === null) {
|
||||
$user->email_verified_at = now();
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
dispatch((new SendEmailJob($user->email, new ChangedPassword($user))))->onQueue('mail');
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondError([
|
||||
'code' => 'The code was not found or has expired'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an email code
|
||||
*
|
||||
* @param UserVerifyEmailRequest $request The verify email request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function verifyEmail(UserVerifyEmailRequest $request)
|
||||
{
|
||||
UserCode::clearExpired();
|
||||
|
||||
$code = UserCode::where('code', $request->input('code'))->where('action', 'verify-email')->first();
|
||||
if ($code !== null) {
|
||||
$user = $code->user()->first();
|
||||
$new_email = $code->data;
|
||||
|
||||
if ($new_email === null) {
|
||||
if ($user->email_verified_at === null) {
|
||||
$user->email_verified_at = now();
|
||||
}
|
||||
} else {
|
||||
dispatch((new SendEmailJob($user->email, new ChangedEmail($user, $user->email, $new_email))))
|
||||
->onQueue('mail');
|
||||
|
||||
$user->email = $new_email;
|
||||
$user->email_verified_at = now();
|
||||
}
|
||||
|
||||
$code->delete();
|
||||
$user->save();
|
||||
|
||||
return $this->respondNoContent();
|
||||
}//end if
|
||||
|
||||
return $this->respondWithErrors([
|
||||
'code' => 'The code was not found or has expired'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend a new verify email
|
||||
*
|
||||
* @param UserResendVerifyEmailRequest $request The resend verify email request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function resendVerifyEmail(UserResendVerifyEmailRequest $request)
|
||||
{
|
||||
UserCode::clearExpired();
|
||||
|
||||
$user = User::where('username', $request->input('username'))->first();
|
||||
if ($user !== null) {
|
||||
$code = $user->codes()->where('action', 'verify-email')->first();
|
||||
$code->regenerate();
|
||||
$code->save();
|
||||
|
||||
if ($code->data === null) {
|
||||
dispatch((new SendEmailJob($user->email, new EmailVerify($user, $code->code))))->onQueue('mail');
|
||||
} else {
|
||||
dispatch((new SendEmailJob($user->email, new ChangeEmailVerify($user, $code->code, $code->data))))
|
||||
->onQueue('mail');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Verify email sent if user registered and required']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend verification email
|
||||
*
|
||||
* @param UserResendVerifyEmailRequest $request The resend user request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request)
|
||||
{
|
||||
$user = User::where('username', $request->input('username'))->first();
|
||||
if ($user !== null) {
|
||||
$user->codes()->where('action', 'verify-email')->delete();
|
||||
|
||||
if ($user->email_verified_at === null) {
|
||||
$code = $user->codes()->create([
|
||||
'action' => 'verify-email'
|
||||
]);
|
||||
|
||||
dispatch((new SendEmailJob($user->email, new EmailVerify($user, $code->code))))->onQueue('mail');
|
||||
}
|
||||
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
}
|
||||
15
app/Http/Controllers/Controller.php
Normal file
15
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
use DispatchesJobs;
|
||||
use ValidatesRequests;
|
||||
}
|
||||
70
app/Http/Kernel.php
Normal file
70
app/Http/Kernel.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
// \App\Http\Middleware\TrimStrings::class,
|
||||
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
'throttle:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
// \App\Http\Middleware\ForceJsonResponse::class,
|
||||
'useSanctumGuard'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'useSanctumGuard' => \App\Http\Middleware\UseSanctumGuard::class
|
||||
];
|
||||
}
|
||||
21
app/Http/Middleware/Authenticate.php
Normal file
21
app/Http/Middleware/Authenticate.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*
|
||||
* @param mixed $request Request.
|
||||
* @return string|null
|
||||
*/
|
||||
protected function redirectTo(mixed $request)
|
||||
{
|
||||
if ($request->expectsJson() === false) {
|
||||
return route('login');
|
||||
}
|
||||
}
|
||||
}
|
||||
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
22
app/Http/Middleware/ForceJsonResponse.php
Normal file
22
app/Http/Middleware/ForceJsonResponse.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ForceJsonResponse
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$request->headers->set('Accept', 'application/json');
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
17
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
17
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||
|
||||
class PreventRequestsDuringMaintenance extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
32
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
32
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request Request.
|
||||
* @param Closure(Request): (Response|RedirectResponse) $next Next.
|
||||
* @param string|null ...$guards Guards.
|
||||
* @return Response|RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$guards)
|
||||
{
|
||||
$guards = empty($guards) === true ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if (Auth::guard($guard)->check() === true) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
19
app/Http/Middleware/TrimStrings.php
Normal file
19
app/Http/Middleware/TrimStrings.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
20
app/Http/Middleware/TrustHosts.php
Normal file
20
app/Http/Middleware/TrustHosts.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the host patterns that should be trusted.
|
||||
*
|
||||
* @return array<int, string|null>
|
||||
*/
|
||||
public function hosts()
|
||||
{
|
||||
return [
|
||||
$this->allSubdomainsOfApplicationUrl(),
|
||||
];
|
||||
}
|
||||
}
|
||||
25
app/Http/Middleware/TrustProxies.php
Normal file
25
app/Http/Middleware/TrustProxies.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
// @codingStandardsIgnoreStart
|
||||
protected $headers = (Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB);
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
23
app/Http/Middleware/UseSanctumGuard.php
Normal file
23
app/Http/Middleware/UseSanctumGuard.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UseSanctumGuard
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
Auth::shouldUse('sanctum');
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
22
app/Http/Middleware/ValidateSignature.php
Normal file
22
app/Http/Middleware/ValidateSignature.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
|
||||
|
||||
class ValidateSignature extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the query string parameters that should be ignored.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
// 'fbclid',
|
||||
// 'utm_campaign',
|
||||
// 'utm_content',
|
||||
// 'utm_medium',
|
||||
// 'utm_source',
|
||||
// 'utm_term',
|
||||
];
|
||||
}
|
||||
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
21
app/Http/Requests/AuthLoginRequest.php
Normal file
21
app/Http/Requests/AuthLoginRequest.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AuthLoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'username' => 'required|string|min:6|max:255',
|
||||
'password' => 'required|string|min:6',
|
||||
];
|
||||
}
|
||||
}
|
||||
95
app/Http/Requests/BaseRequest.php
Normal file
95
app/Http/Requests/BaseRequest.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class BaseRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
if (method_exists($this, 'postAuthorize') === true && request()->isMethod('post') === true) {
|
||||
return $this->postAuthorize();
|
||||
} elseif (method_exists($this, 'putAuthorize') === true && request()->isMethod('put') === true) {
|
||||
return $this->putAuthorize();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if (method_exists($this, 'baseRules') === true) {
|
||||
$rules = $this->baseRules();
|
||||
}
|
||||
|
||||
if (method_exists($this, 'postRules') === true && request()->isMethod('post') === true) {
|
||||
$rules = $this->mergeRules($rules, $this->postRules());
|
||||
} elseif (method_exists($this, 'putRules') === true && request()->isMethod('put') === true) {
|
||||
$rules = $this->mergeRules($rules, $this->postRules());
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two collections of rules.
|
||||
*
|
||||
* @param array $collection1 The first collection of rules.
|
||||
* @param array $collection2 The second collection of rules to merge.
|
||||
* @return array
|
||||
*/
|
||||
private function mergeRules(array $collection1, array $collection2)
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
foreach ($collection1 as $key => $ruleset) {
|
||||
if (array_key_exists($key, $collection2) === true) {
|
||||
if (is_string($collection1[$key]) === true && is_string($collection2[$key]) === true) {
|
||||
$rules[$key] = $collection1[$key] . '|' . $collection2[$key];
|
||||
} else {
|
||||
$key_ruleset = [];
|
||||
|
||||
if (is_array($collection1[$key]) === true) {
|
||||
$key_ruleset = $collection1[$key];
|
||||
} elseif (is_string($collection1[$key]) === true) {
|
||||
$key_ruleset = explode('|', $collection1[$key]);
|
||||
}
|
||||
|
||||
if (is_array($collection2[$key]) === true) {
|
||||
$key_ruleset = array_merge($key_ruleset, $collection2[$key]);
|
||||
} elseif (is_string($collection1[$key]) === true) {
|
||||
$key_ruleset = array_merge($key_ruleset, explode('|', $collection1[$key]));
|
||||
}
|
||||
|
||||
if (count($key_ruleset) > 0) {
|
||||
$rules[$key] = $key_ruleset;
|
||||
}
|
||||
}//end if
|
||||
} else {
|
||||
$rules[$key] = $ruleset;
|
||||
}//end if
|
||||
}//end foreach
|
||||
|
||||
foreach ($collection2 as $key => $ruleset) {
|
||||
if (array_key_exists($key, $rules) === false) {
|
||||
$rules[$key] = $collection2[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
24
app/Http/Requests/ContactSendRequest.php
Normal file
24
app/Http/Requests/ContactSendRequest.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\Recaptcha;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ContactSendRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255',
|
||||
'content' => 'required|max:2000',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
}
|
||||
78
app/Http/Requests/EventRequest.php
Normal file
78
app/Http/Requests/EventRequest.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class EventRequest extends BaseRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function postAuthorize()
|
||||
{
|
||||
return $this->user()?->hasPermission('admin/events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function putAuthorize()
|
||||
{
|
||||
return $this->user()?->hasPermission('admin/events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the base rules to this request
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function baseRules()
|
||||
{
|
||||
return [
|
||||
'title' => 'min:6',
|
||||
'location' => [
|
||||
Rule::in(['online', 'physical']),
|
||||
],
|
||||
'address' => 'string|nullable',
|
||||
'start_at' => 'date',
|
||||
'end_at' => 'date|after:start_date',
|
||||
'publish_at' => 'date|nullable',
|
||||
'status' => [
|
||||
Rule::in(['draft', 'open', 'closed', 'cancelled']),
|
||||
],
|
||||
'registration_type' => [
|
||||
Rule::in(['none', 'email', 'link']),
|
||||
],
|
||||
'registration_data' => [
|
||||
Rule::when(strcasecmp('email', $this->attributes->get('registration_type')) == 0, 'required|email'),
|
||||
Rule::when(strcasecmp('link', $this->attributes->get('registration_type')) == 0, 'required|url')
|
||||
],
|
||||
'hero' => 'uuid|exists:media,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the additional POST base rules to this request
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function postRules()
|
||||
{
|
||||
return [
|
||||
'title' => 'required',
|
||||
'location' => 'required',
|
||||
'address' => 'required_if:location,physical',
|
||||
'start_at' => 'required',
|
||||
'end_at' => 'required',
|
||||
'status' => 'required',
|
||||
'registration_type' => 'required',
|
||||
'hero' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
||||
20
app/Http/Requests/MediaStoreRequest.php
Normal file
20
app/Http/Requests/MediaStoreRequest.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MediaStoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
20
app/Http/Requests/MediaUpdateRequest.php
Normal file
20
app/Http/Requests/MediaUpdateRequest.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MediaUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
23
app/Http/Requests/PostStoreRequest.php
Normal file
23
app/Http/Requests/PostStoreRequest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PostStoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'slug' => 'string|min:6|unique:posts',
|
||||
'title' => 'string|min:6|max:255',
|
||||
'publish_at' => 'date',
|
||||
'user_id' => 'uuid|exists:users,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
28
app/Http/Requests/PostUpdateRequest.php
Normal file
28
app/Http/Requests/PostUpdateRequest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PostUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'slug' => [
|
||||
'string',
|
||||
'min:6',
|
||||
Rule::unique('posts')->ignoreModel($this->post),
|
||||
],
|
||||
'title' => 'string|min:6|max:255',
|
||||
'publish_at' => 'date',
|
||||
'user_id' => 'uuid|exists:users,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
22
app/Http/Requests/UserForgotPasswordRequest.php
Normal file
22
app/Http/Requests/UserForgotPasswordRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\Recaptcha;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserForgotPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'username' => 'required|exists:users,username',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
}
|
||||
22
app/Http/Requests/UserForgotUsernameRequest.php
Normal file
22
app/Http/Requests/UserForgotUsernameRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\Recaptcha;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserForgotUsernameRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email|max:255',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
}
|
||||
24
app/Http/Requests/UserRegisterRequest.php
Normal file
24
app/Http/Requests/UserRegisterRequest.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserRegisterRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'first_name' => 'required|string|max:255',
|
||||
'last_name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255',
|
||||
'username' => 'required|string|min:4|max:255|unique:users',
|
||||
'password' => 'required|string|min:8',
|
||||
];
|
||||
}
|
||||
}
|
||||
22
app/Http/Requests/UserResendVerifyEmailRequest.php
Normal file
22
app/Http/Requests/UserResendVerifyEmailRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\Recaptcha;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserResendVerifyEmailRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'username' => 'required|exists:users,username',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
}
|
||||
23
app/Http/Requests/UserResetPasswordRequest.php
Normal file
23
app/Http/Requests/UserResetPasswordRequest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\Recaptcha;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserResetPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'code' => 'required|digits:6',
|
||||
'password' => 'required|string|min:8',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
}
|
||||
25
app/Http/Requests/UserStoreRequest.php
Normal file
25
app/Http/Requests/UserStoreRequest.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserStoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'username' => 'required|string|max:255|min:4|unique:users',
|
||||
'first_name' => 'required|string|max:255|min:2',
|
||||
'last_name' => 'required|string|max:255|min:2',
|
||||
'email' => 'required|string|email|max:255',
|
||||
'phone' => ['string', 'regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'],
|
||||
'email_verified_at' => 'date'
|
||||
];
|
||||
}
|
||||
}
|
||||
25
app/Http/Requests/UserUpdateRequest.php
Normal file
25
app/Http/Requests/UserUpdateRequest.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'username' => 'string|max:255|min:6|unique:users',
|
||||
'first_name' => 'string|max:255|min:2',
|
||||
'last_name' => 'string|max:255|min:2',
|
||||
'email' => 'string|email|max:255',
|
||||
'phone' => ['nullable','regex:/^(\+|00)?[0-9][0-9 \-\(\)\.]{7,32}$/'],
|
||||
'password' => 'string|min:8'
|
||||
];
|
||||
}
|
||||
}
|
||||
22
app/Http/Requests/UserVerifyEmailRequest.php
Normal file
22
app/Http/Requests/UserVerifyEmailRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\Recaptcha;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserVerifyEmailRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'code' => 'required|digits:6',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
}
|
||||
57
app/Jobs/SendEmailJob.php
Normal file
57
app/Jobs/SendEmailJob.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendEmailJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Mail to receipt
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $to;
|
||||
|
||||
/**
|
||||
* Mailable item
|
||||
*
|
||||
* @var Mailable
|
||||
*/
|
||||
public $mailable;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param string $to The email receipient.
|
||||
* @param Mailable $mailable The mailable.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $to, Mailable $mailable)
|
||||
{
|
||||
$this->to = $to;
|
||||
$this->mailable = $mailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Mail::to($this->to)->send($this->mailable);
|
||||
}
|
||||
}
|
||||
79
app/Mail/ChangeEmailVerify.php
Normal file
79
app/Mail/ChangeEmailVerify.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChangeEmailVerify extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The user instance.
|
||||
*
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* The registration code.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* The new email address.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $new_email;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user The user the email applies to.
|
||||
* @param integer $code The action code.
|
||||
* @param string $new_email The new email address.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, int $code, string $new_email)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->code = $code;
|
||||
$this->new_email = $new_email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '👋🏻 Lets change your email!',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.change_email_verify',
|
||||
text: 'emails.user.change_email_verify_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
79
app/Mail/ChangedEmail.php
Normal file
79
app/Mail/ChangedEmail.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChangedEmail extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The user instance.
|
||||
*
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* The old email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $old_email;
|
||||
|
||||
/**
|
||||
* The new email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $new_email;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user The user the email applies to.
|
||||
* @param string $old_email The previous email address.
|
||||
* @param string $new_email The new email address.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, string $old_email, string $new_email)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->old_email = $old_email;
|
||||
$this->new_email = $new_email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '👍 Your email has been changed!',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.changed_email',
|
||||
text: 'emails.user.changed_email_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
61
app/Mail/ChangedPassword.php
Normal file
61
app/Mail/ChangedPassword.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChangedPassword extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The user instance.
|
||||
*
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user The user the email applies to.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '👍 Your password has been changed!',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.changed_password',
|
||||
text: 'emails.user.changed_password_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
78
app/Mail/Contact.php
Normal file
78
app/Mail/Contact.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class Contact extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The contact name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The contact email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* The contact content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param string $name The contact name.
|
||||
* @param string $email The contact email.
|
||||
* @param string $content The contact content.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $name, string $email, string $content)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->email = $email;
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: config('contact.contact_subject'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.contact',
|
||||
text: 'emails.user.contact_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
70
app/Mail/EmailVerify.php
Normal file
70
app/Mail/EmailVerify.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class EmailVerify extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The user instance.
|
||||
*
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* The registration code.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $code;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user The user the email applies to.
|
||||
* @param integer $code The action code.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, int $code)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '👋🏻 Welcome to STEMMechanics!',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.email_verify',
|
||||
text: 'emails.user.email_verify_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
70
app/Mail/ForgotPassword.php
Normal file
70
app/Mail/ForgotPassword.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ForgotPassword extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The user
|
||||
*
|
||||
* @var \App\Models\User
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* The reset code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $code;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param User $user The user the email applies to.
|
||||
* @param integer $code The action code.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, int $code)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '🤦 Forgot your password?',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.forgot_password',
|
||||
text: 'emails.user.forgot_password_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
60
app/Mail/ForgotUsername.php
Normal file
60
app/Mail/ForgotUsername.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ForgotUsername extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The list of usernames
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $usernames;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param array $usernames The usernames.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $usernames)
|
||||
{
|
||||
$this->usernames = $usernames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Envelope
|
||||
*/
|
||||
public function envelope()
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '🤦 Forgot your username?',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*
|
||||
* @return \Illuminate\Mail\Mailables\Content
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user.forgot_username',
|
||||
text: 'emails.user.forgot_username_plain',
|
||||
);
|
||||
}
|
||||
}
|
||||
32
app/Models/Event.php
Normal file
32
app/Models/Event.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Event extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Uuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'location',
|
||||
'address',
|
||||
'start_at',
|
||||
'end_at',
|
||||
'publish_at',
|
||||
'status',
|
||||
'registration_type',
|
||||
'registration_data',
|
||||
'hero',
|
||||
'content'
|
||||
];
|
||||
}
|
||||
276
app/Models/Media.php
Normal file
276
app/Models/Media.php
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Media extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Uuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'name',
|
||||
'mime',
|
||||
'user_id',
|
||||
'size',
|
||||
'permission'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are hidden.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'path',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are appended.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $appends = [
|
||||
'url',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Model Boot
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::updating(function ($media) {
|
||||
if (array_key_exists('permission', $media->getChanges()) === true) {
|
||||
$origPermission = $media->getOriginal()['permission'];
|
||||
$newPermission = $media->permission;
|
||||
|
||||
$origPath = Storage::disk(Media::getStorageId(empty($origPermission)))->path($media->name);
|
||||
$newPath = Storage::disk(Media::getStorageId(empty($newPermission)))->path($media->name);
|
||||
|
||||
if ($origPath !== $newPath) {
|
||||
if (file_exists($origPath) === true) {
|
||||
if (file_exists($newPath) === true) {
|
||||
$fileParts = pathinfo($newPath);
|
||||
$newName = '';
|
||||
|
||||
// need a new name!
|
||||
$tmpPath = $newPath;
|
||||
while (file_exists($tmpPath) === true) {
|
||||
$newName = uniqid('', true) . $fileParts['extension'];
|
||||
$tmpPath = $fileParts['dirname'] . '/' . $newName;
|
||||
}
|
||||
|
||||
$media->name = $newName;
|
||||
}
|
||||
|
||||
rename($origPath, $newPath);
|
||||
}//end if
|
||||
}//end if
|
||||
}//end if
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlAttribute()
|
||||
{
|
||||
$url = config('filesystems.disks.' . Media::getStorageId($this) . '.url');
|
||||
if (empty($url) === false) {
|
||||
$replace = [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name
|
||||
];
|
||||
|
||||
$url = str_ireplace(array_map(function ($item) {
|
||||
return '%' . $item . '%';
|
||||
}, array_keys($replace)), array_values($replace), $url);
|
||||
|
||||
return $url;
|
||||
}//end if
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file owner
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file full local path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return Storage::disk(Media::getStorageId($this))->path($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Storage ID
|
||||
*
|
||||
* @param mixed $mediaOrPublic Media object or if file is public.
|
||||
* @return string
|
||||
*/
|
||||
public static function getStorageId(mixed $mediaOrPublic)
|
||||
{
|
||||
$isPublic = true;
|
||||
|
||||
if ($mediaOrPublic instanceof Media) {
|
||||
$isPublic = empty($mediaOrPublic->permission);
|
||||
} else {
|
||||
$isPublic = boolval($mediaOrPublic);
|
||||
}
|
||||
|
||||
return $isPublic === true ? 'public' : 'local';
|
||||
}
|
||||
|
||||
/**
|
||||
* Place uploaded file into storage. Return full path or null
|
||||
*
|
||||
* @param UploadedFile $file File to put into storage.
|
||||
* @param boolean $public Is the file available to the public.
|
||||
* @return array|null
|
||||
*/
|
||||
public static function store(UploadedFile $file, bool $public = true)
|
||||
{
|
||||
$storage = Media::getStorageId($public);
|
||||
$name = $file->store('', ['disk' => $storage]);
|
||||
|
||||
if ($name === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = Storage::disk($storage)->path($name);
|
||||
return [
|
||||
'name' => $name,
|
||||
'path' => $path
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server maximum upload size
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function maxUploadSize()
|
||||
{
|
||||
$sizes = [
|
||||
ini_get('upload_max_filesize'),
|
||||
ini_get('post_max_size'),
|
||||
ini_get('memory_limit')
|
||||
];
|
||||
|
||||
foreach ($sizes as &$size) {
|
||||
$size = trim($size);
|
||||
$last = strtolower($size[(strlen($size) - 1)]);
|
||||
switch ($last) {
|
||||
case 'g':
|
||||
$size = (intval($size) * 1024);
|
||||
// Size is in MB - fallthrough
|
||||
case 'm':
|
||||
$size = (intval($size) * 1024);
|
||||
// Size is in KB - fallthrough
|
||||
case 'k':
|
||||
$size = (intval($size) * 1024);
|
||||
// Size is in B - fallthrough
|
||||
}
|
||||
}
|
||||
|
||||
return min($sizes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize filename for upload
|
||||
*
|
||||
* @param string $filename Filename to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitizeFilename(string $filename)
|
||||
{
|
||||
/*
|
||||
# file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
|
||||
[<>:"/\\\|?*]|
|
||||
|
||||
# control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
|
||||
[\x00-\x1F]|
|
||||
|
||||
# non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
|
||||
[\x7F\xA0\xAD]|
|
||||
|
||||
# URI reserved https://www.rfc-editor.org/rfc/rfc3986#section-2.2
|
||||
[#\[\]@!$&\'()+,;=]|
|
||||
|
||||
# URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
|
||||
[{}^\~`]
|
||||
*/
|
||||
|
||||
$filename = preg_replace(
|
||||
'~
|
||||
[<>:"/\\\|?*]|
|
||||
[\x00-\x1F]|
|
||||
[\x7F\xA0\xAD]|
|
||||
[#\[\]@!$&\'()+,;=]|
|
||||
[{}^\~`]
|
||||
~x',
|
||||
'-',
|
||||
$filename
|
||||
);
|
||||
|
||||
$filename = ltrim($filename, '.-');
|
||||
|
||||
$filename = preg_replace([
|
||||
// "file name.zip" becomes "file-name.zip"
|
||||
'/ +/',
|
||||
// "file___name.zip" becomes "file-name.zip"
|
||||
'/_+/',
|
||||
// "file---name.zip" becomes "file-name.zip"
|
||||
'/-+/'
|
||||
], '-', $filename);
|
||||
$filename = preg_replace([
|
||||
// "file--.--.-.--name.zip" becomes "file.name.zip"
|
||||
'/-*\.-*/',
|
||||
// "file...name..zip" becomes "file.name.zip"
|
||||
'/\.{2,}/'
|
||||
], '.', $filename);
|
||||
// lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
|
||||
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
|
||||
// ".file-name.-" becomes "file-name"
|
||||
$filename = trim($filename, '.-');
|
||||
|
||||
$ext = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
$filename = mb_strcut(
|
||||
pathinfo($filename, PATHINFO_FILENAME),
|
||||
0,
|
||||
(255 - ($ext !== '' ? strlen($ext) + 1 : 0)),
|
||||
mb_detect_encoding($filename)
|
||||
) . ($ext !== '' ? '.' . $ext : '');
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
34
app/Models/Permission.php
Normal file
34
app/Models/Permission.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Permission extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Uuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'permission',
|
||||
'user',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Get the User associated with this model
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
38
app/Models/Post.php
Normal file
38
app/Models/Post.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Uuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'publish_at',
|
||||
'content',
|
||||
'user_id',
|
||||
'hero'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Get the file user
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
22
app/Models/Subscription.php
Normal file
22
app/Models/Subscription.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Subscription extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Uuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'email',
|
||||
];
|
||||
}
|
||||
145
app/Models/User.php
Normal file
145
app/Models/User.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
|
||||
class User extends Authenticatable implements Auditable
|
||||
{
|
||||
use HasApiTokens;
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
use Uuids;
|
||||
use \OwenIt\Auditing\Auditable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'phone',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
'permissions'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
// protected $hidden = [
|
||||
// 'permissions'
|
||||
// ];
|
||||
|
||||
/**
|
||||
* The attributes to append.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $appends = [
|
||||
'permissions'
|
||||
];
|
||||
|
||||
|
||||
// public function getPermissionsAttribute() {
|
||||
// return $this->permissions()->pluck('permission')->toArray();
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of files of the user
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function permissions()
|
||||
{
|
||||
return $this->hasMany(Permission::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permission attribute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPermissionsAttribute()
|
||||
{
|
||||
return $this->permissions()->pluck('permission')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if user has permission
|
||||
*
|
||||
* @param string $permission Permission to test.
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPermission(string $permission)
|
||||
{
|
||||
return ($this->permissions()->where('permission', $permission)->first() !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of files of the user
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function media()
|
||||
{
|
||||
return $this->hasMany(Media::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of files of the user
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function posts()
|
||||
{
|
||||
return $this->hasMany(Post::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associated user codes
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function codes()
|
||||
{
|
||||
return $this->hasMany(UserCode::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of logins of the user
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function logins()
|
||||
{
|
||||
return $this->hasMany(UserLogins::class);
|
||||
}
|
||||
}
|
||||
82
app/Models/UserCode.php
Normal file
82
app/Models/UserCode.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserCode extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'action',
|
||||
'user_id',
|
||||
'data',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Boot function from Laravel.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
static::creating(function ($model) {
|
||||
UserCode::clearExpired();
|
||||
|
||||
if (empty($model->{'code'}) === true) {
|
||||
while (true) {
|
||||
$code = random_int(100000, 999999);
|
||||
if (UserCode::where('code', $code)->count() === 0) {
|
||||
$model->{'code'} = $code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new code
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function regenerate()
|
||||
{
|
||||
while (true) {
|
||||
$code = random_int(100000, 999999);
|
||||
if (UserCode::where('code', $code)->count() === 0) {
|
||||
$this->code = $code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear expired user codes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearExpired()
|
||||
{
|
||||
UserCode::where('updated_at', '<=', now()->subDays(5))->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get associated user
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
38
app/Models/UserLogins.php
Normal file
38
app/Models/UserLogins.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Uuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserLogins extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Uuids;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'token',
|
||||
'login',
|
||||
'logout',
|
||||
'ip_address',
|
||||
'user_agent',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Get the file user
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
31
app/Providers/AppServiceProvider.php
Normal file
31
app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PDOException;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
31
app/Providers/AuthServiceProvider.php
Normal file
31
app/Providers/AuthServiceProvider.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
// use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The model to policy mappings for the application.
|
||||
*
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
//
|
||||
}
|
||||
}
|
||||
21
app/Providers/BroadcastServiceProvider.php
Normal file
21
app/Providers/BroadcastServiceProvider.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
require base_path('routes/channels.php');
|
||||
}
|
||||
}
|
||||
61
app/Providers/EventServiceProvider.php
Normal file
61
app/Providers/EventServiceProvider.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Queue\Events\JobProcessed;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event to listener mappings for the application.
|
||||
*
|
||||
* @var array<class-string, array<int, class-string>>
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Queue::after(function (JobProcessed $event) {
|
||||
// Log::info($event->connectionName);
|
||||
// Log::info('ID: ' . $event->job->getJobId());
|
||||
// Log::info('Attempts: ' . $event->job->attempts());
|
||||
// Log::info('Name: ' . $event->job->getName());
|
||||
// Log::info('ResolveNAme: ' . $event->job->resolveName());
|
||||
// Log::info('Queue: ' . $event->job->getQueue());
|
||||
// Log::info('Body: ' . $event->job->getRawBody());
|
||||
// Log::info(print_r($event->job->payload(), true));
|
||||
|
||||
// $payload = $event->job->payload();
|
||||
// $data = unserialize($payload['data']['command']);
|
||||
|
||||
// Log::info('MAIL: ' . $data->to);
|
||||
// Log::info('MAIL: ' . get_class($data->mailable));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if events and listeners should be automatically discovered.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldDiscoverEvents()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
53
app/Providers/RouteServiceProvider.php
Normal file
53
app/Providers/RouteServiceProvider.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to the "home" route for your application.
|
||||
*
|
||||
* Typically, users are redirected here after authentication.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/home';
|
||||
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, and other route configuration.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('api')
|
||||
->prefix('api')
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->user()?->id !== null ?: $request->ip());
|
||||
});
|
||||
}
|
||||
}
|
||||
52
app/Rules/Recaptcha.php
Normal file
52
app/Rules/Recaptcha.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class Recaptcha implements Rule
|
||||
{
|
||||
/**
|
||||
* Create a new rule instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param mixed $attribute Attribute name.
|
||||
* @param mixed $value Attribute value.
|
||||
* @return boolean
|
||||
*/
|
||||
public function passes(mixed $attribute, mixed $value)
|
||||
{
|
||||
$endpoint = config('services.google_recaptcha');
|
||||
|
||||
$response = Http::asForm()->post($endpoint['url'], [
|
||||
'secret' => $endpoint['secret_key'],
|
||||
'response' => $value,
|
||||
])->json();
|
||||
|
||||
if ($response['success'] === true && $response['score'] > 0.5) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return 'Captcha failed. Refresh the page and try again';
|
||||
}
|
||||
}
|
||||
9
app/Services/ImageService.php
Normal file
9
app/Services/ImageService.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use ImageIntervention;
|
||||
|
||||
class ImageService
|
||||
{
|
||||
}
|
||||
42
app/Traits/Uuids.php
Normal file
42
app/Traits/Uuids.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait Uuids
|
||||
{
|
||||
/**
|
||||
* Boot function from Laravel.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function bootUuids()
|
||||
{
|
||||
static::creating(function ($model) {
|
||||
if (empty($model->{$model->getKeyName()}) === true) {
|
||||
$model->{$model->getKeyName()} = Str::uuid()->toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value indicating whether the IDs are incrementing.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getIncrementing()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auto-incrementing key type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKeyType()
|
||||
{
|
||||
return 'string';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user