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));
|
||||
}
|
||||
@@ -122,12 +122,14 @@ class ApiController extends Controller
|
||||
* Return resource data
|
||||
*
|
||||
* @param array|Model|Collection $data Resource data.
|
||||
* @param boolean $isCollection If the data is a group of items.
|
||||
* @param array|null $appendData Data to append to response.
|
||||
* @param integer $respondCode Resource code.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function respondAsResource(
|
||||
mixed $data,
|
||||
bool $isCollection = false,
|
||||
mixed $appendData = null,
|
||||
int $respondCode = HttpResponseCodes::HTTP_OK
|
||||
) {
|
||||
@@ -144,8 +146,6 @@ class ApiController extends Controller
|
||||
$resourceName = strtolower($resourceName);
|
||||
}
|
||||
|
||||
$is_multiple = true;
|
||||
|
||||
$dataArray = [];
|
||||
if ($data instanceof Collection) {
|
||||
$dataArray = $data->toArray();
|
||||
@@ -157,7 +157,7 @@ class ApiController extends Controller
|
||||
}
|
||||
|
||||
$resource = [];
|
||||
if ($is_multiple === true) {
|
||||
if ($isCollection === true) {
|
||||
$resource = [Str::plural($resourceName) => $dataArray];
|
||||
} else {
|
||||
$resource = [Str::singular($resourceName) => $dataArray];
|
||||
|
||||
@@ -73,6 +73,7 @@ class AuthController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$user->makeVisible(['permissions']),
|
||||
false,
|
||||
['token' => $token]
|
||||
);
|
||||
}//end if
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\EventFilter;
|
||||
use App\Http\Requests\EventRequest;
|
||||
use App\Models\Event;
|
||||
use App\Conductors\EventConductor;
|
||||
use App\Http\Requests\EventRequest;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EventController extends ApiController
|
||||
@@ -22,56 +22,72 @@ class EventController extends ApiController
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param EventFilter $filter The event filter.
|
||||
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(EventFilter $filter)
|
||||
public function index(Request $request)
|
||||
{
|
||||
return $this->respondAsResource(
|
||||
$filter->filter(),
|
||||
['total' => $filter->foundTotal()]
|
||||
);
|
||||
}
|
||||
list($collection, $total) = EventConductor::request($request);
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param EventRequest $request The event store request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(EventRequest $request)
|
||||
{
|
||||
$event = Event::create($request->all());
|
||||
return $this->respondAsResource(
|
||||
(new EventFilter($request))->filter($event),
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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.
|
||||
*
|
||||
* @param EventRequest $request The event update request.
|
||||
* @param \App\Http\Requests\EventRequest $request The endpoint request.
|
||||
* @param \App\Models\Event $event The specified event.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(EventRequest $request, Event $event)
|
||||
{
|
||||
if (EventConductor::updatable($event) === true) {
|
||||
$event->update($request->all());
|
||||
return $this->respondAsResource((new EventFilter($request))->filter($event));
|
||||
return $this->respondAsResource(EventConductor::model($request, $event));
|
||||
}
|
||||
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +98,11 @@ class EventController extends ApiController
|
||||
*/
|
||||
public function destroy(Event $event)
|
||||
{
|
||||
if (EventConductor::destroyable($event) === true) {
|
||||
$event->delete();
|
||||
return $this->respondNoContent();
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Conductors\MediaConductor;
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\MediaFilter;
|
||||
use App\Http\Requests\MediaStoreRequest;
|
||||
use App\Http\Requests\MediaUpdateRequest;
|
||||
use App\Http\Requests\MediaRequest;
|
||||
use App\Models\Media;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Sanctum\PersonalAccessToken;
|
||||
|
||||
class MediaController extends ApiController
|
||||
@@ -26,37 +24,45 @@ class MediaController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function index(MediaFilter $filter)
|
||||
public function index(Request $request)
|
||||
{
|
||||
list($collection, $total) = MediaConductor::request($request);
|
||||
|
||||
return $this->respondAsResource(
|
||||
$filter->filter(),
|
||||
['total' => $filter->foundTotal()]
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param MediaFilter $filter The request filter.
|
||||
* @param Media $medium The request media.
|
||||
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||
* @param \App\Models\Media $medium The request media.
|
||||
* @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
|
||||
*
|
||||
* @param MediaStoreRequest $request The uploaded media.
|
||||
* @param \App\Http\Requests\MediaRequest $request The uploaded media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(MediaStoreRequest $request)
|
||||
public function store(MediaRequest $request)
|
||||
{
|
||||
if (MediaConductor::creatable() === true) {
|
||||
$file = $request->file('file');
|
||||
if ($file === null) {
|
||||
return $this->respondWithErrors(['file' => 'The browser did not upload the file correctly to the server.']);
|
||||
@@ -96,22 +102,27 @@ class MediaController extends ApiController
|
||||
]);
|
||||
|
||||
$media = $request->user()->media()->create($request->all());
|
||||
return $this->respondAsResource((new MediaFilter($request))->filter($media));
|
||||
return $this->respondAsResource(
|
||||
MediaConductor::model($request, $media),
|
||||
false,
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
}//end if
|
||||
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the media resource in storage.
|
||||
*
|
||||
* @param MediaUpdateRequest $request The update request.
|
||||
* @param \App\Http\Requests\MediaRequest $request The update request.
|
||||
* @param \App\Models\Media $medium The specified media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(MediaUpdateRequest $request, Media $medium)
|
||||
public function update(MediaRequest $request, Media $medium)
|
||||
{
|
||||
if ((new MediaFilter($request))->filter($medium) === null) {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
|
||||
if (MediaConductor::updatable($medium) === true) {
|
||||
$file = $request->file('file');
|
||||
if ($file !== null) {
|
||||
if ($file->getSize() > Media::maxUploadSize()) {
|
||||
@@ -140,21 +151,21 @@ class MediaController extends ApiController
|
||||
}//end if
|
||||
|
||||
$medium->update($request->all());
|
||||
return $this->respondWithTransformer($file);
|
||||
return $this->respondAsResource(MediaConductor::model($request, $medium));
|
||||
}//end if
|
||||
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param Request $request Request instance.
|
||||
* @param \App\Models\Media $medium Specified media file.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request, Media $medium)
|
||||
public function destroy(Media $medium)
|
||||
{
|
||||
if ((new MediaFilter($request))->filter($medium) !== null) {
|
||||
if (MediaConductor::destroyable($medium) === true) {
|
||||
if (file_exists($medium->path()) === true) {
|
||||
unlink($medium->path());
|
||||
}
|
||||
@@ -163,13 +174,13 @@ class MediaController extends ApiController
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondNotFound();
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param Request $request Request instance.
|
||||
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||
* @param \App\Models\Media $medium Specified media.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Conductors\PostConductor;
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\PostFilter;
|
||||
use App\Http\Requests\PostStoreRequest;
|
||||
use App\Http\Requests\PostUpdateRequest;
|
||||
use App\Http\Requests\PostRequest;
|
||||
use App\Models\Post;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -27,56 +26,72 @@ class PostController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function index(PostFilter $filter)
|
||||
public function index(Request $request)
|
||||
{
|
||||
list($collection, $total) = PostConductor::request($request);
|
||||
|
||||
return $this->respondAsResource(
|
||||
$filter->filter(),
|
||||
['total' => $filter->foundTotal()]
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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.
|
||||
*
|
||||
* @param PostStoreRequest $request The post store request.
|
||||
* @param \App\Http\Requests\PostRequest $request The user request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(PostStoreRequest $request)
|
||||
public function store(PostRequest $request)
|
||||
{
|
||||
if (PostConductor::creatable() === true) {
|
||||
$post = Post::create($request->all());
|
||||
return $this->respondAsResource(
|
||||
(new PostFilter($request))->filter($post),
|
||||
PostConductor::model($request, $post),
|
||||
false,
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(PostUpdateRequest $request, Post $post)
|
||||
public function update(PostRequest $request, Post $post)
|
||||
{
|
||||
if (PostConductor::updatable($post) === true) {
|
||||
$post->update($request->all());
|
||||
return $this->respondAsResource((new PostFilter($request))->filter($post));
|
||||
return $this->respondAsResource(PostConductor::model($request, $post));
|
||||
}
|
||||
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +102,11 @@ class PostController extends ApiController
|
||||
*/
|
||||
public function destroy(Post $post)
|
||||
{
|
||||
if (PostConductor::destroyable($post) === true) {
|
||||
$post->delete();
|
||||
return $this->respondNoContent();
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Conductors\SubscriptionConductor;
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Models\Subscription;
|
||||
use App\Filters\SubscriptionFilter;
|
||||
use App\Http\Requests\SubscriptionRequest;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Mail\SubscriptionConfirm;
|
||||
use App\Mail\SubscriptionUnsubscribed;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SubscriptionController extends ApiController
|
||||
{
|
||||
@@ -23,58 +25,71 @@ class SubscriptionController extends ApiController
|
||||
/**
|
||||
* Display a listing of subscribers.
|
||||
*
|
||||
* @param \App\Filters\SubscriptionFilter $filter Filter object.
|
||||
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||
* @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(
|
||||
$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.
|
||||
*
|
||||
* @param SubscriptionRequest $request The subscriber update request.
|
||||
* @param \App\Http\Requests\SubscriptionRequest $request The subscriber update request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(SubscriptionRequest $request)
|
||||
{
|
||||
if (Subscription::where('email', $request->email)->first() !== null) {
|
||||
return $this->respondWithErrors(['email' => 'This email address has already subscribed']);
|
||||
}
|
||||
|
||||
if (SubscriptionConductor::creatable() === true) {
|
||||
Subscription::create($request->all());
|
||||
dispatch((new SendEmailJob($request->email, new SubscriptionConfirm($request->email))))->onQueue('mail');
|
||||
|
||||
return $this->respondCreated();
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param SubscriptionRequest $request The subscription update request.
|
||||
* @param Subscription $subscription The specified subscription.
|
||||
* @param \App\Http\Requests\SubscriptionRequest $request The subscription update request.
|
||||
* @param \App\Models\Subscription $subscription The specified subscription.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
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 = [];
|
||||
// $updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
||||
|
||||
@@ -103,14 +118,12 @@ class SubscriptionController extends ApiController
|
||||
*/
|
||||
public function destroy(Subscription $subscription)
|
||||
{
|
||||
// if ($user->hasPermission('admin/user') === false) {
|
||||
// return $this->respondForbidden();
|
||||
// }
|
||||
|
||||
$email = $subscription->email;
|
||||
|
||||
if (SubscriptionConductor::destroyable($subscription) === true) {
|
||||
$subscription->delete();
|
||||
return $this->respondNoContent();
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enum\HttpResponseCodes;
|
||||
use App\Filters\UserFilter;
|
||||
use App\Http\Requests\UserUpdateRequest;
|
||||
use App\Http\Requests\UserStoreRequest;
|
||||
use App\Http\Requests\UserRequest;
|
||||
use App\Http\Requests\UserForgotPasswordRequest;
|
||||
use App\Http\Requests\UserForgotUsernameRequest;
|
||||
use App\Http\Requests\UserRegisterRequest;
|
||||
@@ -23,6 +21,7 @@ use App\Models\User;
|
||||
use App\Models\UserCode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Conductors\UserConductor;
|
||||
|
||||
class UserController extends ApiController
|
||||
{
|
||||
@@ -48,63 +47,67 @@ class UserController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function index(UserFilter $filter)
|
||||
public function index(Request $request)
|
||||
{
|
||||
$collection = $filter->filter();
|
||||
list($collection, $total) = UserConductor::request($request);
|
||||
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
['total' => $filter->foundTotal()]
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
$user = User::create($request->all());
|
||||
return $this->respondAsResource((new UserFilter($request))->filter($user), [], HttpResponseCodes::HTTP_CREATED);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified user.
|
||||
*
|
||||
* @param UserFilter $filter The user filter.
|
||||
* @param User $user The user model.
|
||||
* @param \Illuminate\Http\Request $request The endpoint request.
|
||||
* @param \App\Models\User $user The user model.
|
||||
* @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.
|
||||
*
|
||||
* @param UserUpdateRequest $request The user update request.
|
||||
* @param User $user The specified user.
|
||||
* @param \App\Http\Requests\UserRequest $request The user update request.
|
||||
* @param \App\Models\User $user The specified user.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(UserUpdateRequest $request, User $user)
|
||||
public function update(UserRequest $request, User $user)
|
||||
{
|
||||
if (UserConductor::updatable($user) === true) {
|
||||
$input = [];
|
||||
$updatable = ['username', 'first_name', 'last_name', 'email', 'phone', 'password'];
|
||||
|
||||
if ($request->user()->hasPermission('admin/user') === true) {
|
||||
$updatable = array_merge($updatable, ['email_verified_at']);
|
||||
} elseif ($request->user()->is($user) !== true) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
$input = $request->only($updatable);
|
||||
@@ -114,30 +117,32 @@ class UserController extends ApiController
|
||||
|
||||
$user->update($input);
|
||||
|
||||
return $this->respondAsResource((new UserFilter($request))->filter($user));
|
||||
return $this->respondAsResource(UserConductor::model($request, $user));
|
||||
}
|
||||
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the user from the database.
|
||||
*
|
||||
* @param User $user The specified user.
|
||||
* @param \App\Models\User $user The specified user.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if ($user->hasPermission('admin/user') === false) {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
if (UserConductor::destroyable($user) === true) {
|
||||
$user->delete();
|
||||
return $this->respondNoContent();
|
||||
}
|
||||
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function register(UserRegisterRequest $request)
|
||||
@@ -171,7 +176,7 @@ class UserController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function forgotUsername(UserForgotUsernameRequest $request)
|
||||
@@ -191,7 +196,7 @@ class UserController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function forgotPassword(UserForgotPasswordRequest $request)
|
||||
@@ -213,7 +218,7 @@ class UserController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function resetPassword(UserResetPasswordRequest $request)
|
||||
@@ -247,7 +252,7 @@ class UserController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function verifyEmail(UserVerifyEmailRequest $request)
|
||||
@@ -285,7 +290,7 @@ class UserController extends ApiController
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function resendVerifyEmail(UserResendVerifyEmailRequest $request)
|
||||
@@ -312,7 +317,7 @@ class UserController extends ApiController
|
||||
/**
|
||||
* Resend verification email
|
||||
*
|
||||
* @param UserResendVerifyEmailRequest $request The resend user request.
|
||||
* @param \App\Http\Requests\UserResendVerifyEmailRequest $request The resend user request.
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function resendVerifyEmailCode(UserResendVerifyEmailRequest $request)
|
||||
|
||||
@@ -14,10 +14,12 @@ class BaseRequest extends FormRequest
|
||||
*/
|
||||
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();
|
||||
} 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();
|
||||
} elseif (request()->isMethod('delete') === true && method_exists($this, 'destroyAuthorize') === true) {
|
||||
return $this->deleteAuthorize();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -38,8 +40,8 @@ class BaseRequest extends FormRequest
|
||||
|
||||
if (method_exists($this, 'postRules') === true && request()->isMethod('post') === true) {
|
||||
$rules = $this->mergeRules($rules, $this->postRules());
|
||||
} elseif (method_exists($this, 'putRules') === true && request()->isMethod('put') === true) {
|
||||
$rules = $this->mergeRules($rules, $this->postRules());
|
||||
} elseif (method_exists($this, 'putRules') === true && (request()->isMethod('put') === true || request()->isMethod('patch') === true)) {
|
||||
$rules = $this->mergeRules($rules, $this->putRules());
|
||||
} elseif (method_exists($this, 'destroyRules') === true && request()->isMethod('delete') === true) {
|
||||
$rules = $this->mergeRules($rules, $this->destroyRules());
|
||||
}
|
||||
|
||||
@@ -2,31 +2,10 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class EventRequest extends BaseRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function postAuthorize()
|
||||
{
|
||||
return $this->user()?->hasPermission('admin/events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function putAuthorize()
|
||||
{
|
||||
return $this->user()?->hasPermission('admin/events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the base rules to this request
|
||||
*
|
||||
@@ -47,11 +26,12 @@ class EventRequest extends BaseRequest
|
||||
Rule::in(['draft', 'soon', 'open', 'closed', 'cancelled']),
|
||||
],
|
||||
'registration_type' => [
|
||||
Rule::in(['none', 'email', 'link']),
|
||||
Rule::in(['none', 'email', 'link', 'message']),
|
||||
],
|
||||
'registration_data' => [
|
||||
Rule::when(strcasecmp('email', $this->attributes->get('registration_type')) == 0, 'required|email'),
|
||||
Rule::when(strcasecmp('link', $this->attributes->get('registration_type')) == 0, 'required|url')
|
||||
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',
|
||||
];
|
||||
|
||||
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()
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email',
|
||||
'email' => 'required|email|unique:subscriptions',
|
||||
'captcha_token' => [new Recaptcha()],
|
||||
];
|
||||
}
|
||||
@@ -31,4 +31,16 @@ class SubscriptionRequest extends BaseRequest
|
||||
'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',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@@ -46,8 +46,28 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
// RateLimiter::for('api', function (Request $request) {
|
||||
// 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(60)->by($request->user()?->id !== null ?: $request->ip());
|
||||
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",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": ["framework", "laravel"],
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.0.2",
|
||||
@@ -26,6 +29,9 @@
|
||||
"spatie/laravel-ignition": "^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"app/Helpers/Array.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"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
|
||||
*/
|
||||
const computedClassType = computed(() => {
|
||||
return `sm-input-${props.type}`;
|
||||
return `sm-input-type-${props.type}`;
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -187,7 +187,7 @@ if (objControl) {
|
||||
label.value = toTitleCase(props.control);
|
||||
}
|
||||
|
||||
inputActive.value = value.value.length > 0 || props.type == "select";
|
||||
inputActive.value = value.value?.length > 0 || props.type == "select";
|
||||
|
||||
watch(
|
||||
() => objControl.validation.result.valid,
|
||||
@@ -434,15 +434,28 @@ const handleMediaSelect = async (event) => {
|
||||
background-size: 24px 18px;
|
||||
}
|
||||
|
||||
&.sm-input-media {
|
||||
&.sm-input-type-media {
|
||||
label {
|
||||
position: relative;
|
||||
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 {
|
||||
text-align: center;
|
||||
margin-bottom: map-get($spacer, 2);
|
||||
|
||||
.sm-input-media-item {
|
||||
display: block;
|
||||
|
||||
@@ -65,13 +65,17 @@ const mediaItems: Ref<Media[]> = ref([]);
|
||||
* Handle the user adding a new media item.
|
||||
*/
|
||||
const handleClickAdd = async () => {
|
||||
openDialog(SMDialogMedia, { mime: "", accepts: "" }).then((result) => {
|
||||
openDialog(SMDialogMedia, { mime: "", accepts: "" })
|
||||
.then((result) => {
|
||||
const media = result as Media;
|
||||
|
||||
mediaItems.value.push(media);
|
||||
value.value.push(media.id);
|
||||
|
||||
emits("update:modelValue", value);
|
||||
})
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useProgressStore } from "../store/ProgressStore";
|
||||
import { useUserStore } from "../store/UserStore";
|
||||
import { ImportMetaExtras } from "../../../import-meta";
|
||||
|
||||
interface ApiProgressData {
|
||||
loaded: number;
|
||||
total: number;
|
||||
@@ -31,7 +33,8 @@ const apiDefaultHeaders = {
|
||||
|
||||
export const api = {
|
||||
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) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -66,7 +66,8 @@ const waitForElementRender = (elem: Ref): Promise<HTMLElement> => {
|
||||
* @returns {void}
|
||||
*/
|
||||
export const transitionEnter = (elem: Ref, transition: string): void => {
|
||||
waitForElementRender(elem).then((e: HTMLElement) => {
|
||||
waitForElementRender(elem)
|
||||
.then((e: HTMLElement) => {
|
||||
window.setTimeout(() => {
|
||||
e.classList.replace(
|
||||
transition + "-enter-from",
|
||||
@@ -84,6 +85,9 @@ export const transitionEnter = (elem: Ref, transition: string): void => {
|
||||
false
|
||||
);
|
||||
}, 1);
|
||||
})
|
||||
.catch(() => {
|
||||
/* empty */
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -95,11 +95,21 @@ const handleLoad = async () => {
|
||||
try {
|
||||
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) {
|
||||
query["q"] = filterKeywords.value;
|
||||
let value = filterKeywords.value.replace(/"/g, '\\"');
|
||||
|
||||
query["filter"] = `(title:"${value}",OR,content:"${value}")`;
|
||||
}
|
||||
if (filterLocation.value && filterLocation.value.length > 0) {
|
||||
query["qlocation"] = filterLocation.value;
|
||||
query["location"] = filterLocation.value;
|
||||
}
|
||||
if (filterDateRange.value && filterDateRange.value.length > 0) {
|
||||
let error = false;
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
v-if="
|
||||
event.status == 'open' &&
|
||||
expired == false &&
|
||||
event.registration_type != 'none'
|
||||
event.registration_type == 'url'
|
||||
"
|
||||
class="sm-workshop-registration sm-workshop-registration-url">
|
||||
<SMButton
|
||||
@@ -60,6 +60,15 @@
|
||||
:block="true"
|
||||
label="Register for Event"></SMButton>
|
||||
</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">
|
||||
<h4>
|
||||
<ion-icon
|
||||
@@ -380,7 +389,8 @@ handleLoad();
|
||||
}
|
||||
|
||||
.sm-workshop-registration-none,
|
||||
.sm-workshop-registration-soon {
|
||||
.sm-workshop-registration-soon,
|
||||
.sm-workshop-registration-message {
|
||||
border: 1px solid #ffeeba;
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
none: 'None',
|
||||
email: 'Email',
|
||||
link: 'Link',
|
||||
message: 'Message',
|
||||
}" />
|
||||
</SMColumn>
|
||||
<SMColumn>
|
||||
@@ -186,6 +187,10 @@ const registration_data = computed(() => {
|
||||
data.visible = true;
|
||||
data.title = "Registration URL";
|
||||
data.type = "url";
|
||||
} else if (form?.controls.registration_type.value === "message") {
|
||||
data.visible = true;
|
||||
data.title = "Registration message";
|
||||
data.type = "text";
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -41,9 +41,9 @@ Route::post('posts/{post}/attachments', [PostController::class, 'storeAttachment
|
||||
Route::delete('posts/{post}/attachments/{attachment}', [PostController::class, 'deleteAttachment']);
|
||||
|
||||
Route::apiResource('events', EventController::class);
|
||||
Route::get('events/{event}/attachments', [PostController::class, 'getAttachments']);
|
||||
Route::post('events/{event}/attachments', [PostController::class, 'storeAttachment']);
|
||||
Route::delete('events/{event}/attachments/{attachment}', [PostController::class, 'deleteAttachment']);
|
||||
Route::get('events/{event}/attachments', [EventController::class, 'getAttachments']);
|
||||
Route::post('events/{event}/attachments', [EventController::class, 'storeAttachment']);
|
||||
Route::delete('events/{event}/attachments/{attachment}', [EventController::class, 'deleteAttachment']);
|
||||
|
||||
Route::apiResource('subscriptions', SubscriptionController::class);
|
||||
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