Merge pull request #10889 from uberbrady/scim_squashed_grok_library
SCIM integration using laravel-scim-server library
This commit is contained in:
commit
7385a0765e
13 changed files with 511 additions and 816 deletions
16
app/Models/SCIMUser.php
Normal file
16
app/Models/SCIMUser.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class SCIMUser extends User
|
||||
{
|
||||
protected $table = 'users';
|
||||
|
||||
protected $throwValidationExceptions = true; // we want model-level validation to fully THROW, not just return false
|
||||
|
||||
public function __construct(array $attributes = []) {
|
||||
$attributes['password'] = "*NO PASSWORD*";
|
||||
// $attributes['activated'] = 1;
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
}
|
174
app/Models/SnipeSCIMConfig.php
Normal file
174
app/Models/SnipeSCIMConfig.php
Normal file
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Helper;
|
||||
use ArieTimmerman\Laravel\SCIMServer\SCIM\Schema;
|
||||
use ArieTimmerman\Laravel\SCIMServer\Attribute\AttributeMapping;
|
||||
|
||||
|
||||
class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig
|
||||
{
|
||||
public function getUserConfig()
|
||||
{
|
||||
$config = parent::getUserConfig();
|
||||
|
||||
// Much of this is copied verbatim from the library, then adjusted for our needs
|
||||
$config['class'] = SCIMUser::class;
|
||||
|
||||
unset($config['mapping']['example:name:space']);
|
||||
|
||||
$config['map_unmapped'] = false; // anything we don't explicitly map will _not_ show up.
|
||||
|
||||
$core_namespace = 'urn:ietf:params:scim:schemas:core:2.0:User';
|
||||
$core = $core_namespace.':';
|
||||
$mappings =& $config['mapping'][$core_namespace]; //grab this entire key, we don't want to be repeating ourselves
|
||||
|
||||
//username - *REQUIRED*
|
||||
$config['validations'][$core.'userName'] = 'required';
|
||||
$mappings['userName'] = AttributeMapping::eloquent('username');
|
||||
|
||||
//human name - *FIRST NAME REQUIRED*
|
||||
$config['validations'][$core.'name.givenName'] = 'required';
|
||||
$config['validations'][$core.'name.familyName'] = 'string'; //not required
|
||||
|
||||
$mappings['name']['familyName'] = AttributeMapping::eloquent("last_name");
|
||||
$mappings['name']['givenName'] = AttributeMapping::eloquent("first_name");
|
||||
$mappings['name']['formatted'] = (new AttributeMapping())->ignoreWrite()->setRead(
|
||||
function (&$object) {
|
||||
return $object->getFullNameAttribute();
|
||||
}
|
||||
);
|
||||
|
||||
$config['validations'][$core.'emails'] = 'nullable|array'; // emails are not required in Snipe-IT...
|
||||
$config['validations'][$core.'emails.*.value'] = 'required|email'; // ...but if you give us one, it better be an email address
|
||||
|
||||
$mappings['emails'] = [[
|
||||
"value" => AttributeMapping::eloquent("email"),
|
||||
"display" => null,
|
||||
"type" => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
"primary" => AttributeMapping::constant(true)->ignoreWrite()
|
||||
]];
|
||||
|
||||
//active
|
||||
$config['validations'][$core.'active'] = 'boolean';
|
||||
|
||||
$mappings['active'] = AttributeMapping::eloquent('activated');
|
||||
|
||||
//phone
|
||||
$config['validations'][$core.'phoneNumbers'] = 'nullable|array';
|
||||
$config['validations'][$core.'phoneNumbers.*.value'] = 'required';
|
||||
|
||||
$mappings['phoneNumbers'] = [[
|
||||
"value" => AttributeMapping::eloquent("phone"),
|
||||
"display" => null,
|
||||
"type" => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
"primary" => AttributeMapping::constant(true)->ignoreWrite()
|
||||
]];
|
||||
|
||||
//address
|
||||
$config['validations'][$core.'addresses'] = 'nullable|array';
|
||||
$config['validations'][$core.'addresses.*.streetAddress'] = 'required';
|
||||
$config['validations'][$core.'addresses.*.locality'] = 'string';
|
||||
$config['validations'][$core.'addresses.*.region'] = 'string';
|
||||
$config['validations'][$core.'addresses.*.postalCode'] = 'string';
|
||||
$config['validations'][$core.'addresses.*.country'] = 'string';
|
||||
|
||||
$mappings['addresses'] = [[
|
||||
'type' => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
'formatted' => AttributeMapping::constant("n/a")->ignoreWrite(), // TODO - is this right? This doesn't look right.
|
||||
'streetAddress' => AttributeMapping::eloquent("address"),
|
||||
'locality' => AttributeMapping::eloquent("city"),
|
||||
'region' => AttributeMapping::eloquent("state"),
|
||||
'postalCode' => AttributeMapping::eloquent("zip"),
|
||||
'country' => AttributeMapping::eloquent("country"),
|
||||
'primary' => AttributeMapping::constant(true)->ignoreWrite() //this isn't in the example?
|
||||
]];
|
||||
|
||||
//title
|
||||
$config['validations'][$core.'title'] = 'string';
|
||||
$mappings['title'] = AttributeMapping::eloquent('jobtitle');
|
||||
|
||||
//Preferred Language
|
||||
$config['validations'][$core.'preferredLanguage'] = 'string';
|
||||
$mappings['preferredLanguage'] = AttributeMapping::eloquent('locale');
|
||||
|
||||
/*
|
||||
more snipe-it attributes I'd like to check out (to map to 'enterprise' maybe?):
|
||||
- website
|
||||
- notes?
|
||||
- remote???
|
||||
- location_id ?
|
||||
- company_id to "organization?"
|
||||
*/
|
||||
|
||||
$enterprise_namespace = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User';
|
||||
$ent = $enterprise_namespace.':';
|
||||
|
||||
// we remove the 'example' namespace and add the Enterprise one
|
||||
$config['mapping']['schemas'] = AttributeMapping::constant( [$core_namespace, $enterprise_namespace] )->ignoreWrite();
|
||||
|
||||
$config['validations'][$ent.'employeeNumber'] = 'string';
|
||||
$config['validations'][$ent.'department'] = 'string';
|
||||
$config['validations'][$ent.'manager'] = 'nullable';
|
||||
$config['validations'][$ent.'manager.value'] = 'string';
|
||||
|
||||
$config['mapping'][$enterprise_namespace] = [
|
||||
'employeeNumber' => AttributeMapping::eloquent('employee_num'),
|
||||
'department' =>(new AttributeMapping())->setAdd( // FIXME parent?
|
||||
function ($value, &$object) {
|
||||
\Log::error("Department-Add: $value"); //FIXME
|
||||
$department = Department::where("name", $value)->first();
|
||||
if ($department) {
|
||||
$object->department_id = $department->id;
|
||||
}
|
||||
}
|
||||
)->setReplace(
|
||||
function ($value, &$object) {
|
||||
\Log::error("Department-Replace: $value"); //FIXME
|
||||
$department = Department::where("name", $value)->first();
|
||||
if ($department) {
|
||||
$object->department_id = $department->id;
|
||||
}
|
||||
}
|
||||
)->setRead(
|
||||
function (&$object) {
|
||||
\Log::error("Weird department reader firing..."); //FIXME
|
||||
return $object->department ? $object->department->name : null;
|
||||
}
|
||||
),
|
||||
'manager' => [
|
||||
// FIXME - manager writes are disabled. This kinda works but it leaks errors all over the place. Not cool.
|
||||
// '$ref' => (new AttributeMapping())->ignoreWrite()->ignoreRead(),
|
||||
// 'displayName' => (new AttributeMapping())->ignoreWrite()->ignoreRead(),
|
||||
// NOTE: you could probably do a 'plain' Eloquent mapping here, but we don't for future-proofing
|
||||
'value' => (new AttributeMapping())->setAdd(
|
||||
function ($value, &$object) {
|
||||
\Log::error("Manager-Add: $value"); //FIXME
|
||||
$manager = User::find($value);
|
||||
if ($manager) {
|
||||
$object->manager_id = $manager->id;
|
||||
}
|
||||
}
|
||||
)->setReplace(
|
||||
function ($value, &$object) {
|
||||
\Log::error("Manager-Replace: $value"); //FIXME
|
||||
$manager = User::find($value);
|
||||
if ($manager) {
|
||||
$object->manager_id = $manager->id;
|
||||
}
|
||||
}
|
||||
)->setRead(
|
||||
function (&$object) {
|
||||
\Log::error("Weird manager reader firing..."); //FIXME
|
||||
return $object->manager_id;
|
||||
}
|
||||
),
|
||||
]
|
||||
];
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ use App\Models\Component;
|
|||
use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Models\SnipeSCIMConfig;
|
||||
use App\Observers\AccessoryObserver;
|
||||
use App\Observers\AssetObserver;
|
||||
use App\Observers\ComponentObserver;
|
||||
|
@ -80,6 +81,8 @@ class AppServiceProvider extends ServiceProvider
|
|||
if ($this->app->environment(['local', 'develop'])) {
|
||||
$this->app->register(\Laravel\Dusk\DuskServiceProvider::class);
|
||||
}
|
||||
|
||||
$this->app->singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', SnipeSCIMConfig::class); // this overrides the default SCIM configuration with our own
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
|
||||
$this->mapWebRoutes();
|
||||
|
||||
//
|
||||
require base_path('routes/scim.php');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"type": "project",
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/grokability/laravel-scim-server"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4 <8.1",
|
||||
"ext-curl": "*",
|
||||
|
@ -18,6 +24,7 @@
|
|||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"alek13/slack": "^2.0",
|
||||
"arietimmerman/laravel-scim-server": "dev-master",
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"barryvdh/laravel-debugbar": "^3.6",
|
||||
"barryvdh/laravel-dompdf": "^1.0",
|
||||
|
@ -61,7 +68,6 @@
|
|||
"rollbar/rollbar-laravel": "^7.0",
|
||||
"spatie/laravel-backup": "^6.16",
|
||||
"tecnickcom/tc-lib-barcode": "^1.15",
|
||||
"tightenco/ziggy": "v1.2.0",
|
||||
"unicodeveloper/laravel-password": "^1.0",
|
||||
"watson/validating": "^6.1"
|
||||
},
|
||||
|
|
330
composer.lock
generated
330
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "442a6af235589e35cfcaa7e5e39e75ec",
|
||||
"content-hash": "ad10a039e46761e93b4461865b0e20f0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
|
@ -72,6 +72,69 @@
|
|||
],
|
||||
"time": "2021-10-20T22:52:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "arietimmerman/laravel-scim-server",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/grokability/laravel-scim-server.git",
|
||||
"reference": "10be959682d47bb8c78255168262a7cbb7586146"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/10be959682d47bb8c78255168262a7cbb7586146",
|
||||
"reference": "10be959682d47bb8c78255168262a7cbb7586146",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/console": "^6.0|^7.0|^8.0",
|
||||
"illuminate/database": "^6.0|^7.0|^8.0",
|
||||
"illuminate/support": "^6.0|^7.0|^8.0",
|
||||
"php": "^7.0|^8.0",
|
||||
"tmilos/scim-filter-parser": "^1.3",
|
||||
"tmilos/scim-schema": "^0.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/legacy-factories": "*",
|
||||
"orchestra/testbench": "^5.0|^6.0"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"ArieTimmerman\\Laravel\\SCIMServer\\ServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"SCIMServerHelper": "ArieTimmerman\\Laravel\\SCIMServer\\Helper"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ArieTimmerman\\Laravel\\SCIMServer\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ArieTimmerman\\Laravel\\SCIMServer\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arie Timmerman",
|
||||
"email": "arietimmerman@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Package for creating a SCIM server",
|
||||
"support": {
|
||||
"source": "https://github.com/grokability/laravel-scim-server/tree/master"
|
||||
},
|
||||
"time": "2022-03-31T19:29:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
"version": "v2.1.1",
|
||||
|
@ -11030,72 +11093,6 @@
|
|||
],
|
||||
"time": "2021-12-31T09:40:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tightenco/ziggy",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tighten/ziggy.git",
|
||||
"reference": "147804d5f3e98b897fc1ed15efc2807f1099cf83"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tighten/ziggy/zipball/147804d5f3e98b897fc1ed15efc2807f1099cf83",
|
||||
"reference": "147804d5f3e98b897fc1ed15efc2807f1099cf83",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"laravel/framework": ">=5.4@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Tightenco\\Ziggy\\ZiggyServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tightenco\\Ziggy\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daniel Coulbourne",
|
||||
"email": "daniel@tighten.co"
|
||||
},
|
||||
{
|
||||
"name": "Jake Bathman",
|
||||
"email": "jake@tighten.co"
|
||||
},
|
||||
{
|
||||
"name": "Jacob Baker-Kretzmar",
|
||||
"email": "jacob@tighten.co"
|
||||
}
|
||||
],
|
||||
"description": "Generates a Blade directive exporting all of your named Laravel routes. Also provides a nice route() helper function in JavaScript.",
|
||||
"homepage": "https://github.com/tighten/ziggy",
|
||||
"keywords": [
|
||||
"Ziggy",
|
||||
"javascript",
|
||||
"laravel",
|
||||
"routes"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/tighten/ziggy/issues",
|
||||
"source": "https://github.com/tighten/ziggy/tree/v1.2.0"
|
||||
},
|
||||
"time": "2021-05-24T22:46:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tijsverkoyen/css-to-inline-styles",
|
||||
"version": "2.2.4",
|
||||
|
@ -11149,6 +11146,199 @@
|
|||
},
|
||||
"time": "2021-12-08T09:12:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tmilos/lexer",
|
||||
"version": "1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmilos/lexer.git",
|
||||
"reference": "e7885595614759f1da2ff79b66e3fb26d7f875fa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tmilos/lexer/zipball/e7885595614759f1da2ff79b66e3fb26d7f875fa",
|
||||
"reference": "e7885595614759f1da2ff79b66e3fb26d7f875fa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~5.6",
|
||||
"satooshi/php-coveralls": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tmilos\\Lexer\\": "src/",
|
||||
"Tests\\Tmilos\\Lexer\\": "tests/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Milos Tomic",
|
||||
"email": "tmilos@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Lexical analyzer with individual token definition with regular expressions",
|
||||
"keywords": [
|
||||
"lexer",
|
||||
"parser"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/tmilos/lexer/issues",
|
||||
"source": "https://github.com/tmilos/lexer/tree/master"
|
||||
},
|
||||
"time": "2016-12-21T11:22:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tmilos/scim-filter-parser",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmilos/scim-filter-parser.git",
|
||||
"reference": "cfd9ba1f33e1e15adcab2481bffd74cb9fb35704"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tmilos/scim-filter-parser/zipball/cfd9ba1f33e1e15adcab2481bffd74cb9fb35704",
|
||||
"reference": "cfd9ba1f33e1e15adcab2481bffd74cb9fb35704",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"tmilos/lexer": "^1.0",
|
||||
"tmilos/value": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"satooshi/php-coveralls": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tmilos\\ScimFilterParser\\": "src/",
|
||||
"Tests\\Tmilos\\ScimFilterParser\\": "tests/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Milos Tomic",
|
||||
"email": "tmilos@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "System for Cross-domain Identity Management SCIM AST filter parser PHP library",
|
||||
"keywords": [
|
||||
"SCIM AST",
|
||||
"SCIM filter parser",
|
||||
"SCIM parser",
|
||||
"ast",
|
||||
"parser",
|
||||
"scim",
|
||||
"simplecloud"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/tmilos/scim-filter-parser/issues",
|
||||
"source": "https://github.com/tmilos/scim-filter-parser/tree/master"
|
||||
},
|
||||
"time": "2017-01-19T11:17:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tmilos/scim-schema",
|
||||
"version": "0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmilos/scim-schema.git",
|
||||
"reference": "bb871e667b33080b4cd36d7a9b2ac2cdbf796062"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tmilos/scim-schema/zipball/bb871e667b33080b4cd36d7a9b2ac2cdbf796062",
|
||||
"reference": "bb871e667b33080b4cd36d7a9b2ac2cdbf796062",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8|^5.6",
|
||||
"satooshi/php-coveralls": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tmilos\\ScimSchema\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Milos Tomic",
|
||||
"email": "tmilos@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "SCIM schema library",
|
||||
"support": {
|
||||
"issues": "https://github.com/tmilos/scim-schema/issues",
|
||||
"source": "https://github.com/tmilos/scim-schema/tree/master"
|
||||
},
|
||||
"time": "2017-11-25T22:18:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tmilos/value",
|
||||
"version": "1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmilos/value.git",
|
||||
"reference": "9e78ad9c026b14cacec1a27552ee0ada9d7d1c06"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tmilos/value/zipball/9e78ad9c026b14cacec1a27552ee0ada9d7d1c06",
|
||||
"reference": "9e78ad9c026b14cacec1a27552ee0ada9d7d1c06",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"moontoast/math": "~1.1",
|
||||
"phpunit/phpunit": "~4.8",
|
||||
"ramsey/uuid": "^3.3",
|
||||
"satooshi/php-coveralls": "~0.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Tmilos\\Value\\": "src/",
|
||||
"Tmilos\\Value\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Milos Tomic",
|
||||
"email": "tmilos@gmail.com",
|
||||
"homepage": "https://github.com/tmilos/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/tmilos/value/issues",
|
||||
"source": "https://github.com/tmilos/value/tree/master"
|
||||
},
|
||||
"time": "2016-06-06T10:22:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "unicodeveloper/laravel-password",
|
||||
"version": "1.0.4",
|
||||
|
@ -13462,7 +13652,9 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {
|
||||
"arietimmerman/laravel-scim-server": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
@ -13474,5 +13666,5 @@
|
|||
"ext-pdo": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
|
|
@ -342,7 +342,6 @@ return [
|
|||
Laravel\Passport\PassportServiceProvider::class,
|
||||
Laravel\Tinker\TinkerServiceProvider::class,
|
||||
Unicodeveloper\DumbPassword\DumbPasswordServiceProvider::class,
|
||||
Tightenco\Ziggy\ZiggyServiceProvider::class, // Laravel routes in vue
|
||||
Eduardokum\LaravelMailAutoEmbed\ServiceProvider::class,
|
||||
|
||||
/*
|
||||
|
|
5
config/scim.php
Normal file
5
config/scim.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
"publish_routes" => false
|
||||
];
|
|
@ -1,738 +0,0 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ "./resources/assets/js/snipeit.js":
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
|
||||
/**
|
||||
* Module containing core application logic.
|
||||
* @param {jQuery} $ Insulated jQuery object
|
||||
* @param {JSON} settings Insulated `window.snipeit.settings` object.
|
||||
* @return {IIFE} Immediately invoked. Returns self.
|
||||
*/
|
||||
|
||||
lineOptions = {
|
||||
|
||||
legend: {
|
||||
position: "bottom"
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
fontColor: "rgba(0,0,0,0.5)",
|
||||
fontStyle: "bold",
|
||||
beginAtZero: true,
|
||||
maxTicksLimit: 5,
|
||||
padding: 20
|
||||
},
|
||||
gridLines: {
|
||||
drawTicks: false,
|
||||
display: false
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
zeroLineColor: "transparent"
|
||||
},
|
||||
ticks: {
|
||||
padding: 20,
|
||||
fontColor: "rgba(0,0,0,0.5)",
|
||||
fontStyle: "bold"
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
pieOptions = {
|
||||
//Boolean - Whether we should show a stroke on each segment
|
||||
segmentShowStroke: true,
|
||||
//String - The colour of each segment stroke
|
||||
segmentStrokeColor: "#fff",
|
||||
//Number - The width of each segment stroke
|
||||
segmentStrokeWidth: 1,
|
||||
//Number - The percentage of the chart that we cut out of the middle
|
||||
percentageInnerCutout: 50, // This is 0 for Pie charts
|
||||
//Number - Amount of animation steps
|
||||
animationSteps: 100,
|
||||
//String - Animation easing effect
|
||||
animationEasing: "easeOutBounce",
|
||||
//Boolean - Whether we animate the rotation of the Doughnut
|
||||
animateRotate: true,
|
||||
//Boolean - Whether we animate scaling the Doughnut from the centre
|
||||
animateScale: false,
|
||||
//Boolean - whether to make the chart responsive to window resizing
|
||||
responsive: true,
|
||||
// Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
|
||||
maintainAspectRatio: false,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li>" + "<i class='fas fa-circle-o' style='color: <%=segments[i].fillColor%>'></i>" + "<%if(segments[i].label){%><%=segments[i].label%><%}%> foo</li><%}%></ul>",
|
||||
//String - A tooltip template
|
||||
tooltipTemplate: "<%=value %> <%=label%> "
|
||||
};
|
||||
|
||||
//-----------------
|
||||
//- END PIE CHART -
|
||||
//-----------------
|
||||
|
||||
|
||||
(function ($, settings) {
|
||||
var Components = {};
|
||||
Components.modals = {};
|
||||
|
||||
// confirm delete modal
|
||||
Components.modals.confirmDelete = function () {
|
||||
var $el = $('table');
|
||||
|
||||
var events = {
|
||||
'click': function click(evnt) {
|
||||
var $context = $(this);
|
||||
var $dataConfirmModal = $('#dataConfirmModal');
|
||||
var href = $context.attr('href');
|
||||
var message = $context.attr('data-content');
|
||||
var title = $context.attr('data-title');
|
||||
|
||||
$('#myModalLabel').text(title);
|
||||
$dataConfirmModal.find('.modal-body').text(message);
|
||||
$('#deleteForm').attr('action', href);
|
||||
$dataConfirmModal.modal({
|
||||
show: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var render = function render() {
|
||||
$el.on('click', '.delete-asset', events['click']);
|
||||
};
|
||||
|
||||
return {
|
||||
render: render
|
||||
};
|
||||
|
||||
// confirm restore modal
|
||||
Components.modals.confirmRestore = function () {
|
||||
var $el = $('table');
|
||||
|
||||
var events = {
|
||||
'click': function click(evnt) {
|
||||
var $context = $(this);
|
||||
var $dataConfirmModal = $('#restoreConfirmModal');
|
||||
var href = $context.attr('href');
|
||||
var message = $context.attr('data-content');
|
||||
var title = $context.attr('data-title');
|
||||
|
||||
$('#myModalLabel').text(title);
|
||||
$dataConfirmModal.find('.modal-body').text(message);
|
||||
$('#confirmRestoreForm').attr('action', href);
|
||||
$dataConfirmModal.modal({
|
||||
show: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var render = function render() {
|
||||
$el.on('click', '.restore-modal', events['click']);
|
||||
};
|
||||
|
||||
return {
|
||||
render: render
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Application start point
|
||||
* Component definition stays out of load event, execution only happens.
|
||||
*/
|
||||
$(function () {
|
||||
new Components.modals.confirmDelete().render();
|
||||
});
|
||||
})(jQuery, window.snipeit.settings);
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
/*
|
||||
* Slideout help menu
|
||||
*/
|
||||
$('.slideout-menu-toggle').on('click', function (event) {
|
||||
console.log('clicked');
|
||||
event.preventDefault();
|
||||
// create menu variables
|
||||
var slideoutMenu = $('.slideout-menu');
|
||||
var slideoutMenuWidth = $('.slideout-menu').width();
|
||||
|
||||
// toggle open class
|
||||
slideoutMenu.toggleClass("open");
|
||||
|
||||
// slide menu
|
||||
if (slideoutMenu.hasClass("open")) {
|
||||
slideoutMenu.show();
|
||||
slideoutMenu.animate({
|
||||
right: "0px"
|
||||
});
|
||||
} else {
|
||||
slideoutMenu.animate({
|
||||
right: -slideoutMenuWidth
|
||||
}, "-350px");
|
||||
slideoutMenu.fadeOut();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* iCheck checkbox plugin
|
||||
*/
|
||||
|
||||
$('input[type="checkbox"].minimal, input[type="radio"].minimal').iCheck({
|
||||
checkboxClass: 'icheckbox_minimal-blue',
|
||||
radioClass: 'iradio_minimal-blue'
|
||||
});
|
||||
|
||||
/*
|
||||
* Select2
|
||||
*/
|
||||
|
||||
var iOS = /iPhone|iPad|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||
if (!iOS) {
|
||||
// Vue collision: Avoid overriding a vue select2 instance
|
||||
// by checking to see if the item has already been select2'd.
|
||||
$('select.select2:not(".select2-hidden-accessible")').each(function (i, obj) {
|
||||
{
|
||||
$(obj).select2();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('.datepicker').datepicker();
|
||||
|
||||
// var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value
|
||||
// $.fn.bootstrapDP = datepicker;
|
||||
// $('.datepicker').datepicker();
|
||||
|
||||
|
||||
// Crazy select2 rich dropdowns with images!
|
||||
$('.js-data-ajax').each(function (i, item) {
|
||||
var link = $(item);
|
||||
var endpoint = link.data("endpoint");
|
||||
var select = link.data("select");
|
||||
|
||||
link.select2({
|
||||
|
||||
/**
|
||||
* Adds an empty placeholder, allowing every select2 instance to be cleared.
|
||||
* This placeholder can be overridden with the "data-placeholder" attribute.
|
||||
*/
|
||||
placeholder: '',
|
||||
allowClear: true,
|
||||
|
||||
ajax: {
|
||||
|
||||
// the baseUrl includes a trailing slash
|
||||
url: Ziggy.baseUrl + 'api/v1/' + endpoint + '/selectlist',
|
||||
dataType: 'json',
|
||||
delay: 250,
|
||||
headers: {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
data: function data(params) {
|
||||
var data = {
|
||||
search: params.term,
|
||||
page: params.page || 1,
|
||||
assetStatusType: link.data("asset-status-type")
|
||||
};
|
||||
return data;
|
||||
},
|
||||
processResults: function processResults(data, params) {
|
||||
|
||||
params.page = params.page || 1;
|
||||
|
||||
var answer = {
|
||||
results: data.items,
|
||||
pagination: {
|
||||
more: "true" //(params.page < data.page_count)
|
||||
}
|
||||
};
|
||||
|
||||
return answer;
|
||||
},
|
||||
cache: true
|
||||
},
|
||||
escapeMarkup: function escapeMarkup(markup) {
|
||||
return markup;
|
||||
}, // let our custom formatter work
|
||||
templateResult: formatDatalist,
|
||||
templateSelection: formatDataSelection
|
||||
});
|
||||
});
|
||||
|
||||
function getSelect2Value(element) {
|
||||
|
||||
// if the passed object is not a jquery object, assuming 'element' is a selector
|
||||
if (!(element instanceof jQuery)) element = $(element);
|
||||
|
||||
var select = element.data("select2");
|
||||
|
||||
// There's two different locations where the select2-generated input element can be.
|
||||
searchElement = select.dropdown.$search || select.$container.find(".select2-search__field");
|
||||
|
||||
var value = searchElement.val();
|
||||
return value;
|
||||
}
|
||||
|
||||
$(".select2-hidden-accessible").on('select2:selecting', function (e) {
|
||||
var data = e.params.args.data;
|
||||
var isMouseUp = false;
|
||||
var element = $(this);
|
||||
var value = getSelect2Value(element);
|
||||
|
||||
if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup";
|
||||
|
||||
// if selected item does not match typed text, do not allow it to pass - force close for ajax.
|
||||
if (!isMouseUp) {
|
||||
if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) < 0) {
|
||||
e.preventDefault();
|
||||
|
||||
element.select2('close');
|
||||
|
||||
// if it does match, we set a flag in the event (which gets passed to subsequent events), telling it not to worry about the ajax
|
||||
} else if (value.toLowerCase() && data.text.toLowerCase().indexOf(value) > -1) {
|
||||
e.params.args.noForceAjax = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(".select2-hidden-accessible").on('select2:closing', function (e) {
|
||||
var element = $(this);
|
||||
var value = getSelect2Value(element);
|
||||
var noForceAjax = false;
|
||||
var isMouseUp = false;
|
||||
if (e.params.args.originalSelect2Event) noForceAjax = e.params.args.originalSelect2Event.noForceAjax;
|
||||
if (e.params.args.originalEvent) isMouseUp = e.params.args.originalEvent.type == "mouseup";
|
||||
|
||||
if (value && !noForceAjax && !isMouseUp) {
|
||||
var endpoint = element.data("endpoint");
|
||||
var assetStatusType = element.data("asset-status-type");
|
||||
$.ajax({
|
||||
url: Ziggy.baseUrl + 'api/v1/' + endpoint + '/selectlist?search=' + value + '&page=1' + (assetStatusType ? '&assetStatusType=' + assetStatusType : ''),
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
}
|
||||
}).done(function (response) {
|
||||
;
|
||||
var currentlySelected = element.select2('data').map(function (x) {
|
||||
return +x.id;
|
||||
}).filter(function (x) {
|
||||
return x !== 0;
|
||||
});
|
||||
|
||||
// makes sure we're not selecting the same thing twice for multiples
|
||||
var filteredResponse = response.items.filter(function (item) {
|
||||
return currentlySelected.indexOf(+item.id) < 0;
|
||||
});
|
||||
|
||||
var first = currentlySelected.length > 0 ? filteredResponse[0] : response.items[0];
|
||||
|
||||
if (first && first.id) {
|
||||
first.selected = true;
|
||||
|
||||
if ($("option[value='" + first.id + "']", element).length < 1) {
|
||||
var option = new Option(first.text, first.id, true, true);
|
||||
element.append(option);
|
||||
} else {
|
||||
var isMultiple = element.attr("multiple") == "multiple";
|
||||
element.val(isMultiple ? element.val().concat(first.id) : element.val(first.id));
|
||||
}
|
||||
element.trigger('change');
|
||||
|
||||
element.trigger({
|
||||
type: 'select2:select',
|
||||
params: {
|
||||
data: first
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function formatDatalist(datalist) {
|
||||
var loading_markup = '<i class="fas fa-spinner fa-spin" aria-hidden="true"></i> Loading...';
|
||||
if (datalist.loading) {
|
||||
return loading_markup;
|
||||
}
|
||||
|
||||
var markup = "<div class='clearfix'>";
|
||||
markup += "<div class='pull-left' style='padding-right: 10px;'>";
|
||||
if (datalist.image) {
|
||||
markup += "<div style='width: 30px;'><img src='" + datalist.image + "' style='max-height: 20px; max-width: 30px;'></div>";
|
||||
} else {
|
||||
markup += "<div style='height: 20px; width: 30px;'></div>";
|
||||
}
|
||||
|
||||
markup += "</div><div>" + datalist.text + "</div>";
|
||||
markup += "</div>";
|
||||
return markup;
|
||||
}
|
||||
|
||||
function formatDataSelection(datalist) {
|
||||
return datalist.text;
|
||||
}
|
||||
|
||||
// This handles the radio button selectors for the checkout-to-foo options
|
||||
// on asset checkout and also on asset edit
|
||||
$(function () {
|
||||
$('input[name=checkout_to_type]').on("change", function () {
|
||||
var assignto_type = $('input[name=checkout_to_type]:checked').val();
|
||||
var userid = $('#assigned_user option:selected').val();
|
||||
|
||||
if (assignto_type == 'asset') {
|
||||
$('#current_assets_box').fadeOut();
|
||||
$('#assigned_asset').show();
|
||||
$('#assigned_user').hide();
|
||||
$('#assigned_location').hide();
|
||||
$('.notification-callout').fadeOut();
|
||||
} else if (assignto_type == 'location') {
|
||||
$('#current_assets_box').fadeOut();
|
||||
$('#assigned_asset').hide();
|
||||
$('#assigned_user').hide();
|
||||
$('#assigned_location').show();
|
||||
$('.notification-callout').fadeOut();
|
||||
} else {
|
||||
|
||||
$('#assigned_asset').hide();
|
||||
$('#assigned_user').show();
|
||||
$('#assigned_location').hide();
|
||||
if (userid) {
|
||||
$('#current_assets_box').fadeIn();
|
||||
}
|
||||
$('.notification-callout').fadeIn();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------------------------
|
||||
// Deep linking for Bootstrap tabs
|
||||
// ------------------------------------------------
|
||||
var taburl = document.location.toString();
|
||||
|
||||
// Allow full page URL to activate a tab's ID
|
||||
// ------------------------------------------------
|
||||
// This allows linking to a tab on page load via the address bar.
|
||||
// So a URL such as, http://snipe-it.local/hardware/2/#my_tab will
|
||||
// cause the tab on that page with an ID of “my_tab” to be active.
|
||||
if (taburl.match('#')) {
|
||||
$('.nav-tabs a[href="#' + taburl.split('#')[1] + '"]').tab('show');
|
||||
}
|
||||
|
||||
// Allow internal page links to activate a tab's ID.
|
||||
// ------------------------------------------------
|
||||
// This allows you to link to a tab from anywhere on the page
|
||||
// including from within another tab. Also note that internal page
|
||||
// links either inside or out of the tabs need to include data-toggle="tab"
|
||||
// Ex: <a href="#my_tab" data-toggle="tab">Click me</a>
|
||||
$('a[data-toggle="tab"]').click(function (e) {
|
||||
var href = $(this).attr("href");
|
||||
history.pushState(null, null, href);
|
||||
e.preventDefault();
|
||||
$('a[href="' + $(this).attr('href') + '"]').tab('show');
|
||||
});
|
||||
|
||||
// ------------------------------------------------
|
||||
// End Deep Linking for Bootstrap tabs
|
||||
// ------------------------------------------------
|
||||
|
||||
|
||||
// Image preview
|
||||
function readURL(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
$('#imagePreview').attr('src', e.target.result);
|
||||
};
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes < 1024) return bytes + " Bytes";else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + " KB";else if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + " MB";else return (bytes / 1073741824).toFixed(2) + " GB";
|
||||
};
|
||||
|
||||
// File size validation
|
||||
$('#uploadFile').bind('change', function () {
|
||||
$('#upload-file-status').removeClass('text-success').removeClass('text-danger');
|
||||
$('.goodfile').remove();
|
||||
$('.badfile').remove();
|
||||
$('.badfile').remove();
|
||||
$('.previewSize').hide();
|
||||
$('#upload-file-info').html('');
|
||||
|
||||
var max_size = $('#uploadFile').data('maxsize');
|
||||
var total_size = 0;
|
||||
|
||||
for (var i = 0; i < this.files.length; i++) {
|
||||
total_size += this.files[i].size;
|
||||
$('#upload-file-info').append('<span class="label label-default">' + this.files[i].name + ' (' + formatBytes(this.files[i].size) + ')</span> ');
|
||||
}
|
||||
|
||||
if (total_size > max_size) {
|
||||
$('#upload-file-status').addClass('text-danger').removeClass('help-block').prepend('<i class="badfile fas fa-times"></i> ').append('<span class="previewSize"> Upload is ' + formatBytes(total_size) + '.</span>');
|
||||
} else {
|
||||
$('#upload-file-status').addClass('text-success').removeClass('help-block').prepend('<i class="goodfile fas fa-check"></i> ');
|
||||
readURL(this);
|
||||
$('#imagePreview').fadeIn();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./resources/assets/js/snipeit_modals.js":
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
/*
|
||||
*
|
||||
* Snipe-IT Universal Modal support
|
||||
*
|
||||
* Enables modal dialogs to create sub-resources throughout Snipe-IT
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
HOW TO USE
|
||||
Create a Button looking like this:
|
||||
<a href='{{ route('modal.user') }}' data-toggle="modal" data-target="#createModal" data-select='assigned_to' class="btn btn-sm btn-default">New</a>
|
||||
If you don't have access to Blade commands (like {{ and }}, etc), you can hard-code a URL as the 'href'
|
||||
data-toggle="modal" - required for Bootstrap Modals
|
||||
data-target="#createModal" - fixed ID for the modal, do not change
|
||||
data-select="assigned_to" - What is the *ID* of the select-dropdown that you're going to be adding to, if the modal-create was a success? Be on the lookout for duplicate ID's, it will confuse this library!
|
||||
class="btn btn-sm btn-default" - makes it look button-ey, feel free to change :)
|
||||
|
||||
If you want to pass additional variables to the modal (In the Category Create one, for example, you can pass category_id), you can encode them as URL variables in the href
|
||||
|
||||
*/
|
||||
|
||||
$(function () {
|
||||
|
||||
//handle modal-add-interstitial calls
|
||||
var model, select;
|
||||
|
||||
if ($('#createModal').length == 0) {
|
||||
$('body').append('<div class="modal fade" id="createModal"></div><!-- /.modal -->');
|
||||
}
|
||||
|
||||
$('#createModal').on("show.bs.modal", function (event) {
|
||||
var link = $(event.relatedTarget);
|
||||
model = link.data("dependency");
|
||||
select = link.data("select");
|
||||
$('#createModal').load(link.attr('href'), function () {
|
||||
//do we need to re-select2 this, after load? Probably.
|
||||
$('#createModal').find('select.select2').select2();
|
||||
// Initialize the ajaxy select2 with images.
|
||||
// This is a copy/paste of the code from snipeit.js, would be great to only have this in one place.
|
||||
$('.js-data-ajax').each(function (i, item) {
|
||||
var link = $(item);
|
||||
var endpoint = link.data("endpoint");
|
||||
var select = link.data("select");
|
||||
|
||||
link.select2({
|
||||
ajax: {
|
||||
|
||||
// the baseUrl includes a trailing slash
|
||||
url: Ziggy.baseUrl + 'api/v1/' + endpoint + '/selectlist',
|
||||
dataType: 'json',
|
||||
delay: 250,
|
||||
headers: {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
data: function data(params) {
|
||||
var data = {
|
||||
search: params.term,
|
||||
page: params.page || 1,
|
||||
assetStatusType: link.data("asset-status-type")
|
||||
};
|
||||
return data;
|
||||
},
|
||||
processResults: function processResults(data, params) {
|
||||
|
||||
params.page = params.page || 1;
|
||||
|
||||
var answer = {
|
||||
results: data.items,
|
||||
pagination: {
|
||||
more: "true" //(params.page < data.page_count)
|
||||
}
|
||||
};
|
||||
|
||||
return answer;
|
||||
},
|
||||
cache: true
|
||||
},
|
||||
escapeMarkup: function escapeMarkup(markup) {
|
||||
return markup;
|
||||
}, // let our custom formatter work
|
||||
templateResult: formatDatalist,
|
||||
templateSelection: formatDataSelection
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('#createModal').on('click', '#modal-save', function () {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $('.modal-body form').attr('action'),
|
||||
headers: {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
|
||||
data: $('.modal-body form').serialize(),
|
||||
success: function success(result) {
|
||||
|
||||
if (result.status == "error") {
|
||||
var error_message = "";
|
||||
for (var field in result.messages) {
|
||||
error_message += "<li>Problem(s) with field <i><strong>" + field + "</strong></i>: " + result.messages[field];
|
||||
}
|
||||
$('#modal_error_msg').html(error_message).show();
|
||||
return false;
|
||||
}
|
||||
var id = result.payload.id;
|
||||
var name = result.payload.name || result.payload.first_name + " " + result.payload.last_name;
|
||||
if (!id || !name) {
|
||||
console.error("Could not find resulting name or ID from modal-create. Name: " + name + ", id: " + id);
|
||||
return false;
|
||||
}
|
||||
$('#createModal').modal('hide');
|
||||
$('#createModal').html("");
|
||||
|
||||
// "select" is the original drop-down menu that someone
|
||||
// clicked 'add' on to add a new 'thing'
|
||||
// this code adds the newly created object to that select
|
||||
var selector = document.getElementById(select);
|
||||
|
||||
if (!selector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
selector.options[selector.length] = new Option(name, id);
|
||||
selector.selectedIndex = selector.length - 1;
|
||||
$(selector).trigger("change");
|
||||
if (window.fetchCustomFields) {
|
||||
fetchCustomFields();
|
||||
}
|
||||
},
|
||||
error: function error(result) {
|
||||
msg = result.responseJSON.messages || result.responseJSON.error;
|
||||
$('#modal_error_msg').html("Server Error: " + msg).show();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function formatDatalist(datalist) {
|
||||
var loading_markup = '<i class="fas fa-spinner fa-spin" aria-hidden="true"></i> Loading...';
|
||||
if (datalist.loading) {
|
||||
return loading_markup;
|
||||
}
|
||||
|
||||
var markup = "<div class='clearfix'>";
|
||||
markup += "<div class='pull-left' style='padding-right: 10px;'>";
|
||||
if (datalist.image) {
|
||||
markup += "<div style='width: 30px;'><img src='" + datalist.image + "' style='max-height: 20px; max-width: 30px;'></div>";
|
||||
} else {
|
||||
markup += "<div style='height: 20px; width: 30px;'></div>";
|
||||
}
|
||||
|
||||
markup += "</div><div>" + datalist.text + "</div>";
|
||||
markup += "</div>";
|
||||
return markup;
|
||||
}
|
||||
|
||||
function formatDataSelection(datalist) {
|
||||
return datalist.text;
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 2:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
__webpack_require__("./resources/assets/js/snipeit.js");
|
||||
module.exports = __webpack_require__("./resources/assets/js/snipeit_modals.js");
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
|
@ -100,6 +100,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
||||
export default {
|
||||
props: ['file', 'customFields'],
|
||||
data() {
|
||||
|
@ -266,7 +267,7 @@
|
|||
}
|
||||
this.statusType='pending';
|
||||
this.statusText = "Processing...";
|
||||
this.$http.post(route('api.imports.importFile', this.file.id), {
|
||||
this.$http.post(baseUrl + 'api/v1/imports/process/' + this.file.id, {
|
||||
'import-update': this.options.update,
|
||||
'send-welcome': this.options.send_welcome,
|
||||
'import-type': this.options.importType,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
<script>
|
||||
require('blueimp-file-upload');
|
||||
var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
||||
export default {
|
||||
/*
|
||||
* The component's data.
|
||||
|
@ -63,7 +64,7 @@
|
|||
|
||||
methods: {
|
||||
fetchFiles() {
|
||||
this.$http.get(route('api.imports.index'))
|
||||
this.$http.get(baseUrl + 'api/v1/imports')
|
||||
.then( ({data}) => this.files = data, // Success
|
||||
//Fail
|
||||
(response) => {
|
||||
|
@ -73,7 +74,7 @@
|
|||
});
|
||||
},
|
||||
fetchCustomFields() {
|
||||
this.$http.get(route('api.customfields.index'))
|
||||
this.$http.get(baseUrl + 'api/v1/fields')
|
||||
.then( ({data}) => {
|
||||
data = data.rows;
|
||||
data.forEach((item) => {
|
||||
|
@ -85,7 +86,7 @@
|
|||
});
|
||||
},
|
||||
deleteFile(file, key) {
|
||||
this.$http.delete(route('api.imports.destroy', file.id))
|
||||
this.$http.delete(baseUrl + 'api/v1/imports/' + file.id)
|
||||
.then(
|
||||
// Success, remove file from array.
|
||||
(response) => {
|
||||
|
|
|
@ -73,8 +73,6 @@
|
|||
}
|
||||
};
|
||||
</script>
|
||||
<!-- Add laravel routes into javascript Primarily useful for vue.-->
|
||||
@routes
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<script src="{{ url(asset('js/html5shiv.js')) }}" nonce="{{ csrf_token() }}"></script>
|
||||
|
|
38
routes/scim.php
Normal file
38
routes/scim.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use ArieTimmerman\Laravel\SCIMServer\RouteProvider as SCIMRouteProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SCIM Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These are the routes that we have to explicitly inject from the
|
||||
| laravel-scim-server project, which gives Snipe-IT SCIM support
|
||||
|
|
||||
*/
|
||||
|
||||
SCIMRouteProvider::publicRoutes(); // Make sure to load public routes *FIRST*
|
||||
|
||||
Route::middleware(['auth:api','authorize:superadmin'])->group(function () {
|
||||
SCIMRouteProvider::routes(
|
||||
[
|
||||
/*
|
||||
* If we leave public_routes as 'true', the public routes will load *now* and
|
||||
* be jammed into the same middleware that these private routes are loaded
|
||||
* with. That's bad, because these routes are *supposed* to be public.
|
||||
*
|
||||
* We loaded them a few lines above, *first*, otherwise the various
|
||||
* fallback routes in the library defined within these *secured* routes
|
||||
* will "take over" the above routes - and then you will end up losing
|
||||
* like 4 hours of your life trying to figure out why the public routes
|
||||
* aren't quite working right. Ask me how I know (BMW, 3/19/2022)
|
||||
*/
|
||||
'public_routes' => false
|
||||
]
|
||||
);
|
||||
|
||||
SCIMRouteProvider::meRoutes();
|
||||
}); // ->can('superuser');
|
Loading…
Add table
Reference in a new issue