snipe-it/app/Providers/ValidationServiceProvider.php
snipe 7b7d424962
Merge pull request #13880 from Godmartinz/department-validation-bug
Fixed department validation to allow updates
2024-04-12 10:50:26 +01:00

362 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Providers;
use App\Models\CustomField;
use App\Models\Department;
use App\Models\Setting;
use DB;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rule;
use Validator;
/**
* This service provider handles a few custom validation rules.
*
* PHP version 5.5.9
* @version v3.0
*/
class ValidationServiceProvider extends ServiceProvider
{
/**
* Custom email array validation
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return void
*/
public function boot()
{
// Email array validator
Validator::extend('email_array', function ($attribute, $value, $parameters, $validator) {
$value = str_replace(' ', '', $value);
$array = explode(',', $value);
foreach ($array as $email) { //loop over values
$email_to_validate['alert_email'][] = $email;
}
$rules = ['alert_email.*'=>'email'];
$messages = [
'alert_email.*'=>trans('validation.email_array'),
];
$validator = Validator::make($email_to_validate, $rules, $messages);
return $validator->passes();
});
/**
* Unique only if undeleted.
*
* This works around the use case where multiple deleted items have the same unique attribute.
* (I think this is a bug in Laravel's validator?)
*
* $attribute is the FIELDNAME you're checking against
* $value is the VALUE of the item you're checking against the existing values in the fieldname
* $parameters[0] is the TABLE NAME you're querying
* $parameters[1] is the ID of the item you're querying - this makes it work on saving, checkout, etc,
* since it defaults to 0 if there is no item created yet (new item), but populates the ID if editing
*
* The UniqueUndeletedTrait prefills these parameters, so you can just use
* `unique_undeleted:table,fieldname` in your rules out of the box
*/
Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) {
if (count($parameters)) {
// This is a bit of a shim, but serial doesn't have any other rules around it other than that it's nullable
if (($parameters[0]=='assets') && ($attribute == 'serial') && (Setting::getSettings()->unique_serial != '1')) {
return true;
}
$count = DB::table($parameters[0])
->select('id')
->where($attribute, '=', $value)
->whereNull('deleted_at')
->where('id', '!=', $parameters[1])->count();
return $count < 1;
}
});
/**
* Unique if undeleted for two columns
*
* Same as unique_undeleted but taking the combination of two columns as unique constrain.
* This uses the Validator::replacer('two_column_unique_undeleted') below for nicer translations.
*
* $parameters[0] - the name of the first table we're looking at
* $parameters[1] - the ID (this will be 0 on new creations)
* $parameters[2] - the name of the second table we're looking at
* $parameters[3] - the value that the request is passing for the second table we're
* checking for uniqueness across
*
*/
Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) {
if (count($parameters)) {
$count = DB::table($parameters[0])
->select('id')->where($attribute, '=', $value)
->whereNull('deleted_at')
->where('id', '!=', $parameters[1])
->where($parameters[2], $parameters[3])->count();
return $count < 1;
}
});
/**
* This is the validator replace static method that allows us to pass the $parameters of the table names
* into the translation string in validation.two_column_unique_undeleted for two_column_unique_undeleted
* validation messages.
*
* This is invoked automatically by Validator::extend('two_column_unique_undeleted') above and
* produces a translation like: "The name value must be unique across categories and category type."
*
* The $parameters passed coincide with the ones the two_column_unique_undeleted custom validator above
* uses, so $parameter[0] is the first table and so $parameter[2] is the second table.
*/
Validator::replacer('two_column_unique_undeleted', function($message, $attribute, $rule, $parameters) {
$message = str_replace(':table1', $parameters[0], $message);
$message = str_replace(':table2', $parameters[2], $message);
// Change underscores to spaces for a friendlier display
$message = str_replace('_', ' ', $message);
return $message;
});
// Prevent circular references
//
// Example usage in Location model where parent_id references another Location:
//
// protected $rules = array(
// 'parent_id' => 'non_circular:locations,id,10'
// );
//
Validator::extend('non_circular', function ($attribute, $value, $parameters, $validator) {
if (count($parameters) < 2) {
throw new \Exception('Required validator parameters: <table>,<primary key>[,depth]');
}
// Parameters from the rule implementation ($pk will likely be 'id')
$table = array_get($parameters, 0);
$pk = array_get($parameters, 1);
$depth = (int) array_get($parameters, 2, 50);
// Data from the edited model
$data = $validator->getData();
// The primary key value from the edited model
$data_pk = array_get($data, $pk);
$value_pk = $value;
// If were editing an existing model and there is a parent value set…
while ($data_pk && $value_pk) {
// Its not valid for any parent id to be equal to the existing models id
if ($data_pk == $value_pk) {
return false;
}
// Avoid accidental infinite loops
if (--$depth < 0) {
return false;
}
// Get the next parent id
$value_pk = DB::table($table)->select($attribute)->where($pk, '=', $value_pk)->value($attribute);
}
return true;
});
// Yo dawg. I heard you like validators.
// This validates the custom validator regex in custom fields.
// We're just checking that the regex won't throw an exception, not
// that it's actually correct for what the user intended.
Validator::extend('valid_regex', function ($attribute, $value, $parameters, $validator) {
// Make sure it's not just an ANY format
if ($value != '') {
// Check that the string starts with regex:
if (strpos($value, 'regex:') === false) {
return false;
}
$test_string = 'My hovercraft is full of eels';
// We have to stip out the regex: part here to check with preg_match
$test_pattern = str_replace('regex:', '', $value);
try {
preg_match($test_pattern, $test_string);
return true;
} catch (\Exception $e) {
return false;
}
}
return true;
});
// This ONLY works for create/update user forms, since the Update Profile Password form doesn't
// include any of these additional validator fields
Validator::extend('disallow_same_pwd_as_user_fields', function ($attribute, $value, $parameters, $validator) {
$data = $validator->getData();
if (array_key_exists('username', $data)) {
if ($data['username'] == $data['password']) {
return false;
}
}
if (array_key_exists('email', $data)) {
if ($data['email'] == $data['password']) {
return false;
}
}
if (array_key_exists('first_name', $data)) {
if ($data['first_name'] == $data['password']) {
return false;
}
}
if (array_key_exists('last_name', $data)) {
if ($data['last_name'] == $data['password']) {
return false;
}
}
return true;
});
Validator::extend('letters', function ($attribute, $value, $parameters) {
return preg_match('/\pL/', $value);
});
Validator::extend('numbers', function ($attribute, $value, $parameters) {
return preg_match('/\pN/', $value);
});
Validator::extend('case_diff', function ($attribute, $value, $parameters) {
return preg_match('/(\p{Ll}+.*\p{Lu})|(\p{Lu}+.*\p{Ll})/u', $value);
});
Validator::extend('symbols', function ($attribute, $value, $parameters) {
return preg_match('/\p{Z}|\p{S}|\p{P}/', $value);
});
Validator::extend('cant_manage_self', function ($attribute, $value, $parameters, $validator) {
// $value is the actual *value* of the thing that's being validated
// $attribute is the name of the field that the validation is running on - probably manager_id in our case
// $parameters are the optional parameters - an array for everything, split on commas. But we don't take any params here.
// $validator gives us proper access to the rest of the actual data
$data = $validator->getData();
if (array_key_exists('id', $data)) {
if ($value && $value == $data['id']) {
// if you definitely have an ID - you're saving an existing user - and your ID matches your manager's ID - fail.
return false;
} else {
return true;
}
} else {
// no 'id' key to compare against (probably because this is a new user)
// so it automatically passes this validation
return true;
}
});
Validator::extend('is_unique_department', function ($attribute, $value, $parameters, $validator) {
$data = $validator->getData();
if (
array_key_exists('location_id', $data) && $data['location_id'] !== null &&
array_key_exists('company_id', $data) && $data['company_id'] !== null
) {
//for updating existing departments
if(array_key_exists('id', $data) && $data['id'] !== null){
$count = Department::where('name', $data['name'])
->where('location_id', $data['location_id'])
->where('company_id', $data['company_id'])
->whereNotNull('company_id')
->whereNotNull('location_id')
->where('id', '!=', $data['id'])
->count('name');
return $count < 1;
}else // for entering in new departments
{
$count = Department::where('name', $data['name'])
->where('location_id', $data['location_id'])
->where('company_id', $data['company_id'])
->whereNotNull('company_id')
->whereNotNull('location_id')
->count('name');
return $count < 1;
}
}
else {
return true;
}
});
Validator::extend('not_array', function ($attribute, $value, $parameters, $validator) {
return !is_array($value);
});
// This is only used in Models/CustomFieldset.php - it does automatic validation for checkboxes by making sure
// that the submitted values actually exist in the options.
Validator::extend('checkboxes', function ($attribute, $value, $parameters, $validator){
$field = CustomField::where('db_column', $attribute)->first();
$options = $field->formatFieldValuesAsArray();
if(is_array($value)) {
$invalid = array_diff($value, $options);
if(count($invalid) > 0) {
return false;
}
}
// for legacy, allows users to submit a comma separated string of options
elseif(!is_array($value)) {
$exploded = array_map('trim', explode(',', $value));
$invalid = array_diff($exploded, $options);
if(count($invalid) > 0) {
return false;
}
}
return true;
});
// Validates that a radio button option exists
Validator::extend('radio_buttons', function ($attribute, $value) {
$field = CustomField::where('db_column', $attribute)->first();
$options = $field->formatFieldValuesAsArray();
return in_array($value, $options);
});
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
}
}