Use scopes #18
59
.env.testing
Normal file
59
.env.testing
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
APP_NAME=Laravel
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=base64:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://127.0.0.1
|
||||||
|
APP_URL_API="${APP_URL}/api/"
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=sqlite
|
||||||
|
DB_DATABASE=:memory:
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=array
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=log
|
||||||
|
MAIL_HOST=null
|
||||||
|
MAIL_PORT=null
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
|
AWS_BUCKET=
|
||||||
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_HOST=
|
||||||
|
PUSHER_PORT=443
|
||||||
|
PUSHER_SCHEME=https
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||||
|
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||||
|
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||||
|
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
|
|
||||||
|
CONTACT_ADDRESS="hello@stemmechanics.com.au"
|
||||||
|
CONTACT_SUBJECT="Contact from website"
|
||||||
|
|
||||||
|
STORAGE_LOCAL_URL="${APP_URL}/api/media/%ID%/download"
|
||||||
|
STORAGE_PUBLIC_URL="${APP_URL}/uploads/%NAME%"
|
||||||
678
app/Conductors/Conductor.php
Normal file
678
app/Conductors/Conductor.php
Normal file
@@ -0,0 +1,678 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Conductors Model class.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $class = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting fields of a collection. Can be an array. Supports - and + prefixes.
|
||||||
|
*
|
||||||
|
* @var string|array
|
||||||
|
*/
|
||||||
|
protected $sort = "id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default collection size limit per request.
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $limit = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum collection size limit per request.
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $maxLimit = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default includes to include in a request.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $includes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The conductor collection.
|
||||||
|
*
|
||||||
|
* @var Collection
|
||||||
|
*/
|
||||||
|
private $collection = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The conductor query.
|
||||||
|
*
|
||||||
|
* @var Builder
|
||||||
|
*/
|
||||||
|
private $query = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a string on commas, keeping quotes intact.
|
||||||
|
*
|
||||||
|
* @param string $string The string to split.
|
||||||
|
* @return array The split string.
|
||||||
|
*/
|
||||||
|
private function splitString(string $string)
|
||||||
|
{
|
||||||
|
$parts = [];
|
||||||
|
$start = 0;
|
||||||
|
$len = strlen($string);
|
||||||
|
|
||||||
|
while ($start < $len) {
|
||||||
|
$commaPos = strpos($string, ',', $start);
|
||||||
|
$singlePos = strpos($string, '\'', $start);
|
||||||
|
$doublePos = strpos($string, '"', $start);
|
||||||
|
|
||||||
|
// Find the smallest position that is not false
|
||||||
|
$minPos = false;
|
||||||
|
if ($commaPos !== false) {
|
||||||
|
$minPos = $commaPos;
|
||||||
|
}
|
||||||
|
if ($singlePos !== false && ($minPos === false || $singlePos < $minPos)) {
|
||||||
|
$minPos = $singlePos;
|
||||||
|
}
|
||||||
|
if ($doublePos !== false && ($minPos === false || $doublePos < $minPos)) {
|
||||||
|
$minPos = $doublePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($minPos === false) {
|
||||||
|
// No more commas, single quotes, or double quotes found
|
||||||
|
$part = substr($string, $start);
|
||||||
|
$parts[] = trim($part);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Add the current part to the parts array
|
||||||
|
$part = substr($string, $start, ($minPos - $start));
|
||||||
|
$parts[] = trim($part);
|
||||||
|
|
||||||
|
// Update the start position to the next character after the comma, single quote, or double quote
|
||||||
|
if ($string[$minPos] === ',') {
|
||||||
|
$start = ($minPos + 1);
|
||||||
|
} else {
|
||||||
|
$quoteChar = $string[$minPos];
|
||||||
|
$endPos = strpos($string, $quoteChar, ($minPos + 1));
|
||||||
|
if ($endPos === false) {
|
||||||
|
$part = substr($string, ($minPos + 1));
|
||||||
|
$parts[] = trim($part);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$part = substr($string, ($minPos + 1), ($endPos - $minPos - 1));
|
||||||
|
$parts[] = trim($part);
|
||||||
|
$start = ($endPos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
}//end while
|
||||||
|
|
||||||
|
return array_filter($parts, function ($value) {
|
||||||
|
return $value !== '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a field with a specific Builder object
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder object to append.
|
||||||
|
* @param string $field The field name.
|
||||||
|
* @param mixed $value The value or array of values to filter.
|
||||||
|
* @param string $boolean The comparision boolean (AND or OR).
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function filterFieldWithBuilder(Builder $builder, string $field, mixed $value, string $boolean = 'AND')
|
||||||
|
{
|
||||||
|
$values = [];
|
||||||
|
|
||||||
|
// Split by comma, but respect quotation marks
|
||||||
|
if (is_string($value) === true) {
|
||||||
|
$values = $this->splitString($value);
|
||||||
|
} elseif (is_array($value) === true) {
|
||||||
|
$values = $value;
|
||||||
|
} else {
|
||||||
|
throw new \InvalidArgumentException('Expected string or array, got ' . gettype($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add each AND check to the query
|
||||||
|
$builder->where(function ($query) use ($field, $values) {
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$value = trim($value);
|
||||||
|
$prefix = '';
|
||||||
|
|
||||||
|
// Check if value has a prefix and remove it if it's a number
|
||||||
|
if (preg_match('/^(!?=|[<>]=?|<>|!)([^=!<>].*)$/', $value, $matches) > 0) {
|
||||||
|
$prefix = $matches[1];
|
||||||
|
$value = $matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the prefix to the query if the value is a number
|
||||||
|
switch ($prefix) {
|
||||||
|
case '=':
|
||||||
|
$query->orWhere($field, '=', $value);
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
$query->orWhere($field, 'NOT LIKE', "%$value%");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
$query->orWhere($field, '>', $value);
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
$query->orWhere($field, '<', $value);
|
||||||
|
break;
|
||||||
|
case '>=':
|
||||||
|
$query->orWhere($field, '>=', $value);
|
||||||
|
break;
|
||||||
|
case '<=':
|
||||||
|
$query->orWhere($field, '<=', $value);
|
||||||
|
break;
|
||||||
|
case '!=':
|
||||||
|
$query->orWhere($field, '!=', $value);
|
||||||
|
break;
|
||||||
|
case '<>':
|
||||||
|
$seperatorPos = strpos($value, '|');
|
||||||
|
if ($seperatorPos !== false) {
|
||||||
|
$query->orWhereBetween($field, [substr($value, 0, $seperatorPos), substr($value, ($seperatorPos + 1))]);
|
||||||
|
} else {
|
||||||
|
$query->orWhere($field, '!=', $value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$query->orWhere($field, 'LIKE', "%$value%");
|
||||||
|
break;
|
||||||
|
}//end switch
|
||||||
|
}//end foreach
|
||||||
|
}, null, null, $boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the conductor on a Request to generate a collection and total.
|
||||||
|
*
|
||||||
|
* @param Request $request The request data.
|
||||||
|
* @return array The processed and transformed collection | the total rows found.
|
||||||
|
*/
|
||||||
|
final public static function request(Request $request)
|
||||||
|
{
|
||||||
|
$conductor_class = get_called_class();
|
||||||
|
$conductor = new $conductor_class();
|
||||||
|
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$conductor->query = $conductor->class::query();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
throw new \Exception('Failed to create query builder instance for ' . $conductor->class . '.', 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scope query
|
||||||
|
$conductor->scope($conductor->query);
|
||||||
|
|
||||||
|
// Filter request
|
||||||
|
$fields = $conductor->fields(new $conductor->class());
|
||||||
|
if (is_array($fields) === false) {
|
||||||
|
$fields = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $request->all();
|
||||||
|
$filterFields = array_intersect_key($params, array_flip($fields));
|
||||||
|
$conductor->filter($filterFields);
|
||||||
|
if ($request->has('filter') === true) {
|
||||||
|
$conductor->filterRaw($request->input('filter', ''), $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort request
|
||||||
|
$conductor->sort($request->input('sort', $conductor->sort));
|
||||||
|
|
||||||
|
// Get total
|
||||||
|
$total = $conductor->count();
|
||||||
|
|
||||||
|
// Paginate
|
||||||
|
$conductor->paginate($request->input('page', 1), $request->input('limit', -1));
|
||||||
|
|
||||||
|
// Limit fields
|
||||||
|
$limitFields = explode(',', $request->input('fields'));
|
||||||
|
if ($limitFields === null) {
|
||||||
|
$limitFields = $fields;
|
||||||
|
} else {
|
||||||
|
$limitFields = array_intersect($limitFields, $fields);
|
||||||
|
}
|
||||||
|
$conductor->limitFields($limitFields);
|
||||||
|
|
||||||
|
$conductor->collection = $conductor->query->get();
|
||||||
|
|
||||||
|
// Transform and Includes
|
||||||
|
$includes = $conductor->includes;
|
||||||
|
if ($request->has('includes') === true) {
|
||||||
|
$includes = explode(',', $request->input('includes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$conductor->collection = $conductor->collection->map(function ($model) use ($conductor, $includes) {
|
||||||
|
$conductor->includes($model, $includes);
|
||||||
|
$model = $conductor->transform($model);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [$conductor->collection, $total];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the conductor on a Model with the data stored in a Request.
|
||||||
|
*
|
||||||
|
* @param Request $request The request data.
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return array The processed and transformed model data.
|
||||||
|
*/
|
||||||
|
final public static function model(Request $request, Model $model)
|
||||||
|
{
|
||||||
|
$conductor_class = get_called_class();
|
||||||
|
$conductor = new $conductor_class();
|
||||||
|
|
||||||
|
$fields = $conductor->fields(new $conductor->class());
|
||||||
|
|
||||||
|
// Limit fields
|
||||||
|
$limitFields = $fields;
|
||||||
|
if ($request !== null && $request->has('fields') === true) {
|
||||||
|
$requestFields = $request->input('fields');
|
||||||
|
if ($requestFields !== null) {
|
||||||
|
$limitFields = array_intersect(explode(',', $requestFields), $fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($limitFields) === false) {
|
||||||
|
$modelSubset = new $conductor->class();
|
||||||
|
foreach ($limitFields as $field) {
|
||||||
|
$modelSubset->setAttribute($field, $model->$field);
|
||||||
|
}
|
||||||
|
$model = $modelSubset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Includes
|
||||||
|
$includes = $conductor->includes;
|
||||||
|
if ($request !== null && $request->has('includes') === true) {
|
||||||
|
$includes = explode(',', $request->input('includes', ''));
|
||||||
|
}
|
||||||
|
$conductor->includes($model, $includes);
|
||||||
|
|
||||||
|
// Transform
|
||||||
|
$model = $conductor->transform($model);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a single field in the conductor collection.
|
||||||
|
*
|
||||||
|
* @param string $field The field name.
|
||||||
|
* @param mixed $value The value or array of values to filter.
|
||||||
|
* @param string $boolean The comparision boolean (AND or OR).
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function filterField(string $field, mixed $value, string $boolean = 'AND')
|
||||||
|
{
|
||||||
|
$this->filterFieldWithBuilder($this->query, $field, $value, $boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or Set the conductor collection.
|
||||||
|
*
|
||||||
|
* @param Collection $collection If not null, use the passed collection.
|
||||||
|
* @return Collection The current conductor collection.
|
||||||
|
*/
|
||||||
|
final public function collection(Collection $collection = null)
|
||||||
|
{
|
||||||
|
if ($collection !== null) {
|
||||||
|
$this->collection = $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current conductor collection count.
|
||||||
|
*
|
||||||
|
* @return integer The current collection count.
|
||||||
|
*/
|
||||||
|
final public function count()
|
||||||
|
{
|
||||||
|
if ($this->query !== null) {
|
||||||
|
return $this->query->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the conductor collection.
|
||||||
|
*
|
||||||
|
* @param mixed $fields A field name or array of field names to sort. Supports a prefix of + or - to change direction.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function sort(mixed $fields = null)
|
||||||
|
{
|
||||||
|
if (is_string($fields) === true) {
|
||||||
|
$fields = explode(',', $fields);
|
||||||
|
} elseif ($fields === null) {
|
||||||
|
$fields = $this->sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($fields) === true) {
|
||||||
|
foreach ($fields as $orderByField) {
|
||||||
|
$direction = 'asc';
|
||||||
|
$directionChar = substr($orderByField, 0, 1);
|
||||||
|
|
||||||
|
if (in_array($directionChar, ['-', '+']) === true) {
|
||||||
|
$orderByField = substr($orderByField, 1);
|
||||||
|
if ($directionChar === '-') {
|
||||||
|
$direction = 'desc';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->query->orderBy(trim($orderByField), $direction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \InvalidArgumentException('Expected string or array, got ' . gettype($fields));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the conductor collection based on an array of field => value.
|
||||||
|
*
|
||||||
|
* @param array $filters An array of field => value to filter.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function filter(array $filters)
|
||||||
|
{
|
||||||
|
foreach ($filters as $param => $value) {
|
||||||
|
$this->filterField($param, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paginate the conductor collection.
|
||||||
|
*
|
||||||
|
* @param integer $page The current page to return.
|
||||||
|
* @param integer $limit The limit of items to include or use default.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function paginate(int $page = 1, int $limit = -1)
|
||||||
|
{
|
||||||
|
// Limit
|
||||||
|
if ($limit < 1) {
|
||||||
|
$limit = $this->limit;
|
||||||
|
} else {
|
||||||
|
$limit = min($limit, $this->maxLimit);
|
||||||
|
}
|
||||||
|
$this->query->limit($limit);
|
||||||
|
|
||||||
|
// Page
|
||||||
|
if ($page < 1) {
|
||||||
|
$page = 1;
|
||||||
|
}
|
||||||
|
$this->query->offset(($page - 1) * $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a list of includes to the model.
|
||||||
|
*
|
||||||
|
* @param Model $model The model to append.
|
||||||
|
* @param array $includes The list of includes to include.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function includes(Model $model, array $includes)
|
||||||
|
{
|
||||||
|
foreach ($includes as $include) {
|
||||||
|
$includeMethodName = 'include' . Str::studly($include);
|
||||||
|
if (method_exists($this, $includeMethodName) === true) {
|
||||||
|
$attributeName = Str::snake($include);
|
||||||
|
$attributeValue = $this->{$includeMethodName}($model);
|
||||||
|
if ($attributeValue !== null) {
|
||||||
|
$model->$attributeName = $this->{$includeMethodName}($model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the returned fields in the conductor collection.
|
||||||
|
*
|
||||||
|
* @param array $fields An array of field names.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function limitFields(array $fields)
|
||||||
|
{
|
||||||
|
if (empty($fields) !== true) {
|
||||||
|
$this->query->select($fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the conductor collection using raw data.
|
||||||
|
*
|
||||||
|
* @param string $filterString The raw filter string to parse.
|
||||||
|
* @param array|null $limitFields The fields to ignore in the filter string.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
final public function filterRaw(string $filterString, array|null $limitFields = null)
|
||||||
|
{
|
||||||
|
if (is_array($limitFields) === false || empty($limitFields) === true) {
|
||||||
|
$limitFields = null;
|
||||||
|
} else {
|
||||||
|
$limitFields = array_map('strtolower', $limitFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens = preg_split('/([()]|,OR,|,AND,|,)/', $filterString, -1, (PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE));
|
||||||
|
$glued = [];
|
||||||
|
$glueToken = '';
|
||||||
|
foreach ($tokens as $item) {
|
||||||
|
if ($glueToken === '') {
|
||||||
|
if (preg_match('/(?<!\\\\)[\'"]/', $item, $matches, PREG_OFFSET_CAPTURE) === 1) {
|
||||||
|
$glueToken = $matches[0][0];
|
||||||
|
$item = substr($item, 0, $matches[0][1]) . substr($item, ($matches[0][1] + 1));
|
||||||
|
$item = str_replace("\\$glueToken", $glueToken, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$glued[] = $item;
|
||||||
|
} else {
|
||||||
|
// search for ending glue token
|
||||||
|
if (preg_match('/(?<!\\\\)' . $glueToken . '/', $item, $matches, PREG_OFFSET_CAPTURE) === 1) {
|
||||||
|
$item = substr($item, 0, $matches[0][1]) . substr($item, ($matches[0][1] + 1));
|
||||||
|
$glueToken = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = str_replace("\\$glueToken", $glueToken, $item);
|
||||||
|
|
||||||
|
$glued[(count($glued) - 1)] .= $item;
|
||||||
|
}
|
||||||
|
}//end foreach
|
||||||
|
$tokens = $glued;
|
||||||
|
|
||||||
|
$parseTokens = function ($tokenList, $level, $index, $groupBoolean = null) use ($limitFields, &$parseTokens) {
|
||||||
|
$tokenGroup = [];
|
||||||
|
$firstToken = false;
|
||||||
|
$tokenGroupBoolean = 'AND';
|
||||||
|
|
||||||
|
if ($groupBoolean !== null) {
|
||||||
|
$firstToken = true;
|
||||||
|
$tokenGroupBoolean = $groupBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($index < count($tokenList)) {
|
||||||
|
$token = $tokenList[$index];
|
||||||
|
|
||||||
|
++$index;
|
||||||
|
if ($token === '(') {
|
||||||
|
// next group
|
||||||
|
$nextGroupBoolean = null;
|
||||||
|
if (count($tokenGroup) > 0 && strlen($tokenGroup[(count($tokenGroup) - 1)]['field']) === 0) {
|
||||||
|
$nextGroupBoolean = $tokenGroup[(count($tokenGroup) - 1)]['boolean'];
|
||||||
|
unset($tokenGroup[(count($tokenGroup) - 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$index = $parseTokens($tokenList, $level + 1, $index, $nextGroupBoolean);
|
||||||
|
} elseif ($token === ')') {
|
||||||
|
// end group
|
||||||
|
break;
|
||||||
|
} elseif (in_array(strtoupper($token), [',AND,', ',OR,']) === true) {
|
||||||
|
// update boolean
|
||||||
|
$boolean = trim(strtoupper($token), ',');
|
||||||
|
|
||||||
|
if ($firstToken === false && $level > 0) {
|
||||||
|
$tokenGroupBoolean = $boolean;
|
||||||
|
} else {
|
||||||
|
$firstToken = true;
|
||||||
|
$tokenGroup[] = [
|
||||||
|
'field' => '',
|
||||||
|
'value' => '',
|
||||||
|
'boolean' => $boolean
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} elseif (strpos($token, ':') !== false) {
|
||||||
|
// set tokenGroup
|
||||||
|
$firstToken = true;
|
||||||
|
$field = substr($token, 0, strpos($token, ':'));
|
||||||
|
$value = substr($token, (strpos($token, ':') + 1));
|
||||||
|
$boolean = 'AND';
|
||||||
|
|
||||||
|
if (count($tokenGroup) > 0 && strlen($tokenGroup[(count($tokenGroup) - 1)]['field']) === 0) {
|
||||||
|
$tokenGroup[(count($tokenGroup) - 1)]['field'] = $field;
|
||||||
|
$tokenGroup[(count($tokenGroup) - 1)]['value'] = $value;
|
||||||
|
$boolean = $tokenGroup[(count($tokenGroup) - 1)]['boolean'];
|
||||||
|
} else {
|
||||||
|
$tokenGroup[] = [
|
||||||
|
'field' => $field,
|
||||||
|
'value' => $value,
|
||||||
|
'boolean' => 'AND'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($limitFields === null || in_array(strtolower($field), $limitFields) !== true) {
|
||||||
|
unset($tokenGroup[(count($tokenGroup) - 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($level === 0) {
|
||||||
|
$this->filterFieldWithBuilder($this->query, $field, $value, $boolean);
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
}//end while
|
||||||
|
|
||||||
|
if ($level > 0) {
|
||||||
|
if ($tokenGroupBoolean === 'OR') {
|
||||||
|
$this->query->orWhere(function ($query) use ($tokenGroup) {
|
||||||
|
foreach ($tokenGroup as $tokenItem) {
|
||||||
|
if (strlen($tokenItem['field']) > 0) {
|
||||||
|
$this->filterFieldWithBuilder($query, $tokenItem['field'], $tokenItem['value'], $tokenItem['boolean']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$this->query->where(function ($query) use ($tokenGroup) {
|
||||||
|
foreach ($tokenGroup as $tokenItem) {
|
||||||
|
if (strlen($tokenItem['field']) > 0) {
|
||||||
|
$this->filterFieldWithBuilder($query, $tokenItem['field'], $tokenItem['value'], $tokenItem['boolean']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $index;
|
||||||
|
};
|
||||||
|
|
||||||
|
$parseTokens($tokens, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of model fields visible to the current user.
|
||||||
|
*
|
||||||
|
* @param Model $model The model in question.
|
||||||
|
* @return array The array of field names.
|
||||||
|
*/
|
||||||
|
public function fields(Model $model)
|
||||||
|
{
|
||||||
|
$visibleFields = $model->getVisible();
|
||||||
|
if (empty($visibleFields) === true) {
|
||||||
|
$tableColumns = $model->getConnection()
|
||||||
|
->getSchemaBuilder()
|
||||||
|
->getColumnListing($model->getTable());
|
||||||
|
return $tableColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $visibleFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the passed Model to an array
|
||||||
|
*
|
||||||
|
* @param Model $model The model to transform.
|
||||||
|
* @return array The transformed model.
|
||||||
|
*/
|
||||||
|
public function transform(Model $model)
|
||||||
|
{
|
||||||
|
return $model->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the passed model viewable by the current user?
|
||||||
|
*
|
||||||
|
* @param Model $model The model in question.
|
||||||
|
* @return boolean Is the model viewable.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the model creatable by the current user?
|
||||||
|
*
|
||||||
|
* @return boolean Is the model creatable.
|
||||||
|
*/
|
||||||
|
public static function creatable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the passed model updateable by the current user?
|
||||||
|
*
|
||||||
|
* @param Model $model The model in question.
|
||||||
|
* @return boolean Is the model updateable.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the passed model destroyable by the current user?
|
||||||
|
*
|
||||||
|
* @param Model $model The model in question.
|
||||||
|
* @return boolean Is the model destroyable.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
app/Conductors/EventConductor.php
Normal file
92
app/Conductors/EventConductor.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class EventConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = '\App\Models\Event';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = 'start_at';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/events') === false) {
|
||||||
|
$builder
|
||||||
|
->where('status', '!=', 'draft')
|
||||||
|
->where('publish_at', '<=', now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model)
|
||||||
|
{
|
||||||
|
if (strtolower($model->status) === 'draft' || Carbon::parse($model->publish_at)->isFuture() === true) {
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/events') === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable()
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
app/Conductors/MediaConductor.php
Normal file
109
app/Conductors/MediaConductor.php
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class MediaConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = '\App\Models\Media';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = 'created_at';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of model fields visible to the current user.
|
||||||
|
*
|
||||||
|
* @param Model $model The model in question.
|
||||||
|
* @return array The array of field names.
|
||||||
|
*/
|
||||||
|
public function fields(Model $model)
|
||||||
|
{
|
||||||
|
$fields = parent::fields($model);
|
||||||
|
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/media') === false) {
|
||||||
|
$fields = arrayRemoveItem($fields, 'permission');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null) {
|
||||||
|
$builder->whereNull('permission');
|
||||||
|
} else {
|
||||||
|
$builder->whereNull('permission')->orWhereIn('permission', $user->permissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model)
|
||||||
|
{
|
||||||
|
if ($model->permission !== null) {
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission($model->permission) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable()
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && (strcasecmp($model->user_id, $user->id) === 0 || $user->hasPermission('admin/media') === true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && ($model->user_id === $user->id || $user->hasPermission('admin/media') === true));
|
||||||
|
}
|
||||||
|
}
|
||||||
91
app/Conductors/PostConductor.php
Normal file
91
app/Conductors/PostConductor.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class PostConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = '\App\Models\Post';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sorting field
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $sort = '-publish_at';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a scope query on the collection before anything else.
|
||||||
|
*
|
||||||
|
* @param Builder $builder The builder in use.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function scope(Builder $builder)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/posts') === false) {
|
||||||
|
$builder
|
||||||
|
->where('publish_at', '<=', now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is visible.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow model to be visible.
|
||||||
|
*/
|
||||||
|
public static function viewable(Model $model)
|
||||||
|
{
|
||||||
|
if (Carbon::parse($model->publish_at)->isFuture() === true) {
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/posts') === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is creatable.
|
||||||
|
*
|
||||||
|
* @return boolean Allow creating model.
|
||||||
|
*/
|
||||||
|
public static function creatable()
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/posts') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/posts') === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/posts') === true);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Conductors/SubscriptionConductor.php
Normal file
39
app/Conductors/SubscriptionConductor.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class SubscriptionConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = '\App\Models\Subscription';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) || $user->hasPermission('admin/subscriptions') === true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) || $user->hasPermission('admin/subscriptions') === true));
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/Conductors/UserConductor.php
Normal file
78
app/Conductors/UserConductor.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Conductors;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class UserConductor extends Conductor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Model Class
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $class = '\App\Models\User';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the visible API fields.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return string[] The fields visible.
|
||||||
|
*/
|
||||||
|
public function fields(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user === null || $user->hasPermission('admin/users') === false) {
|
||||||
|
return ['id', 'username'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::fields($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the passed Model to an array
|
||||||
|
*
|
||||||
|
* @param Model $model The model to transform.
|
||||||
|
* @return array The transformed model.
|
||||||
|
*/
|
||||||
|
public function transform(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
$data = $model->toArray();
|
||||||
|
|
||||||
|
if ($user === null || ($user->hasPermission('admin/users') === false && strcasecmp($user->id, $model->id) !== 0)) {
|
||||||
|
$fields = ['id', 'username'];
|
||||||
|
$data = arrayLimitKeys($data, $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is updatable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow updating model.
|
||||||
|
*/
|
||||||
|
public static function updatable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user !== null) {
|
||||||
|
return ($user->hasPermission('admin/users') === true || strcasecmp($user->id, $model->id) === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the current model is destroyable.
|
||||||
|
*
|
||||||
|
* @param Model $model The model.
|
||||||
|
* @return boolean Allow deleting model.
|
||||||
|
*/
|
||||||
|
public static function destroyable(Model $model)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
return ($user !== null && $user->hasPermission('admin/users') === true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?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();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<?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'],
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,597 +0,0 @@
|
|||||||
<?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.
|
|
||||||
* @param object $modelData Model data if a single object is requested.
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function seeAttributes(array $attributes, mixed $user, ?object $modelData = null)
|
|
||||||
{
|
|
||||||
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(), $model);
|
|
||||||
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_filter($sortList, function ($item) use ($attributes) {
|
|
||||||
$parsedItem = $item;
|
|
||||||
if (substr($parsedItem, 0, 1) === '-') {
|
|
||||||
$parsedItem = substr($parsedItem, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return in_array($parsedItem, $attributes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<?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'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?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';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default column sorting (prefix with - for descending)
|
|
||||||
*
|
|
||||||
* @var string|array
|
|
||||||
*/
|
|
||||||
protected $defaultSort = '-publish_at';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?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.
|
|
||||||
* @param object $userData User model if single object is requested.
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function seeAttributes(array $attributes, mixed $user, ?object $userData = null)
|
|
||||||
{
|
|
||||||
if ($user?->hasPermission('admin/users') !== true && ($user === null || $userData === null || $user?->id !== $userData?->id)) {
|
|
||||||
return ['id', 'username'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
app/Helpers/Array.php
Normal file
40
app/Helpers/Array.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* Array Helper Functions */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from an array.
|
||||||
|
*
|
||||||
|
* @param array $arr The array to check.
|
||||||
|
* @param string|array $item The item or items to remove.
|
||||||
|
* @return array The filtered array.
|
||||||
|
*/
|
||||||
|
function arrayRemoveItem(array $arr, string|array $item): array
|
||||||
|
{
|
||||||
|
$filteredArr = $arr;
|
||||||
|
|
||||||
|
if (is_string($item) === true) {
|
||||||
|
$item = [$item];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($item as $str) {
|
||||||
|
$filteredArr = array_filter($arr, function ($item) use ($str) {
|
||||||
|
return $item !== $str;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array with specified the keys
|
||||||
|
*
|
||||||
|
* @param array $arr The array to filter.
|
||||||
|
* @param string|array $keys The keys to keep.
|
||||||
|
* @return array The filtered array.
|
||||||
|
*/
|
||||||
|
function arrayLimitKeys(array $arr, array $keys): array
|
||||||
|
{
|
||||||
|
return array_intersect_key($arr, array_flip($keys));
|
||||||
|
}
|
||||||
@@ -121,13 +121,15 @@ class ApiController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Return resource data
|
* Return resource data
|
||||||
*
|
*
|
||||||
* @param array|Model|Collection $data Resource data.
|
* @param array|Model|Collection $data Resource data.
|
||||||
* @param array|null $appendData Data to append to response.
|
* @param boolean $isCollection If the data is a group of items.
|
||||||
* @param integer $respondCode Resource code.
|
* @param array|null $appendData Data to append to response.
|
||||||
|
* @param integer $respondCode Resource code.
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
protected function respondAsResource(
|
protected function respondAsResource(
|
||||||
mixed $data,
|
mixed $data,
|
||||||
|
bool $isCollection = false,
|
||||||
mixed $appendData = null,
|
mixed $appendData = null,
|
||||||
int $respondCode = HttpResponseCodes::HTTP_OK
|
int $respondCode = HttpResponseCodes::HTTP_OK
|
||||||
) {
|
) {
|
||||||
@@ -144,8 +146,6 @@ class ApiController extends Controller
|
|||||||
$resourceName = strtolower($resourceName);
|
$resourceName = strtolower($resourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
$is_multiple = true;
|
|
||||||
|
|
||||||
$dataArray = [];
|
$dataArray = [];
|
||||||
if ($data instanceof Collection) {
|
if ($data instanceof Collection) {
|
||||||
$dataArray = $data->toArray();
|
$dataArray = $data->toArray();
|
||||||
@@ -157,7 +157,7 @@ class ApiController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$resource = [];
|
$resource = [];
|
||||||
if ($is_multiple === true) {
|
if ($isCollection === true) {
|
||||||
$resource = [Str::plural($resourceName) => $dataArray];
|
$resource = [Str::plural($resourceName) => $dataArray];
|
||||||
} else {
|
} else {
|
||||||
$resource = [Str::singular($resourceName) => $dataArray];
|
$resource = [Str::singular($resourceName) => $dataArray];
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class AuthController extends ApiController
|
|||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$user->makeVisible(['permissions']),
|
$user->makeVisible(['permissions']),
|
||||||
|
false,
|
||||||
['token' => $token]
|
['token' => $token]
|
||||||
);
|
);
|
||||||
}//end if
|
}//end if
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\EventFilter;
|
|
||||||
use App\Http\Requests\EventRequest;
|
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
|
use App\Conductors\EventConductor;
|
||||||
|
use App\Http\Requests\EventRequest;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class EventController extends ApiController
|
class EventController extends ApiController
|
||||||
@@ -22,56 +22,72 @@ class EventController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param EventFilter $filter The event filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(EventFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource(
|
list($collection, $total) = EventConductor::request($request);
|
||||||
$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(
|
return $this->respondAsResource(
|
||||||
(new EventFilter($request))->filter($event),
|
$collection,
|
||||||
null,
|
true,
|
||||||
HttpResponseCodes::HTTP_CREATED
|
['total' => $total]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param EventFilter $filter The event filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param \App\Models\Event $event The specified event.
|
* @param \App\Models\Event $event The specified event.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(EventFilter $filter, Event $event)
|
public function show(Request $request, Event $event)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($event));
|
if (EventConductor::viewable($event) === true) {
|
||||||
|
return $this->respondAsResource(EventConductor::model($request, $event));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\EventRequest $request The request.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function store(EventRequest $request)
|
||||||
|
{
|
||||||
|
if (EventConductor::creatable() === true) {
|
||||||
|
$event = Event::create($request->all());
|
||||||
|
return $this->respondAsResource(
|
||||||
|
EventConductor::model($request, $event),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
HttpResponseCodes::HTTP_CREATED
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param EventRequest $request The event update request.
|
* @param \App\Http\Requests\EventRequest $request The endpoint request.
|
||||||
* @param \App\Models\Event $event The specified event.
|
* @param \App\Models\Event $event The specified event.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function update(EventRequest $request, Event $event)
|
public function update(EventRequest $request, Event $event)
|
||||||
{
|
{
|
||||||
$event->update($request->all());
|
if (EventConductor::updatable($event) === true) {
|
||||||
return $this->respondAsResource((new EventFilter($request))->filter($event));
|
$event->update($request->all());
|
||||||
|
return $this->respondAsResource(EventConductor::model($request, $event));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,7 +98,11 @@ class EventController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function destroy(Event $event)
|
public function destroy(Event $event)
|
||||||
{
|
{
|
||||||
$event->delete();
|
if (EventConductor::destroyable($event) === true) {
|
||||||
return $this->respondNoContent();
|
$event->delete();
|
||||||
|
return $this->respondNoContent();
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\MediaConductor;
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\MediaFilter;
|
use App\Http\Requests\MediaRequest;
|
||||||
use App\Http\Requests\MediaStoreRequest;
|
|
||||||
use App\Http\Requests\MediaUpdateRequest;
|
|
||||||
use App\Models\Media;
|
use App\Models\Media;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use Laravel\Sanctum\PersonalAccessToken;
|
use Laravel\Sanctum\PersonalAccessToken;
|
||||||
|
|
||||||
class MediaController extends ApiController
|
class MediaController extends ApiController
|
||||||
@@ -26,99 +24,68 @@ class MediaController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param \App\Filters\MediaFilter $filter Created filter object.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(MediaFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
|
list($collection, $total) = MediaConductor::request($request);
|
||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$filter->filter(),
|
$collection,
|
||||||
['total' => $filter->foundTotal()]
|
true,
|
||||||
|
['total' => $total]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param MediaFilter $filter The request filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param Media $medium The request media.
|
* @param \App\Models\Media $medium The request media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(MediaFilter $filter, Media $medium)
|
public function show(Request $request, Media $medium)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($medium));
|
if (MediaConductor::viewable($medium) === true) {
|
||||||
|
return $this->respondAsResource(MediaConductor::model($request, $medium));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a new media resource
|
* Store a new media resource
|
||||||
*
|
*
|
||||||
* @param MediaStoreRequest $request The uploaded media.
|
* @param \App\Http\Requests\MediaRequest $request The uploaded media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function store(MediaStoreRequest $request)
|
public function store(MediaRequest $request)
|
||||||
{
|
{
|
||||||
$file = $request->file('file');
|
if (MediaConductor::creatable() === true) {
|
||||||
if ($file === null) {
|
$file = $request->file('file');
|
||||||
return $this->respondWithErrors(['file' => 'The browser did not upload the file correctly to the server.']);
|
if ($file === null) {
|
||||||
}
|
return $this->respondWithErrors(['file' => 'The browser did not upload the file correctly 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->respondWithErrors(['file' => 'The file upload was interrupted.']);
|
|
||||||
default:
|
|
||||||
return $this->respondWithErrors(['file' => 'An error occurred uploading the file to the server.']);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ($file->getSize() > Media::maxUploadSize()) {
|
if ($file->isValid() !== true) {
|
||||||
return $this->respondTooLarge();
|
switch ($file->getError()) {
|
||||||
}
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
|
return $this->respondTooLarge();
|
||||||
|
case UPLOAD_ERR_PARTIAL:
|
||||||
|
return $this->respondWithErrors(['file' => 'The file upload was interrupted.']);
|
||||||
|
default:
|
||||||
|
return $this->respondWithErrors(['file' => 'An error occurred uploading the file to the server.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$title = $file->getClientOriginalName();
|
|
||||||
$mime = $file->getMimeType();
|
|
||||||
$fileInfo = Media::store($file, empty($request->input('permission')));
|
|
||||||
if ($fileInfo === null) {
|
|
||||||
return $this->respondWithErrors(
|
|
||||||
['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()) {
|
if ($file->getSize() > Media::maxUploadSize()) {
|
||||||
return $this->respondTooLarge();
|
return $this->respondTooLarge();
|
||||||
}
|
}
|
||||||
|
|
||||||
$oldPath = $medium->path();
|
$title = $file->getClientOriginalName();
|
||||||
|
$mime = $file->getMimeType();
|
||||||
$fileInfo = Media::store($file, empty($request->input('permission')));
|
$fileInfo = Media::store($file, empty($request->input('permission')));
|
||||||
if ($fileInfo === null) {
|
if ($fileInfo === null) {
|
||||||
return $this->respondWithErrors(
|
return $this->respondWithErrors(
|
||||||
@@ -127,34 +94,78 @@ class MediaController extends ApiController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($oldPath) === true) {
|
|
||||||
unlink($oldPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->merge([
|
$request->merge([
|
||||||
'title' => $file->getClientOriginalName(),
|
'title' => $title,
|
||||||
'mime' => $file->getMimeType(),
|
'mime' => $mime,
|
||||||
'name' => $fileInfo['name'],
|
'name' => $fileInfo['name'],
|
||||||
'size' => filesize($fileInfo['path'])
|
'size' => filesize($fileInfo['path'])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$media = $request->user()->media()->create($request->all());
|
||||||
|
return $this->respondAsResource(
|
||||||
|
MediaConductor::model($request, $media),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
HttpResponseCodes::HTTP_CREATED
|
||||||
|
);
|
||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
$medium->update($request->all());
|
return $this->respondForbidden();
|
||||||
return $this->respondWithTransformer($file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the media resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\MediaRequest $request The update request.
|
||||||
|
* @param \App\Models\Media $medium The specified media.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function update(MediaRequest $request, Media $medium)
|
||||||
|
{
|
||||||
|
if (MediaConductor::updatable($medium) === true) {
|
||||||
|
$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->respondWithErrors(
|
||||||
|
['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->respondAsResource(MediaConductor::model($request, $medium));
|
||||||
|
}//end if
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the specified resource from storage.
|
* Remove the specified resource from storage.
|
||||||
*
|
*
|
||||||
* @param Request $request Request instance.
|
* @param \App\Models\Media $medium Specified media file.
|
||||||
* @param \App\Models\Media $medium Specified media file.
|
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function destroy(Request $request, Media $medium)
|
public function destroy(Media $medium)
|
||||||
{
|
{
|
||||||
if ((new MediaFilter($request))->filter($medium) !== null) {
|
if (MediaConductor::destroyable($medium) === true) {
|
||||||
if (file_exists($medium->path()) === true) {
|
if (file_exists($medium->path()) === true) {
|
||||||
unlink($medium->path());
|
unlink($medium->path());
|
||||||
}
|
}
|
||||||
@@ -163,14 +174,14 @@ class MediaController extends ApiController
|
|||||||
return $this->respondNoContent();
|
return $this->respondNoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->respondNotFound();
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param Request $request Request instance.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param \App\Models\Media $medium Specified media.
|
* @param \App\Models\Media $medium Specified media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function download(Request $request, Media $medium)
|
public function download(Request $request, Media $medium)
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\PostConductor;
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\PostFilter;
|
use App\Http\Requests\PostRequest;
|
||||||
use App\Http\Requests\PostStoreRequest;
|
|
||||||
use App\Http\Requests\PostUpdateRequest;
|
|
||||||
use App\Models\Post;
|
use App\Models\Post;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@@ -27,56 +26,72 @@ class PostController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param \App\Filters\PostFilter $filter Post filter request.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(PostFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
|
list($collection, $total) = PostConductor::request($request);
|
||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$filter->filter(),
|
$collection,
|
||||||
['total' => $filter->foundTotal()]
|
true,
|
||||||
|
['total' => $total]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*
|
*
|
||||||
* @param PostFilter $filter The filter request.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param \App\Models\Post $post The post model.
|
* @param \App\Models\Post $post The post model.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(PostFilter $filter, Post $post)
|
public function show(Request $request, Post $post)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($post));
|
if (PostConductor::viewable($post) === true) {
|
||||||
|
return $this->respondAsResource(PostConductor::model($request, $post));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*
|
*
|
||||||
* @param PostStoreRequest $request The post store request.
|
* @param \App\Http\Requests\PostRequest $request The user request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function store(PostStoreRequest $request)
|
public function store(PostRequest $request)
|
||||||
{
|
{
|
||||||
$post = Post::create($request->all());
|
if (PostConductor::creatable() === true) {
|
||||||
return $this->respondAsResource(
|
$post = Post::create($request->all());
|
||||||
(new PostFilter($request))->filter($post),
|
return $this->respondAsResource(
|
||||||
null,
|
PostConductor::model($request, $post),
|
||||||
HttpResponseCodes::HTTP_CREATED
|
false,
|
||||||
);
|
null,
|
||||||
|
HttpResponseCodes::HTTP_CREATED
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param PostUpdateRequest $request The post update request.
|
* @param \App\Http\Requests\PostRequest $request The post update request.
|
||||||
* @param \App\Models\Post $post The specified post.
|
* @param \App\Models\Post $post The specified post.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function update(PostUpdateRequest $request, Post $post)
|
public function update(PostRequest $request, Post $post)
|
||||||
{
|
{
|
||||||
$post->update($request->all());
|
if (PostConductor::updatable($post) === true) {
|
||||||
return $this->respondAsResource((new PostFilter($request))->filter($post));
|
$post->update($request->all());
|
||||||
|
return $this->respondAsResource(PostConductor::model($request, $post));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,7 +102,11 @@ class PostController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function destroy(Post $post)
|
public function destroy(Post $post)
|
||||||
{
|
{
|
||||||
$post->delete();
|
if (PostConductor::destroyable($post) === true) {
|
||||||
return $this->respondNoContent();
|
$post->delete();
|
||||||
|
return $this->respondNoContent();
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Conductors\SubscriptionConductor;
|
||||||
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Models\Subscription;
|
use App\Models\Subscription;
|
||||||
use App\Filters\SubscriptionFilter;
|
|
||||||
use App\Http\Requests\SubscriptionRequest;
|
use App\Http\Requests\SubscriptionRequest;
|
||||||
use App\Jobs\SendEmailJob;
|
use App\Jobs\SendEmailJob;
|
||||||
use App\Mail\SubscriptionConfirm;
|
use App\Mail\SubscriptionConfirm;
|
||||||
use App\Mail\SubscriptionUnsubscribed;
|
use App\Mail\SubscriptionUnsubscribed;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class SubscriptionController extends ApiController
|
class SubscriptionController extends ApiController
|
||||||
{
|
{
|
||||||
@@ -23,58 +25,71 @@ class SubscriptionController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Display a listing of subscribers.
|
* Display a listing of subscribers.
|
||||||
*
|
*
|
||||||
* @param \App\Filters\SubscriptionFilter $filter Filter object.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(SubscriptionFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$collection = $filter->filter();
|
list($collection, $total) = SubscriptionConductor::request($request);
|
||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$collection,
|
$collection,
|
||||||
['total' => $filter->foundTotal()]
|
true,
|
||||||
|
['total' => $total]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified user.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
|
* @param \App\Models\Subscription $subscription The subscription model.
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Subscription $subscription)
|
||||||
|
{
|
||||||
|
if (SubscriptionConductor::viewable($subscription) === true) {
|
||||||
|
return $this->respondAsResource(SubscriptionConductor::model($request, $subscription));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a subscriber email in the database.
|
* Store a subscriber email in the database.
|
||||||
*
|
*
|
||||||
* @param SubscriptionRequest $request The subscriber update request.
|
* @param \App\Http\Requests\SubscriptionRequest $request The subscriber update request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function store(SubscriptionRequest $request)
|
public function store(SubscriptionRequest $request)
|
||||||
{
|
{
|
||||||
if (Subscription::where('email', $request->email)->first() !== null) {
|
if (SubscriptionConductor::creatable() === true) {
|
||||||
return $this->respondWithErrors(['email' => 'This email address has already subscribed']);
|
Subscription::create($request->all());
|
||||||
|
dispatch((new SendEmailJob($request->email, new SubscriptionConfirm($request->email))))->onQueue('mail');
|
||||||
|
|
||||||
|
return $this->respondCreated();
|
||||||
|
} else {
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
Subscription::create($request->all());
|
|
||||||
dispatch((new SendEmailJob($request->email, new SubscriptionConfirm($request->email))))->onQueue('mail');
|
|
||||||
|
|
||||||
return $this->respondCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified user.
|
|
||||||
*
|
|
||||||
* @param SubscriptionFilter $filter The subscription filter.
|
|
||||||
* @param Subscription $subscription The subscription model.
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function show(SubscriptionFilter $filter, Subscription $subscription)
|
|
||||||
{
|
|
||||||
return $this->respondAsResource($filter->filter($subscription));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param SubscriptionRequest $request The subscription update request.
|
* @param \App\Http\Requests\SubscriptionRequest $request The subscription update request.
|
||||||
* @param Subscription $subscription The specified subscription.
|
* @param \App\Models\Subscription $subscription The specified subscription.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function update(SubscriptionRequest $request, Subscription $subscription)
|
public function update(SubscriptionRequest $request, Subscription $subscription)
|
||||||
{
|
{
|
||||||
|
// if (EventConductor::updatable($event) === true) {
|
||||||
|
// $event->update($request->all());
|
||||||
|
// return $this->respondAsResource(EventConductor::model($request, $event));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return $this->respondForbidden();
|
||||||
|
|
||||||
|
|
||||||
// $input = [];
|
// $input = [];
|
||||||
// $updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
// $updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
||||||
|
|
||||||
@@ -103,14 +118,12 @@ class SubscriptionController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function destroy(Subscription $subscription)
|
public function destroy(Subscription $subscription)
|
||||||
{
|
{
|
||||||
// if ($user->hasPermission('admin/user') === false) {
|
if (SubscriptionConductor::destroyable($subscription) === true) {
|
||||||
// return $this->respondForbidden();
|
$subscription->delete();
|
||||||
// }
|
return $this->respondNoContent();
|
||||||
|
} else {
|
||||||
$email = $subscription->email;
|
return $this->respondForbidden();
|
||||||
|
}
|
||||||
$subscription->delete();
|
|
||||||
return $this->respondNoContent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Enum\HttpResponseCodes;
|
use App\Enum\HttpResponseCodes;
|
||||||
use App\Filters\UserFilter;
|
use App\Http\Requests\UserRequest;
|
||||||
use App\Http\Requests\UserUpdateRequest;
|
|
||||||
use App\Http\Requests\UserStoreRequest;
|
|
||||||
use App\Http\Requests\UserForgotPasswordRequest;
|
use App\Http\Requests\UserForgotPasswordRequest;
|
||||||
use App\Http\Requests\UserForgotUsernameRequest;
|
use App\Http\Requests\UserForgotUsernameRequest;
|
||||||
use App\Http\Requests\UserRegisterRequest;
|
use App\Http\Requests\UserRegisterRequest;
|
||||||
@@ -23,6 +21,7 @@ use App\Models\User;
|
|||||||
use App\Models\UserCode;
|
use App\Models\UserCode;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use App\Conductors\UserConductor;
|
||||||
|
|
||||||
class UserController extends ApiController
|
class UserController extends ApiController
|
||||||
{
|
{
|
||||||
@@ -48,96 +47,102 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
* @param \App\Filters\UserFilter $filter Filter object.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function index(UserFilter $filter)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$collection = $filter->filter();
|
list($collection, $total) = UserConductor::request($request);
|
||||||
|
|
||||||
return $this->respondAsResource(
|
return $this->respondAsResource(
|
||||||
$collection,
|
$collection,
|
||||||
['total' => $filter->foundTotal()]
|
true,
|
||||||
|
['total' => $total]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created user in the database.
|
* Store a newly created user in the database.
|
||||||
*
|
*
|
||||||
* @param UserStoreRequest $request The user update request.
|
* @param \App\Http\Requests\UserRequest $request The endpoint request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function store(UserStoreRequest $request)
|
public function store(UserRequest $request)
|
||||||
{
|
{
|
||||||
if ($request->user()->hasPermission('admin/user') !== true) {
|
if (UserConductor::creatable() === true) {
|
||||||
|
$user = User::create($request->all());
|
||||||
|
return $this->respondAsResource(UserConductor::model($request, $user), false, [], HttpResponseCodes::HTTP_CREATED);
|
||||||
|
} else {
|
||||||
return $this->respondForbidden();
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = User::create($request->all());
|
|
||||||
return $this->respondAsResource((new UserFilter($request))->filter($user), [], HttpResponseCodes::HTTP_CREATED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified user.
|
* Display the specified user.
|
||||||
*
|
*
|
||||||
* @param UserFilter $filter The user filter.
|
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||||
* @param User $user The user model.
|
* @param \App\Models\User $user The user model.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function show(UserFilter $filter, User $user)
|
public function show(Request $request, User $user)
|
||||||
{
|
{
|
||||||
return $this->respondAsResource($filter->filter($user));
|
if (UserConductor::viewable($user) === true) {
|
||||||
|
return $this->respondAsResource(UserConductor::model($request, $user));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*
|
*
|
||||||
* @param UserUpdateRequest $request The user update request.
|
* @param \App\Http\Requests\UserRequest $request The user update request.
|
||||||
* @param User $user The specified user.
|
* @param \App\Models\User $user The specified user.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function update(UserUpdateRequest $request, User $user)
|
public function update(UserRequest $request, User $user)
|
||||||
{
|
{
|
||||||
$input = [];
|
if (UserConductor::updatable($user) === true) {
|
||||||
$updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
$input = [];
|
||||||
|
$updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
||||||
|
|
||||||
if ($request->user()->hasPermission('admin/user') === true) {
|
if ($request->user()->hasPermission('admin/user') === true) {
|
||||||
$updatable = array_merge($updatable, ['email_verified_at']);
|
$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(UserConductor::model($request, $user));
|
||||||
}
|
}
|
||||||
|
|
||||||
$input = $request->only($updatable);
|
return $this->respondForbidden();
|
||||||
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.
|
* Remove the user from the database.
|
||||||
*
|
*
|
||||||
* @param User $user The specified user.
|
* @param \App\Models\User $user The specified user.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function destroy(User $user)
|
public function destroy(User $user)
|
||||||
{
|
{
|
||||||
if ($user->hasPermission('admin/user') === false) {
|
if (UserConductor::destroyable($user) === true) {
|
||||||
return $this->respondForbidden();
|
$user->delete();
|
||||||
|
return $this->respondNoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->delete();
|
return $this->respondForbidden();
|
||||||
return $this->respondNoContent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a new user
|
* Register a new user
|
||||||
*
|
*
|
||||||
* @param UserRegisterRequest $request The register user request.
|
* @param \App\Http\Requests\UserRegisterRequest $request The register user request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function register(UserRegisterRequest $request)
|
public function register(UserRegisterRequest $request)
|
||||||
@@ -171,7 +176,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Sends an email with all the usernames registered at that address
|
* Sends an email with all the usernames registered at that address
|
||||||
*
|
*
|
||||||
* @param UserForgotUsernameRequest $request The forgot username request.
|
* @param \App\Http\Requests\UserForgotUsernameRequest $request The forgot username request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function forgotUsername(UserForgotUsernameRequest $request)
|
public function forgotUsername(UserForgotUsernameRequest $request)
|
||||||
@@ -191,7 +196,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Generates a new reset password code
|
* Generates a new reset password code
|
||||||
*
|
*
|
||||||
* @param UserForgotPasswordRequest $request The reset password request.
|
* @param \App\Http\Requests\UserForgotPasswordRequest $request The reset password request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function forgotPassword(UserForgotPasswordRequest $request)
|
public function forgotPassword(UserForgotPasswordRequest $request)
|
||||||
@@ -213,7 +218,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Resets a user password
|
* Resets a user password
|
||||||
*
|
*
|
||||||
* @param UserResetPasswordRequest $request The reset password request.
|
* @param \App\Http\Requests\UserResetPasswordRequest $request The reset password request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function resetPassword(UserResetPasswordRequest $request)
|
public function resetPassword(UserResetPasswordRequest $request)
|
||||||
@@ -247,7 +252,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Verify an email code
|
* Verify an email code
|
||||||
*
|
*
|
||||||
* @param UserVerifyEmailRequest $request The verify email request.
|
* @param \App\Http\Requests\UserVerifyEmailRequest $request The verify email request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function verifyEmail(UserVerifyEmailRequest $request)
|
public function verifyEmail(UserVerifyEmailRequest $request)
|
||||||
@@ -285,7 +290,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Resend a new verify email
|
* Resend a new verify email
|
||||||
*
|
*
|
||||||
* @param UserResendVerifyEmailRequest $request The resend verify email request.
|
* @param \App\Http\Requests\UserResendVerifyEmailRequest $request The resend verify email request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function resendVerifyEmail(UserResendVerifyEmailRequest $request)
|
public function resendVerifyEmail(UserResendVerifyEmailRequest $request)
|
||||||
@@ -312,7 +317,7 @@ class UserController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Resend verification email
|
* Resend verification email
|
||||||
*
|
*
|
||||||
* @param UserResendVerifyEmailRequest $request The resend user request.
|
* @param \App\Http\Requests\UserResendVerifyEmailRequest $request The resend user request.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request)
|
public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request)
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ class BaseRequest extends FormRequest
|
|||||||
*/
|
*/
|
||||||
public function authorize()
|
public function authorize()
|
||||||
{
|
{
|
||||||
if (method_exists($this, 'postAuthorize') === true && request()->isMethod('post') === true) {
|
if (request()->isMethod('post') === true && method_exists($this, 'postAuthorize') === true) {
|
||||||
return $this->postAuthorize();
|
return $this->postAuthorize();
|
||||||
} elseif (method_exists($this, 'putAuthorize') === true && request()->isMethod('put') === true) {
|
} elseif ((request()->isMethod('put') === true || request()->isMethod('patch') === true) && method_exists($this, 'putAuthorize') === true) {
|
||||||
return $this->putAuthorize();
|
return $this->putAuthorize();
|
||||||
|
} elseif (request()->isMethod('delete') === true && method_exists($this, 'destroyAuthorize') === true) {
|
||||||
|
return $this->deleteAuthorize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -38,8 +40,8 @@ class BaseRequest extends FormRequest
|
|||||||
|
|
||||||
if (method_exists($this, 'postRules') === true && request()->isMethod('post') === true) {
|
if (method_exists($this, 'postRules') === true && request()->isMethod('post') === true) {
|
||||||
$rules = $this->mergeRules($rules, $this->postRules());
|
$rules = $this->mergeRules($rules, $this->postRules());
|
||||||
} elseif (method_exists($this, 'putRules') === true && request()->isMethod('put') === true) {
|
} elseif (method_exists($this, 'putRules') === true && (request()->isMethod('put') === true || request()->isMethod('patch') === true)) {
|
||||||
$rules = $this->mergeRules($rules, $this->postRules());
|
$rules = $this->mergeRules($rules, $this->putRules());
|
||||||
} elseif (method_exists($this, 'destroyRules') === true && request()->isMethod('delete') === true) {
|
} elseif (method_exists($this, 'destroyRules') === true && request()->isMethod('delete') === true) {
|
||||||
$rules = $this->mergeRules($rules, $this->destroyRules());
|
$rules = $this->mergeRules($rules, $this->destroyRules());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class EventRequest extends BaseRequest
|
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
|
* Apply the base rules to this request
|
||||||
*
|
*
|
||||||
@@ -47,11 +26,12 @@ class EventRequest extends BaseRequest
|
|||||||
Rule::in(['draft', 'soon', 'open', 'closed', 'cancelled']),
|
Rule::in(['draft', 'soon', 'open', 'closed', 'cancelled']),
|
||||||
],
|
],
|
||||||
'registration_type' => [
|
'registration_type' => [
|
||||||
Rule::in(['none', 'email', 'link']),
|
Rule::in(['none', 'email', 'link', 'message']),
|
||||||
],
|
],
|
||||||
'registration_data' => [
|
'registration_data' => [
|
||||||
Rule::when(strcasecmp('email', $this->attributes->get('registration_type')) == 0, 'required|email'),
|
Rule::when(strcasecmp('email', $this->attributes->get('registration_type')) == 0, 'required|email'),
|
||||||
Rule::when(strcasecmp('link', $this->attributes->get('registration_type')) == 0, 'required|url')
|
Rule::when(strcasecmp('link', $this->attributes->get('registration_type')) == 0, 'required|url'),
|
||||||
|
Rule::when(strcasecmp('message', $this->attributes->get('registration_type')) == 0, 'required|message'),
|
||||||
],
|
],
|
||||||
'hero' => 'uuid|exists:media,id',
|
'hero' => 'uuid|exists:media,id',
|
||||||
];
|
];
|
||||||
|
|||||||
8
app/Http/Requests/MediaRequest.php
Normal file
8
app/Http/Requests/MediaRequest.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class MediaRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?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 [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?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 [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
46
app/Http/Requests/PostRequest.php
Normal file
46
app/Http/Requests/PostRequest.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class PostRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to POST requests.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function postRules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => 'required|string|min:6|unique:posts',
|
||||||
|
'title' => 'required|string|min:6|max:255',
|
||||||
|
'publish_at' => 'required|date',
|
||||||
|
'user_id' => 'required|uuid|exists:users,id',
|
||||||
|
'content' => 'required|string|min:6',
|
||||||
|
'hero' => 'required|uuid|exists:media,id',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to PUT request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function putRules()
|
||||||
|
{
|
||||||
|
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',
|
||||||
|
'content' => 'string|min:6',
|
||||||
|
'hero' => 'uuid|exists:media,id',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?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',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?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',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ class SubscriptionRequest extends BaseRequest
|
|||||||
public function postRules()
|
public function postRules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'email' => 'required|email',
|
'email' => 'required|email|unique:subscriptions',
|
||||||
'captcha_token' => [new Recaptcha()],
|
'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -31,4 +31,16 @@ class SubscriptionRequest extends BaseRequest
|
|||||||
'captcha_token' => [new Recaptcha()],
|
'captcha_token' => [new Recaptcha()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the custom error messages.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function messages()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email.unique' => 'This email address has already subscribed',
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
app/Http/Requests/UserRequest.php
Normal file
54
app/Http/Requests/UserRequest.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class UserRequest extends BaseRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Apply the additional POST base rules to this request
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function postRules()
|
||||||
|
{
|
||||||
|
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'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to PUT request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function putRules()
|
||||||
|
{
|
||||||
|
$user = $this->route('user');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'username' => [
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
'min:4',
|
||||||
|
Rule::unique('users')->ignore($user->id)->when(
|
||||||
|
$this->username !== $user->username,
|
||||||
|
function ($query) {
|
||||||
|
return $query->where('username', $this->username);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'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'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?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'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?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'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,6 @@ class Event extends Model
|
|||||||
'ages',
|
'ages',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the post's attachments.
|
* Get all of the post's attachments.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -103,6 +103,48 @@ class User extends Authenticatable implements Auditable
|
|||||||
return ($this->permissions()->where('permission', $permission)->first() !== null);
|
return ($this->permissions()->where('permission', $permission)->first() !== null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give permissions to the user
|
||||||
|
*
|
||||||
|
* @param string|array $permissions The permission(s) to give.
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function givePermission($permissions)
|
||||||
|
{
|
||||||
|
if (!is_array($permissions)) {
|
||||||
|
$permissions = [$permissions];
|
||||||
|
}
|
||||||
|
|
||||||
|
$permissions = collect($permissions)->map(function ($permission) {
|
||||||
|
return ['permission' => $permission];
|
||||||
|
});
|
||||||
|
|
||||||
|
$existingPermissions = $this->permissions()->whereIn('permission', $permissions->pluck('permission'))->get();
|
||||||
|
$newPermissions = $permissions->reject(function ($permission) use ($existingPermissions) {
|
||||||
|
return $existingPermissions->contains('permission', $permission['permission']);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->permissions()->createMany($newPermissions->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke permissions from the user
|
||||||
|
*
|
||||||
|
* @param string|array $permissions The permission(s) to revoke.
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function revokePermission($permissions)
|
||||||
|
{
|
||||||
|
if (!is_array($permissions)) {
|
||||||
|
$permissions = [$permissions];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->permissions()
|
||||||
|
->whereIn('permission', $permissions)
|
||||||
|
->delete();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of files of the user
|
* Get the list of files of the user
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -46,8 +46,28 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
protected function configureRateLimiting()
|
protected function configureRateLimiting()
|
||||||
{
|
{
|
||||||
RateLimiter::for('api', function (Request $request) {
|
// RateLimiter::for('api', function (Request $request) {
|
||||||
return Limit::perMinute(60)->by($request->user()?->id !== null ?: $request->ip());
|
// return Limit::perMinute(60)->by($request->user()?->id !== null ?: $request->ip());
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
$rateLimitEnabled = true;
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
if (app()->environment('testing')) {
|
||||||
|
$rateLimitEnabled = false;
|
||||||
|
} elseif ($user !== null && $user->hasPermission('admin/ratelimit') === true) {
|
||||||
|
// Admin users with the "admin/ratelimit" permission are not rate limited
|
||||||
|
$rateLimitEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rateLimitEnabled === true) {
|
||||||
|
RateLimiter::for('api', function (Request $request) {
|
||||||
|
return Limit::perMinute(180)->by($request->user()?->id ?: $request->ip());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
RateLimiter::for('api', function () {
|
||||||
|
return Limit::none();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
"name": "laravel/laravel",
|
"name": "laravel/laravel",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"description": "The Laravel Framework.",
|
"description": "The Laravel Framework.",
|
||||||
"keywords": ["framework", "laravel"],
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"laravel"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.0.2",
|
"php": "^8.0.2",
|
||||||
@@ -26,6 +29,9 @@
|
|||||||
"spatie/laravel-ignition": "^1.0"
|
"spatie/laravel-ignition": "^1.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"app/Helpers/Array.php"
|
||||||
|
],
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"App\\": "app/",
|
"App\\": "app/",
|
||||||
"Database\\Factories\\": "database/factories/",
|
"Database\\Factories\\": "database/factories/",
|
||||||
|
|||||||
40
database/factories/EventFactory.php
Normal file
40
database/factories/EventFactory.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Event>
|
||||||
|
*/
|
||||||
|
class EventFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
$startDate = Carbon::parse($this->faker->dateTimeBetween('now', '+1 year'));
|
||||||
|
$endDate = Carbon::parse($this->faker->dateTimeBetween($startDate, '+1 year'));
|
||||||
|
$publishDate = Carbon::parse($this->faker->dateTimeBetween('-1 month', '+1 month'));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'title' => $this->faker->sentence(),
|
||||||
|
'location' => $this->faker->randomElement(['online', 'physical']),
|
||||||
|
'address' => $this->faker->address,
|
||||||
|
'start_at' => $startDate,
|
||||||
|
'end_at' => $endDate,
|
||||||
|
'publish_at' => $publishDate,
|
||||||
|
'status' => $this->faker->randomElement(['draft', 'soon', 'open', 'closed', 'cancelled']),
|
||||||
|
'registration_type' => $this->faker->randomElement(['none', 'email', 'link', 'message']),
|
||||||
|
'registration_data' => $this->faker->sentence(),
|
||||||
|
'hero' => $this->faker->uuid,
|
||||||
|
'content' => $this->faker->paragraphs(3, true),
|
||||||
|
'price' => $this->faker->numberBetween(0, 150),
|
||||||
|
'ages' => $this->faker->regexify('\d+(\+|\-\d+)?'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
29
database/factories/MediaFactory.php
Normal file
29
database/factories/MediaFactory.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Event>
|
||||||
|
*/
|
||||||
|
class MediaFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => $this->faker->sentence(),
|
||||||
|
'name' => storage_path('app/public/') . $this->faker->slug() . '.' . $this->faker->fileExtension,
|
||||||
|
'mime' => $this->faker->mimeType,
|
||||||
|
'user_id' => $this->faker->uuid,
|
||||||
|
'size' => $this->faker->numberBetween(1000, 1000000),
|
||||||
|
'permission' => null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
31
database/factories/PostFactory.php
Normal file
31
database/factories/PostFactory.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Event>
|
||||||
|
*/
|
||||||
|
class PostFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
$publishDate = Carbon::parse($this->faker->dateTimeBetween('-1 month', '+1 month'));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'title' => $this->faker->sentence(),
|
||||||
|
'slug' => $this->faker->slug(),
|
||||||
|
'publish_at' => $publishDate,
|
||||||
|
'content' => $this->faker->paragraphs(3, true),
|
||||||
|
'user_id' => $this->faker->uuid,
|
||||||
|
'hero' => $this->faker->uuid,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -166,7 +166,7 @@ const inputActive = ref(value.value.length > 0 || props.type == "select");
|
|||||||
* Return the classname based on type
|
* Return the classname based on type
|
||||||
*/
|
*/
|
||||||
const computedClassType = computed(() => {
|
const computedClassType = computed(() => {
|
||||||
return `sm-input-${props.type}`;
|
return `sm-input-type-${props.type}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -187,7 +187,7 @@ if (objControl) {
|
|||||||
label.value = toTitleCase(props.control);
|
label.value = toTitleCase(props.control);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputActive.value = value.value.length > 0 || props.type == "select";
|
inputActive.value = value.value?.length > 0 || props.type == "select";
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => objControl.validation.result.valid,
|
() => objControl.validation.result.valid,
|
||||||
@@ -434,15 +434,28 @@ const handleMediaSelect = async (event) => {
|
|||||||
background-size: 24px 18px;
|
background-size: 24px 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.sm-input-media {
|
&.sm-input-type-media {
|
||||||
label {
|
label {
|
||||||
position: relative;
|
position: relative;
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sm-input-help {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sm-feedback-invalid .sm-input-media .sm-input-media-item ion-icon {
|
||||||
|
border: 2px solid $danger-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sm-feedback-invalid .sm-invalid-icon {
|
||||||
|
// position: relative;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sm-input-media {
|
.sm-input-media {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-bottom: map-get($spacer, 2);
|
||||||
|
|
||||||
.sm-input-media-item {
|
.sm-input-media-item {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -65,14 +65,18 @@ const mediaItems: Ref<Media[]> = ref([]);
|
|||||||
* Handle the user adding a new media item.
|
* Handle the user adding a new media item.
|
||||||
*/
|
*/
|
||||||
const handleClickAdd = async () => {
|
const handleClickAdd = async () => {
|
||||||
openDialog(SMDialogMedia, { mime: "", accepts: "" }).then((result) => {
|
openDialog(SMDialogMedia, { mime: "", accepts: "" })
|
||||||
const media = result as Media;
|
.then((result) => {
|
||||||
|
const media = result as Media;
|
||||||
|
|
||||||
mediaItems.value.push(media);
|
mediaItems.value.push(media);
|
||||||
value.value.push(media.id);
|
value.value.push(media.id);
|
||||||
|
|
||||||
emits("update:modelValue", value);
|
emits("update:modelValue", value);
|
||||||
});
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* empty */
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { useProgressStore } from "../store/ProgressStore";
|
import { useProgressStore } from "../store/ProgressStore";
|
||||||
import { useUserStore } from "../store/UserStore";
|
import { useUserStore } from "../store/UserStore";
|
||||||
|
import { ImportMetaExtras } from "../../../import-meta";
|
||||||
|
|
||||||
interface ApiProgressData {
|
interface ApiProgressData {
|
||||||
loaded: number;
|
loaded: number;
|
||||||
total: number;
|
total: number;
|
||||||
@@ -31,7 +33,8 @@ const apiDefaultHeaders = {
|
|||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
timeout: 8000,
|
timeout: 8000,
|
||||||
baseUrl: "https://www.stemmechanics.com.au/api",
|
baseUrl: (import.meta as ImportMetaExtras).env.APP_URL_API,
|
||||||
|
// baseUrl: "https://www.stemmechanics.com.au/api",
|
||||||
|
|
||||||
send: function (options: ApiOptions) {
|
send: function (options: ApiOptions) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
@@ -66,25 +66,29 @@ const waitForElementRender = (elem: Ref): Promise<HTMLElement> => {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const transitionEnter = (elem: Ref, transition: string): void => {
|
export const transitionEnter = (elem: Ref, transition: string): void => {
|
||||||
waitForElementRender(elem).then((e: HTMLElement) => {
|
waitForElementRender(elem)
|
||||||
window.setTimeout(() => {
|
.then((e: HTMLElement) => {
|
||||||
e.classList.replace(
|
window.setTimeout(() => {
|
||||||
transition + "-enter-from",
|
e.classList.replace(
|
||||||
transition + "-enter-active"
|
transition + "-enter-from",
|
||||||
);
|
transition + "-enter-active"
|
||||||
const transitionName = transitionEndEventName();
|
);
|
||||||
e.addEventListener(
|
const transitionName = transitionEndEventName();
|
||||||
transitionName,
|
e.addEventListener(
|
||||||
() => {
|
transitionName,
|
||||||
e.classList.replace(
|
() => {
|
||||||
transition + "-enter-active",
|
e.classList.replace(
|
||||||
transition + "-enter-to"
|
transition + "-enter-active",
|
||||||
);
|
transition + "-enter-to"
|
||||||
},
|
);
|
||||||
false
|
},
|
||||||
);
|
false
|
||||||
}, 1);
|
);
|
||||||
});
|
}, 1);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* empty */
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -95,11 +95,21 @@ const handleLoad = async () => {
|
|||||||
try {
|
try {
|
||||||
let query = {};
|
let query = {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
cats, dogs
|
||||||
|
(title:"cats, dogs",OR,content:"cats, dogs")
|
||||||
|
|
||||||
|
"cats, dogs", mice
|
||||||
|
(title:""cats, dogs", mice",OR,content:"\"cats, dogs\", mice")
|
||||||
|
*/
|
||||||
|
|
||||||
if (filterKeywords.value && filterKeywords.value.length > 0) {
|
if (filterKeywords.value && filterKeywords.value.length > 0) {
|
||||||
query["q"] = filterKeywords.value;
|
let value = filterKeywords.value.replace(/"/g, '\\"');
|
||||||
|
|
||||||
|
query["filter"] = `(title:"${value}",OR,content:"${value}")`;
|
||||||
}
|
}
|
||||||
if (filterLocation.value && filterLocation.value.length > 0) {
|
if (filterLocation.value && filterLocation.value.length > 0) {
|
||||||
query["qlocation"] = filterLocation.value;
|
query["location"] = filterLocation.value;
|
||||||
}
|
}
|
||||||
if (filterDateRange.value && filterDateRange.value.length > 0) {
|
if (filterDateRange.value && filterDateRange.value.length > 0) {
|
||||||
let error = false;
|
let error = false;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
v-if="
|
v-if="
|
||||||
event.status == 'open' &&
|
event.status == 'open' &&
|
||||||
expired == false &&
|
expired == false &&
|
||||||
event.registration_type != 'none'
|
event.registration_type == 'url'
|
||||||
"
|
"
|
||||||
class="sm-workshop-registration sm-workshop-registration-url">
|
class="sm-workshop-registration sm-workshop-registration-url">
|
||||||
<SMButton
|
<SMButton
|
||||||
@@ -60,6 +60,15 @@
|
|||||||
:block="true"
|
:block="true"
|
||||||
label="Register for Event"></SMButton>
|
label="Register for Event"></SMButton>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
event.status == 'open' &&
|
||||||
|
expired == false &&
|
||||||
|
event.registration_type == 'message'
|
||||||
|
"
|
||||||
|
class="sm-workshop-registration sm-workshop-registration-message">
|
||||||
|
{{ event.registration_data }}
|
||||||
|
</div>
|
||||||
<div class="sm-workshop-date">
|
<div class="sm-workshop-date">
|
||||||
<h4>
|
<h4>
|
||||||
<ion-icon
|
<ion-icon
|
||||||
@@ -380,7 +389,8 @@ handleLoad();
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sm-workshop-registration-none,
|
.sm-workshop-registration-none,
|
||||||
.sm-workshop-registration-soon {
|
.sm-workshop-registration-soon,
|
||||||
|
.sm-workshop-registration-message {
|
||||||
border: 1px solid #ffeeba;
|
border: 1px solid #ffeeba;
|
||||||
background-color: #fff3cd;
|
background-color: #fff3cd;
|
||||||
color: #856404;
|
color: #856404;
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
none: 'None',
|
none: 'None',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
link: 'Link',
|
link: 'Link',
|
||||||
|
message: 'Message',
|
||||||
}" />
|
}" />
|
||||||
</SMColumn>
|
</SMColumn>
|
||||||
<SMColumn>
|
<SMColumn>
|
||||||
@@ -186,6 +187,10 @@ const registration_data = computed(() => {
|
|||||||
data.visible = true;
|
data.visible = true;
|
||||||
data.title = "Registration URL";
|
data.title = "Registration URL";
|
||||||
data.type = "url";
|
data.type = "url";
|
||||||
|
} else if (form?.controls.registration_type.value === "message") {
|
||||||
|
data.visible = true;
|
||||||
|
data.title = "Registration message";
|
||||||
|
data.type = "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ Route::post('posts/{post}/attachments', [PostController::class, 'storeAttachment
|
|||||||
Route::delete('posts/{post}/attachments/{attachment}', [PostController::class, 'deleteAttachment']);
|
Route::delete('posts/{post}/attachments/{attachment}', [PostController::class, 'deleteAttachment']);
|
||||||
|
|
||||||
Route::apiResource('events', EventController::class);
|
Route::apiResource('events', EventController::class);
|
||||||
Route::get('events/{event}/attachments', [PostController::class, 'getAttachments']);
|
Route::get('events/{event}/attachments', [EventController::class, 'getAttachments']);
|
||||||
Route::post('events/{event}/attachments', [PostController::class, 'storeAttachment']);
|
Route::post('events/{event}/attachments', [EventController::class, 'storeAttachment']);
|
||||||
Route::delete('events/{event}/attachments/{attachment}', [PostController::class, 'deleteAttachment']);
|
Route::delete('events/{event}/attachments/{attachment}', [EventController::class, 'deleteAttachment']);
|
||||||
|
|
||||||
Route::apiResource('subscriptions', SubscriptionController::class);
|
Route::apiResource('subscriptions', SubscriptionController::class);
|
||||||
Route::delete('subscriptions', [SubscriptionController::class, 'destroyByEmail']);
|
Route::delete('subscriptions', [SubscriptionController::class, 'destroyByEmail']);
|
||||||
|
|||||||
52
tests/Feature/AuthApiTest.php
Normal file
52
tests/Feature/AuthApiTest.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class AuthApiTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function testLogin()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'password' => bcrypt('password'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test successful login
|
||||||
|
$response = $this->postJson('/api/login', [
|
||||||
|
'username' => $user->username,
|
||||||
|
'password' => 'password',
|
||||||
|
]);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonStructure([
|
||||||
|
'token',
|
||||||
|
]);
|
||||||
|
$token = $response->json('token');
|
||||||
|
|
||||||
|
// Test getting authenticated user
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => "Bearer $token",
|
||||||
|
])->get('/api/me');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJson([
|
||||||
|
'user' => [
|
||||||
|
'id' => $user->id,
|
||||||
|
'username' => $user->username,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test logout
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => "Bearer $token",
|
||||||
|
])->postJson('/api/logout');
|
||||||
|
$response->assertStatus(204);
|
||||||
|
|
||||||
|
// Test failed login
|
||||||
|
$response = $this->postJson('/api/login', [
|
||||||
|
'username' => $user->username,
|
||||||
|
'password' => 'wrongpassword',
|
||||||
|
]);
|
||||||
|
$response->assertStatus(422);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
tests/Feature/ContactFormTest.php
Normal file
28
tests/Feature/ContactFormTest.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ContactFormTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function testContactForm()
|
||||||
|
{
|
||||||
|
$formData = [
|
||||||
|
'name' => 'John Doe',
|
||||||
|
'email' => 'johndoe@example.com',
|
||||||
|
'content' => 'Hello, this is a test message.',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/contact', $formData);
|
||||||
|
$response->assertStatus(201);
|
||||||
|
|
||||||
|
$formData = [
|
||||||
|
'name' => 'John Doe',
|
||||||
|
'content' => 'Hello, this is a test message.',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/contact', $formData);
|
||||||
|
$response->assertStatus(422);
|
||||||
|
}
|
||||||
|
}
|
||||||
136
tests/Feature/EventsApiTest.php
Normal file
136
tests/Feature/EventsApiTest.php
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\Models\Media;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Faker\Factory as FakerFactory;
|
||||||
|
|
||||||
|
class EventsApiTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected $faker;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->faker = FakerFactory::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAnyUserCanViewEvent()
|
||||||
|
{
|
||||||
|
// Create an event
|
||||||
|
$event = Event::factory()->create([
|
||||||
|
'publish_at' => Carbon::parse($this->faker->dateTimeBetween('-2 months', '-1 month')),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a future event
|
||||||
|
$futureEvent = Event::factory()->create([
|
||||||
|
'publish_at' => Carbon::parse($this->faker->dateTimeBetween('+1 month', '+2 months')),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Send GET request to the /api/events endpoint
|
||||||
|
$response = $this->getJson('/api/events');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
// Assert that the event is in the response data
|
||||||
|
$response->assertJsonCount(1, 'events');
|
||||||
|
$response->assertJsonFragment([
|
||||||
|
'id' => $event->id,
|
||||||
|
'title' => $event->title,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertJsonMissing([
|
||||||
|
'id' => $futureEvent->id,
|
||||||
|
'title' => $futureEvent->title,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAdminCanCreateUpdateDeleteEvent()
|
||||||
|
{
|
||||||
|
// Create a user with the admin/events permission
|
||||||
|
$adminUser = User::factory()->create();
|
||||||
|
$adminUser->givePermission('admin/events');
|
||||||
|
|
||||||
|
// Create media data
|
||||||
|
$media = Media::factory()->create(['user_id' => $adminUser->id]);
|
||||||
|
|
||||||
|
// Create event data
|
||||||
|
$eventData = Event::factory()->make([
|
||||||
|
'start_at' => now()->addDays(7),
|
||||||
|
'end_at' => now()->addDays(7)->addHours(2),
|
||||||
|
'hero' => $media->id,
|
||||||
|
])->toArray();
|
||||||
|
|
||||||
|
// Test creating event
|
||||||
|
$response = $this->actingAs($adminUser)->postJson('/api/events', $eventData);
|
||||||
|
$response->assertStatus(201);
|
||||||
|
$this->assertDatabaseHas('events', [
|
||||||
|
'title' => $eventData['title'],
|
||||||
|
'content' => $eventData['content'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test viewing event
|
||||||
|
$event = Event::where('title', $eventData['title'])->first();
|
||||||
|
$response = $this->get("/api/events/$event->id");
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonStructure([
|
||||||
|
'event' => [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'content',
|
||||||
|
'start_at',
|
||||||
|
'end_at',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test updating event
|
||||||
|
$eventData['title'] = 'Updated Event';
|
||||||
|
$response = $this->actingAs($adminUser)->putJson("/api/events/$event->id", $eventData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('events', [
|
||||||
|
'title' => 'Updated Event',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test deleting event
|
||||||
|
$response = $this->actingAs($adminUser)->delete("/api/events/$event->id");
|
||||||
|
$response->assertStatus(204);
|
||||||
|
$this->assertDatabaseMissing('events', [
|
||||||
|
'title' => 'Updated Event',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonAdminCannotCreateUpdateDeleteEvent()
|
||||||
|
{
|
||||||
|
// Create a user without admin/events permission
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
// Authenticate as the user
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
// Try to create a new event
|
||||||
|
$media = Media::factory()->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$newEventData = Event::factory()->make(['hero' => $media->id])->toArray();
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/events', $newEventData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
|
||||||
|
// Try to update an event
|
||||||
|
$event = Event::factory()->create();
|
||||||
|
$updatedEventData = [
|
||||||
|
'title' => 'Updated Event',
|
||||||
|
'content' => 'This is an updated event.',
|
||||||
|
// Add more fields as needed
|
||||||
|
];
|
||||||
|
$response = $this->putJson('/api/events/' . $event->id, $updatedEventData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
|
||||||
|
// Try to delete an event
|
||||||
|
$event = Event::factory()->create();
|
||||||
|
$response = $this->deleteJson('/api/events/' . $event->id);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
// use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class ExampleTest extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A basic test example.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function test_the_application_returns_a_successful_response()
|
|
||||||
{
|
|
||||||
$response = $this->get('/');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
134
tests/Feature/PostsApiTest.php
Normal file
134
tests/Feature/PostsApiTest.php
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Media;
|
||||||
|
use App\Models\Post;
|
||||||
|
use Faker\Factory as FakerFactory;
|
||||||
|
|
||||||
|
class PostsApiTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected $faker;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->faker = FakerFactory::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAnyUserCanViewPost()
|
||||||
|
{
|
||||||
|
// Create an event
|
||||||
|
$post = Post::factory()->create([
|
||||||
|
'publish_at' => $this->faker->dateTimeBetween('-2 months', '-1 month'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create a future event
|
||||||
|
$futurePost = Post::factory()->create([
|
||||||
|
'publish_at' => $this->faker->dateTimeBetween('+1 month', '+2 months'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Send GET request to the /api/posts endpoint
|
||||||
|
$response = $this->getJson('/api/posts');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
// Assert that the event is in the response data
|
||||||
|
$response->assertJsonCount(1, 'posts');
|
||||||
|
$response->assertJsonFragment([
|
||||||
|
'id' => $post->id,
|
||||||
|
'title' => $post->title,
|
||||||
|
'content' => $post->content,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertJsonMissing([
|
||||||
|
'id' => $futurePost->id,
|
||||||
|
'title' => $futurePost->title,
|
||||||
|
'content' => $futurePost->content,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAdminCanCreateUpdateDeletePost()
|
||||||
|
{
|
||||||
|
// Create a user with the admin/events permission
|
||||||
|
$adminUser = User::factory()->create();
|
||||||
|
$adminUser->givePermission('admin/posts');
|
||||||
|
|
||||||
|
// Create media data
|
||||||
|
$media = Media::factory()->create(['user_id' => $adminUser->id]);
|
||||||
|
|
||||||
|
// Create event data
|
||||||
|
$postData = Post::factory()->make([
|
||||||
|
'user_id' => $adminUser->id,
|
||||||
|
'hero' => $media->id,
|
||||||
|
])->toArray();
|
||||||
|
|
||||||
|
// Test creating event
|
||||||
|
$response = $this->actingAs($adminUser)->postJson('/api/posts', $postData);
|
||||||
|
$response->assertStatus(201);
|
||||||
|
$this->assertDatabaseHas('posts', [
|
||||||
|
'title' => $postData['title'],
|
||||||
|
'content' => $postData['content'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test viewing event
|
||||||
|
$post = Post::where('title', $postData['title'])->first();
|
||||||
|
$response = $this->get("/api/posts/$post->id");
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonStructure([
|
||||||
|
'post' => [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'content',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test updating event
|
||||||
|
$postData['title'] = 'Updated Post';
|
||||||
|
$response = $this->actingAs($adminUser)->putJson("/api/posts/$post->id", $postData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('posts', [
|
||||||
|
'title' => 'Updated Post',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test deleting event
|
||||||
|
$response = $this->actingAs($adminUser)->delete("/api/posts/$post->id");
|
||||||
|
$response->assertStatus(204);
|
||||||
|
$this->assertDatabaseMissing('posts', [
|
||||||
|
'title' => 'Updated Post',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonAdminCannotCreateUpdateDeletePost()
|
||||||
|
{
|
||||||
|
// Create a user without admin/events permission
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
// Authenticate as the user
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
// Try to create a new post
|
||||||
|
$media = Media::factory()->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$newPostData = Post::factory()->make(['user_id' => $user->id, 'hero' => $media->id])->toArray();
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/posts', $newPostData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
|
||||||
|
// Try to update an event
|
||||||
|
$post = Post::factory()->create();
|
||||||
|
$updatedPostData = [
|
||||||
|
'title' => 'Updated Event',
|
||||||
|
'content' => 'This is an updated event.',
|
||||||
|
// Add more fields as needed
|
||||||
|
];
|
||||||
|
$response = $this->putJson('/api/posts/' . $post->id, $updatedPostData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
|
||||||
|
// Try to delete an event
|
||||||
|
$post = Post::factory()->create();
|
||||||
|
$response = $this->deleteJson('/api/posts/' . $post->id);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
235
tests/Feature/UsersApiTest.php
Normal file
235
tests/Feature/UsersApiTest.php
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
<?php
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UsersApiTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function testNonAdminUsersCanOnlyViewBasicUserInfo()
|
||||||
|
{
|
||||||
|
// create a non-admin user
|
||||||
|
$nonAdminUser = User::factory()->create();
|
||||||
|
$nonAdminUser->revokePermission('admin/users');
|
||||||
|
|
||||||
|
// create an admin user
|
||||||
|
$adminUser = User::factory()->create();
|
||||||
|
$adminUser->givePermission('admin/users');
|
||||||
|
|
||||||
|
// ensure the non-admin user can access the endpoint and see basic user info only
|
||||||
|
$response = $this->actingAs($nonAdminUser)->get('/api/users');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonStructure([
|
||||||
|
'users' => [
|
||||||
|
'*' => [
|
||||||
|
'id',
|
||||||
|
'username'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'total'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertJsonMissing([
|
||||||
|
'users' => [
|
||||||
|
'*' => [
|
||||||
|
'email',
|
||||||
|
'password'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$response->assertJsonFragment([
|
||||||
|
'id' => $nonAdminUser->id,
|
||||||
|
'username' => $nonAdminUser->username
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ensure the admin user can access the endpoint and see additional user info
|
||||||
|
$response = $this->actingAs($adminUser)->get('/api/users');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonStructure([
|
||||||
|
'users' => [
|
||||||
|
'*' => [
|
||||||
|
'id',
|
||||||
|
'username',
|
||||||
|
'email'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'total'
|
||||||
|
]);
|
||||||
|
$response->assertJsonMissing([
|
||||||
|
'users' => [
|
||||||
|
'*' => [
|
||||||
|
'password'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$response->assertJsonFragment([
|
||||||
|
'id' => $nonAdminUser->id,
|
||||||
|
'username' => $nonAdminUser->username
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGuestCannotCreateUser()
|
||||||
|
{
|
||||||
|
$userData = [
|
||||||
|
'username' => 'johndoe',
|
||||||
|
'email' => 'johndoe@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/users', $userData);
|
||||||
|
$response->assertStatus(401);
|
||||||
|
$this->assertDatabaseMissing('users', [
|
||||||
|
'username' => $userData['username'],
|
||||||
|
'email' => $userData['email'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGuestCanRegisterUser()
|
||||||
|
{
|
||||||
|
$userData = [
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'username' => 'johndoe',
|
||||||
|
'email' => 'johndoe@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/register', $userData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('users', [
|
||||||
|
'username' => $userData['username'],
|
||||||
|
'email' => $userData['email'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCannotCreateDuplicateUsername()
|
||||||
|
{
|
||||||
|
$userData = [
|
||||||
|
'first_name' => 'Jack',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'username' => 'jackdoe',
|
||||||
|
'email' => 'jackdoe@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test creating user
|
||||||
|
$response = $this->postJson('/api/register', $userData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('users', [
|
||||||
|
'username' => 'jackdoe',
|
||||||
|
'email' => 'jackdoe@example.com',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test creating duplicate user
|
||||||
|
$response = $this->postJson('/api/register', $userData);
|
||||||
|
$response->assertStatus(422);
|
||||||
|
$response->assertJsonValidationErrors('username');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserCanOnlyUpdateOwnUser()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$userData = [
|
||||||
|
'username' => 'raffi',
|
||||||
|
'email' => 'raffi@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test updating own user
|
||||||
|
$response = $this->actingAs($user)->putJson('/api/users/' . $user->id, $userData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('users', [
|
||||||
|
'id' => $user->id,
|
||||||
|
'username' => 'raffi',
|
||||||
|
'email' => 'raffi@example.com',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test updating another user
|
||||||
|
$otherUser = User::factory()->create();
|
||||||
|
$otherUserData = [
|
||||||
|
'username' => 'otherraffi',
|
||||||
|
'email' => 'otherraffi@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->putJson('/api/users/' . $otherUser->id, $otherUserData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserCannotDeleteUsers()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
// Test deleting own user
|
||||||
|
$response = $this->actingAs($user)->deleteJson('/api/users/' . $user->id);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
$this->assertDatabaseHas('users', ['id' => $user->id]);
|
||||||
|
|
||||||
|
// Test deleting another user
|
||||||
|
$otherUser = User::factory()->create();
|
||||||
|
$response = $this->actingAs($user)->deleteJson('/api/users/' . $otherUser->id);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
$this->assertDatabaseHas('users', ['id' => $otherUser->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAdminCanUpdateAnyUser()
|
||||||
|
{
|
||||||
|
$admin = User::factory()->create();
|
||||||
|
$admin->givePermission('admin/users');
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$userData = [
|
||||||
|
'username' => 'Todd Doe',
|
||||||
|
'email' => 'todddoe@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test updating own user
|
||||||
|
$response = $this->actingAs($admin)->putJson('/api/users/' . $user->id, $userData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('users', [
|
||||||
|
'id' => $user->id,
|
||||||
|
'username' => 'Todd Doe',
|
||||||
|
'email' => 'todddoe@example.com'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Test updating another user
|
||||||
|
$otherUser = User::factory()->create();
|
||||||
|
$otherUserData = [
|
||||||
|
'username' => 'Kim Doe',
|
||||||
|
'email' => 'kimdoe@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->actingAs($admin)->putJson('/api/users/' . $otherUser->id, $otherUserData);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$this->assertDatabaseHas('users', [
|
||||||
|
'id' => $otherUser->id,
|
||||||
|
'username' => 'Kim Doe',
|
||||||
|
'email' => 'kimdoe@example.com',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAdminCanDeleteAnyUser()
|
||||||
|
{
|
||||||
|
$admin = User::factory()->create();
|
||||||
|
$admin->givePermission('admin/users');
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
// Test deleting own user
|
||||||
|
$response = $this->actingAs($admin)->deleteJson('/api/users/' . $user->id);
|
||||||
|
$response->assertStatus(204);
|
||||||
|
$this->assertDatabaseMissing('users', ['id' => $user->id]);
|
||||||
|
|
||||||
|
// Test deleting another user
|
||||||
|
$otherUser = User::factory()->create();
|
||||||
|
$response = $this->actingAs($admin)->deleteJson('/api/users/' . $otherUser->id);
|
||||||
|
$response->assertStatus(204);
|
||||||
|
$this->assertDatabaseMissing('users', ['id' => $otherUser->id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user