diff --git a/.all-contributorsrc b/.all-contributorsrc index 42a34dab5f..133e8849d2 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2882,6 +2882,76 @@ "contributions": [ "code" ] + }, + { + "login": "Mezzle", + "name": "Martin Meredith", + "avatar_url": "https://avatars.githubusercontent.com/u/570639?v=4", + "profile": "https://github.com/Mezzle", + "contributions": [] + }, + { + "login": "dboth", + "name": "dboth", + "avatar_url": "https://avatars.githubusercontent.com/u/5731963?v=4", + "profile": "http://dboth.de", + "contributions": [ + "code" + ] + }, + { + "login": "zacharyfleck", + "name": "Zachary Fleck", + "avatar_url": "https://avatars.githubusercontent.com/u/87536651?v=4", + "profile": "https://github.com/zacharyfleck", + "contributions": [ + "code" + ] + }, + { + "login": "vikaas-cyper", + "name": "VIKAAS-A", + "avatar_url": "https://avatars.githubusercontent.com/u/74609912?v=4", + "profile": "https://github.com/vikaas-cyper", + "contributions": [ + "code" + ] + }, + { + "login": "ak-piracha", + "name": "Abdul Kareem", + "avatar_url": "https://avatars.githubusercontent.com/u/88882041?v=4", + "profile": "https://github.com/ak-piracha", + "contributions": [ + "code" + ] + }, + { + "login": "NojoudAlshehri", + "name": "NojoudAlshehri", + "avatar_url": "https://avatars.githubusercontent.com/u/111287779?v=4", + "profile": "https://github.com/NojoudAlshehri", + "contributions": [ + "code" + ] + }, + { + "login": "stefanstidlffg", + "name": "Stefan Stidl", + "avatar_url": "https://avatars.githubusercontent.com/u/54367449?v=4", + "profile": "https://github.com/stefanstidlffg", + "contributions": [ + "code" + ] + }, + { + "login": "qay21", + "name": "Quentin Aymard", + "avatar_url": "https://avatars.githubusercontent.com/u/87803479?v=4", + "profile": "https://github.com/qay21", + "contributions": [ + "code" + ] } ] } diff --git a/.chipperci.yml b/.chipperci.yml new file mode 100644 index 0000000000..663a06350a --- /dev/null +++ b/.chipperci.yml @@ -0,0 +1,58 @@ +version: 1 + +environment: + php: 8.0 + node: 12 + +services: + - mysql: 5.7 + - dusk: + +on: + push: + branches: + - master + - develop + +pipeline: + - name: Setup + cmd: | + cp -v .env.example .env + + composer install --no-interaction --prefer-dist --optimize-autoloader + + - name: Generate Key + cmd: | + php artisan key:generate --force + + - name: Passport Keys + cmd: | + php artisan passport:keys + + - name: Run Migrations + cmd: | + # php artisan migrate --force + + - name: PHPUnit Unit Tests + cmd: | + # php artisan test --testsuite Unit + + - name: PHPUnit Feature Tests + cmd: | + # php artisan test --testsuite Feature + +# - name: Browser Tests +# cmd: | +# cp -v .env.dusk.example .env.dusk.ci +# sed -i "s@APP_ENV=.*@APP_ENV=ci@g" .env.dusk.ci +# sed -i "s@APP_URL=.*@APP_URL=http://$BUILD_HOST:8000@g" .env.dusk.ci +# #sed -i "s@DB_HOST=.*@DB_HOST=mysql@g" .env.dusk.ci +# sed -i "s@DB_HOST=.*@DB_HOST=$DB_HOST@g" .env.dusk.ci +# sed -i "s@DB_USERNAME=.*@DB_USERNAME=chipperci@g" .env.dusk.ci +# sed -i "s@DB_DATABASE=.*@DB_DATABASE=chipperci@g" .env.dusk.ci +# sed -i "s@DB_PASSWORD=.*@DB_PASSWORD=secret@g" .env.dusk.ci +# +# php -S [::0]:8000 -t public 2>server.log & +# sleep 2 +# php artisan dusk:chrome-driver $CHROME_DRIVER +# php artisan dusk --env=ci diff --git a/.env.example b/.env.example index c523ef0c04..fbf5c4cfff 100644 --- a/.env.example +++ b/.env.example @@ -175,6 +175,15 @@ REQUIRE_SAML=false API_THROTTLE_PER_MINUTE=120 CSV_ESCAPE_FORMULAS=true +# -------------------------------------------- +# OPTIONAL: HASHING +# -------------------------------------------- +HASHING_DRIVER='bcrypt' +BCRYPT_ROUNDS=10 +ARGON_MEMORY=1024 +ARGON_THREADS=2 +ARGON_TIME=2 + # -------------------------------------------- # OPTIONAL: SCIM # -------------------------------------------- diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 8f13e1f1b7..dec3a8fe08 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -2,8 +2,6 @@ name: Feature Request description: Suggest an idea for this project title: "[Feature Request]: " labels: ["feature request"] -assignees: - - snipe body: - type: textarea attributes: diff --git a/.github/autolabeler.yml b/.github/autolabeler.yml index 9320437c78..a27d5fe5fc 100644 --- a/.github/autolabeler.yml +++ b/.github/autolabeler.yml @@ -1,18 +1,22 @@ -frontend: ["*.js", "*.css", "*.vue", "*.scss", "*.less", "*.blade.*", "*livewire*"] +frontend: ["*.js", "*.css", "*.vue", "*.scss", "*.less", "*.blade.*", "resources/views/livewire/*"] skins: ["*.js", "*.css", "*.scss", "*.less"] css: ["*.css","*.scss", "*.less"] -backend: ["/app/*", "*.php"] +javascript: ["*.js", "package.json", "package.lock"] +backend: ["/app/*", "composer.json", "composer.lock"] +translations: ["/resources/lang"] +livewire: ["/app/Http/Livewire/*", "resources/views/livewire/*"] backups: ["*backup*"] restore: ["*restore*"] saml: ["*saml*"] scim: ["*scim*"] custom fields: ["*fields*", "*fieldsets*"] -dependencies: ["composer.json"] +dependencies: ["composer.json", "composer.lock", "package.json", "package.lock"] consumables: ["*consumables*"] -api: ["/app/Http/Controllers/api/*"] +api: ["/app/Http/Controllers/Api/*"] notifications: ["/app/Notifications/*"] -importer: ["/app/Importer/*"] +importer: ["/app/Importer/*","/app/Http/Livewire/Importer.php", "resources/views/livewire/importer.php"] cli / artisan: ["/app/Console/*"] -LDAP: ["*LDAP*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"] +LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"] docker: ["*docker/*", "Dockerfile", "Dockerfile.alpine", "Dockerfile.fpm-alpine", ".dockerignore", ".env.docker"] +tests: ["/tests/*", "/stubs"] config: .github diff --git a/.github/workflows/docker-alpine.yml b/.github/workflows/docker-alpine.yml index f274fc2c40..0a5c28ee53 100644 --- a/.github/workflows/docker-alpine.yml +++ b/.github/workflows/docker-alpine.yml @@ -76,7 +76,7 @@ jobs: with: context: . file: ./Dockerfile.alpine - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64 # For pull requests, we run the Docker build (to ensure no PR changes break the build), # but we ONLY do an image push to DockerHub if it's NOT a PR push: ${{ github.event_name != 'pull_request' }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 323d90e41c..5aa2758e79 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: with: context: . file: ./Dockerfile - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64 # For pull requests, we run the Docker build (to ensure no PR changes break the build), # but we ONLY do an image push to DockerHub if it's NOT a PR push: ${{ github.event_name != 'pull_request' }} diff --git a/Dockerfile b/Dockerfile index b572ff6896..88de52858b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 LABEL maintainer="Brady Wetherington " # No need to add `apt-get clean` here, reference: @@ -14,16 +14,16 @@ RUN export DEBIAN_FRONTEND=noninteractive; \ apt-utils \ apache2 \ apache2-bin \ -libapache2-mod-php7.4 \ -php7.4-curl \ -php7.4-ldap \ -php7.4-mysql \ -php7.4-gd \ -php7.4-xml \ -php7.4-mbstring \ -php7.4-zip \ -php7.4-bcmath \ -php7.4-redis \ +libapache2-mod-php8.1 \ +php8.1-curl \ +php8.1-ldap \ +php8.1-mysql \ +php8.1-gd \ +php8.1-xml \ +php8.1-mbstring \ +php8.1-zip \ +php8.1-bcmath \ +php8.1-redis \ php-memcached \ patch \ curl \ @@ -38,9 +38,10 @@ gcc \ make \ autoconf \ libc-dev \ +libldap-common \ pkg-config \ libmcrypt-dev \ -php7.4-dev \ +php8.1-dev \ ca-certificates \ unzip \ dnsutils \ @@ -50,16 +51,16 @@ dnsutils \ RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar RUN php go-pear.phar -RUN pecl install mcrypt-1.0.3 +RUN pecl install mcrypt -RUN bash -c "echo extension=/usr/lib/php/20190902/mcrypt.so > /etc/php/7.4/mods-available/mcrypt.ini" +RUN bash -c "echo extension=/usr/lib/php/20210902/mcrypt.so > /etc/php/8.1/mods-available/mcrypt.ini" RUN phpenmod mcrypt RUN phpenmod gd RUN phpenmod bcmath -RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.4/apache2/php.ini -RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.4/cli/php.ini +RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.1/apache2/php.ini +RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.1/cli/php.ini RUN useradd -m --uid 1000 --gid 50 docker diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 93dba995a0..62a928f8ad 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,34 +1,34 @@ -FROM alpine:3.14.2 +FROM alpine:3.17.3 # Apache + PHP RUN apk add --no-cache \ apache2 \ - php7 \ - php7-common \ - php7-apache2 \ - php7-curl \ - php7-ldap \ - php7-mysqli \ - php7-gd \ - php7-xml \ - php7-mbstring \ - php7-zip \ - php7-ctype \ - php7-tokenizer \ - php7-pdo_mysql \ - php7-openssl \ - php7-bcmath \ - php7-phar \ - php7-json \ - php7-iconv \ - php7-fileinfo \ - php7-simplexml \ - php7-session \ - php7-dom \ - php7-xmlwriter \ - php7-xmlreader \ - php7-sodium \ - php7-redis \ - php7-pecl-memcached \ + php81 \ + php81-common \ + php81-apache2 \ + php81-curl \ + php81-ldap \ + php81-mysqli \ + php81-gd \ + php81-xml \ + php81-mbstring \ + php81-zip \ + php81-ctype \ + php81-tokenizer \ + php81-pdo_mysql \ + php81-openssl \ + php81-bcmath \ + php81-phar \ + php81-json \ + php81-iconv \ + php81-fileinfo \ + php81-simplexml \ + php81-session \ + php81-dom \ + php81-xmlwriter \ + php81-xmlreader \ + php81-sodium \ + php81-redis \ + php81-pecl-memcached \ curl \ wget \ vim \ @@ -41,7 +41,7 @@ COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf # Where apache's PID lives RUN mkdir -p /run/apache2 && chown apache:apache /run/apache2 -RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php7/php.ini +RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php81/php.ini COPY docker/000-default-2.4.conf /etc/apache2/conf.d/default.conf # Enable mod_rewrite diff --git a/Dockerfile.fpm-alpine b/Dockerfile.fpm-alpine index 3bb4566ffa..77302524d4 100644 --- a/Dockerfile.fpm-alpine +++ b/Dockerfile.fpm-alpine @@ -1,8 +1,8 @@ ARG ENVIRONMENT=production -ARG SNIPEIT_RELEASE=5.1.3 -ARG PHP_VERSION=7.4.16 -ARG PHP_ALPINE_VERSION=3.13 -ARG COMPOSER_VERSION=2.0.11 +ARG SNIPEIT_RELEASE=6.1.0 +ARG PHP_VERSION=8.2 +ARG PHP_ALPINE_VERSION=3.17 +ARG COMPOSER_VERSION=2 # Cannot use arguments with 'COPY --from' workaround # https://github.com/moby/moby/issues/34482#issuecomment-454716952 @@ -52,7 +52,7 @@ RUN { \ # Install php extensions inside docker containers easily # https://github.com/mlocati/docker-php-extension-installer -COPY --from=mlocati/php-extension-installer:1.2.19 /usr/bin/install-php-extensions /usr/local/bin/ +COPY --from=mlocati/php-extension-installer:2.1.15 /usr/bin/install-php-extensions /usr/local/bin/ RUN set -eux; \ install-php-extensions \ bcmath \ diff --git a/README.md b/README.md index ceb040145e..19310b976b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade) -[![All Contributors](https://img.shields.io/badge/all_contributors-317-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev) +[![All Contributors](https://img.shields.io/badge/all_contributors-325-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev) ## Snipe-IT - Open Source Asset Management System @@ -66,8 +66,11 @@ Since the release of the JSON REST API, several third-party developers have been - [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag. - [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit). - [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it. -- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@RodneyLeeBrands](https://github.com/RodneyLeeBrands) - Python script to synchronize information between Mosyle and Snipe-IT +- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT - [WWW::SnipeIT](https://github.com/SEDC/perl-www-snipeit) by [@SEDC](https://github.com/SEDC) - perl module for accessing the API +- [UniFi to Snipe-IT](https://github.com/RodneyLeeBrands/UnifiSnipeSync) by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT. +- [Kandji2Snipe](https://github.com/grokability/kandji2snipe) by [@briangoldstein](https://github.com/briangoldstein) - Python script that synchronizes Kandji with Snipe-IT. +- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by @ReticentRobot - Windows agent for Snipe-IT As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :) @@ -141,7 +144,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken | [
Peace](https://github.com/julian-piehl)
[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") | [
Kyle Gordon](https://github.com/kylegordon)
[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [
Katharina Drexel](http://www.bfh.ch)
[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [
David Sferruzza](https://david.sferruzza.fr/)
[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [
Rick Nelson](https://github.com/rnelsonee)
[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [
BasO12](https://github.com/BasO12)
[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [
Vautia](https://github.com/Vautia)
[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") | | [
Chris Hartjes](http://www.littlehart.net/atthekeyboard)
[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [
geo-chen](https://github.com/geo-chen)
[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [
Phan Nguyen](https://github.com/nh314)
[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [
Iisakki Jaakkola](https://github.com/StarlessNights)
[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [
Ikko Ashimine](https://bandism.net/)
[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [
Lukas Fehling](https://github.com/lukasfehling)
[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [
Fernando Almeida](https://github.com/fernando-almeida)
[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") | | [
akemidx](https://github.com/akemidx)
[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [
Oguz Bilgic](http://oguz.site)
[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [
Scooter Crawford](https://github.com/scoo73r)
[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [
subdriven](https://github.com/subdriven)
[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [
Andrew Savinykh](https://github.com/AndrewSav)
[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [
Tadayuki Onishi](https://kenchan0130.github.io)
[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [
Florian](https://github.com/floschoepfer)
[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") | -| [
Spencer Long](http://spencerlong.com)
[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [
Marcus Moore](https://github.com/marcusmoore)
[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | +| [
Spencer Long](http://spencerlong.com)
[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [
Marcus Moore](https://github.com/marcusmoore)
[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [
Martin Meredith](https://github.com/Mezzle)
| [
dboth](http://dboth.de)
[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [
Zachary Fleck](https://github.com/zacharyfleck)
[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [
VIKAAS-A](https://github.com/vikaas-cyper)
[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [
Abdul Kareem](https://github.com/ak-piracha)
[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") | +| [
NojoudAlshehri](https://github.com/NojoudAlshehri)
[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [
Stefan Stidl](https://github.com/stefanstidlffg)
[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [
Quentin Aymard](https://github.com/qay21)
[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/app/Console/Commands/CheckoutLicenseToAllUsers.php b/app/Console/Commands/CheckoutLicenseToAllUsers.php index c81408442a..801b3d187a 100644 --- a/app/Console/Commands/CheckoutLicenseToAllUsers.php +++ b/app/Console/Commands/CheckoutLicenseToAllUsers.php @@ -56,7 +56,7 @@ class CheckoutLicenseToAllUsers extends Command return false; } - $users = User::whereNull('deleted_at')->where('autoassign_licenses', '==', 1)->with('licenses')->get(); + $users = User::whereNull('deleted_at')->where('autoassign_licenses', '=', 1)->with('licenses')->get(); if ($users->count() > $license->getAvailSeatsCountAttribute()) { $this->info('You do not have enough free seats to complete this task, so we will check out as many as we can. '); diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php index 5aebde3777..114f92dae4 100644 --- a/app/Console/Commands/CreateAdmin.php +++ b/app/Console/Commands/CreateAdmin.php @@ -20,13 +20,14 @@ class CreateAdmin extends Command * @property string $password * @property boolean $activated * @property boolean $show_in_list + * @property boolean $autoassign_licenses * @property \Illuminate\Support\Carbon|null $created_at * @property mixed $created_by */ - protected $signature = 'snipeit:create-admin {--first_name=} {--last_name=} {--email=} {--username=} {--password=} {show_in_list?}'; + protected $signature = 'snipeit:create-admin {--first_name=} {--last_name=} {--email=} {--username=} {--password=} {show_in_list?} {autoassign_licenses?}'; /** * The console command description. @@ -54,6 +55,9 @@ class CreateAdmin extends Command $email = $this->option('email'); $password = $this->option('password'); $show_in_list = $this->argument('show_in_list'); + $autoassign_licenses = $this->argument('autoassign_licenses'); + + if (($first_name == '') || ($last_name == '') || ($username == '') || ($email == '') || ($password == '')) { $this->info('ERROR: All fields are required.'); @@ -70,6 +74,11 @@ class CreateAdmin extends Command if ($show_in_list == 'false') { $user->show_in_list = 0; } + + if ($autoassign_licenses == 'false') { + $user->autoassign_licenses = 0; + } + if ($user->save()) { $this->info('New user created'); $user->groups()->attach(1); diff --git a/app/Console/Commands/LdapSync.php b/app/Console/Commands/LdapSync.php index 975db4f5d7..dd6fea8b66 100755 --- a/app/Console/Commands/LdapSync.php +++ b/app/Console/Commands/LdapSync.php @@ -62,6 +62,7 @@ class LdapSync extends Command $ldap_result_phone = Setting::getSettings()->ldap_phone_field; $ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle; $ldap_result_country = Setting::getSettings()->ldap_country; + $ldap_result_location = Setting::getSettings()->ldap_location; $ldap_result_dept = Setting::getSettings()->ldap_dept; $ldap_result_manager = Setting::getSettings()->ldap_manager; $ldap_default_group = Setting::getSettings()->ldap_default_group; @@ -209,8 +210,11 @@ class LdapSync extends Command $item['country'] = $results[$i][$ldap_result_country][0] ?? ''; $item['department'] = $results[$i][$ldap_result_dept][0] ?? ''; $item['manager'] = $results[$i][$ldap_result_manager][0] ?? ''; + $item['location'] = $results[$i][$ldap_result_location][0] ?? ''; - + $location = Location::firstOrCreate([ + 'name' => $item['location'], + ]); $department = Department::firstOrCreate([ 'name' => $item['department'], ]); @@ -236,6 +240,7 @@ class LdapSync extends Command $user->jobtitle = $item['jobtitle']; $user->country = $item['country']; $user->department_id = $department->id; + $user->location_id = $location->id; if($item['manager'] != null) { // Check Cache first diff --git a/app/Console/Commands/SystemBackup.php b/app/Console/Commands/SystemBackup.php index b7ca526052..3b51bcc736 100644 --- a/app/Console/Commands/SystemBackup.php +++ b/app/Console/Commands/SystemBackup.php @@ -11,7 +11,7 @@ class SystemBackup extends Command * * @var string */ - protected $name = 'snipeit:backup'; + protected $signature = 'snipeit:backup {--filename=}'; /** * The console command description. @@ -37,7 +37,18 @@ class SystemBackup extends Command */ public function handle() { - // - $this->call('backup:run'); + if ($this->option('filename')) { + $filename = $this->option('filename'); + + // Make sure the filename ends in .zip + if (!ends_with($filename, '.zip')) { + $filename = $filename.'.zip'; + } + + $this->call('backup:run', ['--filename' => $filename]); + } else { + $this->call('backup:run'); + } + } } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 65a13edb2e..3b1ffb9b31 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -10,7 +10,7 @@ use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException; use Log; use Throwable; use JsonException; - +use Carbon\Exceptions\InvalidFormatException; class Handler extends ExceptionHandler { @@ -30,6 +30,7 @@ class Handler extends ExceptionHandler \League\OAuth2\Server\Exception\OAuthServerException::class, JsonException::class, SCIMException::class, //these generally don't need to be reported + InvalidFormatException::class, ]; /** @@ -69,21 +70,34 @@ class Handler extends ExceptionHandler // Invalid JSON exception // TODO: don't understand why we have to do this when we have the invalidJson() method, below, but, well, whatever if ($e instanceof JsonException) { - return response()->json(Helper::formatStandardApiResponse('error', null, 'invalid JSON'), 422); + return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid JSON'), 422); } + // Handle SCIM exceptions if ($e instanceof SCIMException) { - return response()->json(Helper::formatStandardApiResponse('error', null, 'invalid SCIM Request'), 400); + return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid SCIM Request'), 400); } - // Handle Ajax requests that fail because the model doesn't exist + // Handle standard requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date) + if ($e instanceof InvalidFormatException) { + return redirect()->back()->withInput()->with('error', trans('validation.date', ['attribute' => 'date'])); + } + + // Handle API requests that fail if ($request->ajax() || $request->wantsJson()) { + // Handle API requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date) + if ($e instanceof InvalidFormatException) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('validation.date', ['attribute' => 'date'])), 200); + } + + // Handle API requests that fail because the model doesn't exist if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) { $className = last(explode('\\', $e->getModel())); return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200); } + // Handle API requests that fail because of an HTTP status code and return a useful error message if ($this->isHttpException($e)) { $statusCode = $e->getStatusCode(); @@ -103,6 +117,8 @@ class Handler extends ExceptionHandler } + + if ($this->isHttpException($e) && (isset($statusCode)) && ($statusCode == '404' )) { return response()->view('layouts/basic', [ 'content' => view('errors/404') diff --git a/app/Http/Controllers/Accessories/AccessoriesController.php b/app/Http/Controllers/Accessories/AccessoriesController.php index 7d4e697b98..111cbb3c8b 100755 --- a/app/Http/Controllers/Accessories/AccessoriesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesController.php @@ -77,7 +77,7 @@ class AccessoriesController extends Controller $accessory->manufacturer_id = request('manufacturer_id'); $accessory->model_number = request('model_number'); $accessory->purchase_date = request('purchase_date'); - $accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost')); + $accessory->purchase_cost = request('purchase_cost'); $accessory->qty = request('qty'); $accessory->user_id = Auth::user()->id; $accessory->supplier_id = request('supplier_id'); @@ -180,7 +180,7 @@ class AccessoriesController extends Controller $accessory->order_number = request('order_number'); $accessory->model_number = request('model_number'); $accessory->purchase_date = request('purchase_date'); - $accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost')); + $accessory->purchase_cost = request('purchase_cost'); $accessory->qty = request('qty'); $accessory->supplier_id = request('supplier_id'); $accessory->notes = request('notes'); diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 6144bb6f92..1ea036e6ed 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -25,11 +25,16 @@ class AccessoryCheckoutController extends Controller public function create($accessoryId) { // Check if the accessory exists - if (is_null($accessory = Accessory::find($accessoryId))) { + if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) { // Redirect to the accessory management page with error return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found')); } + // Make sure there is at least one available to checkout + if ($accessory->numRemaining() <= 0){ + return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable')); + } + if ($accessory->category) { $this->authorize('checkout', $accessory); @@ -55,17 +60,23 @@ class AccessoryCheckoutController extends Controller public function store(Request $request, $accessoryId) { // Check if the accessory exists - if (is_null($accessory = Accessory::find($accessoryId))) { + if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) { // Redirect to the accessory management page with error return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.user_not_found')); } $this->authorize('checkout', $accessory); - if (! $user = User::find($request->input('assigned_to'))) { + if (!$user = User::find($request->input('assigned_to'))) { return redirect()->route('accessories.checkout.show', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist')); } + // Make sure there is at least one available to checkout + if ($accessory->numRemaining() <= 0){ + return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable')); + } + + // Update the accessory data $accessory->assigned_to = e($request->input('assigned_to')); diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 726e164ba8..645e2624b2 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -121,7 +121,6 @@ class AcceptanceController extends Controller $pdf_filename = 'accepted-eula-'.date('Y-m-d-h-i-s').'.pdf'; $sig_filename=''; - if ($request->input('asset_acceptance') == 'accepted') { /** @@ -153,12 +152,14 @@ class AcceptanceController extends Controller } } - // this is horrible switch($acceptance->checkoutable_type){ case 'App\Models\Asset': $pdf_view_route ='account.accept.accept-asset-eula'; $asset_model = AssetModel::find($item->model_id); + if (!$asset_model) { + return redirect()->back()->with('error', trans('admin/models/message.does_not_exist')); + } $display_model = $asset_model->name; $assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName; break; @@ -167,7 +168,7 @@ class AcceptanceController extends Controller $pdf_view_route ='account.accept.accept-accessory-eula'; $accessory = Accessory::find($item->id); $display_model = $accessory->name; - $assigned_to = User::find($item->assignedTo); + $assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName; break; case 'App\Models\LicenseSeat': @@ -250,11 +251,15 @@ class AcceptanceController extends Controller // This is the most horriblest switch($acceptance->checkoutable_type){ case 'App\Models\Asset': + $asset_model = AssetModel::find($item->model_id); + $display_model = $asset_model->name; $assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName; break; case 'App\Models\Accessory': - $assigned_to = User::find($item->assignedTo); + $accessory = Accessory::find($item->id); + $display_model = $accessory->name; + $assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName; break; case 'App\Models\LicenseSeat': @@ -266,6 +271,8 @@ class AcceptanceController extends Controller break; case 'App\Models\Consumable': + $consumable = Consumable::find($item->id); + $display_model = $consumable->name; $assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName; break; } @@ -289,4 +296,4 @@ class AcceptanceController extends Controller return redirect()->to('account/accept')->with('success', $return_msg); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/AccessoriesController.php b/app/Http/Controllers/Api/AccessoriesController.php index dc2130e986..263bd2086f 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -80,12 +80,9 @@ class AccessoriesController extends Controller $accessories->where('notes','=',$request->input('notes')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $accessories->count()) ? $accessories->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort_override = $request->input('sort'); @@ -153,7 +150,7 @@ class AccessoriesController extends Controller public function show($id) { $this->authorize('view', Accessory::class); - $accessory = Accessory::findOrFail($id); + $accessory = Accessory::withCount('users as users_count')->findOrFail($id); return (new AccessoriesTransformer)->transformAccessory($accessory); } diff --git a/app/Http/Controllers/Api/AssetMaintenancesController.php b/app/Http/Controllers/Api/AssetMaintenancesController.php index e38d5382fa..6da7ce23a1 100644 --- a/app/Http/Controllers/Api/AssetMaintenancesController.php +++ b/app/Http/Controllers/Api/AssetMaintenancesController.php @@ -36,7 +36,7 @@ class AssetMaintenancesController extends Controller { $this->authorize('view', Asset::class); - $maintenances = AssetMaintenance::select('asset_maintenances.*')->with('asset', 'asset.model', 'asset.location', 'supplier', 'asset.company', 'admin'); + $maintenances = AssetMaintenance::select('asset_maintenances.*')->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'admin'); if ($request->filled('search')) { $maintenances = $maintenances->TextSearch($request->input('search')); @@ -55,12 +55,9 @@ class AssetMaintenancesController extends Controller } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($maintenances) && ($request->get('offset') > $maintenances->count())) ? $maintenances->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $maintenances->count()) ? $maintenances->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $allowed_columns = [ 'id', @@ -74,7 +71,8 @@ class AssetMaintenancesController extends Controller 'asset_tag', 'asset_name', 'user_id', - 'supplier' + 'supplier', + 'is_warranty', ]; $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at'; @@ -121,7 +119,7 @@ class AssetMaintenancesController extends Controller $assetMaintenance = new AssetMaintenance(); $assetMaintenance->supplier_id = $request->input('supplier_id'); $assetMaintenance->is_warranty = $request->input('is_warranty'); - $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost')); + $assetMaintenance->cost = $request->input('cost'); $assetMaintenance->notes = e($request->input('notes')); $asset = Asset::find(e($request->input('asset_id'))); @@ -178,7 +176,7 @@ class AssetMaintenancesController extends Controller $assetMaintenance->supplier_id = e($request->input('supplier_id')); $assetMaintenance->is_warranty = e($request->input('is_warranty')); - $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost')); + $assetMaintenance->cost = $request->input('cost'); $assetMaintenance->notes = e($request->input('notes')); $asset = Asset::find(request('asset_id')); diff --git a/app/Http/Controllers/Api/AssetModelsController.php b/app/Http/Controllers/Api/AssetModelsController.php index 5e9b3ad78d..cf13d24fc4 100644 --- a/app/Http/Controllers/Api/AssetModelsController.php +++ b/app/Http/Controllers/Api/AssetModelsController.php @@ -63,7 +63,7 @@ class AssetModelsController extends Controller 'models.deleted_at', 'models.updated_at', ]) - ->with('category', 'depreciation', 'manufacturer', 'fieldset') + ->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues') ->withCount('assets as assets_count'); if ($request->input('status')=='deleted') { @@ -78,12 +78,9 @@ class AssetModelsController extends Controller $assetmodels->TextSearch($request->input('search')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($assetmodels) && ($request->get('offset') > $assetmodels->count())) ? $assetmodels->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $assetmodels->count()) ? $assetmodels->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'models.created_at'; diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index 382990b576..16cef00d4d 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -136,76 +136,10 @@ class AssetsController extends Controller } } - if ($request->filled('status_id')) { - $assets->where('assets.status_id', '=', $request->input('status_id')); - } - if ($request->filled('asset_tag')) { - $assets->where('assets.asset_tag', '=', $request->input('asset_tag')); - } - - if ($request->filled('serial')) { - $assets->where('assets.serial', '=', $request->input('serial')); - } - - if ($request->input('requestable') == 'true') { - $assets->where('assets.requestable', '=', '1'); - } - - if ($request->filled('model_id')) { - $assets->InModelList([$request->input('model_id')]); - } - - if ($request->filled('category_id')) { - $assets->InCategory($request->input('category_id')); - } - - if ($request->filled('location_id')) { - $assets->where('assets.location_id', '=', $request->input('location_id')); - } - - if ($request->filled('rtd_location_id')) { - $assets->where('assets.rtd_location_id', '=', $request->input('rtd_location_id')); - } - - if ($request->filled('supplier_id')) { - $assets->where('assets.supplier_id', '=', $request->input('supplier_id')); - } - - if ($request->filled('asset_eol_date')) { - $assets->where('assets.asset_eol_date', '=', $request->input('asset_eol_date')); - } - - if (($request->filled('assigned_to')) && ($request->filled('assigned_type'))) { - $assets->where('assets.assigned_to', '=', $request->input('assigned_to')) - ->where('assets.assigned_type', '=', $request->input('assigned_type')); - } - - if ($request->filled('company_id')) { - $assets->where('assets.company_id', '=', $request->input('company_id')); - } - - if ($request->filled('manufacturer_id')) { - $assets->ByManufacturer($request->input('manufacturer_id')); - } - - if ($request->filled('depreciation_id')) { - $assets->ByDepreciationId($request->input('depreciation_id')); - } - - if ($request->filled('byod')) { - $assets->where('assets.byod', '=', $request->input('byod')); - } - - $request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : ''; - - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($assets) && ($request->get('offset') > $assets->count())) ? $assets->count() : $request->get('offset', 0); - - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $assets->count()) ? $assets->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; @@ -304,6 +238,71 @@ class AssetsController extends Controller $assets->TextSearch($request->input('search')); } + // Leave these under the TextSearch scope, else the fuzziness will override the specific ID (status ID, etc) requested + if ($request->filled('status_id')) { + $assets->where('assets.status_id', '=', $request->input('status_id')); + } + + if ($request->filled('asset_tag')) { + $assets->where('assets.asset_tag', '=', $request->input('asset_tag')); + } + + if ($request->filled('serial')) { + $assets->where('assets.serial', '=', $request->input('serial')); + } + + if ($request->input('requestable') == 'true') { + $assets->where('assets.requestable', '=', '1'); + } + + if ($request->filled('model_id')) { + $assets->InModelList([$request->input('model_id')]); + } + + if ($request->filled('category_id')) { + $assets->InCategory($request->input('category_id')); + } + + if ($request->filled('location_id')) { + $assets->where('assets.location_id', '=', $request->input('location_id')); + } + + if ($request->filled('rtd_location_id')) { + $assets->where('assets.rtd_location_id', '=', $request->input('rtd_location_id')); + } + + if ($request->filled('supplier_id')) { + $assets->where('assets.supplier_id', '=', $request->input('supplier_id')); + } + + if ($request->filled('asset_eol_date')) { + $assets->where('assets.asset_eol_date', '=', $request->input('asset_eol_date')); + } + + if (($request->filled('assigned_to')) && ($request->filled('assigned_type'))) { + $assets->where('assets.assigned_to', '=', $request->input('assigned_to')) + ->where('assets.assigned_type', '=', $request->input('assigned_type')); + } + + if ($request->filled('company_id')) { + $assets->where('assets.company_id', '=', $request->input('company_id')); + } + + if ($request->filled('manufacturer_id')) { + $assets->ByManufacturer($request->input('manufacturer_id')); + } + + if ($request->filled('depreciation_id')) { + $assets->ByDepreciationId($request->input('depreciation_id')); + } + + if ($request->filled('byod')) { + $assets->where('assets.byod', '=', $request->input('byod')); + } + + if ($request->filled('order_number')) { + $assets->where('assets.order_number', '=', $request->get('order_number')); + } // This is kinda gross, but we need to do this because the Bootstrap Tables // API passes custom field ordering as custom_fields.fieldname, and we have to strip @@ -463,7 +462,7 @@ class AssetsController extends Controller { $this->authorize('view', Asset::class); $this->authorize('view', License::class); - $asset = Asset::where('id', $id)->withTrashed()->first(); + $asset = Asset::where('id', $id)->withTrashed()->firstorfail(); $licenses = $asset->licenses()->get(); return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count()); @@ -554,7 +553,8 @@ class AssetsController extends Controller $asset->depreciate = '0'; $asset->status_id = $request->get('status_id', 0); $asset->warranty_months = $request->get('warranty_months', null); - $asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost')); // this is the API's store method, so I don't know that I want to do this? Confusing. FIXME (or not?!) + $asset->purchase_cost = $request->get('purchase_cost'); + $asset->asset_eol_date = $request->get('asset_eol_date', $asset->present()->eol_date()); $asset->purchase_date = $request->get('purchase_date', null); $asset->assigned_to = $request->get('assigned_to', null); $asset->supplier_id = $request->get('supplier_id'); @@ -562,6 +562,7 @@ class AssetsController extends Controller $asset->rtd_location_id = $request->get('rtd_location_id', null); $asset->location_id = $request->get('rtd_location_id', null); + /** * this is here just legacy reasons. Api\AssetController * used image_source once to allow encoded image uploads. @@ -575,6 +576,7 @@ class AssetsController extends Controller // Update custom fields in the database. // Validation for these fields is handled through the AssetRequest form request $model = AssetModel::find($request->get('model_id')); + if (($model) && ($model->fieldset)) { foreach ($model->fieldset->fields as $field) { @@ -832,7 +834,6 @@ class AssetsController extends Controller } elseif (request('checkout_to_type') == 'asset') { $target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset')); - $asset->location_id = $target->rtd_location_id; // Override with the asset's location_id if it has one $asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : ''; $error_payload['target_id'] = $request->input('assigned_asset'); diff --git a/app/Http/Controllers/Api/CategoriesController.php b/app/Http/Controllers/Api/CategoriesController.php index 91b0d7f7dc..2781fa101f 100644 --- a/app/Http/Controllers/Api/CategoriesController.php +++ b/app/Http/Controllers/Api/CategoriesController.php @@ -67,8 +67,6 @@ class CategoriesController extends Controller $categories = $categories->withCount('showableAssets as assets_count'); } - - if ($request->filled('search')) { $categories = $categories->TextSearch($request->input('search')); } @@ -93,14 +91,9 @@ class CategoriesController extends Controller $categories->where('checkin_email', '=', $request->input('checkin_email')); } - - - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($categories) && ($request->get('offset') > $categories->count())) ? $categories->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $categories->count()) ? $categories->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count'; diff --git a/app/Http/Controllers/Api/CompaniesController.php b/app/Http/Controllers/Api/CompaniesController.php index 11f5845674..d6c8f6e76e 100644 --- a/app/Http/Controllers/Api/CompaniesController.php +++ b/app/Http/Controllers/Api/CompaniesController.php @@ -48,12 +48,10 @@ class CompaniesController extends Controller } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($companies) && ($request->get('offset') > $companies->count())) ? $companies->count() : $request->get('offset', 0); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $companies->count()) ? $companies->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index 473fbf1482..24eb1044b8 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -12,6 +12,7 @@ use App\Http\Requests\ImageUploadRequest; use App\Events\CheckoutableCheckedIn; use App\Events\ComponentCheckedIn; use App\Models\Asset; +use Illuminate\Support\Facades\Validator; class ComponentsController extends Controller { @@ -45,7 +46,7 @@ class ComponentsController extends Controller $components = Company::scopeCompanyables(Component::select('components.*') - ->with('company', 'location', 'category', 'assets')); + ->with('company', 'location', 'category', 'assets', 'supplier')); if ($request->filled('search')) { $components = $components->TextSearch($request->input('search')); @@ -63,6 +64,10 @@ class ComponentsController extends Controller $components->where('category_id', '=', $request->input('category_id')); } + if ($request->filled('supplier_id')) { + $components->where('supplier_id', '=', $request->input('supplier_id')); + } + if ($request->filled('location_id')) { $components->where('location_id', '=', $request->input('location_id')); } @@ -71,14 +76,10 @@ class ComponentsController extends Controller $components->where('notes','=',$request->input('notes')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($components) && ($request->get('offset') > $components->count())) ? $components->count() : $request->get('offset', 0); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $components->count()) ? $components->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); - - $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort_override = $request->input('sort'); $column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at'; @@ -93,6 +94,9 @@ class ComponentsController extends Controller case 'company': $components = $components->OrderCompany($order); break; + case 'supplier': + $components = $components->OrderSupplier($order); + break; default: $components = $components->orderBy($column_sort, $order); break; @@ -225,20 +229,30 @@ class ComponentsController extends Controller public function checkout(Request $request, $componentId) { // Check if the component exists - if (is_null($component = Component::find($componentId))) { + if (!$component = Component::find($componentId)) { return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist'))); } $this->authorize('checkout', $component); + $validator = Validator::make($request->all(), [ + 'asset_id' => 'required|exists:assets,id', + 'assigned_qty' => "required|numeric|min:1|digits_between:1,".$component->numRemaining(), + ]); + + if ($validator->fails()) { + return response()->json(Helper::formatStandardApiResponse('error', $validator->errors())); + + } + + // Make sure there is at least one available to checkout + if ($component->numRemaining() <= $request->get('assigned_qty')) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')]))); + } if ($component->numRemaining() >= $request->get('assigned_qty')) { - if (!$asset = Asset::find($request->input('assigned_to'))) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist'))); - } - - // Update the accessory data + $asset = Asset::find($request->input('assigned_to')); $component->assigned_to = $request->input('assigned_to'); $component->assets()->attach($component->id, [ @@ -255,7 +269,7 @@ class ComponentsController extends Controller return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkout.success'))); } - return response()->json(Helper::formatStandardApiResponse('error', null, 'Not enough components remaining: '.$component->numRemaining().' remaining, '.$request->get('assigned_qty').' requested.')); + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')]))); } /** diff --git a/app/Http/Controllers/Api/ConsumablesController.php b/app/Http/Controllers/Api/ConsumablesController.php index fc6620df48..bac9440dca 100644 --- a/app/Http/Controllers/Api/ConsumablesController.php +++ b/app/Http/Controllers/Api/ConsumablesController.php @@ -75,6 +75,10 @@ class ConsumablesController extends Controller $consumables->where('manufacturer_id', '=', $request->input('manufacturer_id')); } + if ($request->filled('supplier_id')) { + $consumables->where('supplier_id', '=', $request->input('supplier_id')); + } + if ($request->filled('location_id')) { $consumables->where('location_id','=',$request->input('location_id')); } @@ -84,12 +88,9 @@ class ConsumablesController extends Controller } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($consumables) && ($request->get('offset') > $consumables->count())) ? $consumables->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $consumables->count()) ? $consumables->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $allowed_columns = ['id', 'name', 'order_number', 'min_amt', 'purchase_date', 'purchase_cost', 'company', 'category', 'model_number', 'item_no', 'manufacturer', 'location', 'qty', 'image']; $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; @@ -111,6 +112,9 @@ class ConsumablesController extends Controller case 'company': $consumables = $consumables->OrderCompany($order); break; + case 'supplier': + $components = $consumables->OrderSupplier($order); + break; default: $consumables = $consumables->orderBy($column_sort, $order); break; @@ -154,7 +158,7 @@ class ConsumablesController extends Controller public function show($id) { $this->authorize('view', Consumable::class); - $consumable = Consumable::findOrFail($id); + $consumable = Consumable::with('users')->findOrFail($id); return (new ConsumablesTransformer)->transformConsumable($consumable); } @@ -253,33 +257,39 @@ class ConsumablesController extends Controller public function checkout(Request $request, $id) { // Check if the consumable exists - if (is_null($consumable = Consumable::find($id))) { + if (!$consumable = Consumable::with('users')->find($id)) { return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.does_not_exist'))); } $this->authorize('checkout', $consumable); - if ($consumable->qty > 0) { + // Make sure there is at least one available to checkout + if ($consumable->numRemaining() <= 0) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable'))); + \Log::debug('No enough remaining'); + } - // Check if the user exists - $assigned_to = $request->input('assigned_to'); - if (is_null($user = User::find($assigned_to))) { - // Return error message - return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found')); - } + // Check if the user exists - @TODO: this should probably be handled via validation, not here?? + if (!$user = User::find($request->input('assigned_to'))) { + // Return error message + return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found')); + \Log::debug('No valid user'); + } - // Update the consumable data - $consumable->assigned_to = e($assigned_to); + // Update the consumable data + $consumable->assigned_to = $request->input('assigned_to'); - $consumable->users()->attach($consumable->id, [ - 'consumable_id' => $consumable->id, - 'user_id' => $user->id, - 'assigned_to' => $assigned_to, - 'note' => $request->input('note'), - ]); + $consumable->users()->attach($consumable->id, + [ + 'consumable_id' => $consumable->id, + 'user_id' => $user->id, + 'assigned_to' => $request->input('assigned_to'), + 'note' => $request->input('note'), + ] + ); // Log checkout event - $logaction = $consumable->logCheckout(e($request->input('note')), $user); + $logaction = $consumable->logCheckout($request->input('note'), $user); $data['log_id'] = $logaction->id; $data['eula'] = $consumable->getEula(); $data['first_name'] = $user->first_name; @@ -289,9 +299,7 @@ class ConsumablesController extends Controller $data['require_acceptance'] = $consumable->requireAcceptance(); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success'))); - } - return response()->json(Helper::formatStandardApiResponse('error', null, 'No consumables remaining')); } /** diff --git a/app/Http/Controllers/Api/CustomFieldsetsController.php b/app/Http/Controllers/Api/CustomFieldsetsController.php index 27da7733cd..df0fc940cf 100644 --- a/app/Http/Controllers/Api/CustomFieldsetsController.php +++ b/app/Http/Controllers/Api/CustomFieldsetsController.php @@ -7,6 +7,7 @@ use App\Http\Controllers\Controller; use App\Http\Transformers\CustomFieldsetsTransformer; use App\Http\Transformers\CustomFieldsTransformer; use App\Models\CustomFieldset; +use App\Models\CustomField; use Illuminate\Http\Request; use Redirect; use View; @@ -94,6 +95,18 @@ class CustomFieldsetsController extends Controller $fieldset->fill($request->all()); if ($fieldset->save()) { + // Sync fieldset with auto_add_to_fieldsets + $fields = CustomField::select('id')->where('auto_add_to_fieldsets', '=', '1')->get(); + + if ($fields->count() > 0) { + + foreach ($fields as $field) { + $field_ids[] = $field->id; + } + + $fieldset->fields()->sync($field_ids); + } + return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.create.success'))); } diff --git a/app/Http/Controllers/Api/DepartmentsController.php b/app/Http/Controllers/Api/DepartmentsController.php index 09e3677360..cab3991baa 100644 --- a/app/Http/Controllers/Api/DepartmentsController.php +++ b/app/Http/Controllers/Api/DepartmentsController.php @@ -58,12 +58,9 @@ class DepartmentsController extends Controller $departments->where('location_id', '=', $request->input('location_id')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($departments) && ($request->get('offset') > $departments->count())) ? $departments->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $departments->count()) ? $departments->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; diff --git a/app/Http/Controllers/Api/DepreciationsController.php b/app/Http/Controllers/Api/DepreciationsController.php index 2dd6b9d8e5..3d86c1b096 100644 --- a/app/Http/Controllers/Api/DepreciationsController.php +++ b/app/Http/Controllers/Api/DepreciationsController.php @@ -28,12 +28,9 @@ class DepreciationsController extends Controller $depreciations = $depreciations->TextSearch($request->input('search')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($depreciations) && ($request->get('offset') > $depreciations->count())) ? $depreciations->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; diff --git a/app/Http/Controllers/Api/GroupsController.php b/app/Http/Controllers/Api/GroupsController.php index 0e5d391e57..7cc5d2d756 100644 --- a/app/Http/Controllers/Api/GroupsController.php +++ b/app/Http/Controllers/Api/GroupsController.php @@ -35,12 +35,9 @@ class GroupsController extends Controller $groups->where('name', '=', $request->input('name')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($groups) && ($request->get('offset') > $groups->count())) ? $groups->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $groups->count()) ? $groups->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; diff --git a/app/Http/Controllers/Api/ImportController.php b/app/Http/Controllers/Api/ImportController.php index 6f5fc05ff3..2d7e2f7d78 100644 --- a/app/Http/Controllers/Api/ImportController.php +++ b/app/Http/Controllers/Api/ImportController.php @@ -160,7 +160,7 @@ class ImportController extends Controller // Run a backup immediately before processing if ($request->get('run-backup')) { \Log::debug('Backup manually requested via importer'); - Artisan::call('backup:run'); + Artisan::call('snipeit:backup', ['--filename' => 'pre-import-backup-'.date('Y-m-d-H:i:s')]); } else { \Log::debug('NO BACKUP requested via importer'); } @@ -193,6 +193,9 @@ class ImportController extends Controller case 'user': $redirectTo = 'users.index'; break; + case 'location': + $redirectTo = 'locations.index'; + break; } if ($errors) { //Failure diff --git a/app/Http/Controllers/Api/LicenseSeatsController.php b/app/Http/Controllers/Api/LicenseSeatsController.php index 759f11e956..884c9c59c4 100644 --- a/app/Http/Controllers/Api/LicenseSeatsController.php +++ b/app/Http/Controllers/Api/LicenseSeatsController.php @@ -39,8 +39,10 @@ class LicenseSeatsController extends Controller } $total = $seats->count(); - $offset = (($seats) && (request('offset') >= $total)) ? 0 : request('offset', 0); - $limit = request('limit', 50); + + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $seats->count()) ? $seats->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $seats = $seats->skip($offset)->take($limit)->get(); diff --git a/app/Http/Controllers/Api/LicensesController.php b/app/Http/Controllers/Api/LicensesController.php index 6e67f07ee9..df74b60895 100644 --- a/app/Http/Controllers/Api/LicensesController.php +++ b/app/Http/Controllers/Api/LicensesController.php @@ -94,12 +94,9 @@ class LicensesController extends Controller $licenses->onlyTrashed(); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($licenses) && ($request->get('offset') > $licenses->count())) ? $licenses->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/LocationsController.php b/app/Http/Controllers/Api/LocationsController.php index 3a8df10587..4b1feee117 100644 --- a/app/Http/Controllers/Api/LocationsController.php +++ b/app/Http/Controllers/Api/LocationsController.php @@ -78,14 +78,15 @@ class LocationsController extends Controller $locations->where('locations.country', '=', $request->input('country')); } - $offset = (($locations) && (request('offset') > $locations->count())) ? $locations->count() : request('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $locations->count()) ? $locations->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; + + switch ($request->input('sort')) { case 'parent': $locations->OrderParent($order); diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index e88ef5fedf..9ff0a0c202 100644 --- a/app/Http/Controllers/Api/ManufacturersController.php +++ b/app/Http/Controllers/Api/ManufacturersController.php @@ -23,10 +23,10 @@ class ManufacturersController extends Controller public function index(Request $request) { $this->authorize('view', Manufacturer::class); - $allowed_columns = ['id', 'name', 'url', 'support_url', 'support_email', 'support_phone', 'created_at', 'updated_at', 'image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count']; + $allowed_columns = ['id', 'name', 'url', 'support_url', 'support_email', 'warranty_lookup_url', 'support_phone', 'created_at', 'updated_at', 'image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count']; $manufacturers = Manufacturer::select( - ['id', 'name', 'url', 'support_url', 'support_email', 'support_phone', 'created_at', 'updated_at', 'image', 'deleted_at'] + ['id', 'name', 'url', 'support_url', 'warranty_lookup_url', 'support_email', 'support_phone', 'created_at', 'updated_at', 'image', 'deleted_at'] )->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('consumables as consumables_count')->withCount('accessories as accessories_count'); if ($request->input('deleted') == 'true') { @@ -49,6 +49,10 @@ class ManufacturersController extends Controller $manufacturers->where('support_url', '=', $request->input('support_url')); } + if ($request->filled('warranty_lookup_url')) { + $manufacturers->where('warranty_lookup_url', '=', $request->input('warranty_lookup_url')); + } + if ($request->filled('support_phone')) { $manufacturers->where('support_phone', '=', $request->input('support_phone')); } @@ -57,12 +61,9 @@ class ManufacturersController extends Controller $manufacturers->where('support_email', '=', $request->input('support_email')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($manufacturers) && ($request->get('offset') > $manufacturers->count())) ? $manufacturers->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; diff --git a/app/Http/Controllers/Api/PredefinedKitsController.php b/app/Http/Controllers/Api/PredefinedKitsController.php index a2c0c1a902..85d05c422e 100644 --- a/app/Http/Controllers/Api/PredefinedKitsController.php +++ b/app/Http/Controllers/Api/PredefinedKitsController.php @@ -29,8 +29,10 @@ class PredefinedKitsController extends Controller $kits = $kits->TextSearch($request->input('search')); } - $offset = $request->input('offset', 0); - $limit = $request->input('limit', 50); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $kits->count()) ? $kits->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); + $order = $request->input('order') === 'desc' ? 'desc' : 'asc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'name'; $kits->orderBy($sort, $order); diff --git a/app/Http/Controllers/Api/ReportsController.php b/app/Http/Controllers/Api/ReportsController.php index f42a2d0f81..21294c5779 100644 --- a/app/Http/Controllers/Api/ReportsController.php +++ b/app/Http/Controllers/Api/ReportsController.php @@ -54,15 +54,15 @@ class ReportsController extends Controller 'note', ]; + + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); + $sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at'; $order = ($request->input('order') == 'asc') ? 'asc' : 'desc'; - $offset = request('offset', 0); $total = $actionlogs->count(); - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); - - $actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get(); return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE); diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index 76055f2d9b..30a0b699e9 100644 --- a/app/Http/Controllers/Api/StatuslabelsController.php +++ b/app/Http/Controllers/Api/StatuslabelsController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Transformers\AssetsTransformer; +use App\Http\Transformers\SelectlistTransformer; use App\Http\Transformers\StatuslabelsTransformer; use App\Models\Asset; use App\Models\Statuslabel; @@ -50,12 +51,9 @@ class StatuslabelsController extends Controller } } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($statuslabels) && ($request->get('offset') > $statuslabels->count())) ? $statuslabels->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; @@ -294,4 +292,45 @@ class StatuslabelsController extends Controller return '0'; } + + /** + * Gets a paginated collection for the select2 menus + * + * @author [A. Gianotto] [] + * @since [v6.1.1] + * @see \App\Http\Transformers\SelectlistTransformer + */ + public function selectlist(Request $request) + { + + $this->authorize('view.selectlists'); + $statuslabels = Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('deployable', 'desc'); + + if ($request->filled('search')) { + $statuslabels = $statuslabels->where('name', 'LIKE', '%'.$request->get('search').'%'); + } + + if ($request->filled('deployable')) { + $statuslabels = $statuslabels->where('deployable', '=', '1'); + } + + if ($request->filled('pending')) { + $statuslabels = $statuslabels->where('pending', '=', '1'); + } + + if ($request->filled('archived')) { + $statuslabels = $statuslabels->where('archived', '=', '1'); + } + + $statuslabels = $statuslabels->orderBy('name', 'ASC')->paginate(50); + + // Loop through and set some custom properties for the transformer to use. + // This lets us have more flexibility in special cases like assets, where + // they may not have a ->name value but we want to display something anyway + foreach ($statuslabels as $statuslabel) { + $statuslabels->use_text = $statuslabel->name; + } + + return (new SelectlistTransformer)->transformSelectlist($statuslabels); + } } diff --git a/app/Http/Controllers/Api/SuppliersController.php b/app/Http/Controllers/Api/SuppliersController.php index f73f150499..a26c33b1f8 100644 --- a/app/Http/Controllers/Api/SuppliersController.php +++ b/app/Http/Controllers/Api/SuppliersController.php @@ -23,11 +23,30 @@ class SuppliersController extends Controller public function index(Request $request) { $this->authorize('view', Supplier::class); - $allowed_columns = ['id', 'name', 'address', 'phone', 'contact', 'fax', 'email', 'image', 'assets_count', 'licenses_count', 'accessories_count', 'url']; + $allowed_columns = [' + id', + 'name', + 'address', + 'phone', + 'contact', + 'fax', + 'email', + 'image', + 'assets_count', + 'licenses_count', + 'accessories_count', + 'components_count', + 'consumables_count', + 'url', + ]; $suppliers = Supplier::select( - ['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes'] - )->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count'); + ['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes']) + ->withCount('assets as assets_count') + ->withCount('licenses as licenses_count') + ->withCount('accessories as accessories_count') + ->withCount('components as components_count') + ->withCount('consumables as consumables_count'); if ($request->filled('search')) { @@ -74,12 +93,9 @@ class SuppliersController extends Controller $suppliers->where('notes', '=', $request->input('notes')); } - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($suppliers) && ($request->get('offset') > $suppliers->count())) ? $suppliers->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index ff18b87910..dc444ec9e9 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -71,6 +71,7 @@ class UsersController extends Controller 'users.start_date', 'users.end_date', 'users.vip', + 'users.autoassign_licenses', ])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',) ->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count'); @@ -187,18 +188,19 @@ class UsersController extends Controller $users->has('accessories', '=', $request->input('accessories_count')); } + if ($request->filled('autoassign_licenses')) { + $users->where('autoassign_licenses', '=', $request->input('autoassign_licenses')); + } + if ($request->filled('search')) { $users = $users->TextSearch($request->input('search')); } $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; - // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which - // case we override with the actual count, so we should return 0 items. - $offset = (($users) && ($request->get('offset') > $users->count())) ? $users->count() : $request->get('offset', 0); - - // Check to make sure the limit is not higher than the max allowed - ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $users->count()) ? $users->count() : abs($request->input('offset')); + $limit = app('api_limit_value'); switch ($request->input('sort')) { @@ -262,6 +264,7 @@ class UsersController extends Controller 'vip', 'start_date', 'end_date', + 'autoassign_licenses', ]; $sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name'; @@ -359,7 +362,7 @@ class UsersController extends Controller $user->permissions = $permissions_array; } - $tmp_pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 20); + $tmp_pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40); $user->password = bcrypt($request->get('password', $tmp_pass)); app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar'); diff --git a/app/Http/Controllers/AssetMaintenancesController.php b/app/Http/Controllers/AssetMaintenancesController.php index 5f3221d051..dc6bc84347 100644 --- a/app/Http/Controllers/AssetMaintenancesController.php +++ b/app/Http/Controllers/AssetMaintenancesController.php @@ -101,7 +101,7 @@ class AssetMaintenancesController extends Controller $assetMaintenance = new AssetMaintenance(); $assetMaintenance->supplier_id = $request->input('supplier_id'); $assetMaintenance->is_warranty = $request->input('is_warranty'); - $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost')); + $assetMaintenance->cost = $request->input('cost'); $assetMaintenance->notes = $request->input('notes'); $asset = Asset::find($request->input('asset_id')); @@ -211,7 +211,7 @@ class AssetMaintenancesController extends Controller $assetMaintenance->supplier_id = $request->input('supplier_id'); $assetMaintenance->is_warranty = $request->input('is_warranty'); - $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost')); + $assetMaintenance->cost = $request->input('cost'); $assetMaintenance->notes = $request->input('notes'); $asset = Asset::find(request('asset_id')); diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 3b2ff4623f..76c12d3cf9 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -12,6 +12,7 @@ use App\Models\CheckoutRequest; use App\Models\Company; use App\Models\Location; use App\Models\Setting; +use App\Models\Statuslabel; use App\Models\User; use Auth; use Carbon\Carbon; @@ -140,9 +141,9 @@ class AssetsController extends Controller $asset->depreciate = '0'; $asset->status_id = request('status_id'); $asset->warranty_months = request('warranty_months', null); - $asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost')); + $asset->purchase_cost = request('purchase_cost'); $asset->purchase_date = request('purchase_date', null); - $asset->asset_eol_date = request('asset_eol_date', null); + $asset->asset_eol_date = request('asset_eol_date', $asset->present()->eol_date()); $asset->assigned_to = request('assigned_to', null); $asset->supplier_id = request('supplier_id', null); $asset->requestable = request('requestable', 0); @@ -312,7 +313,7 @@ class AssetsController extends Controller $asset->status_id = $request->input('status_id', null); $asset->warranty_months = $request->input('warranty_months', null); - $asset->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null)); + $asset->purchase_cost = $request->input('purchase_cost', null); $asset->asset_eol_date = request('asset_eol_date', null); $asset->purchase_date = $request->input('purchase_date', null); @@ -324,6 +325,12 @@ class AssetsController extends Controller $asset->rtd_location_id = $request->input('rtd_location_id', null); $asset->byod = $request->input('byod', 0); + $status = Statuslabel::find($asset->status_id); + + if($status->archived){ + $asset->assigned_to = null; + } + if ($asset->assigned_to == '') { $asset->location_id = $request->input('rtd_location_id', null); } diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index 1171cdb8fd..c291a7fad0 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -35,9 +35,9 @@ class BulkAssetsController extends Controller { // dd($request->all()); // dd(Session::get('ids')); - - $this->authorize('update', Asset::class); - + + $this->authorize('view', Asset::class); + if (! $request->filled('ids')) { return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected')); } @@ -68,19 +68,33 @@ class BulkAssetsController extends Controller if ($request->filled('bulk_actions')) { switch ($request->input('bulk_actions')) { case 'labels': + $this->authorize('view', Asset::class); return view('hardware/labels') ->with('assets', Asset::find($asset_ids)) ->with('settings', Setting::getSettings()) ->with('bulkedit', true) ->with('count', 0); + case 'delete': + $this->authorize('delete', Asset::class); $assets = Asset::with('assignedTo', 'location')->find($asset_ids); $assets->each(function ($asset) { $this->authorize('delete', $asset); }); return view('hardware/bulk-delete')->with('assets', $assets); + + case 'restore': + $this->authorize('update', Asset::class); + $assets = Asset::withTrashed()->find($asset_ids); + $assets->each(function ($asset) { + $this->authorize('delete', $asset); + }); + + return view('hardware/bulk-restore')->with('assets', $assets); + case 'edit': + $this->authorize('update', Asset::class); return view('hardware/bulk') ->with('assets', $asset_ids) ->with('statuslabel_list', Helper::statusLabelList()) @@ -178,7 +192,7 @@ class BulkAssetsController extends Controller } if ($request->filled('purchase_cost')) { - $this->update_array['purchase_cost'] = Helper::ParseCurrency($request->input('purchase_cost')); + $this->update_array['purchase_cost'] = $request->input('purchase_cost'); } if ($request->filled('company_id')) { @@ -399,5 +413,19 @@ class BulkAssetsController extends Controller } catch (ModelNotFoundException $e) { return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors()); } + + } + public function restore(Request $request) { + $this->authorize('update', Asset::class); + $assetIds = $request->get('ids'); + if (empty($assetIds)) { + return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.restore.nothing_updated')); + } else { + foreach ($assetIds as $key => $assetId) { + $asset = Asset::withTrashed()->find($assetId); + $asset->restore(); + } + return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success')); + } } } diff --git a/app/Http/Controllers/BulkAssetModelsController.php b/app/Http/Controllers/BulkAssetModelsController.php index 43381da783..a312ba1f6e 100644 --- a/app/Http/Controllers/BulkAssetModelsController.php +++ b/app/Http/Controllers/BulkAssetModelsController.php @@ -92,7 +92,7 @@ class BulkAssetModelsController extends Controller AssetModel::whereIn('id', $models_raw_array)->update($update_array); return redirect()->route('models.index') - ->with('success', trans('admin/models/message.bulkedit.success')); + ->with('success', trans_choice('admin/models/message.bulkedit.success', count($models_raw_array), ['model_count' => count($models_raw_array)])); } return redirect()->route('models.index') diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index b2be07c661..412d9dde62 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -33,6 +33,11 @@ class ComponentCheckoutController extends Controller } $this->authorize('checkout', $component); + // Make sure there is at least one available to checkout + if ($component->numRemaining() <= 0){ + return redirect()->route('components.index')->with('error', trans('admin/components/message.checkout.unavailable')); + } + return view('components/checkout', compact('component')); } @@ -50,7 +55,7 @@ class ComponentCheckoutController extends Controller public function store(Request $request, $componentId) { // Check if the component exists - if (is_null($component = Component::find($componentId))) { + if (!$component = Component::find($componentId)) { // Redirect to the component management page with error return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found')); } @@ -58,9 +63,15 @@ class ComponentCheckoutController extends Controller $this->authorize('checkout', $component); $max_to_checkout = $component->numRemaining(); + + // Make sure there are at least the requested number of components available to checkout + if ($max_to_checkout < $request->get('assigned_qty')) { + return redirect()->back()->withInput()->with('error', trans('admin/components/message.checkout.unavailable', ['remaining' => $max_to_checkout, 'requested' => $request->get('assigned_qty')])); + } + $validator = Validator::make($request->all(), [ - 'asset_id' => 'required', - 'assigned_qty' => "required|numeric|between:1,$max_to_checkout", + 'asset_id' => 'required|exists:assets,id', + 'assigned_qty' => "required|numeric|min:1|digits_between:1,$max_to_checkout", ]); if ($validator->fails()) { @@ -69,24 +80,17 @@ class ComponentCheckoutController extends Controller ->withInput(); } - $admin_user = Auth::user(); - $asset_id = e($request->input('asset_id')); - // Check if the user exists - if (is_null($asset = Asset::find($asset_id))) { - // Redirect to the component management page with error - return redirect()->route('components.index')->with('error', trans('admin/components/message.asset_does_not_exist')); - } + $asset = Asset::find($request->input('asset_id')); // Update the component data - $component->asset_id = $asset_id; - + $component->asset_id = $request->input('asset_id'); $component->assets()->attach($component->id, [ 'component_id' => $component->id, - 'user_id' => $admin_user->id, + 'user_id' => Auth::user(), 'created_at' => date('Y-m-d H:i:s'), 'assigned_qty' => $request->input('assigned_qty'), - 'asset_id' => $asset_id, + 'asset_id' => $request->input('asset_id'), 'note' => $request->input('note'), ]); diff --git a/app/Http/Controllers/Components/ComponentsController.php b/app/Http/Controllers/Components/ComponentsController.php index f943a71a2e..34c9aed16b 100644 --- a/app/Http/Controllers/Components/ComponentsController.php +++ b/app/Http/Controllers/Components/ComponentsController.php @@ -71,13 +71,14 @@ class ComponentsController extends Controller $component = new Component(); $component->name = $request->input('name'); $component->category_id = $request->input('category_id'); + $component->supplier_id = $request->input('supplier_id'); $component->location_id = $request->input('location_id'); $component->company_id = Company::getIdForCurrentUser($request->input('company_id')); $component->order_number = $request->input('order_number', null); $component->min_amt = $request->input('min_amt', null); $component->serial = $request->input('serial', null); $component->purchase_date = $request->input('purchase_date', null); - $component->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null)); + $component->purchase_cost = $request->input('purchase_cost', null); $component->qty = $request->input('qty'); $component->user_id = Auth::id(); $component->notes = $request->input('notes'); @@ -145,13 +146,14 @@ class ComponentsController extends Controller // Update the component data $component->name = $request->input('name'); $component->category_id = $request->input('category_id'); + $component->supplier_id = $request->input('supplier_id'); $component->location_id = $request->input('location_id'); $component->company_id = Company::getIdForCurrentUser($request->input('company_id')); $component->order_number = $request->input('order_number'); $component->min_amt = $request->input('min_amt'); $component->serial = $request->input('serial'); $component->purchase_date = $request->input('purchase_date'); - $component->purchase_cost = Helper::ParseCurrency(request('purchase_cost')); + $component->purchase_cost = request('purchase_cost'); $component->qty = $request->input('qty'); $component->notes = $request->input('notes'); diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index f7f2b6e54d..6585624d82 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -24,9 +24,16 @@ class ConsumableCheckoutController extends Controller */ public function create($consumableId) { - if (is_null($consumable = Consumable::find($consumableId))) { + + if (is_null($consumable = Consumable::with('users')->find($consumableId))) { return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist')); } + + // Make sure there is at least one available to checkout + if ($consumable->numRemaining() <= 0){ + return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable')); + } + $this->authorize('checkout', $consumable); return view('consumables/checkout', compact('consumable')); @@ -44,12 +51,18 @@ class ConsumableCheckoutController extends Controller */ public function store(Request $request, $consumableId) { - if (is_null($consumable = Consumable::find($consumableId))) { + if (is_null($consumable = Consumable::with('users')->find($consumableId))) { return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found')); } $this->authorize('checkout', $consumable); + // Make sure there is at least one available to checkout + if ($consumable->numRemaining() <= 0) { + return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable')); + } + + $admin_user = Auth::user(); $assigned_to = e($request->input('assigned_to')); diff --git a/app/Http/Controllers/Consumables/ConsumablesController.php b/app/Http/Controllers/Consumables/ConsumablesController.php index f068e9868d..b33e6e07a9 100644 --- a/app/Http/Controllers/Consumables/ConsumablesController.php +++ b/app/Http/Controllers/Consumables/ConsumablesController.php @@ -68,6 +68,7 @@ class ConsumablesController extends Controller $consumable = new Consumable(); $consumable->name = $request->input('name'); $consumable->category_id = $request->input('category_id'); + $consumable->supplier_id = $request->input('supplier_id'); $consumable->location_id = $request->input('location_id'); $consumable->company_id = Company::getIdForCurrentUser($request->input('company_id')); $consumable->order_number = $request->input('order_number'); @@ -76,7 +77,7 @@ class ConsumablesController extends Controller $consumable->model_number = $request->input('model_number'); $consumable->item_no = $request->input('item_no'); $consumable->purchase_date = $request->input('purchase_date'); - $consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost')); + $consumable->purchase_cost = $request->input('purchase_cost'); $consumable->qty = $request->input('qty'); $consumable->user_id = Auth::id(); $consumable->notes = $request->input('notes'); @@ -144,6 +145,7 @@ class ConsumablesController extends Controller $consumable->name = $request->input('name'); $consumable->category_id = $request->input('category_id'); + $consumable->supplier_id = $request->input('supplier_id'); $consumable->location_id = $request->input('location_id'); $consumable->company_id = Company::getIdForCurrentUser($request->input('company_id')); $consumable->order_number = $request->input('order_number'); @@ -152,7 +154,7 @@ class ConsumablesController extends Controller $consumable->model_number = $request->input('model_number'); $consumable->item_no = $request->input('item_no'); $consumable->purchase_date = $request->input('purchase_date'); - $consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost')); + $consumable->purchase_cost = $request->input('purchase_cost'); $consumable->qty = Helper::ParseFloat($request->input('qty')); $consumable->notes = $request->input('notes'); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 96d761ef5b..c0e9454d62 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -9,11 +9,11 @@ * * **THIS DOCUMENTATION DOES NOT COVER INSTALLATION.** If you're here and you're not a * developer, you're probably in the wrong place. Please see the - * [Installation documentation](http://docs.snipeitapp.com) for + * [Installation documentation](https://snipe-it.readme.io) for * information on how to install Snipe-IT. * * To learn how to set up a development environment and get started developing for Snipe-IT, - * please see the [contributing documentation](http://docs.snipeitapp.com/contributing.html). + * please see the [contributing documentation](https://snipe-it.readme.io/docs/contributing-overview). * * Only the Snipe-IT specific controllers, models, helpers, service providers, * etc have been included in this documentation (excluding vendors, Laravel core, etc) diff --git a/app/Http/Controllers/CustomFieldsController.php b/app/Http/Controllers/CustomFieldsController.php index e29cbaa3fc..bbc3790d63 100644 --- a/app/Http/Controllers/CustomFieldsController.php +++ b/app/Http/Controllers/CustomFieldsController.php @@ -7,6 +7,7 @@ use App\Http\Requests\CustomFieldRequest; use App\Models\CustomField; use App\Models\CustomFieldset; use Illuminate\Support\Facades\Auth; +use Illuminate\Http\Request; use Redirect; /** @@ -45,7 +46,7 @@ class CustomFieldsController extends Controller * @see CustomFieldsController::storeField() * @author [A. Gianotto] [] * @since [v5.1.5] - * @return Redirect + * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ public function show() @@ -63,14 +64,17 @@ class CustomFieldsController extends Controller * @return \Illuminate\Support\Facades\View * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function create() + public function create(Request $request) { $this->authorize('create', CustomField::class); + $fieldsets = CustomFieldset::get(); return view('custom_fields.fields.edit', [ 'predefinedFormats' => Helper::predefined_formats(), - 'customFormat' => '', - ])->with('field', new CustomField()); + 'customFormat' => '', + 'fieldsets' => $fieldsets, + 'field' => new CustomField(), + ]); } /** @@ -79,7 +83,7 @@ class CustomFieldsController extends Controller * @see CustomFieldsController::createField() * @author [Brady Wetherington] [] * @since [v1.8] - * @return Redirect + * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ public function store(CustomFieldRequest $request) @@ -104,6 +108,7 @@ class CustomFieldsController extends Controller "show_in_email" => $show_in_email, "is_unique" => $request->get("is_unique", 0), "display_in_user_view" => $display_in_user_view, + "auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0), "user_id" => Auth::id() ]); @@ -115,10 +120,20 @@ class CustomFieldsController extends Controller } if ($field->save()) { + + // Sync fields with fieldsets + $fieldset_array = $request->input('associate_fieldsets'); + if ($request->has('associate_fieldsets') && (is_array($fieldset_array))) { + $field->fieldset()->sync(array_keys($fieldset_array)); + } else { + $field->fieldset()->sync([]); + } + + return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.create.success')); } - return redirect()->back()->withInput() + return redirect()->back()->with('selected_fieldsets', $request->input('associate_fieldsets'))->withInput() ->with('error', trans('admin/custom_fields/message.field.create.error')); } @@ -128,7 +143,7 @@ class CustomFieldsController extends Controller * * @author [A. Gianotto] [] * @since [v3.0] - * @return Redirect + * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ public function deleteFieldFromFieldset($field_id, $fieldset_id) @@ -147,8 +162,7 @@ class CustomFieldsController extends Controller ->with('success', trans('admin/custom_fields/message.field.delete.success')); } else { return redirect()->back()->withErrors(['message' => "Field is in use and cannot be deleted."]); - } - + } } return redirect()->back()->withErrors(['message' => "Error deleting field from fieldset"]); @@ -161,7 +175,7 @@ class CustomFieldsController extends Controller * * @author [Brady Wetherington] [] * @since [v1.8] - * @return Redirect + * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ public function destroy($field_id) @@ -190,12 +204,12 @@ class CustomFieldsController extends Controller * @return \Illuminate\Support\Facades\View * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function edit($id) + public function edit(Request $request, $id) { if ($field = CustomField::find($id)) { $this->authorize('update', $field); - + $fieldsets = CustomFieldset::get(); $customFormat = ''; if ((stripos($field->format, 'regex') === 0) && ($field->format !== CustomField::PREDEFINED_FORMATS['MAC'])) { $customFormat = $field->format; @@ -204,6 +218,7 @@ class CustomFieldsController extends Controller return view('custom_fields.fields.edit', [ 'field' => $field, 'customFormat' => $customFormat, + 'fieldsets' => $fieldsets, 'predefinedFormats' => Helper::predefined_formats(), ]); } @@ -222,7 +237,7 @@ class CustomFieldsController extends Controller * @author [A. Gianotto] [] * @param int $id * @since [v4.0] - * @return Redirect + * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ public function update(CustomFieldRequest $request, $id) @@ -249,6 +264,7 @@ class CustomFieldsController extends Controller $field->show_in_email = $show_in_email; $field->is_unique = $request->get("is_unique", 0); $field->display_in_user_view = $display_in_user_view; + $field->auto_add_to_fieldsets = $request->get("auto_add_to_fieldsets", 0); if ($request->get('format') == 'CUSTOM REGEX') { $field->format = e($request->get('custom_format')); @@ -256,11 +272,21 @@ class CustomFieldsController extends Controller $field->format = e($request->get('format')); } - if($field->element == 'checkbox' || $field->element == 'radio'){ + if ($field->element == 'checkbox' || $field->element == 'radio'){ $field->format = 'ANY'; } if ($field->save()) { + + + // Sync fields with fieldsets + $fieldset_array = $request->input('associate_fieldsets'); + if ($request->has('associate_fieldsets') && (is_array($fieldset_array))) { + $field->fieldset()->sync(array_keys($fieldset_array)); + } else { + $field->fieldset()->sync([]); + } + return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.update.success')); } diff --git a/app/Http/Controllers/CustomFieldsetsController.php b/app/Http/Controllers/CustomFieldsetsController.php index 8c14502285..dbee97b776 100644 --- a/app/Http/Controllers/CustomFieldsetsController.php +++ b/app/Http/Controllers/CustomFieldsetsController.php @@ -93,16 +93,27 @@ class CustomFieldsetsController extends Controller { $this->authorize('create', CustomField::class); - $cfset = new CustomFieldset([ + $fieldset = new CustomFieldset([ 'name' => e($request->get('name')), 'user_id' => Auth::user()->id, ]); - $validator = Validator::make($request->all(), $cfset->rules); - if ($validator->passes()) { - $cfset->save(); + $validator = Validator::make($request->all(), $fieldset->rules); - return redirect()->route('fieldsets.show', [$cfset->id]) + if ($validator->passes()) { + $fieldset->save(); + + // Sync fieldset with auto_add_to_fieldsets + $fields = CustomField::select('id')->where('auto_add_to_fieldsets', '=', '1')->get(); + if ($fields->count() > 0) { + foreach ($fields as $field) { + $field_ids[] = $field->id; + } + + $fieldset->fields()->sync($field_ids); + } + + return redirect()->route('fieldsets.show', [$fieldset->id]) ->with('success', trans('admin/custom_fields/message.fieldset.create.success')); } diff --git a/app/Http/Controllers/GoogleAuthController.php b/app/Http/Controllers/GoogleAuthController.php new file mode 100644 index 0000000000..a93fcd1fc2 --- /dev/null +++ b/app/Http/Controllers/GoogleAuthController.php @@ -0,0 +1,74 @@ + config('app.url').'/google/callback']); + config(['services.google.client_id' => $setting->google_client_id]); + config(['services.google.client_secret' => $setting->google_client_secret]); + } + + public function redirectToGoogle() + { + return Socialite::driver('google')->redirect(); + } + + public function handleGoogleCallback() + { + try { + $socialUser = Socialite::driver('google')->user(); + \Log::debug('Google user found in Google Workspace'); + } catch (InvalidStateException $exception) { + \Log::debug('Google user NOT found in Google Workspace'); + return redirect()->route('login') + ->withErrors( + [ + 'username' => [ + trans('auth/general.google_login_failed') + ], + ] + ); + } + + + $user = User::where('username', $socialUser->getEmail())->first(); + + + if ($user) { + \Log::debug('Google user '.$socialUser->getEmail().' found in Snipe-IT'); + $user->update([ + 'avatar' => $socialUser->avatar, + ]); + + Auth::login($user, true); + return redirect()->route('home'); + } + + \Log::debug('Google user '.$socialUser->getEmail().' NOT found in Snipe-IT'); + return redirect()->route('login') + ->withErrors( + [ + 'username' => [ + trans('auth/general.google_login_failed'), + ], + ] + ); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/GroupsController.php b/app/Http/Controllers/GroupsController.php index abe3e0682a..b98156824a 100755 --- a/app/Http/Controllers/GroupsController.php +++ b/app/Http/Controllers/GroupsController.php @@ -92,7 +92,7 @@ class GroupsController extends Controller return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions')); } - return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found')); + return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id])); } /** @@ -107,7 +107,7 @@ class GroupsController extends Controller public function update(Request $request, $id = null) { if (! $group = Group::find($id)) { - return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id'))); + return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id])); } $group->name = $request->input('name'); $group->permissions = json_encode($request->input('permission')); @@ -133,14 +133,13 @@ class GroupsController extends Controller * @return \Illuminate\Http\RedirectResponse * @throws \Exception */ - public function destroy($id = null) + public function destroy($id) { if (! config('app.lock_passwords')) { if (! $group = Group::find($id)) { - return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id'))); + return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id])); } $group->delete(); - // Redirect to the group management page return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.delete')); } @@ -164,6 +163,6 @@ class GroupsController extends Controller return view('groups/view', compact('group')); } - return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id'))); + return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id])); } } diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 257722b005..50e20c7985 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -112,4 +112,54 @@ class LicenseCheckinController extends Controller // Redirect to the license page with error return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkin.error')); } + + /** + * Bulk checkin all license seats + * + * @author [A. Gianotto] [] + * @see LicenseCheckinController::create() method that provides the form view + * @since [v6.1.1] + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + + public function bulkCheckin(Request $request, $licenseId) { + + $license = License::findOrFail($licenseId); + $this->authorize('checkin', $license); + + $licenseSeatsByUser = LicenseSeat::where('license_id', '=', $licenseId) + ->whereNotNull('assigned_to') + ->with('user') + ->get(); + + foreach ($licenseSeatsByUser as $user_seat) { + $user_seat->assigned_to = null; + + if ($user_seat->save()) { + \Log::debug('Checking in '.$license->name.' from user '.$user_seat->username); + $user_seat->logCheckin($user_seat->user, trans('admin/licenses/general.bulk.checkin_all.log_msg')); + } + } + + $licenseSeatsByAsset = LicenseSeat::where('license_id', '=', $licenseId) + ->whereNotNull('asset_id') + ->with('asset') + ->get(); + + $count = 0; + foreach ($licenseSeatsByAsset as $asset_seat) { + $asset_seat->asset_id = null; + + if ($asset_seat->save()) { + \Log::debug('Checking in '.$license->name.' from asset '.$asset_seat->asset_tag); + $asset_seat->logCheckin($asset_seat->asset, trans('admin/licenses/general.bulk.checkin_all.log_msg')); + $count++; + } + } + + return redirect()->back()->with('success', trans_choice('admin/licenses/general.bulk.checkin_all.success', 2, ['count' => $count] )); + + } + } diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index 6f2ae003ca..a710497692 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -126,4 +126,70 @@ class LicenseCheckoutController extends Controller return false; } + + /** + * Bulk checkin all license seats + * + * @author [A. Gianotto] [] + * @see LicenseCheckinController::create() method that provides the form view + * @since [v6.1.1] + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + + public function bulkCheckout($licenseId) { + + \Log::debug('Checking out '.$licenseId.' via bulk'); + $license = License::findOrFail($licenseId); + $this->authorize('checkin', $license); + $avail_count = $license->getAvailSeatsCountAttribute(); + + $users = User::whereNull('deleted_at')->where('autoassign_licenses', '=', 1)->with('licenses')->get(); + \Log::debug($avail_count.' will be assigned'); + + if ($users->count() > $avail_count) { + \Log::debug('You do not have enough free seats to complete this task, so we will check out as many as we can. '); + } + + // If the license is valid, check that there is an available seat + if ($license->availCount()->count() < 1) { + return redirect()->back()->with('error', trans('admin/licenses/general.bulk.checkout_all.error_no_seats')); + } + + + $assigned_count = 0; + + foreach ($users as $user) { + + // Check to make sure this user doesn't already have this license checked out to them + if ($user->licenses->where('id', '=', $licenseId)->count()) { + \Log::debug($user->username.' already has this license checked out to them. Skipping... '); + continue; + } + + $licenseSeat = $license->freeSeat(); + + // Update the seat with checkout info + $licenseSeat->assigned_to = $user->id; + + if ($licenseSeat->save()) { + $avail_count--; + $assigned_count++; + $licenseSeat->logCheckout(trans('admin/licenses/general.bulk.checkout_all.log_msg'), $user); + \Log::debug('License '.$license->name.' seat '.$licenseSeat->id.' checked out to '.$user->username); + } + + if ($avail_count == 0) { + return redirect()->back()->with('warning', trans('admin/licenses/general.bulk.checkout_all.warn_not_enough_seats', ['count' => $assigned_count])); + } + } + + if ($assigned_count == 0) { + return redirect()->back()->with('warning', trans('admin/licenses/general.bulk.checkout_all.warn_no_avail_users', ['count' => $assigned_count])); + } + + return redirect()->back()->with('success', trans_choice('admin/licenses/general.bulk.checkout_all.success', 2, ['count' => $assigned_count] )); + + + } } diff --git a/app/Http/Controllers/Licenses/LicensesController.php b/app/Http/Controllers/Licenses/LicensesController.php index a0467654ca..26cba33281 100755 --- a/app/Http/Controllers/Licenses/LicensesController.php +++ b/app/Http/Controllers/Licenses/LicensesController.php @@ -6,6 +6,8 @@ use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Models\Company; use App\Models\License; +use App\Models\LicenseSeat; +use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -86,7 +88,7 @@ class LicensesController extends Controller $license->name = $request->input('name'); $license->notes = $request->input('notes'); $license->order_number = $request->input('order_number'); - $license->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost')); + $license->purchase_cost = $request->input('purchase_cost'); $license->purchase_date = $request->input('purchase_date'); $license->purchase_order = $request->input('purchase_order'); $license->purchase_order = $request->input('purchase_order'); @@ -164,7 +166,7 @@ class LicensesController extends Controller $license->name = $request->input('name'); $license->notes = $request->input('notes'); $license->order_number = $request->input('order_number'); - $license->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost')); + $license->purchase_cost = $request->input('purchase_cost'); $license->purchase_date = $request->input('purchase_date'); $license->purchase_order = $request->input('purchase_order'); $license->reassignable = $request->input('reassignable', 0); @@ -233,16 +235,40 @@ class LicensesController extends Controller { $license = License::with('assignedusers')->find($licenseId); - if ($license) { - $this->authorize('view', $license); - - return view('licenses/view', compact('license')); + if (!$license) { + return redirect()->route('licenses.index') + ->with('error', trans('admin/licenses/message.does_not_exist')); } - return redirect()->route('licenses.index') - ->with('error', trans('admin/licenses/message.does_not_exist')); + $users_count = User::where('autoassign_licenses', '1')->count(); + $total_seats_count = $license->totalSeatsByLicenseID(); + $available_seats_count = $license->availCount()->count(); + $checkedout_seats_count = ($total_seats_count - $available_seats_count); + + \Log::debug('Total: '.$total_seats_count); + \Log::debug('Users: '.$users_count); + \Log::debug('Available: '.$available_seats_count); + \Log::debug('Checkedout: '.$checkedout_seats_count); + + + $this->authorize('view', $license); + return view('licenses.view', compact('license')) + ->with('users_count', $users_count) + ->with('total_seats_count', $total_seats_count) + ->with('available_seats_count', $available_seats_count) + ->with('checkedout_seats_count', $checkedout_seats_count); + } + + /** + * Returns a view with prepopulated data for clone + * + * @author [A. Gianotto] [] + * @param int $licenseId + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ public function getClone($licenseId = null) { if (is_null($license_to_clone = License::find($licenseId))) { diff --git a/app/Http/Controllers/ManufacturersController.php b/app/Http/Controllers/ManufacturersController.php index 1f7d2f2c07..e98644f46f 100755 --- a/app/Http/Controllers/ManufacturersController.php +++ b/app/Http/Controllers/ManufacturersController.php @@ -68,6 +68,7 @@ class ManufacturersController extends Controller $manufacturer->user_id = Auth::id(); $manufacturer->url = $request->input('url'); $manufacturer->support_url = $request->input('support_url'); + $manufacturer->warranty_lookup_url = $request->input('warranty_lookup_url'); $manufacturer->support_phone = $request->input('support_phone'); $manufacturer->support_email = $request->input('support_email'); $manufacturer = $request->handleImages($manufacturer); @@ -123,10 +124,11 @@ class ManufacturersController extends Controller return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist')); } - // Save the data + // Save the data $manufacturer->name = $request->input('name'); $manufacturer->url = $request->input('url'); $manufacturer->support_url = $request->input('support_url'); + $manufacturer->warranty_lookup_url = $request->input('warranty_lookup_url'); $manufacturer->support_phone = $request->input('support_phone'); $manufacturer->support_email = $request->input('support_email'); diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 9764df923e..4fa7990a22 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -595,23 +595,23 @@ class ReportsController extends Controller 'model.category', 'model.manufacturer', 'supplier'); if ($request->filled('by_location_id')) { - $assets->where('assets.location_id', $request->input('by_location_id')); + $assets->whereIn('assets.location_id', $request->input('by_location_id')); } if ($request->filled('by_rtd_location_id')) { - $assets->where('assets.rtd_location_id', $request->input('by_rtd_location_id')); + $assets->whereIn('assets.rtd_location_id', $request->input('by_rtd_location_id')); } if ($request->filled('by_supplier_id')) { - $assets->where('assets.supplier_id', $request->input('by_supplier_id')); + $assets->whereIn('assets.supplier_id', $request->input('by_supplier_id')); } if ($request->filled('by_company_id')) { - $assets->where('assets.company_id', $request->input('by_company_id')); + $assets->whereIn('assets.company_id', $request->input('by_company_id')); } if ($request->filled('by_model_id')) { - $assets->where('assets.model_id', $request->input('by_model_id')); + $assets->whereIn('assets.model_id', $request->input('by_model_id')); } if ($request->filled('by_category_id')) { @@ -631,7 +631,7 @@ class ReportsController extends Controller } if ($request->filled('by_status_id')) { - $assets->where('assets.status_id', $request->input('by_status_id')); + $assets->whereIn('assets.status_id', $request->input('by_status_id')); } if (($request->filled('purchase_start')) && ($request->filled('purchase_end'))) { @@ -641,6 +641,9 @@ class ReportsController extends Controller if (($request->filled('created_start')) && ($request->filled('created_end'))) { $assets->whereBetween('assets.created_at', [$request->input('created_start'), $request->input('created_end')]); } + if (($request->filled('checkout_date_start')) && ($request->filled('checkout_date_end'))) { + $assets->whereBetween('assets.last_checkout', [$request->input('checkout_date_start'), $request->input('checkout_date_end')]); + } if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) { $assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]); @@ -663,6 +666,7 @@ class ReportsController extends Controller $assets->onlyTrashed(); } + \Log::debug($assets->toSql()); $assets->orderBy('assets.id', 'ASC')->chunk(20, function ($assets) use ($handle, $customfields, $request) { $executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']; @@ -898,12 +902,8 @@ class ReportsController extends Controller public function getAssetMaintenancesReport() { $this->authorize('reports.view'); - // Grab all the improvements - $assetMaintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company') - ->orderBy('created_at', 'DESC') - ->get(); - return view('reports/asset_maintenances', compact('assetMaintenances')); + return view('reports.asset_maintenances'); } /** diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 1deaa5d104..fbb7f4e4ce 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -84,7 +84,7 @@ class SettingsController extends Controller } $pageURL = $protocol.$host.$_SERVER['REQUEST_URI']; - $start_settings['url_config'] = url('/').'/setup'; + $start_settings['url_config'] = config('app.url').'/setup'; $start_settings['url_valid'] = ($start_settings['url_config'] === $pageURL); $start_settings['real_url'] = $pageURL; $start_settings['php_version_min'] = true; @@ -961,6 +961,7 @@ class SettingsController extends Controller $setting->ldap_phone_field = $request->input('ldap_phone'); $setting->ldap_jobtitle = $request->input('ldap_jobtitle'); $setting->ldap_country = $request->input('ldap_country'); + $setting->ldap_location = $request->input('ldap_location'); $setting->ldap_dept = $request->input('ldap_dept'); $setting->ldap_client_tls_cert = $request->input('ldap_client_tls_cert'); $setting->ldap_client_tls_key = $request->input('ldap_client_tls_key'); @@ -1038,6 +1039,48 @@ class SettingsController extends Controller return $pdf_branding; } + + /** + * Show Google login settings form + * + * @author [A. Gianotto] [] + * @since [v6.1.1] + * @return View + */ + public function getGoogleLoginSettings() + { + $setting = Setting::getSettings(); + return view('settings.google', compact('setting')); + } + + /** + * ShSaveow Google login settings form + * + * @author [A. Gianotto] [] + * @since [v6.1.1] + * @return View + */ + public function postGoogleLoginSettings(Request $request) + { + if (!config('app.lock_passwords')) { + $setting = Setting::getSettings(); + + $setting->google_login = $request->input('google_login', 0); + $setting->google_client_id = $request->input('google_client_id'); + $setting->google_client_secret = $request->input('google_client_secret'); + + if ($setting->save()) { + return redirect()->route('settings.index') + ->with('success', trans('admin/settings/message.update.success')); + } + + return redirect()->back()->withInput()->withErrors($setting->getErrors()); + } + + return redirect()->back()->with('error', trans('general.feature_disabled')); + } + + /** * Show the listing of backups. * @@ -1093,7 +1136,7 @@ class SettingsController extends Controller public function postBackups() { if (! config('app.lock_passwords')) { - Artisan::call('backup:run'); + Artisan::call('snipeit:backup', ['--filename' => 'manual-backup-'.date('Y-m-d-H:i:s')]); $output = Artisan::output(); // Backup completed diff --git a/app/Http/Controllers/Users/BulkUsersController.php b/app/Http/Controllers/Users/BulkUsersController.php index 67ecae542e..a2d3d496da 100644 --- a/app/Http/Controllers/Users/BulkUsersController.php +++ b/app/Http/Controllers/Users/BulkUsersController.php @@ -113,7 +113,8 @@ class BulkUsersController extends Controller ->conditionallyAddItem('locale') ->conditionallyAddItem('remote') ->conditionallyAddItem('ldap_import') - ->conditionallyAddItem('activated'); + ->conditionallyAddItem('activated') + ->conditionallyAddItem('autoassign_licenses'); // If the manager_id is one of the users being updated, generate a warning. diff --git a/app/Http/Controllers/Users/UsersController.php b/app/Http/Controllers/Users/UsersController.php index 83f271362a..25a64e5cb2 100755 --- a/app/Http/Controllers/Users/UsersController.php +++ b/app/Http/Controllers/Users/UsersController.php @@ -74,7 +74,6 @@ class UsersController extends Controller $permissions = $this->filterDisplayable($permissions); $user = new User; - $user->activated = 1; return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions')) ->with('user', $user); @@ -121,7 +120,7 @@ class UsersController extends Controller $user->created_by = Auth::user()->id; $user->start_date = $request->input('start_date', null); $user->end_date = $request->input('end_date', null); - $user->autoassign_licenses= $request->input('autoassign_licenses', 1); + $user->autoassign_licenses = $request->input('autoassign_licenses', 0); // Strip out the superuser permission if the user isn't a superadmin $permissions_array = $request->input('permission'); @@ -211,7 +210,6 @@ class UsersController extends Controller */ public function update(SaveUserRequest $request, $id = null) { - // We need to reverse the UI specific logic for our // permissions here before we update the user. $permissions = $request->input('permissions', []); @@ -269,14 +267,15 @@ class UsersController extends Controller $user->city = $request->input('city', null); $user->state = $request->input('state', null); $user->country = $request->input('country', null); - $user->activated = $request->input('activated', 0); + // if a user is editing themselves we should always keep activated true + $user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0); $user->zip = $request->input('zip', null); $user->remote = $request->input('remote', 0); $user->vip = $request->input('vip', 0); $user->website = $request->input('website', null); $user->start_date = $request->input('start_date', null); $user->end_date = $request->input('end_date', null); - $user->autoassign_licenses = $request->input('autoassign_licenses', 1); + $user->autoassign_licenses = $request->input('autoassign_licenses', 0); // Update the location of any assets checked out to this user Asset::where('assigned_type', User::class) @@ -671,4 +670,4 @@ class UsersController extends Controller return redirect()->back()->with('error', 'User is not activated, is LDAP synced, or does not have an email address '); } -} \ No newline at end of file +} diff --git a/app/Http/Livewire/Importer.php b/app/Http/Livewire/Importer.php index 784d3b2982..94f48fbad2 100644 --- a/app/Http/Livewire/Importer.php +++ b/app/Http/Livewire/Importer.php @@ -38,6 +38,16 @@ class Importer extends Component public $field_map; // we need a separate variable for the field-mapping, because the keys in the normal array are too complicated for Livewire to understand public $file_id; // TODO: I can't figure out *why* we need this, but it really seems like we do. I can't seem to pull the id from the activeFile for some reason? + // Make these variables public - we set the properties in the constructor so we can localize them (versus the old static arrays) + public $accessories_fields; + public $assets_fields; + public $users_fields; + public $licenses_fields; + public $locations_fields; + public $consumables_fields; + public $components_fields; + public $aliases_fields; + protected $rules = [ 'files.*.file_path' => 'required|string', 'files.*.created_at' => 'required|string', @@ -57,132 +67,34 @@ class Importer extends Component return json_encode(array_filter($tmp)); } - // all of these 'statics', alas, may have to change to something else to handle translations? - // I'm not sure. Maybe I use them to 'populate' the translations? TBH, I don't know yet. - static $general = [ - 'category' => 'Category', - 'company' => 'Company', - 'email' => 'Email', - 'item_name' => 'Item Name', - 'location' => 'Location', - 'maintained' => 'Maintained', - 'manufacturer' => 'Manufacturer', - 'notes' => 'Notes', - 'order_number' => 'Order Number', - 'purchase_cost' => 'Purchase Cost', - 'purchase_date' => 'Purchase Date', - 'quantity' => 'Quantity', - 'requestable' => 'Requestable', - 'serial' => 'Serial Number', - 'supplier' => 'Supplier', - 'username' => 'Username', - 'department' => 'Department', - ]; - static $accessories = [ - 'model_number' => 'Model Number', - ]; - - static $assets = [ - 'asset_tag' => 'Asset Tag', - 'asset_model' => 'Model Name', - 'byod' => 'BYOD', - 'checkout_class' => 'Checkout Type', - 'checkout_location' => 'Checkout Location', - 'image' => 'Image Filename', - 'model_number' => 'Model Number', - 'full_name' => 'Full Name', - 'status' => 'Status', - 'warranty_months' => 'Warranty Months', - ]; - - static $consumables = [ - 'item_no' => "Item Number", - 'model_number' => "Model Number", - 'min_amt' => "Minimum Quantity", - ]; - - static $licenses = [ - 'asset_tag' => 'Assigned To Asset', - 'expiration_date' => 'Expiration Date', - 'full_name' => 'Full Name', - 'license_email' => 'Licensed To Email', - 'license_name' => 'Licensed To Name', - 'purchase_order' => 'Purchase Order', - 'reassignable' => 'Reassignable', - 'seats' => 'Seats', - ]; - - static $users = [ - 'employee_num' => 'Employee Number', - 'first_name' => 'First Name', - 'jobtitle' => 'Job Title', - 'last_name' => 'Last Name', - 'phone_number' => 'Phone Number', - 'manager_first_name' => 'Manager First Name', - 'manager_last_name' => 'Manager Last Name', - 'activated' => 'Activated', - 'address' => 'Address', - 'city' => 'City', - 'state' => 'State', - 'country' => 'Country', - 'vip' => 'VIP' - ]; - - //array of "real fieldnames" to a list of aliases for that field - static $aliases = [ - 'model_number' => - [ - 'model', - 'model no', - 'model no.', - 'model number', - 'model num', - 'model num.' - ], - 'warranty_months' => - [ - 'Warranty', - 'Warranty Months' - ], - 'qty' => - [ - 'QTY', - 'Quantity' - ], - 'min_amt' => - [ - 'Min Amount', - 'Min QTY' - ], - 'next_audit_date' => - [ - 'Next Audit', - ], - - - ]; private function getColumns($type) { switch ($type) { case 'asset': - $results = self::$general + self::$assets; + $results = $this->assets_fields; break; case 'accessory': - $results = self::$general + self::$accessories; + $results = $this->accessories_fields; break; case 'consumable': - $results = self::$general + self::$consumables; + $results = $this->consumables_fields; + break; + case 'component': + $results = $this->components_fields; break; case 'license': - $results = self::$general + self::$licenses; + $results = $this->licenses_fields; break; case 'user': - $results = self::$general + self::$users; + $results = $this->users_fields; + break; + case 'location': + $results = $this->locations_fields; break; default: - $results = self::$general; + $results = []; } asort($results, SORT_FLAG_CASE | SORT_STRING); if ($type == "asset") { @@ -211,7 +123,7 @@ class Importer extends Component continue; } else { //no, this key is *INVALID* for this import type. Better set it to null - // and we'll hope that the aliases or something else picks it up. + // and we'll hope that the $aliases_fields or something else picks it up. $this->field_map[$i] = null; // fingers crossed! But it's not likely, tbh. } // TODO - strictly speaking, this isn't necessary here I don't think. } @@ -222,8 +134,8 @@ class Importer extends Component continue 2; //don't bother with the alias check, go to the next header } } - // if you got here, we didn't find a match. Try the aliases - foreach (self::$aliases as $key => $alias_values) { + // if you got here, we didn't find a match. Try the $aliases_fields + foreach ($this->aliases_fields as $key => $alias_values) { foreach ($alias_values as $alias_value) { if (strcasecmp($alias_value, $header) === 0) { // aLsO CaSe-INSENSitiVE! // Make *absolutely* sure that this key actually _exists_ in this import type - @@ -252,7 +164,6 @@ class Importer extends Component $this->authorize('import'); $this->progress = -1; // '-1' means 'don't show the progressbar' $this->progress_bar_class = 'progress-bar-warning'; - \Log::debug("Hey, we are calling MOUNT (in the importer-file) !!!!!!!!"); //fcuk $this->importTypes = [ 'asset' => trans('general.assets'), 'accessory' => trans('general.accessories'), @@ -260,6 +171,303 @@ class Importer extends Component 'component' => trans('general.components'), 'license' => trans('general.licenses'), 'user' => trans('general.users'), + 'location' => trans('general.locations'), + ]; + + /** + * These are the item-type specific columns + */ + $this->accessories_fields = [ + 'company' => trans('general.company'), + 'location' => trans('general.location'), + 'quantity' => trans('general.qty'), + 'item_name' => trans('general.item_name_var', ['item' => trans('general.accessory')]), + 'model_number' => trans('general.model_no'), + 'notes' => trans('general.notes'), + 'category' => trans('general.category'), + 'supplier' => trans('general.supplier'), + 'min_amt' => trans('mail.min_QTY'), + 'purchase_cost' => trans('general.purchase_cost'), + 'purchase_date' => trans('general.purchase_date'), + 'manufacturer' => trans('general.manufacturer'), + 'order_number' => trans('general.order_number'), + ]; + + $this->assets_fields = [ + 'company' => trans('general.company'), + 'location' => trans('general.location'), + 'item_name' => trans('general.item_name_var', ['item' => trans('general.asset')]), + 'asset_tag' => trans('general.asset_tag'), + 'asset_model' => trans('general.model_name'), + 'byod' => trans('general.byod'), + 'model_number' => trans('general.model_no'), + 'status' => trans('general.status'), + 'warranty_months' => trans('admin/hardware/form.warranty'), + 'category' => trans('general.category'), + 'requestable' => trans('admin/hardware/general.requestable'), + 'serial' => trans('general.serial_number'), + 'supplier' => trans('general.supplier'), + 'purchase_cost' => trans('general.purchase_cost'), + 'purchase_date' => trans('general.purchase_date'), + 'purchase_order' => trans('admin/licenses/form.purchase_order'), + 'asset_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/general.asset')]), + 'model_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]), + 'manufacturer' => trans('general.manufacturer'), + 'order_number' => trans('general.order_number'), + 'notes' => trans('general.notes'), + 'image' => trans('general.importer.image_filename'), + /** + * Checkout fields: + * Assets can be checked out to other assets, people, or locations, but we currently + * only support checkout to people and locations in the importer + **/ + 'checkout_class' => trans('general.importer.checkout_type'), + 'first_name' => trans('general.importer.checked_out_to_first_name'), + 'last_name' => trans('general.importer.checked_out_to_last_name'), + 'full_name' => trans('general.importer.checked_out_to_fullname'), + 'email' => trans('general.importer.checked_out_to_email'), + 'username' => trans('general.importer.checked_out_to_username'), + 'checkout_location' => trans('general.importer.checkout_location'), + ]; + + $this->consumables_fields = [ + 'company' => trans('general.company'), + 'location' => trans('general.location'), + 'quantity' => trans('general.qty'), + 'item_name' => trans('general.item_name_var', ['item' => trans('general.consumable')]), + 'model_number' => trans('general.model_no'), + 'notes' => trans('general.notes'), + 'min_amt' => trans('mail.min_QTY'), + 'category' => trans('general.category'), + 'purchase_cost' => trans('general.purchase_cost'), + 'purchase_date' => trans('general.purchase_date'), + 'checkout_class' => trans('general.importer.checkout_type'), + 'supplier' => trans('general.supplier'), + 'manufacturer' => trans('general.manufacturer'), + 'order_number' => trans('general.order_number'), + 'item_no' => trans('admin/consumables/general.item_no'), + ]; + + $this->components_fields = [ + 'company' => trans('general.company'), + 'location' => trans('general.location'), + 'quantity' => trans('general.qty'), + 'item_name' => trans('general.item_name_var', ['item' => trans('general.component')]), + 'model_number' => trans('general.model_no'), + 'notes' => trans('general.notes'), + 'category' => trans('general.category'), + 'supplier' => trans('general.supplier'), + 'min_amt' => trans('mail.min_QTY'), + 'purchase_cost' => trans('general.purchase_cost'), + 'purchase_date' => trans('general.purchase_date'), + 'manufacturer' => trans('general.manufacturer'), + 'order_number' => trans('general.order_number'), + 'serial' => trans('general.serial_number'), + ]; + + $this->licenses_fields = [ + 'company' => trans('general.company'), + 'location' => trans('general.location'), + 'item_name' => trans('general.item_name_var', ['item' => trans('general.license')]), + 'asset_tag' => trans('general.importer.checked_out_to_tag'), + 'expiration_date' => trans('admin/licenses/form.expiration'), + 'full_name' => trans('general.importer.checked_out_to_fullname'), + 'license_email' => trans('admin/licenses/form.to_email'), + 'license_name' => trans('admin/licenses/form.to_name'), + 'purchase_order' => trans('admin/licenses/form.purchase_order'), + 'reassignable' => trans('admin/licenses/form.reassignable'), + 'seats' => trans('admin/licenses/form.seats'), + 'notes' => trans('general.notes'), + 'category' => trans('general.category'), + 'supplier' => trans('general.supplier'), + 'purchase_cost' => trans('general.purchase_cost'), + 'purchase_date' => trans('general.purchase_date'), + 'maintained' => trans('admin/licenses/form.maintained'), + 'checkout_class' => trans('general.importer.checkout_type'), + 'serial' => trans('general.license_serial'), + ]; + + $this->users_fields = [ + 'id' => trans('general.id'), + 'company' => trans('general.company'), + 'location' => trans('general.location'), + 'department' => trans('general.department'), + 'first_name' => trans('general.first_name'), + 'last_name' => trans('general.last_name'), + 'notes' => trans('general.notes'), + 'username' => trans('admin/users/table.username'), + 'jobtitle' => trans('admin/users/table.title'), + 'phone_number' => trans('admin/users/table.phone'), + 'manager_first_name' => trans('general.importer.manager_first_name'), + 'manager_last_name' => trans('general.importer.manager_last_name'), + 'activated' => trans('general.activated'), + 'address' => trans('general.address'), + 'city' => trans('general.city'), + 'state' => trans('general.state'), + 'country' => trans('general.country'), + 'zip' => trans('general.zip'), + 'vip' => trans('general.importer.vip'), + 'remote' => trans('admin/users/general.remote'), + 'email' => trans('admin/users/table.email'), + 'website' => trans('general.website'), + 'avatar' => trans('general.image'), + 'gravatar' => trans('general.importer.gravatar'), + 'start_date' => trans('general.start_date'), + 'end_date' => trans('general.end_date'), + 'employee_number' => trans('general.employee_number'), + ]; + + $this->locations_fields = [ + 'name' => trans('general.item_name_var', ['item' => trans('general.location')]), + 'address' => trans('general.address'), + 'address2' => trans('general.importer.address2'), + 'city' => trans('general.city'), + 'state' => trans('general.state'), + 'country' => trans('general.country'), + 'zip' => trans('general.zip'), + 'currency' => trans('general.importer.currency'), + 'ldap_ou' => trans('admin/locations/table.ldap_ou'), + 'manager_username' => trans('general.importer.manager_username'), + 'manager' => trans('general.importer.manager_full_name'), + 'parent_location' => trans('admin/locations/table.parent'), + ]; + + // "real fieldnames" to a list of aliases for that field + $this->aliases_fields = [ + 'item_name' => + [ + 'item name', + 'asset name', + 'accessory name', + 'user name', + 'consumable name', + 'component name', + 'name', + ], + 'item_no' => [ + 'item number', + 'item no.', + 'item #', + ], + 'asset_model' => + [ + 'model name', + 'model', + ], + 'gravatar' => + [ + 'gravatar', + ], + 'currency' => + [ + '$', + ], + 'jobtitle' => + [ + 'job title for user', + 'job title', + ], + 'username' => + [ + 'user name', + 'username', + trans('general.importer.checked_out_to_username'), + ], + 'first_name' => + [ + 'first name', + trans('general.importer.checked_out_to_first_name'), + ], + 'last_name' => + [ + 'last name', + 'lastname', + trans('general.importer.checked_out_to_last_name'), + ], + 'email' => + [ + 'email', + 'e-mail', + trans('general.importer.checked_out_to_email'), + ], + 'phone_number' => + [ + 'phone', + 'phone number', + 'phone num', + 'telephone number', + 'telephone', + 'tel.', + ], + 'serial' => + [ + 'serial number', + 'serial no.', + 'serial no', + 'product key', + 'key', + ], + 'model_number' => + [ + 'model', + 'model no', + 'model no.', + 'model number', + 'model num', + 'model num.' + ], + 'warranty_months' => + [ + 'Warranty', + 'Warranty Months' + ], + 'qty' => + [ + 'QTY', + 'Quantity' + ], + 'zip' => + [ + 'Postal Code', + 'Post Code', + 'Zip Code' + ], + 'min_amt' => + [ + 'Min Amount', + 'Minimum Amount', + 'Min Quantity', + 'Minimum Quantity', + ], + 'next_audit_date' => + [ + 'Next Audit', + ], + 'address2' => + [ + 'Address 2', + 'Address2', + ], + 'ldap_ou' => + [ + 'LDAP OU', + 'OU', + ], + 'parent_location' => + [ + 'Parent', + 'Parent Location', + ], + 'manager' => + [ + 'Managed By', + 'Manager Name', + 'Manager Full Name', + ], + 'manager_username' => + [ + 'Manager Username', + ], ]; $this->columnOptions[''] = $this->getColumns(''); //blank mode? I don't know what this is supposed to mean @@ -273,8 +481,7 @@ class Importer extends Component public function selectFile($id) { - \Log::debug("TOGGLE EVENT FIRED!"); - \Log::debug("The ID we are trying to find is AS FOLLOWS: ".$id); + $this->activeFile = Import::find($id); $this->field_map = null; foreach($this->activeFile->header_row as $element) { @@ -284,11 +491,9 @@ class Importer extends Component $this->field_map[] = null; // re-inject the 'nulls' if a file was imported with some 'Do Not Import' settings } } - //$this->field_map = $this->activeFile->field_map ? array_values($this->activeFile->field_map) : []; // this is wrong $this->file_id = $id; $this->import_errors = null; $this->statusText = null; - \Log::debug("The import type we are about to try and load up is gonna be this: ".$this->activeFile->import_type); } diff --git a/app/Http/Middleware/CheckForSetup.php b/app/Http/Middleware/CheckForSetup.php index b4beb70588..4e399ffcff 100644 --- a/app/Http/Middleware/CheckForSetup.php +++ b/app/Http/Middleware/CheckForSetup.php @@ -20,13 +20,13 @@ class CheckForSetup if (Setting::setupCompleted()) { if ($request->is('setup*')) { - return redirect(url('/')); + return redirect(config('app.url')); } else { return $next($request); } } else { if (! ($request->is('setup*')) && ! ($request->is('.env')) && ! ($request->is('health'))) { - return redirect(url('/').'/setup'); + return redirect(config('app.url').'/setup'); } return $next($request); diff --git a/app/Http/Requests/CustomFieldRequest.php b/app/Http/Requests/CustomFieldRequest.php index 7c6ba1e97e..0c2ec0ae60 100644 --- a/app/Http/Requests/CustomFieldRequest.php +++ b/app/Http/Requests/CustomFieldRequest.php @@ -26,6 +26,8 @@ class CustomFieldRequest extends FormRequest { $rules = []; + $rules['associate_fieldsets.*'] = 'nullable|integer|exists:custom_fieldsets,id'; + switch ($this->method()) { // Brand new @@ -54,4 +56,11 @@ class CustomFieldRequest extends FormRequest return $rules; } + + public function messages() + { + return [ + 'associate_fieldsets.*.exists' => trans('admin/custom_fields/message/does_not_exist'), + ]; + } } diff --git a/app/Http/Transformers/AssetMaintenancesTransformer.php b/app/Http/Transformers/AssetMaintenancesTransformer.php index 7766d7cd3a..10b05b33af 100644 --- a/app/Http/Transformers/AssetMaintenancesTransformer.php +++ b/app/Http/Transformers/AssetMaintenancesTransformer.php @@ -59,6 +59,7 @@ class AssetMaintenancesTransformer 'user_id' => ($assetmaintenance->admin) ? ['id' => $assetmaintenance->admin->id, 'name'=> e($assetmaintenance->admin->getFullNameAttribute())] : null, 'created_at' => Helper::getFormattedDateObject($assetmaintenance->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($assetmaintenance->updated_at, 'datetime'), + 'is_warranty'=> $assetmaintenance->is_warranty, ]; diff --git a/app/Http/Transformers/CategoriesTransformer.php b/app/Http/Transformers/CategoriesTransformer.php index faf05f7f4b..d5e1ceb51b 100644 --- a/app/Http/Transformers/CategoriesTransformer.php +++ b/app/Http/Transformers/CategoriesTransformer.php @@ -38,6 +38,9 @@ class CategoriesTransformer case 'component': $category->item_count = $category->components_count; break; + case 'license': + $category->item_count = $category->licenses_count; + break; default: $category->item_count = 0; } diff --git a/app/Http/Transformers/ComponentsTransformer.php b/app/Http/Transformers/ComponentsTransformer.php index 1610c3da5b..97677af283 100644 --- a/app/Http/Transformers/ComponentsTransformer.php +++ b/app/Http/Transformers/ComponentsTransformer.php @@ -37,6 +37,7 @@ class ComponentsTransformer 'id' => (int) $component->category->id, 'name' => e($component->category->name), ] : null, + 'supplier' => ($component->supplier) ? ['id' => $component->supplier->id, 'name'=> e($component->supplier->name)] : null, 'order_number' => e($component->order_number), 'purchase_date' => Helper::getFormattedDateObject($component->purchase_date, 'date'), 'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost), diff --git a/app/Http/Transformers/ConsumablesTransformer.php b/app/Http/Transformers/ConsumablesTransformer.php index 5079b28961..b92f843b7f 100644 --- a/app/Http/Transformers/ConsumablesTransformer.php +++ b/app/Http/Transformers/ConsumablesTransformer.php @@ -31,6 +31,7 @@ class ConsumablesTransformer 'item_no' => e($consumable->item_no), 'location' => ($consumable->location) ? ['id' => (int) $consumable->location->id, 'name' => e($consumable->location->name)] : null, 'manufacturer' => ($consumable->manufacturer) ? ['id' => (int) $consumable->manufacturer->id, 'name' => e($consumable->manufacturer->name)] : null, + 'supplier' => ($consumable->supplier) ? ['id' => $consumable->supplier->id, 'name'=> e($consumable->supplier->name)] : null, 'min_amt' => (int) $consumable->min_amt, 'model_number' => ($consumable->model_number != '') ? e($consumable->model_number) : null, 'remaining' => $consumable->numRemaining(), diff --git a/app/Http/Transformers/CustomFieldsTransformer.php b/app/Http/Transformers/CustomFieldsTransformer.php index 9ada5183ab..db467be0bd 100644 --- a/app/Http/Transformers/CustomFieldsTransformer.php +++ b/app/Http/Transformers/CustomFieldsTransformer.php @@ -48,6 +48,7 @@ class CustomFieldsTransformer 'type' => e($field->element), 'required' => (($field->pivot) && ($field->pivot->required=='1')) ? true : false, 'display_in_user_view' => ($field->display_in_user_view =='1') ? true : false, + 'auto_add_to_fieldsets' => ($field->auto_add_to_fieldsets == '1') ? true : false, 'created_at' => Helper::getFormattedDateObject($field->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($field->updated_at, 'datetime'), ]; diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php index 3c389a1b18..8df6b89f19 100644 --- a/app/Http/Transformers/LicensesTransformer.php +++ b/app/Http/Transformers/LicensesTransformer.php @@ -56,7 +56,7 @@ class LicensesTransformer 'checkin' => Gate::allows('checkin', License::class), 'clone' => Gate::allows('create', License::class), 'update' => Gate::allows('update', License::class), - 'delete' => Gate::allows('delete', License::class), + 'delete' => (Gate::allows('delete', License::class) && ($license->seats == $license->availCount()->count())) ? true : false, ]; $array += $permissions_array; diff --git a/app/Http/Transformers/ManufacturersTransformer.php b/app/Http/Transformers/ManufacturersTransformer.php index bbcbda12b4..9c84fd50fe 100644 --- a/app/Http/Transformers/ManufacturersTransformer.php +++ b/app/Http/Transformers/ManufacturersTransformer.php @@ -29,6 +29,7 @@ class ManufacturersTransformer 'url' => e($manufacturer->url), 'image' => ($manufacturer->image) ? Storage::disk('public')->url('manufacturers/'.e($manufacturer->image)) : null, 'support_url' => e($manufacturer->support_url), + 'warranty_lookup_url' => e($manufacturer->warranty_lookup_url), 'support_phone' => e($manufacturer->support_phone), 'support_email' => e($manufacturer->support_email), 'assets_count' => (int) $manufacturer->assets_count, diff --git a/app/Http/Transformers/SuppliersTransformer.php b/app/Http/Transformers/SuppliersTransformer.php index 71307a750d..e7546bfd15 100644 --- a/app/Http/Transformers/SuppliersTransformer.php +++ b/app/Http/Transformers/SuppliersTransformer.php @@ -41,6 +41,8 @@ class SuppliersTransformer 'assets_count' => (int) $supplier->assets_count, 'accessories_count' => (int) $supplier->accessories_count, 'licenses_count' => (int) $supplier->licenses_count, + 'consumables_count' => (int) $supplier->consumables_count, + 'components_count' => (int) $supplier->components_count, 'notes' => ($supplier->notes) ? e($supplier->notes) : null, 'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'), diff --git a/app/Http/Transformers/UsersTransformer.php b/app/Http/Transformers/UsersTransformer.php index 9447d65455..867a884619 100644 --- a/app/Http/Transformers/UsersTransformer.php +++ b/app/Http/Transformers/UsersTransformer.php @@ -56,6 +56,7 @@ class UsersTransformer 'notes'=> e($user->notes), 'permissions' => $user->decodePermissions(), 'activated' => ($user->activated == '1') ? true : false, + 'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false, 'ldap_import' => ($user->ldap_import == '1') ? true : false, 'two_factor_enrolled' => ($user->two_factor_active_and_enrolled()) ? true : false, 'two_factor_optin' => ($user->two_factor_active()) ? true : false, diff --git a/app/Importer/AssetImporter.php b/app/Importer/AssetImporter.php index bf47c73608..be19455618 100644 --- a/app/Importer/AssetImporter.php +++ b/app/Importer/AssetImporter.php @@ -12,7 +12,10 @@ class AssetImporter extends ItemImporter public function __construct($filename) { parent::__construct($filename); - $this->defaultStatusLabelId = Statuslabel::first()->id; + + if (!is_null(Statuslabel::first())) { + $this->defaultStatusLabelId = Statuslabel::first()->id; + } } protected function handle($row) @@ -60,7 +63,7 @@ class AssetImporter extends ItemImporter $asset_tag = Asset::autoincrement_asset(); } - $asset = Asset::where(['asset_tag'=> $asset_tag])->first(); + $asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first(); if ($asset) { if (! $this->updating) { $this->log('A matching Asset '.$asset_tag.' already exists'); @@ -114,6 +117,11 @@ class AssetImporter extends ItemImporter $item['next_audit_date'] = $this->item['next_audit_date']; } + $item['asset_eol_date'] = null; + if (isset($this->item['asset_eol_date'])) { + $item['asset_eol_date'] = $this->item['asset_eol_date']; + } + if ($editingAsset) { $asset->update($item); } else { @@ -127,10 +135,9 @@ class AssetImporter extends ItemImporter } } - //FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything. - // $asset->unsetEventDispatcher(); + if ($asset->save()) { - $asset->logCreate('Imported using csv importer'); + $asset->logCreate(trans('general.importer.import_note')); $this->log('Asset '.$this->item['name'].' with serial number '.$this->item['serial'].' was created'); // If we have a target to checkout to, lets do so. diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php index 1ee90b399c..4c60f0beed 100644 --- a/app/Importer/Importer.php +++ b/app/Importer/Importer.php @@ -27,57 +27,14 @@ abstract class Importer protected $updating; /** * Default Map of item fields->csv names + * + * This has been moved into Livewire/Importer.php to be more granular. + * @todo - remove references to this property since we don't use it anymore. + * * @var array */ private $defaultFieldMap = [ - 'asset_tag' => 'asset tag', - 'activated' => 'activated', - 'category' => 'category', - 'checkout_class' => 'checkout type', // Supports Location or User for assets. Using checkout_class instead of checkout_type because type exists on asset already. - 'checkout_location' => 'checkout location', - 'company' => 'company', - 'item_name' => 'item name', - 'item_number' => 'item number', - 'image' => 'image', - 'expiration_date' => 'expiration date', - 'location' => 'location', - 'notes' => 'notes', - 'license_email' => 'licensed to email', - 'license_name' => 'licensed to name', - 'maintained' => 'maintained', - 'manufacturer' => 'manufacturer', - 'asset_model' => 'model name', - 'model_number' => 'model number', - 'order_number' => 'order number', - 'purchase_cost' => 'purchase cost', - 'purchase_date' => 'purchase date', - 'purchase_order' => 'purchase order', - 'qty' => 'quantity', - 'reassignable' => 'reassignable', - 'requestable' => 'requestable', - 'seats' => 'seats', - 'serial' => 'serial number', - 'status' => 'status', - 'supplier' => 'supplier', - 'termination_date' => 'termination date', - 'warranty_months' => 'warranty', - 'full_name' => 'full name', - 'email' => 'email', - 'username' => 'username', - 'address' => 'address', - 'city' => 'city', - 'state' => 'state', - 'country' => 'country', - 'jobtitle' => 'job title', - 'employee_num' => 'employee number', - 'phone_number' => 'phone number', - 'first_name' => 'first name', - 'last_name' => 'last name', - 'department' => 'department', - 'manager_first_name' => 'manager first name', - 'manager_last_name' => 'manager last name', - 'min_amt' => 'minimum quantity', - 'remote' => 'remote', + ]; /** * Map of item fields->csv names @@ -119,7 +76,7 @@ abstract class Importer } else { $this->csv = Reader::createFromString($file); } - $this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 20); + $this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40); } // Cached Values for import lookups @@ -198,11 +155,11 @@ abstract class Importer $val = $default; $key = $this->lookupCustomKey($key); - $this->log("Custom Key: ${key}"); + // $this->log("Custom Key: ${key}"); if (array_key_exists($key, $array)) { $val = Encoding::toUTF8(trim($array[$key])); } - $this->log("${key}: ${val}"); + //$this->log("${key}: ${val}"); return $val; } @@ -280,10 +237,13 @@ abstract class Importer * @return User Model w/ matching name * @internal param array $user_array User details parsed from csv */ - protected function createOrFetchUser($row) + protected function createOrFetchUser($row, $type = 'user') { + $user_array = [ 'full_name' => $this->findCsvMatch($row, 'full_name'), + 'first_name' => $this->findCsvMatch($row, 'first_name'), + 'last_name' => $this->findCsvMatch($row, 'last_name'), 'email' => $this->findCsvMatch($row, 'email'), 'manager_id'=> '', 'department_id' => '', @@ -292,48 +252,53 @@ abstract class Importer 'remote' => $this->fetchHumanBoolean(($this->findCsvMatch($row, 'remote'))), ]; - // Maybe we're lucky and the user already exists. - if ($user = User::where('username', $user_array['username'])->first()) { - $this->log('User '.$user_array['username'].' already exists'); - - return $user; + if ($type == 'manager') { + $user_array['full_name'] = $this->findCsvMatch($row, 'manager'); + $user_array['username'] = $this->findCsvMatch($row, 'manager_username'); } - // If the full name is empty, bail out--we need this to extract first name (at the very least) - if (empty($user_array['full_name'])) { - $this->log('Insufficient user data provided (Full name is required)- skipping user creation, just adding asset'); + // Maybe we're lucky and the username was passed and it already exists. + if (!empty($user_array['username'])) { + if ($user = User::where('username', $user_array['username'])->first()) { + $this->log('User '.$user_array['username'].' already exists'); + return $user; + } + } + + // If the full name and username is empty, bail out--we need this to extract first name (at the very least) + if ((empty($user_array['username'])) && (empty($user_array['full_name'])) && (empty($user_array['first_name']))) { + $this->log('Insufficient user data provided (Full name, first name or username is required) - skipping user creation.'); + \Log::debug('User array: '); + \Log::debug(print_r($user_array, true)); + \Log::debug(print_r($row, true)); return false; } - // Is the user actually an ID? - if ($user = $this->findUserByNumber($user_array['full_name'])) { - return $user; - } - $this->log('User does not appear to be an id with number: '.$user_array['full_name'].'. Continuing through our processes'); // Populate email if it does not exist. if (empty($user_array['email'])) { $user_array['email'] = User::generateEmailFromFullName($user_array['full_name']); } - $user_formatted_array = User::generateFormattedNameFromFullName($user_array['full_name'], Setting::getSettings()->username_format); - $user_array['first_name'] = $user_formatted_array['first_name']; - $user_array['last_name'] = $user_formatted_array['last_name']; + if (empty($user_array['first_name'])) { + // Get some fields for first name and last name based off of full name + $user_formatted_array = User::generateFormattedNameFromFullName($user_array['full_name'], Setting::getSettings()->username_format); + $user_array['first_name'] = $user_formatted_array['first_name']; + $user_array['last_name'] = $user_formatted_array['last_name']; + } if (empty($user_array['username'])) { $user_array['username'] = $user_formatted_array['username']; if ($this->usernameFormat == 'email') { $user_array['username'] = $user_array['email']; } - } - // Does this ever actually fire?? - // Check for a matching user after trying to guess username. - if ($user = User::where('username', $user_array['username'])->first()) { - $this->log('User '.$user_array['username'].' already exists'); - - return $user; + // Check for a matching username one more time after trying to guess username. + if ($user = User::where('username', $user_array['username'])->first()) { + $this->log('User '.$user_array['username'].' already exists'); + return $user; + } } // If at this point we have not found a username or first name, bail out in shame. @@ -341,7 +306,7 @@ abstract class Importer return false; } - // No Luck, let's create one. + // No luck finding a user on username or first name, let's create one. $user = new User; $user->first_name = $user_array['first_name']; $user->last_name = $user_array['last_name']; @@ -356,9 +321,9 @@ abstract class Importer if ($user->save()) { $this->log('User '.$user_array['username'].' created'); - return $user; } + $this->logError($user, 'User "'.$user_array['username'].'" was not able to be created.'); return false; diff --git a/app/Importer/ItemImporter.php b/app/Importer/ItemImporter.php index 7848ec6b85..9e80cb9572 100644 --- a/app/Importer/ItemImporter.php +++ b/app/Importer/ItemImporter.php @@ -60,8 +60,8 @@ class ItemImporter extends Importer $this->item['department_id'] = $this->createOrFetchDepartment($item_department); } - $item_manager_first_name = $this->findCsvMatch($row, 'manage_first_name'); - $item_manager_last_name = $this->findCsvMatch($row, 'manage_last_name'); + $item_manager_first_name = $this->findCsvMatch($row, 'manager_first_name'); + $item_manager_last_name = $this->findCsvMatch($row, 'manager_last_name'); if ($this->shouldUpdateField($item_manager_first_name)) { $this->item['manager_id'] = $this->fetchManager($item_manager_first_name, $item_manager_last_name); @@ -87,6 +87,11 @@ class ItemImporter extends Importer $this->item['next_audit_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'next_audit_date'))); } + $this->item['asset_eol_date'] = null; + if ($this->findCsvMatch($row, 'asset_eol_date') != '') { + $this->item['asset_eol_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'asset_eol_date'))); + } + $this->item['qty'] = $this->findCsvMatch($row, 'quantity'); $this->item['requestable'] = $this->findCsvMatch($row, 'requestable'); $this->item['user_id'] = $this->user_id; @@ -103,13 +108,13 @@ class ItemImporter extends Importer /** * Parse row to determine what (if anything) we should checkout to. * @param array $row CSV Row being parsed - * @return SnipeModel Model to be checked out to + * @return ?SnipeModel Model to be checked out to */ protected function determineCheckout($row) { - // We only support checkout-to-location for asset, so short circuit otherwise. - if (get_class($this) != AssetImporter::class) { - return $this->createOrFetchUser($row); + // Locations don't get checked out to anyone/anything + if (get_class($this) == LocationImporter::class) { + return; } if (strtolower($this->item['checkout_class']) === 'location' && $this->findCsvMatch($row, 'checkout_location') != null ) { diff --git a/app/Importer/LocationImporter.php b/app/Importer/LocationImporter.php new file mode 100644 index 0000000000..25140abe00 --- /dev/null +++ b/app/Importer/LocationImporter.php @@ -0,0 +1,102 @@ +createLocationIfNotExists($row); + } + + /** + * Create a location if a duplicate does not exist. + * @todo Investigate how this should interact with Importer::createLocationIfNotExists + * + * @author A. Gianotto + * @since 6.1.0 + * @param array $row + */ + public function createLocationIfNotExists(array $row) + { + + $editingLocation = false; + $location = Location::where('name', '=', $this->findCsvMatch($row, 'name'))->first(); + + if ($location) { + if (! $this->updating) { + $this->log('A matching Location '.$this->item['name'].' already exists'); + return; + } + + $this->log('Updating Location'); + $editingLocation = true; + } else { + $this->log('No Matching Location, Create a new one'); + $location = new Location; + } + + // Pull the records from the CSV to determine their values + $this->item['name'] = $this->findCsvMatch($row, 'name'); + $this->item['address'] = $this->findCsvMatch($row, 'address'); + $this->item['address2'] = $this->findCsvMatch($row, 'address2'); + $this->item['city'] = $this->findCsvMatch($row, 'city'); + $this->item['state'] = $this->findCsvMatch($row, 'state'); + $this->item['country'] = $this->findCsvMatch($row, 'country'); + $this->item['zip'] = $this->findCsvMatch($row, 'zip'); + $this->item['currency'] = $this->findCsvMatch($row, 'currency'); + $this->item['ldap_ou'] = $this->findCsvMatch($row, 'ldap_ou'); + $this->item['manager'] = $this->findCsvMatch($row, 'manager'); + $this->item['manager_username'] = $this->findCsvMatch($row, 'manager_username'); + $this->item['user_id'] = \Auth::user()->id; + + if ($this->findCsvMatch($row, 'parent_location')) { + $this->item['parent_id'] = $this->createOrFetchLocation($this->findCsvMatch($row, 'parent_location')); + } + + if (!empty($this->item['manager'])) { + if ($manager = $this->createOrFetchUser($row, 'manager')) { + $this->item['manager_id'] = $manager->id; + } + } + + \Log::debug('Item array is: '); + \Log::debug(print_r($this->item, true)); + + + if ($editingLocation) { + \Log::debug('Updating existing location'); + $location->update($this->sanitizeItemForUpdating($location)); + } else { + \Log::debug('Creating location'); + $location->fill($this->sanitizeItemForStoring($location)); + } + + if ($location->save()) { + $this->log('Location '.$location->name.' created or updated from CSV import'); + return $location; + + } else { + \Log::debug($location->getErrors()); + return $location->errors; + } + + + } +} \ No newline at end of file diff --git a/app/Importer/UserImporter.php b/app/Importer/UserImporter.php index bcbc632eb3..e13d4c4cce 100644 --- a/app/Importer/UserImporter.php +++ b/app/Importer/UserImporter.php @@ -42,23 +42,29 @@ class UserImporter extends ItemImporter public function createUserIfNotExists(array $row) { // Pull the records from the CSV to determine their values + $this->item['id'] = $this->findCsvMatch($row, 'id'); $this->item['username'] = $this->findCsvMatch($row, 'username'); $this->item['first_name'] = $this->findCsvMatch($row, 'first_name'); $this->item['last_name'] = $this->findCsvMatch($row, 'last_name'); $this->item['email'] = $this->findCsvMatch($row, 'email'); + $this->item['gravatar'] = $this->findCsvMatch($row, 'gravatar'); $this->item['phone'] = $this->findCsvMatch($row, 'phone_number'); + $this->item['website'] = $this->findCsvMatch($row, 'website'); $this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle'); $this->item['address'] = $this->findCsvMatch($row, 'address'); $this->item['city'] = $this->findCsvMatch($row, 'city'); $this->item['state'] = $this->findCsvMatch($row, 'state'); $this->item['country'] = $this->findCsvMatch($row, 'country'); + $this->item['start_date'] = $this->findCsvMatch($row, 'start_date'); + $this->item['end_date'] = $this->findCsvMatch($row, 'end_date'); $this->item['zip'] = $this->findCsvMatch($row, 'zip'); $this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0; $this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num'); $this->item['department_id'] = $this->createOrFetchDepartment($this->findCsvMatch($row, 'department')); $this->item['manager_id'] = $this->fetchManager($this->findCsvMatch($row, 'manager_first_name'), $this->findCsvMatch($row, 'manager_last_name')); $this->item['remote'] =($this->fetchHumanBoolean($this->findCsvMatch($row, 'remote')) ==1 ) ? '1' : 0; - $this->item['vip'] =($this->fetchHumanBoolean($this->findCsvMatch($row, 'vip')) ==1 ) ? '1' : 0; + $this->item['vip'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'vip')) ==1 ) ? '1' : 0; + $this->item['autoassign_licenses'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'autoassign_licenses')) ==1 ) ? '1' : 0; $user_department = $this->findCsvMatch($row, 'department'); @@ -71,13 +77,18 @@ class UserImporter extends ItemImporter $user_formatted_array = User::generateFormattedNameFromFullName($user_full_name, Setting::getSettings()->username_format); $this->item['username'] = $user_formatted_array['username']; } - - $user = User::where('username', $this->item['username'])->first(); - if ($user) { - if (! $this->updating) { - $this->log('A matching User '.$this->item['name'].' already exists. '); - \Log::debug('A matching User '.$this->item['name'].' already exists. '); + // Check if a numeric ID was passed. If it does, use that above all else. + if ((array_key_exists('id', $this->item) && ($this->item['id'] != "") && (is_numeric($this->item['id'])))) { + $user = User::find($this->item['id']); + } else { + $user = User::where('username', $this->item['username'])->first(); + } + + if ($user) { + + if (! $this->updating) { + \Log::debug('A matching User '.$this->item['name'].' already exists. '); return; } $this->log('Updating User'); @@ -104,7 +115,6 @@ class UserImporter extends ItemImporter $user->fill($this->sanitizeItemForStoring($user)); if ($user->save()) { - // $user->logCreate('Imported using CSV Importer'); $this->log('User '.$this->item['name'].' was created'); if (($user->email) && ($user->activated == '1')) { diff --git a/app/Importer/import_mappings.md b/app/Importer/import_mappings.md deleted file mode 100644 index a76c981e8e..0000000000 --- a/app/Importer/import_mappings.md +++ /dev/null @@ -1,45 +0,0 @@ -| CSV | Item | Applicable Types | -|---------------------|------------------|-------------------------------------------| -| activated | | User | -| asset tag | asset_tag | Asset | -| category | category | All | -| company | company | All | -| department_id | | User ? All | -| item name | item_name | All | -| image | image | Asset | -| email | | | -| expiration date | expiration_date | License | -| location | location | All | -| notes | notes | All | -| licensed to email | license_email | License | -| licensed to name | license_name | License | -| maintained | maintained | License | -| manager_id | | User | -| manufacturer | manufacturer | All | -| model name | asset_model | Asset | -| model number | model_number | Asset | -| order number | order_number | All ? | -| purchase cost | purchase_cost | All ? | -| purchase date | purchase_date | All ? | -| purchase order | purchase_order | License | -| quantity | qty | Accessory, Consumable, Component, License | -| reassignable | reassignable | License | -| requestable | requestable | Asset, Accessory? | -| seats | seats | License | -| serial number | serial | Asset, license | -| status | status | Asset ? All | -| supplier | supplier | Asset ? All | -| minimum quantity | min_amt | Consumable | -| termination date | termination_date | License | -| warranty months | warranty_months | Asset | -| User Related Fields | assigned_to | Asset | -| name | | | -| email | | | -| username | | | -| address | address | User | -| city | city | User | -| state | state | User | -| country | country | User | -| vip | vip | User | -| byod | byod | Asset | - diff --git a/app/Listeners/CheckoutableListener.php b/app/Listeners/CheckoutableListener.php index 9d6cd942bd..09cb3ae8f2 100644 --- a/app/Listeners/CheckoutableListener.php +++ b/app/Listeners/CheckoutableListener.php @@ -2,22 +2,21 @@ namespace App\Listeners; +use App\Events\CheckoutableCheckedOut; use App\Models\Accessory; use App\Models\Asset; use App\Models\CheckoutAcceptance; +use App\Models\Component; use App\Models\Consumable; use App\Models\LicenseSeat; use App\Models\Recipients\AdminRecipient; use App\Models\Setting; -use App\Models\User; use App\Notifications\CheckinAccessoryNotification; use App\Notifications\CheckinAssetNotification; -use App\Notifications\CheckinLicenseNotification; use App\Notifications\CheckinLicenseSeatNotification; use App\Notifications\CheckoutAccessoryNotification; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; -use App\Notifications\CheckoutLicenseNotification; use App\Notifications\CheckoutLicenseSeatNotification; use Illuminate\Support\Facades\Notification; use Exception; @@ -25,18 +24,17 @@ use Log; class CheckoutableListener { + private array $skipNotificationsFor = [ + Component::class, + ]; + /** - * Notify the user about the checked out checkoutable and add a record to the - * checkout_requests table. + * Notify the user and post to webhook about the checked out checkoutable + * and add a record to the checkout_requests table. */ public function onCheckedOut($event) { - - - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if (! $event->checkedOutTo instanceof User) { + if ($this->shouldNotSendAnyNotifications($event->checkoutable)){ return; } @@ -46,6 +44,11 @@ class CheckoutableListener $acceptance = $this->getCheckoutAcceptance($event); try { + if ($this->shouldSendWebhookNotification()) { + Notification::route('slack', Setting::getSettings()->webhook_endpoint) + ->notify($this->getCheckoutNotification($event)); + } + if (! $event->checkedOutTo->locale) { Notification::locale(Setting::getSettings()->locale)->send( $this->getNotifiables($event), @@ -63,16 +66,13 @@ class CheckoutableListener } /** - * Notify the user about the checked in checkoutable + * Notify the user and post to webhook about the checked in checkoutable */ public function onCheckedIn($event) { \Log::debug('onCheckedIn in the Checkoutable listener fired'); - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if (! $event->checkedOutTo instanceof User) { + if ($this->shouldNotSendAnyNotifications($event->checkoutable)) { return; } @@ -90,6 +90,11 @@ class CheckoutableListener } try { + if ($this->shouldSendWebhookNotification()) { + Notification::route('slack', Setting::getSettings()->webhook_endpoint) + ->notify($this->getCheckinNotification($event)); + } + // Use default locale if (! $event->checkedOutTo->locale) { Notification::locale(Setting::getSettings()->locale)->send( @@ -182,11 +187,11 @@ class CheckoutableListener /** * Get the appropriate notification for the event * - * @param CheckoutableCheckedIn $event - * @param CheckoutAcceptance $acceptance + * @param CheckoutableCheckedOut $event + * @param CheckoutAcceptance|null $acceptance * @return Notification */ - private function getCheckoutNotification($event, $acceptance) + private function getCheckoutNotification($event, $acceptance = null) { $notificationClass = null; @@ -225,4 +230,14 @@ class CheckoutableListener 'App\Listeners\CheckoutableListener@onCheckedOut' ); } + + private function shouldNotSendAnyNotifications($checkoutable): bool + { + return in_array(get_class($checkoutable), $this->skipNotificationsFor); + } + + private function shouldSendWebhookNotification(): bool + { + return Setting::getSettings() && Setting::getSettings()->webhook_endpoint; + } } diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index 0457cf253f..7576cc644f 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -330,7 +330,11 @@ class Accessory extends SnipeModel /** - * Check how many items of an accessory remain + * Check how many items of an accessory remain. + * + * In order to use this model method, you MUST call withCount('users as users_count') + * on the eloquent query in the controller, otherwise $this->>users_count will be null and + * bad things happen. * * @author [A. Gianotto] [] * @since [v3.0] diff --git a/app/Models/Asset.php b/app/Models/Asset.php index e992298d86..1d7595e5d9 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -70,19 +70,6 @@ class Asset extends Depreciable */ protected $injectUniqueIdentifier = true; - // We set these as protected dates so that they will be easily accessible via Carbon - protected $dates = [ - 'created_at', - 'updated_at', - 'deleted_at', - 'purchase_date', - 'last_checkout', - 'expected_checkin', - 'last_audit_date', - 'next_audit_date' - ]; - - protected $casts = [ 'purchase_date' => 'date', 'last_checkout' => 'datetime', @@ -96,11 +83,14 @@ class Asset extends Depreciable 'rtd_company_id' => 'integer', 'supplier_id' => 'integer', 'byod' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', ]; protected $rules = [ 'name' => 'max:255|nullable', - 'model_id' => 'required|integer|exists:models,id', + 'model_id' => 'required|integer|exists:models,id,deleted_at,NULL', 'status_id' => 'required|integer|exists:status_labels,id', 'company_id' => 'integer|nullable', 'warranty_months' => 'numeric|nullable|digits_between:0,240', @@ -413,7 +403,7 @@ class Asset extends Depreciable */ public function components() { - return $this->belongsToMany('\App\Models\Component', 'components_assets', 'asset_id', 'component_id')->withPivot('id', 'assigned_qty', 'created_at')->withTrashed(); + return $this->belongsToMany('\App\Models\Component', 'components_assets', 'asset_id', 'component_id')->withPivot('id', 'assigned_qty', 'created_at'); } @@ -919,7 +909,13 @@ class Asset extends Depreciable return false; } - + public function getComponentCost(){ + $cost = 0; + foreach($this->components as $component) { + $cost += $component->pivot->assigned_qty*$component->purchase_cost; + } + return $cost; + } /** * ----------------------------------------------- @@ -951,8 +947,10 @@ class Asset extends Depreciable ->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.username', 'LIKE', '%'.$term.'%') - ->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%"]); - + ->orWhereMultipleColumns([ + 'assets_users.first_name', + 'assets_users.last_name', + ], $term); } /** @@ -1347,7 +1345,10 @@ class Asset extends Depreciable })->orWhere(function ($query) use ($search) { $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%') - ->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%"]) + ->orWhereMultipleColumns([ + 'assets_users.first_name', + 'assets_users.last_name', + ], $search) ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%') ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%') ->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%'); @@ -1666,7 +1667,7 @@ class Asset extends Depreciable public function scopeOrderManufacturer($query, $order) { return $query->join('models as order_asset_model', 'assets.model_id', '=', 'order_asset_model.id') - ->join('manufacturers as manufacturer_order', 'order_asset_model.manufacturer_id', '=', 'manufacturer_order.id') + ->leftjoin('manufacturers as manufacturer_order', 'order_asset_model.manufacturer_id', '=', 'manufacturer_order.id') ->orderBy('manufacturer_order.name', $order); } diff --git a/app/Models/AssetMaintenance.php b/app/Models/AssetMaintenance.php index 41ab802579..292e529571 100644 --- a/app/Models/AssetMaintenance.php +++ b/app/Models/AssetMaintenance.php @@ -95,8 +95,8 @@ class AssetMaintenance extends Model implements ICompanyableChild */ public function setCostAttribute($value) { - $value = Helper::ParseFloat($value); - if ($value == '0.0') { + $value = Helper::ParseCurrency($value); + if ($value == 0) { $value = null; } $this->attributes['cost'] = $value; diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php index c3c34b0571..4a4360c40a 100644 --- a/app/Models/CheckoutAcceptance.php +++ b/app/Models/CheckoutAcceptance.php @@ -3,13 +3,14 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Notifications\Notifiable; class CheckoutAcceptance extends Model { - use SoftDeletes, Notifiable; + use HasFactory, SoftDeletes, Notifiable; protected $casts = [ 'accepted_at' => 'datetime', diff --git a/app/Models/Component.php b/app/Models/Component.php index 98230132be..052ec1219d 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -33,7 +33,8 @@ class Component extends SnipeModel 'name' => 'required|min:3|max:255', 'qty' => 'required|integer|min:1', 'category_id' => 'required|integer|exists:categories,id', - 'company_id' => 'integer|nullable', + 'supplier_id' => 'nullable|integer|exists:suppliers,id', + 'company_id' => 'integer|nullable|exists:companies,id', 'min_amt' => 'integer|min:0|nullable', 'purchase_date' => 'date_format:Y-m-d|nullable', 'purchase_cost' => 'numeric|nullable|gte:0', @@ -57,6 +58,7 @@ class Component extends SnipeModel protected $fillable = [ 'category_id', 'company_id', + 'supplier_id', 'location_id', 'name', 'purchase_cost', @@ -86,6 +88,7 @@ class Component extends SnipeModel 'category' => ['name'], 'company' => ['name'], 'location' => ['name'], + 'supplier' => ['name'], ]; @@ -168,6 +171,18 @@ class Component extends SnipeModel return $this->belongsTo(\App\Models\Category::class, 'category_id'); } + /** + * Establishes the item -> supplier relationship + * + * @author [A. Gianotto] [] + * @since [v6.1.1] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function supplier() + { + return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id'); + } + /** * Establishes the component -> action logs relationship * @@ -247,4 +262,17 @@ class Component extends SnipeModel { return $query->leftJoin('companies', 'components.company_id', '=', 'companies.id')->orderBy('companies.name', $order); } + + /** + * Query builder scope to order on supplier + * + * @param \Illuminate\Database\Query\Builder $query Query builder instance + * @param text $order Order + * + * @return \Illuminate\Database\Query\Builder Modified query builder + */ + public function scopeOrderSupplier($query, $order) + { + return $query->leftJoin('suppliers', 'components.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order); + } } diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index ea4ac6086b..a3a0d59178 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -27,6 +27,7 @@ class Consumable extends SnipeModel 'requestable' => 'boolean', 'category_id' => 'integer', 'company_id' => 'integer', + 'supplier_id', 'qty' => 'integer', 'min_amt' => 'integer', ]; @@ -95,6 +96,7 @@ class Consumable extends SnipeModel 'company' => ['name'], 'location' => ['name'], 'manufacturer' => ['name'], + 'supplier' => ['name'], ]; @@ -249,6 +251,18 @@ class Consumable extends SnipeModel return $this->belongsToMany(\App\Models\User::class, 'consumables_users', 'consumable_id', 'assigned_to')->withPivot('user_id')->withTrashed()->withTimestamps(); } + /** + * Establishes the item -> supplier relationship + * + * @author [A. Gianotto] [] + * @since [v6.1.1] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function supplier() + { + return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id'); + } + /** * Determine whether to send a checkin/checkout email based on @@ -376,4 +390,17 @@ class Consumable extends SnipeModel { return $query->leftJoin('companies', 'consumables.company_id', '=', 'companies.id')->orderBy('companies.name', $order); } + + /** + * Query builder scope to order on supplier + * + * @param \Illuminate\Database\Query\Builder $query Query builder instance + * @param text $order Order + * + * @return \Illuminate\Database\Query\Builder Modified query builder + */ + public function scopeOrderSupplier($query, $order) + { + return $query->leftJoin('suppliers', 'consumables.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order); + } } diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php index 55738cd034..60f726de92 100644 --- a/app/Models/CustomField.php +++ b/app/Models/CustomField.php @@ -52,6 +52,7 @@ class CustomField extends Model 'name' => 'required|unique:custom_fields', 'element' => 'required|in:text,listbox,textarea,checkbox,radio', 'field_encrypted' => 'nullable|boolean', + 'auto_add_to_fieldsets' => 'boolean', ]; /** @@ -69,6 +70,8 @@ class CustomField extends Model 'show_in_email', 'is_unique', 'display_in_user_view', + 'auto_add_to_fieldsets', + ]; /** @@ -308,9 +311,9 @@ class CustomField extends Model $arr_parts = explode('|', $arr[$x]); if ($arr_parts[0] != '') { if (array_key_exists('1', $arr_parts)) { - $result[$arr_parts[0]] = $arr_parts[1]; + $result[$arr_parts[0]] = trim($arr_parts[1]); } else { - $result[$arr_parts[0]] = $arr_parts[0]; + $result[$arr_parts[0]] = trim($arr_parts[0]); } } } diff --git a/app/Models/Ldap.php b/app/Models/Ldap.php index a29581bf97..4eb496a2ab 100644 --- a/app/Models/Ldap.php +++ b/app/Models/Ldap.php @@ -213,6 +213,7 @@ class Ldap extends Model $ldap_result_phone = Setting::getSettings()->ldap_phone; $ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle; $ldap_result_country = Setting::getSettings()->ldap_country; + $ldap_result_location = Setting::getSettings()->ldap_location; $ldap_result_dept = Setting::getSettings()->ldap_dept; $ldap_result_manager = Setting::getSettings()->ldap_manager; // Get LDAP user data @@ -227,6 +228,7 @@ class Ldap extends Model $item['country'] = $ldapattributes[$ldap_result_country][0] ?? ''; $item['department'] = $ldapattributes[$ldap_result_dept][0] ?? ''; $item['manager'] = $ldapattributes[$ldap_result_manager][0] ?? ''; + $item['location'] = $ldapattributes[$ldap_result_location][0] ?? ''; return $item; } diff --git a/app/Models/License.php b/app/Models/License.php index 94263ee0d8..162b3d662a 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -33,9 +33,9 @@ class License extends Depreciable protected $table = 'licenses'; protected $casts = [ - 'purchase_date' => 'datetime', - 'expiration_date' => 'datetime', - 'termination_date' => 'datetime', + 'purchase_date' => 'date', + 'expiration_date' => 'date', + 'termination_date' => 'date', 'category_id' => 'integer', 'company_id' => 'integer', ]; @@ -49,9 +49,9 @@ class License extends Depreciable 'category_id' => 'required|exists:categories,id', 'company_id' => 'integer|nullable', 'purchase_cost'=> 'numeric|nullable|gte:0', - 'purchase_date' => 'date_format:Y-m-d|nullable', - 'expiration_date' => 'date_format:Y-m-d|nullable', - 'termination_date' => 'date_format:Y-m-d|nullable', + 'purchase_date' => 'date_format:Y-m-d|nullable|max:10', + 'expiration_date' => 'date_format:Y-m-d|nullable|max:10', + 'termination_date' => 'date_format:Y-m-d|nullable|max:10', ]; /** @@ -106,10 +106,10 @@ class License extends Depreciable * @var array */ protected $searchableRelations = [ - 'manufacturer' => ['name'], - 'company' => ['name'], - 'category' => ['name'], - 'depreciation' => ['name'], + 'manufacturer' => ['name'], + 'company' => ['name'], + 'category' => ['name'], + 'depreciation' => ['name'], ]; /** @@ -425,7 +425,7 @@ class License extends Depreciable public static function assetcount() { return LicenseSeat::whereNull('deleted_at') - ->count(); + ->count(); } @@ -441,8 +441,8 @@ class License extends Depreciable public function totalSeatsByLicenseID() { return LicenseSeat::where('license_id', '=', $this->id) - ->whereNull('deleted_at') - ->count(); + ->whereNull('deleted_at') + ->count(); } /** @@ -486,11 +486,12 @@ class License extends Depreciable public static function availassetcount() { return LicenseSeat::whereNull('assigned_to') - ->whereNull('asset_id') - ->whereNull('deleted_at') - ->count(); + ->whereNull('asset_id') + ->whereNull('deleted_at') + ->count(); } + /** * Returns the number of total available seats for this license * @@ -533,7 +534,7 @@ class License extends Depreciable { return $this->licenseSeatsRelation()->where(function ($query) { $query->whereNotNull('assigned_to') - ->orWhereNotNull('asset_id'); + ->orWhereNotNull('asset_id'); }); } @@ -621,13 +622,13 @@ class License extends Depreciable public function freeSeat() { return $this->licenseseats() - ->whereNull('deleted_at') - ->where(function ($query) { - $query->whereNull('assigned_to') - ->whereNull('asset_id'); - }) - ->orderBy('id', 'asc') - ->first(); + ->whereNull('deleted_at') + ->where(function ($query) { + $query->whereNull('assigned_to') + ->whereNull('asset_id'); + }) + ->orderBy('id', 'asc') + ->first(); } @@ -657,11 +658,11 @@ class License extends Depreciable $days = (is_null($days)) ? 60 : $days; return self::whereNotNull('expiration_date') - ->whereNull('deleted_at') - ->whereRaw(DB::raw('DATE_SUB(`expiration_date`,INTERVAL '.$days.' DAY) <= DATE(NOW()) ')) - ->where('expiration_date', '>', date('Y-m-d')) - ->orderBy('expiration_date', 'ASC') - ->get(); + ->whereNull('deleted_at') + ->whereRaw(DB::raw('DATE_SUB(`expiration_date`,INTERVAL '.$days.' DAY) <= DATE(NOW()) ')) + ->where('expiration_date', '>', date('Y-m-d')) + ->orderBy('expiration_date', 'ASC') + ->get(); } /** @@ -705,4 +706,4 @@ class License extends Depreciable return $query->leftJoin('companies as companies', 'licenses.company_id', '=', 'companies.id')->select('licenses.*') ->orderBy('companies.name', $order); } -} +} \ No newline at end of file diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index 2207edd02c..d2a99d3c56 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -6,13 +6,15 @@ use App\Models\Traits\Acceptable; use App\Notifications\CheckinLicenseNotification; use App\Notifications\CheckoutLicenseNotification; use App\Presenters\Presentable; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes; class LicenseSeat extends SnipeModel implements ICompanyableChild { use CompanyableChildTrait; - use SoftDeletes; + use HasFactory; use Loggable; + use SoftDeletes; protected $presenter = \App\Presenters\LicenseSeatPresenter::class; use Presentable; diff --git a/app/Models/Location.php b/app/Models/Location.php index 8181f406cd..2f4e78dcc7 100755 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -26,11 +26,12 @@ class Location extends SnipeModel protected $table = 'locations'; protected $rules = [ 'name' => 'required|min:2|max:255|unique_undeleted', - 'city' => 'min:2|max:255|nullable', - 'country' => 'min:2|max:255|nullable', - 'address' => 'max:80|nullable', - 'address2' => 'max:80|nullable', - 'zip' => 'min:3|max:10|nullable', + 'address' => 'max:191|nullable', + 'address2' => 'max:191|nullable', + 'city' => 'max:191|nullable', + 'state' => 'min:2|max:191|nullable', + 'country' => 'min:2|max:191|nullable', + 'zip' => 'max:10|nullable', 'manager_id' => 'exists:users,id|nullable', 'parent_id' => 'non_circular:locations,id', ]; @@ -267,7 +268,7 @@ class Location extends SnipeModel foreach ($locations_with_children[$parent_id] as $location) { $location->use_text = $prefix.' '.$location->name; - $location->use_image = ($location->image) ? url('/').'/uploads/locations/'.$location->image : null; + $location->use_image = ($location->image) ? config('app.url').'/uploads/locations/'.$location->image : null; $results[] = $location; //now append the children. (if we have any) if (array_key_exists($location->id, $locations_with_children)) { diff --git a/app/Models/Manufacturer.php b/app/Models/Manufacturer.php index 5f01c3c273..da4f26b02f 100755 --- a/app/Models/Manufacturer.php +++ b/app/Models/Manufacturer.php @@ -23,8 +23,9 @@ class Manufacturer extends SnipeModel protected $rules = [ 'name' => 'required|min:2|max:255|unique:manufacturers,name,NULL,id,deleted_at,NULL', 'url' => 'url|nullable', - 'support_url' => 'url|nullable', 'support_email' => 'email|nullable', + 'support_url' => 'nullable|url', + 'warranty_lookup_url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://' ]; protected $hidden = ['user_id']; @@ -51,6 +52,7 @@ class Manufacturer extends SnipeModel 'support_phone', 'support_url', 'url', + 'warranty_lookup_url', ]; use Searchable; diff --git a/app/Models/Setting.php b/app/Models/Setting.php index ecac183356..6c95d6b01e 100755 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -76,6 +76,7 @@ class Setting extends Model 'audit_interval' => 'numeric|nullable', 'custom_forgot_pass_url' => 'url|nullable', 'privacy_policy_link' => 'nullable|url', + 'google_client_id' => 'nullable|ends_with:apps.googleusercontent.com' ]; protected $fillable = [ @@ -86,6 +87,9 @@ class Setting extends Model 'webhook_endpoint', 'webhook_channel', 'webhook_botname', + 'google_login', + 'google_client_id', + 'google_client_secret', ]; /** @@ -341,7 +345,15 @@ class Setting extends Model 'ad_domain', 'ad_append_domain', 'ldap_client_tls_key', - 'ldap_client_tls_cert' + 'ldap_client_tls_cert', + 'ldap_default_group', + 'ldap_dept', + 'ldap_emp_num', + 'ldap_phone_field', + 'ldap_jobtitle', + 'ldap_manager', + 'ldap_country', + 'ldap_location', ])->first()->getAttributes(); return collect($ldapSettings); diff --git a/app/Models/SnipeModel.php b/app/Models/SnipeModel.php index e5a039a4e1..af12c3d29b 100644 --- a/app/Models/SnipeModel.php +++ b/app/Models/SnipeModel.php @@ -21,9 +21,9 @@ class SnipeModel extends Model */ public function setPurchaseCostAttribute($value) { - $value = Helper::ParseFloat($value); + $value = Helper::ParseCurrency($value); - if ($value == '0.0') { + if ($value == 0) { $value = null; } $this->attributes['purchase_cost'] = $value; diff --git a/app/Models/SnipeSCIMConfig.php b/app/Models/SnipeSCIMConfig.php index 77cbf01c1a..7ec25645e6 100644 --- a/app/Models/SnipeSCIMConfig.php +++ b/app/Models/SnipeSCIMConfig.php @@ -70,7 +70,11 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig // Map a SCIM attribute to an attribute of the object. 'mapping' => [ - 'id' => AttributeMapping::eloquent("id")->disableWrite(), + 'id' => (new AttributeMapping())->setRead( + function (&$object) { + return (string)$object->id; + } + )->disableWrite(), 'externalId' => AttributeMapping::eloquent('scim_externalid'), // FIXME - I have a PR that changes a lot of this. @@ -174,7 +178,6 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig '$ref' => null, 'display' => null, 'type' => null, - 'type' => null ]], 'entitlements' => null, diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index 393e7ddb29..e198d10c10 100755 --- a/app/Models/Supplier.php +++ b/app/Models/Supplier.php @@ -16,17 +16,17 @@ class Supplier extends SnipeModel protected $table = 'suppliers'; protected $rules = [ - 'name' => 'required|min:1|max:255|unique_undeleted', - 'address' => 'max:250|nullable', - 'address2' => 'max:250|nullable', - 'city' => 'max:255|nullable', - 'state' => 'max:32|nullable', - 'country' => 'max:3|nullable', + 'name' => 'required|min:1|max:255|unique_undeleted', 'fax' => 'min:7|max:35|nullable', 'phone' => 'min:7|max:35|nullable', 'contact' => 'max:100|nullable', 'notes' => 'max:191|nullable', // Default string length is 191 characters.. 'email' => 'email|max:150|nullable', + 'address' => 'max:250|nullable', + 'address2' => 'max:250|nullable', + 'city' => 'max:191|nullable', + 'state' => 'min:2|max:191|nullable', + 'country' => 'min:2|max:191|nullable', 'zip' => 'max:10|nullable', 'url' => 'sometimes|nullable|string|max:250', ]; @@ -78,24 +78,7 @@ class Supplier extends SnipeModel { return $this->hasMany(Asset::class)->whereNull('deleted_at')->selectRaw('supplier_id, count(*) as count')->groupBy('supplier_id'); } - - /** - * Sets the license seat count attribute - * - * @todo I don't see the licenseSeatsRelation here? - * - * @author A. Gianotto - * @since [v1.0] - * @return \Illuminate\Database\Eloquent\Relations\Relation - */ - public function getLicenseSeatsCountAttribute() - { - if ($this->licenseSeatsRelation->first()) { - return $this->licenseSeatsRelation->first()->count; - } - - return 0; - } + /** * Establishes the supplier -> assets relationship @@ -121,6 +104,30 @@ class Supplier extends SnipeModel return $this->hasMany(\App\Models\Accessory::class, 'supplier_id'); } + /** + * Establishes the supplier -> component relationship + * + * @author A. Gianotto + * @since [v6.1.1] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function components() + { + return $this->hasMany(\App\Models\Component::class, 'supplier_id'); + } + + /** + * Establishes the supplier -> component relationship + * + * @author A. Gianotto + * @since [v6.1.1] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function consumables() + { + return $this->hasMany(\App\Models\Consumable::class, 'supplier_id'); + } + /** * Establishes the supplier -> asset maintenances relationship * diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index a7feb62957..06e05348fb 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -5,6 +5,7 @@ namespace App\Models\Traits; use App\Models\Asset; use App\Models\CustomField; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Facades\DB; /** * This trait allows for cleaner searching of models, @@ -164,7 +165,13 @@ trait Searchable } // I put this here because I only want to add the concat one time in the end of the user relation search if($relation == 'user') { - $query->orWhereRaw('CONCAT (users.first_name, " ", users.last_name) LIKE ?', ["%{$term}%"]); + $query->orWhereRaw( + $this->buildMultipleColumnSearch([ + 'users.first_name', + 'users.last_name', + ]), + ["%{$term}%"] + ); } }); } @@ -257,4 +264,37 @@ trait Searchable return $related->getTable(); } + + /** + * Builds a search string for either MySQL or sqlite by separating the provided columns with a space. + * + * @param array $columns Columns to include in search string. + * @return string + */ + private function buildMultipleColumnSearch(array $columns): string + { + $mappedColumns = collect($columns)->map(fn($column) => DB::getTablePrefix() . $column)->toArray(); + + $driver = config('database.connections.' . config('database.default') . '.driver'); + + if ($driver === 'sqlite') { + return implode("||' '||", $mappedColumns) . ' LIKE ?'; + } + + // Default to MySQL's concatenation method + return 'CONCAT(' . implode('," ",', $mappedColumns) . ') LIKE ?'; + } + + /** + * Search a string across multiple columns separated with a space. + * + * @param Builder $query + * @param array $columns - Columns to include in search string. + * @param $term + * @return Builder + */ + public function scopeOrWhereMultipleColumns($query, array $columns, $term) + { + return $query->orWhereRaw($this->buildMultipleColumnSearch($columns), ["%{$term}%"]); + } } diff --git a/app/Models/User.php b/app/Models/User.php index bf40982db3..98a3ec346b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -65,6 +65,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo 'avatar', 'gravatar', 'vip', + 'autoassign_licenses', ]; protected $casts = [ @@ -73,18 +74,12 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo 'location_id' => 'integer', 'company_id' => 'integer', 'vip' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'autoassign_licenses' => 'boolean', ]; - - protected $dates = [ - 'created_at', - 'updated_at', - 'deleted_at', - 'start_date' => 'date_format:Y-m-d', - 'end_date' => 'date_format:Y-m-d', - ]; - - /** * Model validation rules * @@ -102,6 +97,12 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo 'location_id' => 'exists:locations,id|nullable', 'start_date' => 'nullable|date_format:Y-m-d', 'end_date' => 'nullable|date_format:Y-m-d|after_or_equal:start_date', + 'autoassign_licenses' => 'boolean', + 'address' => 'max:191|nullable', + 'city' => 'max:191|nullable', + 'state' => 'min:2|max:191|nullable', + 'country' => 'min:2|max:191|nullable', + 'zip' => 'max:10|nullable', ]; /** @@ -263,20 +264,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo return $this->last_name.', '.$this->first_name.' ('.$this->username.')'; } - /** - * The url for slack notifications. - * Used by Notifiable trait. - * @return mixed - */ - public function routeNotificationForSlack() - { - // At this point the endpoint is the same for everything. - // In the future this may want to be adapted for individual notifications. - $this->endpoint = \App\Models\Setting::getSettings()->webhook_endpoint; - - return $this->endpoint; - } - /** * Establishes the user -> assets relationship @@ -657,14 +644,14 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo */ public function scopeSimpleNameSearch($query, $search) { - $query = $query->where('first_name', 'LIKE', '%'.$search.'%') - ->orWhere('last_name', 'LIKE', '%'.$search.'%') - ->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%{$search}%"]); - - return $query; + return $query->where('first_name', 'LIKE', '%' . $search . '%') + ->orWhere('last_name', 'LIKE', '%' . $search . '%') + ->orWhereMultipleColumns([ + 'users.first_name', + 'users.last_name', + ], $search); } - /** * Run additional, advanced searches. * @@ -673,9 +660,11 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo * @return \Illuminate\Database\Eloquent\Builder */ public function advancedTextSearch(Builder $query, array $terms) { - foreach($terms as $term) { - $query = $query->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%{$term}%"]); + $query->orWhereMultipleColumns([ + 'users.first_name', + 'users.last_name', + ], $term); } return $query; diff --git a/app/Notifications/AuditNotification.php b/app/Notifications/AuditNotification.php index 9f5ae96687..1377c29e7e 100644 --- a/app/Notifications/AuditNotification.php +++ b/app/Notifications/AuditNotification.php @@ -23,6 +23,7 @@ class AuditNotification extends Notification public function __construct($params) { // + $this->settings = Setting::getSettings(); $this->params = $params; } @@ -43,9 +44,12 @@ class AuditNotification extends Notification public function toSlack() { + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; return (new SlackMessage) ->success() ->content(class_basename(get_class($this->params['item'])).' Audited') + ->from(($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot') + ->to($channel) ->attachment(function ($attachment) { $item = $this->params['item']; $admin_user = $this->params['admin']; diff --git a/app/Notifications/CheckinAccessoryNotification.php b/app/Notifications/CheckinAccessoryNotification.php index a375f37ad3..53be68f58f 100644 --- a/app/Notifications/CheckinAccessoryNotification.php +++ b/app/Notifications/CheckinAccessoryNotification.php @@ -92,6 +92,7 @@ class CheckinAccessoryNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>', @@ -101,6 +102,7 @@ class CheckinAccessoryNotification extends Notification return (new SlackMessage) ->content(':arrow_down: :keyboard: '.trans('mail.Accessory_Checkin_Notification')) ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/CheckinAssetNotification.php b/app/Notifications/CheckinAssetNotification.php index e8fdcf678b..5389c8ddde 100644 --- a/app/Notifications/CheckinAssetNotification.php +++ b/app/Notifications/CheckinAssetNotification.php @@ -65,6 +65,7 @@ class CheckinAssetNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname != '') ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ trans('general.administrator') => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>', @@ -75,6 +76,7 @@ class CheckinAssetNotification extends Notification return (new SlackMessage) ->content(':arrow_down: :computer: '.trans('mail.Asset_Checkin_Notification')) ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/CheckinLicenseSeatNotification.php b/app/Notifications/CheckinLicenseSeatNotification.php index a68930a683..2c7fe2fd85 100644 --- a/app/Notifications/CheckinLicenseSeatNotification.php +++ b/app/Notifications/CheckinLicenseSeatNotification.php @@ -63,6 +63,7 @@ class CheckinLicenseSeatNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; if ($admin) { $fields = [ @@ -79,6 +80,7 @@ class CheckinLicenseSeatNotification extends Notification return (new SlackMessage) ->content(':arrow_down: :floppy_disk: '.trans('mail.License_Checkin_Notification')) ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 982fd863c5..f5635d1af0 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -79,6 +79,7 @@ class CheckoutAccessoryNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>', @@ -88,6 +89,7 @@ class CheckoutAccessoryNotification extends Notification return (new SlackMessage) ->content(':arrow_up: :keyboard: Accessory Checked Out') ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index 303fc08f18..e57825f5c6 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -96,6 +96,7 @@ class CheckoutAssetNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>', @@ -107,8 +108,9 @@ class CheckoutAssetNotification extends Notification } return (new SlackMessage) - ->content(':arrow_up: :computer: Asset Checked Out') + ->content(':arrow_up: :computer: '.trans('mail.Asset_Checkout_Notification')) ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index 84e52fea77..376c70fdea 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -85,6 +85,7 @@ class CheckoutConsumableNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>', @@ -94,6 +95,7 @@ class CheckoutConsumableNotification extends Notification return (new SlackMessage) ->content(':arrow_up: :paperclip: Consumable Checked Out') ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/CheckoutLicenseSeatNotification.php b/app/Notifications/CheckoutLicenseSeatNotification.php index d4741d9beb..2dd6480a30 100644 --- a/app/Notifications/CheckoutLicenseSeatNotification.php +++ b/app/Notifications/CheckoutLicenseSeatNotification.php @@ -85,6 +85,7 @@ class CheckoutLicenseSeatNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>', @@ -94,6 +95,7 @@ class CheckoutLicenseSeatNotification extends Notification return (new SlackMessage) ->content(':arrow_up: :floppy_disk: License Checked Out') ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $admin, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/FirstAdminNotification.php b/app/Notifications/FirstAdminNotification.php index 71637b5e80..83e3a65de7 100644 --- a/app/Notifications/FirstAdminNotification.php +++ b/app/Notifications/FirstAdminNotification.php @@ -24,7 +24,7 @@ class FirstAdminNotification extends Notification $this->_data['last_name'] = $content['last_name']; $this->_data['username'] = $content['username']; $this->_data['password'] = $content['password']; - $this->_data['url'] = url('/'); + $this->_data['url'] = config('app.url'); } /** diff --git a/app/Notifications/RequestAssetCancelation.php b/app/Notifications/RequestAssetCancelation.php index 905900de09..fa3546bbc8 100644 --- a/app/Notifications/RequestAssetCancelation.php +++ b/app/Notifications/RequestAssetCancelation.php @@ -74,6 +74,7 @@ class RequestAssetCancelation extends Notification $note = $this->note; $qty = $this->item_quantity; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'QTY' => $qty, @@ -87,6 +88,7 @@ class RequestAssetCancelation extends Notification return (new SlackMessage) ->content(trans('mail.a_user_canceled')) ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/RequestAssetNotification.php b/app/Notifications/RequestAssetNotification.php index 028719f168..d2001f2e13 100644 --- a/app/Notifications/RequestAssetNotification.php +++ b/app/Notifications/RequestAssetNotification.php @@ -74,6 +74,7 @@ class RequestAssetNotification extends Notification $item = $this->item; $note = $this->note; $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ 'QTY' => $qty, @@ -83,6 +84,7 @@ class RequestAssetNotification extends Notification return (new SlackMessage) ->content(trans('mail.Item_Requested')) ->from($botname) + ->to($channel) ->attachment(function ($attachment) use ($item, $note, $fields) { $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl()) ->fields($fields) diff --git a/app/Notifications/WelcomeNotification.php b/app/Notifications/WelcomeNotification.php index a5754be4d9..1e27ca7364 100644 --- a/app/Notifications/WelcomeNotification.php +++ b/app/Notifications/WelcomeNotification.php @@ -24,7 +24,7 @@ class WelcomeNotification extends Notification $this->_data['last_name'] = htmlspecialchars_decode($content['last_name']); $this->_data['username'] = htmlspecialchars_decode($content['username']); $this->_data['password'] = htmlspecialchars_decode($content['password']); - $this->_data['url'] = url('/'); + $this->_data['url'] = config('app.url'); } /** diff --git a/app/Presenters/AssetAuditPresenter.php b/app/Presenters/AssetAuditPresenter.php index 2c25a6aa06..a332950de1 100644 --- a/app/Presenters/AssetAuditPresenter.php +++ b/app/Presenters/AssetAuditPresenter.php @@ -44,13 +44,13 @@ class AssetAuditPresenter extends Presenter 'visible' => true, 'formatter' => 'hardwareLinkFormatter', ], [ - 'field' => 'image', + 'field' => 'file', 'searchable' => false, 'sortable' => true, 'switchable' => true, 'title' => trans('admin/hardware/table.image'), 'visible' => false, - 'formatter' => 'imageFormatter', + 'formatter' => 'auditImageFormatter', ], [ 'field' => 'asset_tag', 'searchable' => true, diff --git a/app/Presenters/AssetMaintenancesPresenter.php b/app/Presenters/AssetMaintenancesPresenter.php index 8906b420d3..c4446c0b2a 100644 --- a/app/Presenters/AssetMaintenancesPresenter.php +++ b/app/Presenters/AssetMaintenancesPresenter.php @@ -95,6 +95,7 @@ class AssetMaintenancesPresenter extends Presenter 'searchable' => true, 'sortable' => true, 'title' => trans('admin/asset_maintenances/table.is_warranty'), + 'formatter' => 'trueFalseFormatter' ], [ 'field' => 'cost', 'searchable' => true, diff --git a/app/Presenters/AssetModelPresenter.php b/app/Presenters/AssetModelPresenter.php index fd74108ee3..8e3d109045 100644 --- a/app/Presenters/AssetModelPresenter.php +++ b/app/Presenters/AssetModelPresenter.php @@ -210,7 +210,7 @@ class AssetModelPresenter extends Presenter public function imageUrl() { if (! empty($this->image)) { - return ''.$this->name.''; + return ''.$this->name.''; } return ''; @@ -223,7 +223,7 @@ class AssetModelPresenter extends Presenter public function imageSrc() { if (! empty($this->image)) { - return url('/').'/uploads/models/'.$this->image; + return config('app.url').'/uploads/models/'.$this->image; } return ''; diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php index 4be0f56010..ce476d082c 100644 --- a/app/Presenters/AssetPresenter.php +++ b/app/Presenters/AssetPresenter.php @@ -534,6 +534,18 @@ class AssetPresenter extends Presenter return false; } + /** + * Used to take user created warranty URL and dynamically fill in the needed values per asset + * @return string + */ + public function dynamicWarrantyUrl() + { + $warranty_lookup_url = $this->model->model->manufacturer->warranty_lookup_url; + $url = (str_replace('{LOCALE}',\App\Models\Setting::getSettings()->locale,$warranty_lookup_url)); + $url = (str_replace('{SERIAL}',$this->model->serial,$url)); + return $url; + } + /** * Url to view this item. * @return string diff --git a/app/Presenters/ComponentPresenter.php b/app/Presenters/ComponentPresenter.php index 16a96f3c4c..c7468911a1 100644 --- a/app/Presenters/ComponentPresenter.php +++ b/app/Presenters/ComponentPresenter.php @@ -59,6 +59,15 @@ class ComponentPresenter extends Presenter 'title' => trans('general.category'), 'formatter' => 'categoriesLinkObjFormatter', ], [ + 'field' => 'supplier', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.supplier'), + 'visible' => false, + 'formatter' => 'suppliersLinkObjFormatter', + ], + [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, diff --git a/app/Presenters/ConsumablePresenter.php b/app/Presenters/ConsumablePresenter.php index e931fc1228..abb599de4f 100644 --- a/app/Presenters/ConsumablePresenter.php +++ b/app/Presenters/ConsumablePresenter.php @@ -53,6 +53,14 @@ class ConsumablePresenter extends Presenter 'sortable' => true, 'title' => trans('general.category'), 'formatter' => 'categoriesLinkObjFormatter', + ], [ + 'field' => 'supplier', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.supplier'), + 'visible' => false, + 'formatter' => 'suppliersLinkObjFormatter', ], [ 'field' => 'model_number', 'searchable' => true, diff --git a/app/Presenters/LicensePresenter.php b/app/Presenters/LicensePresenter.php index 1e8784f2fc..f2d54549e1 100644 --- a/app/Presenters/LicensePresenter.php +++ b/app/Presenters/LicensePresenter.php @@ -33,7 +33,7 @@ class LicensePresenter extends Presenter 'field' => 'name', 'searchable' => true, 'sortable' => true, - 'title' => trans('admin/licenses/table.title'), + 'title' => trans('general.name'), 'formatter' => 'licensesLinkFormatter', ], [ 'field' => 'product_key', @@ -151,6 +151,21 @@ class LicensePresenter extends Presenter 'visible' => false, 'title' => trans('general.order_number'), ], [ + 'field' => 'created_at', + 'searchable' => false, + 'sortable' => true, + 'visible' => false, + 'title' => trans('general.created_at'), + 'formatter' => 'dateDisplayFormatter', + ], [ + 'field' => 'updated_at', + 'searchable' => false, + 'sortable' => true, + 'visible' => false, + 'title' => trans('general.updated_at'), + 'formatter' => 'dateDisplayFormatter', + ], + [ 'field' => 'notes', 'searchable' => true, 'sortable' => true, diff --git a/app/Presenters/ManufacturerPresenter.php b/app/Presenters/ManufacturerPresenter.php index 5b44a76933..f5c15f1fe5 100644 --- a/app/Presenters/ManufacturerPresenter.php +++ b/app/Presenters/ManufacturerPresenter.php @@ -47,7 +47,7 @@ class ManufacturerPresenter extends Presenter 'switchable' => true, 'title' => trans('admin/manufacturers/table.url'), 'visible' => true, - 'formatter' => 'linkFormatter', + 'formatter' => 'externalLinkFormatter', ], [ 'field' => 'support_url', @@ -56,7 +56,7 @@ class ManufacturerPresenter extends Presenter 'switchable' => true, 'title' => trans('admin/manufacturers/table.support_url'), 'visible' => true, - 'formatter' => 'linkFormatter', + 'formatter' => 'externalLinkFormatter', ], [ @@ -78,6 +78,15 @@ class ManufacturerPresenter extends Presenter 'visible' => true, 'formatter' => 'emailFormatter', ], + [ + 'field' => 'warranty_lookup_url', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('admin/manufacturers/table.warranty_lookup_url'), + 'visible' => false, + 'formatter' => 'externalLinkFormatter', + ], [ 'field' => 'assets_count', diff --git a/app/Presenters/UserPresenter.php b/app/Presenters/UserPresenter.php index 4bfe4492ff..080a2d10e9 100644 --- a/app/Presenters/UserPresenter.php +++ b/app/Presenters/UserPresenter.php @@ -7,6 +7,7 @@ use App\Models\Setting; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; /** * Class UserPresenter @@ -294,6 +295,15 @@ class UserPresenter extends Presenter 'visible' => true, 'formatter' => 'trueFalseFormatter', ], + [ + 'field' => 'autoassign_licenses', + 'searchable' => false, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.autoassign_licenses'), + 'visible' => false, + 'formatter' => 'trueFalseFormatter', + ], [ 'field' => 'created_by', 'searchable' => false, @@ -390,23 +400,31 @@ class UserPresenter extends Presenter public function gravatar() { if ($this->avatar) { + + // Check if it's a google avatar or some external avatar + if (Str::startsWith($this->avatar, ['http://', 'https://'])) { + return $this->avatar; + } + + // Otherwise assume it's an uploaded image return Storage::disk('public')->url('avatars/'.e($this->avatar)); } if (Setting::getSettings()->load_remote == '1') { if ($this->model->gravatar != '') { + $gravatar = md5(strtolower(trim($this->model->gravatar))); - return '//gravatar.com/avatar/'.$gravatar; - } elseif ($this->email != '') { - $gravatar = md5(strtolower(trim($this->email))); + } elseif ($this->email != '') { + + $gravatar = md5(strtolower(trim($this->email))); return '//gravatar.com/avatar/'.$gravatar; } } // Set a fun, gender-neutral default icon - return url('/').'/img/default-sm.png'; + return config('app.url').'/img/default-sm.png'; } /** diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php index ed5bd48dd2..371fc234de 100644 --- a/app/Providers/SettingsServiceProvider.php +++ b/app/Providers/SettingsServiceProvider.php @@ -29,6 +29,23 @@ class SettingsServiceProvider extends ServiceProvider $view->with('snipeSettings', Setting::getSettings()); }); + + // Make sure the limit is actually set, is an integer and does not exceed system limits + \App::singleton('api_limit_value', function () { + $limit = config('app.max_results'); + + if ((abs(intval(request('limit'))) > 0) && (abs(request('limit')) <= config('app.max_results'))) { + $limit = abs(request('limit')); + } + \Log::debug('Max in env: '.config('app.max_results')); + \Log::debug('Original requested limit: '.request('limit')); + \Log::debug('Modified limit: '.$limit); + \Log::debug('------------------------------'); + + return $limit; + }); + + /** * Set some common variables so that they're globally available. * The paths should always be public (versus private uploads) diff --git a/app/Services/Saml.php b/app/Services/Saml.php index 3f39be29ff..f80b1c1fb9 100644 --- a/app/Services/Saml.php +++ b/app/Services/Saml.php @@ -161,7 +161,7 @@ class Saml //Let onelogin/php-saml know to use 'X-Forwarded-*' headers if it is from a trusted proxy OneLogin_Saml2_Utils::setProxyVars(request()->isFromTrustedProxy()); - data_set($settings, 'sp.entityId', url('/')); + data_set($settings, 'sp.entityId', config('app.url')); data_set($settings, 'sp.assertionConsumerService.url', route('saml.acs')); data_set($settings, 'sp.singleLogoutService.url', route('saml.sls')); data_set($settings, 'sp.x509cert', $setting->saml_sp_x509cert); diff --git a/composer.json b/composer.json index 2f1007fa73..165a4a08f6 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ } ], "require": { - "php": ">=7.4 <8.3", + "php": ">=7.4.3 <8.2", "ext-curl": "*", "ext-fileinfo": "*", "ext-json": "*", @@ -46,6 +46,7 @@ "laravel/helpers": "^1.4", "laravel/passport": "^10.1", "laravel/slack-notification-channel": "^2.3", + "laravel/socialite": "^5.6", "laravel/tinker": "^2.6", "laravel/ui": "^3.3", "laravelcollective/html": "^6.2", diff --git a/composer.lock b/composer.lock index c34d46ce69..1ef75690aa 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "590171872e4a6a29c78efde99fbbf00e", + "content-hash": "4c82b2e171fb02a3ef024906db5d74c9", "packages": [ { "name": "alek13/slack", @@ -2673,6 +2673,7 @@ "type": "github" } ], + "abandoned": true, "time": "2022-02-23T14:25:13+00:00" }, { @@ -3604,6 +3605,75 @@ }, "time": "2022-01-12T18:07:54+00:00" }, + { + "name": "laravel/socialite", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "a14a177f2cc71d8add71e2b19e00800e83bdda09" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/a14a177f2cc71d8add71e2b19e00800e83bdda09", + "reference": "a14a177f2cc71d8add71e2b19e00800e83bdda09", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "league/oauth1-client": "^1.10.1", + "php": "^7.2|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", + "phpunit/phpunit": "^8.0|^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ], + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + } + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2023-01-20T15:42:35+00:00" + }, { "name": "laravel/tinker", "version": "v2.7.2", @@ -3803,6 +3873,7 @@ "issues": "https://github.com/LaravelCollective/html/issues", "source": "https://github.com/LaravelCollective/html" }, + "abandoned": "spatie/laravel-html", "time": "2022-02-08T21:02:54+00:00" }, { @@ -4532,6 +4603,82 @@ ], "time": "2022-04-17T13:12:02+00:00" }, + { + "name": "league/oauth1-client", + "version": "v1.10.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "d6365b901b5c287dd41f143033315e2f777e1167" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167", + "reference": "d6365b901b5c287dd41f143033315e2f777e1167", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1" + }, + "time": "2022-04-15T14:02:14+00:00" + }, { "name": "league/oauth2-server", "version": "8.3.5", @@ -5007,6 +5154,10 @@ "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.1" }, "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + }, { "url": "https://opencollective.com/zipstream", "type": "open_collective" @@ -13984,6 +14135,7 @@ "type": "github" } ], + "abandoned": true, "time": "2020-12-07T05:51:20+00:00" }, { @@ -16410,7 +16562,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4 <8.3", + "php": ">=7.4.3 <8.2", "ext-curl": "*", "ext-fileinfo": "*", "ext-json": "*", diff --git a/config/app.php b/config/app.php index 8833ea2ae7..2559b8012c 100755 --- a/config/app.php +++ b/config/app.php @@ -216,7 +216,8 @@ return [ */ 'require_saml' => env('REQUIRE_SAML', false), - + + /* |-------------------------------------------------------------------------- | Demo Mode Lockdown @@ -294,6 +295,7 @@ return [ Laravel\Tinker\TinkerServiceProvider::class, Unicodeveloper\DumbPassword\DumbPasswordServiceProvider::class, Eduardokum\LaravelMailAutoEmbed\ServiceProvider::class, + Laravel\Socialite\SocialiteServiceProvider::class, /* * Application Service Providers... @@ -366,6 +368,7 @@ return [ 'Image' => Intervention\Image\ImageServiceProvider::class, 'Carbon' => Carbon\Carbon::class, 'Helper' => App\Helpers\Helper::class, // makes it much easier to use 'Helper::blah' in blades (which is where we usually use this) + 'Socialite' => Laravel\Socialite\Facades\Socialite::class, ], diff --git a/config/dompdf.php b/config/dompdf.php index a2cdae8599..22f5f6c27e 100644 --- a/config/dompdf.php +++ b/config/dompdf.php @@ -74,8 +74,11 @@ return array( * This is only checked on command line call by dompdf.php, but not by * direct class use like: * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output(); + * + * + * Hardcoding 2 Paths needed for Docker Containers */ - "chroot" => realpath(base_path()), + "chroot" => realpath(base_path()) . ",/var/lib/snipeit/data/uploads,/var/lib/snipeit/data/private_uploads", /** * Whether to enable font subsetting or not. diff --git a/config/hashing.php b/config/hashing.php index d3c8e2fb22..06f18a0c6d 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -15,7 +15,7 @@ return [ | */ - 'driver' => 'bcrypt', + 'driver' => env('HASHING_DRIVER', 'bcrypt'), /* |-------------------------------------------------------------------------- @@ -44,9 +44,9 @@ return [ */ 'argon' => [ - 'memory' => 1024, - 'threads' => 2, - 'time' => 2, + 'memory' => env('ARGON_MEMORY', 1024), + 'threads' => env('ARGON_THREADS', 2), + 'time' => env('ARGON_TIME', 2), ], ]; diff --git a/config/mail.php b/config/mail.php index d47d182ed3..f650cf5387 100755 --- a/config/mail.php +++ b/config/mail.php @@ -140,8 +140,8 @@ return [ | */ - 'sendmail' => '/usr/sbin/sendmail -bs', - + 'sendmail' => env('SENDMAIL_PATH', ini_get('sendmail_path')), + 'markdown' => [ 'theme' => 'default', 'paths' => [ diff --git a/config/services.php b/config/services.php index 3f7362fd51..de8c4ed71a 100644 --- a/config/services.php +++ b/config/services.php @@ -43,10 +43,10 @@ return [ 'secret' => env('STRIPE_SECRET'), ], - 'stunning' => [ - 'enabled' => env('ENABLE_STUNNING', false), - 'app_key' => env('STUNNING_APP_KEY'), - 'stripe_id' => env('STUNNING_STRIPE_ID'), + 'baremetrics' => [ + 'enabled' => env('ENABLE_BMPAY', false), + 'app_key' => env('BMPAY_PUBLIC_KEY', null), + 'stripe_id' => env('BMPAY_STRIPE_ID', null), ], 'google' => [ diff --git a/config/session.php b/config/session.php index dee8d9103e..688340c9e2 100644 --- a/config/session.php +++ b/config/session.php @@ -130,7 +130,7 @@ return [ | */ - 'path' => '/', + 'path' => env('SESSION_COOKIE_PATH', '/'), /* |-------------------------------------------------------------------------- diff --git a/config/version.php b/config/version.php index fba609ce3a..ba58e48dc2 100644 --- a/config/version.php +++ b/config/version.php @@ -1,10 +1,10 @@ 'v6.1.0-pre', - 'full_app_version' => 'v6.1.0-pre - build 10030-gdcbd216e2', - 'build_version' => '10030', + 'app_version' => 'v6.1.1', + 'full_app_version' => 'v6.1.1 - build 10847-g2ac4449ea', + 'build_version' => '10847', 'prerelease_version' => '', - 'hash_version' => 'gdcbd216e2', - 'full_hash' => 'v6.1.0-pre-986-gdcbd216e2', + 'hash_version' => 'g2ac4449ea', + 'full_hash' => 'v6.1.1-605-g2ac4449ea', 'branch' => 'develop', ); \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml index 4eccb2adbc..7abe441e4f 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -3,6 +3,7 @@ "files": [ { "source" : "/resources/lang/en/**/*.php", - "translation" : "/resources/lang/%locale%/%original_file_name%" + # https://developer.crowdin.com/configuration-file/#placeholders + "translation" : "/resources/lang/%locale%/**/%original_file_name%" } ] diff --git a/database/factories/CheckoutAcceptanceFactory.php b/database/factories/CheckoutAcceptanceFactory.php new file mode 100644 index 0000000000..b5744527f8 --- /dev/null +++ b/database/factories/CheckoutAcceptanceFactory.php @@ -0,0 +1,41 @@ + Asset::class, + 'checkoutable_id' => Asset::factory(), + 'assigned_to_id' => User::factory(), + ]; + } + + public function forAccessory() + { + return $this->state([ + 'checkoutable_type' => Accessory::class, + 'checkoutable_id' => Accessory::factory(), + ]); + } + + public function pending() + { + return $this->state([ + 'accepted_at' => null, + 'declined_at' => null, + ]); + } +} diff --git a/database/factories/ComponentFactory.php b/database/factories/ComponentFactory.php index 6db9ef24fa..caac70078f 100644 --- a/database/factories/ComponentFactory.php +++ b/database/factories/ComponentFactory.php @@ -7,6 +7,7 @@ use App\Models\Company; use App\Models\Component; use App\Models\Location; use Illuminate\Database\Eloquent\Factories\Factory; +use App\Models\Supplier; class ComponentFactory extends Factory { @@ -35,6 +36,7 @@ class ComponentFactory extends Factory 'purchase_cost' => $this->faker->randomFloat(2), 'min_amt' => $this->faker->numberBetween($min = 1, $max = 2), 'company_id' => Company::factory(), + 'supplier_id' => Supplier::factory(), ]; } diff --git a/database/factories/ConsumableFactory.php b/database/factories/ConsumableFactory.php index 8dd56229e0..18a116418b 100644 --- a/database/factories/ConsumableFactory.php +++ b/database/factories/ConsumableFactory.php @@ -8,6 +8,7 @@ use App\Models\Consumable; use App\Models\Manufacturer; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; +use App\Models\Supplier; class ConsumableFactory extends Factory { @@ -26,7 +27,7 @@ class ConsumableFactory extends Factory public function definition() { return [ - 'name' => $this->faker->word(), + 'name' => $this->faker->words(3, true), 'category_id' => Category::factory(), 'user_id' => User::factory()->superuser(), 'item_no' => $this->faker->numberBetween(1000000, 50000000), @@ -36,6 +37,7 @@ class ConsumableFactory extends Factory 'qty' => $this->faker->numberBetween(5, 10), 'min_amt' => $this->faker->numberBetween($min = 1, $max = 2), 'company_id' => Company::factory(), + 'supplier_id' => Supplier::factory(), ]; } diff --git a/database/factories/CustomFieldFactory.php b/database/factories/CustomFieldFactory.php index bfa41b4d8d..adcca9cae1 100644 --- a/database/factories/CustomFieldFactory.php +++ b/database/factories/CustomFieldFactory.php @@ -25,6 +25,7 @@ class CustomFieldFactory extends Factory 'name' => $this->faker->catchPhrase(), 'format' => '', 'element' => 'text', + 'auto_add_to_fieldsets' => '0', ]; } diff --git a/database/factories/LicenseSeatFactory.php b/database/factories/LicenseSeatFactory.php new file mode 100644 index 0000000000..3c6cc4246b --- /dev/null +++ b/database/factories/LicenseSeatFactory.php @@ -0,0 +1,16 @@ + License::factory(), + ]; + } +} diff --git a/database/factories/ManufacturerFactory.php b/database/factories/ManufacturerFactory.php index ab22262a78..4e736b8d8a 100644 --- a/database/factories/ManufacturerFactory.php +++ b/database/factories/ManufacturerFactory.php @@ -38,6 +38,7 @@ class ManufacturerFactory extends Factory 'name' => 'Apple', 'url' => 'https://apple.com', 'support_url' => 'https://support.apple.com', + 'warranty_lookup_url' => 'https://checkcoverage.apple.com', 'image' => 'apple.jpg', ]; }); @@ -50,6 +51,7 @@ class ManufacturerFactory extends Factory 'name' => 'Microsoft', 'url' => 'https://microsoft.com', 'support_url' => 'https://support.microsoft.com', + 'warranty_lookup_url' => 'https://account.microsoft.com/devices', 'image' => 'microsoft.png', ]; }); @@ -62,6 +64,7 @@ class ManufacturerFactory extends Factory 'name' => 'Dell', 'url' => 'https://dell.com', 'support_url' => 'https://support.dell.com', + 'warranty_lookup_url' => 'https://www.dell.com/support/home/en-us/Products/?app=warranty', 'image' => 'dell.png', ]; }); diff --git a/database/factories/SettingFactory.php b/database/factories/SettingFactory.php index 970d00cd68..1655bd3350 100644 --- a/database/factories/SettingFactory.php +++ b/database/factories/SettingFactory.php @@ -34,13 +34,4 @@ class SettingFactory extends Factory 'email_domain' => 'test.com', ]; } - - public function withMultipleFullCompanySupport() - { - return $this->state(function () { - return [ - 'full_multiple_companies_support' => 1, - ]; - }); - } } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index f40301753b..8aa38d2325 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -65,6 +65,20 @@ class UserFactory extends Factory }); } + public function testAdmin() + { + return $this->state(function () { + return [ + 'first_name' => 'Alison', + 'last_name' => 'Gianotto', + 'username' => 'agianotto@grokability.com', + 'avatar' => '2.jpg', + 'email' => 'agianotto@grokability.com', + 'permissions' => '{"superuser":"1"}', + ]; + }); + } + public function superuser() { return $this->state(function () { diff --git a/database/migrations/2023_04_12_135822_add_supplier_to_components.php b/database/migrations/2023_04_12_135822_add_supplier_to_components.php new file mode 100644 index 0000000000..447c7850a9 --- /dev/null +++ b/database/migrations/2023_04_12_135822_add_supplier_to_components.php @@ -0,0 +1,48 @@ +integer('supplier_id')->after('user_id')->nullable()->default(null); + } + }); + + Schema::table('consumables', function (Blueprint $table) { + if (!Schema::hasColumn('consumables', 'supplier_id')) { + $table->integer('supplier_id')->after('user_id')->nullable()->default(null); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('components', function (Blueprint $table) { + if (Schema::hasColumn('components', 'supplier_id')) { + $table->dropColumn('supplier_id'); + } + }); + + Schema::table('consumables', function (Blueprint $table) { + if (Schema::hasColumn('consumables', 'supplier_id')) { + $table->dropColumn('supplier_id'); + } + }); + } +} diff --git a/database/migrations/2023_04_25_085912_add_autoadd_to_customfields.php b/database/migrations/2023_04_25_085912_add_autoadd_to_customfields.php new file mode 100644 index 0000000000..918bec9c5c --- /dev/null +++ b/database/migrations/2023_04_25_085912_add_autoadd_to_customfields.php @@ -0,0 +1,34 @@ +boolean('auto_add_to_fieldsets')->nullable()->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('custom_fields', function (Blueprint $table) { + if (Schema::hasColumn('custom_fields', 'auto_add_to_fieldsets')) { + $table->dropColumn('auto_add_to_fieldsets'); + } + }); + } +} diff --git a/database/migrations/2023_04_25_181817_adds_ldap_location_to_settings_table.php b/database/migrations/2023_04_25_181817_adds_ldap_location_to_settings_table.php new file mode 100644 index 0000000000..60c0e31a67 --- /dev/null +++ b/database/migrations/2023_04_25_181817_adds_ldap_location_to_settings_table.php @@ -0,0 +1,32 @@ +string('ldap_location')->after('ldap_country')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('ldap_location'); + }); + } +} diff --git a/database/migrations/2023_04_26_160235_add_warranty_url_to_manufacturers.php b/database/migrations/2023_04_26_160235_add_warranty_url_to_manufacturers.php new file mode 100644 index 0000000000..52a655f2e4 --- /dev/null +++ b/database/migrations/2023_04_26_160235_add_warranty_url_to_manufacturers.php @@ -0,0 +1,32 @@ +string('warranty_lookup_url')->after('support_url')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('manufacturers', function (Blueprint $table) { + $table->dropColumn('warranty_lookup_url'); + }); + } +} diff --git a/database/migrations/2023_05_08_132921_increase_state_to_more_than_3.php b/database/migrations/2023_05_08_132921_increase_state_to_more_than_3.php new file mode 100644 index 0000000000..7ffc0c3860 --- /dev/null +++ b/database/migrations/2023_05_08_132921_increase_state_to_more_than_3.php @@ -0,0 +1,40 @@ +string('state', 191)->nullable()->default(null)->change(); + }); + + Schema::table('suppliers', function (Blueprint $table) { + $table->string('state', 191)->nullable()->default(null)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->string('state', 3)->nullable()->default(null)->change(); + }); + + Schema::table('suppliers', function (Blueprint $table) { + $table->string('state', 32)->nullable()->default(null)->change(); + }); + } +} diff --git a/database/migrations/2023_05_10_001836_add_google_auth_to_settings.php b/database/migrations/2023_05_10_001836_add_google_auth_to_settings.php new file mode 100644 index 0000000000..7f15e0d6e1 --- /dev/null +++ b/database/migrations/2023_05_10_001836_add_google_auth_to_settings.php @@ -0,0 +1,37 @@ +boolean('google_login')->nullable()->default(0); + $table->string('google_client_id')->nullable()->default(null); + $table->string('google_client_secret')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('google_login'); + $table->dropColumn('google_client_id'); + $table->dropColumn('google_client_secret'); + + }); + } +} diff --git a/database/seeders/AssetSeeder.php b/database/seeders/AssetSeeder.php index 53af19758b..5fdc09bdb3 100644 --- a/database/seeders/AssetSeeder.php +++ b/database/seeders/AssetSeeder.php @@ -29,26 +29,26 @@ class AssetSeeder extends Seeder $this->locationIds = Location::all()->pluck('id'); $this->supplierIds = Supplier::all()->pluck('id'); - Asset::factory()->count(1000)->laptopMbp()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(2000)->laptopMbp()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(50)->laptopMbpPending()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(50)->laptopMbpArchived()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(50)->laptopAir()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(5)->laptopSurface()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(50)->laptopSurface()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(5)->laptopXps()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(5)->laptopSpectre()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(5)->laptopZenbook()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(3)->laptopYoga()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(50)->laptopZenbook()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(30)->laptopYoga()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(30)->desktopMacpro()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(30)->desktopLenovoI5()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(30)->desktopOptiplex()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(5)->confPolycom()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(2)->confPolycomcx()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(12)->tabletIpad()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(4)->tabletTab3()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(50)->confPolycom()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(20)->confPolycomcx()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(30)->tabletIpad()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(10)->tabletTab3()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(27)->phoneIphone11()->state(new Sequence($this->getState()))->create(); Asset::factory()->count(40)->phoneIphone12()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(10)->ultrafine()->state(new Sequence($this->getState()))->create(); - Asset::factory()->count(10)->ultrasharp()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(20)->ultrafine()->state(new Sequence($this->getState()))->create(); + Asset::factory()->count(20)->ultrasharp()->state(new Sequence($this->getState()))->create(); $del_files = Storage::files('assets'); foreach ($del_files as $del_file) { // iterate files diff --git a/database/seeders/CustomFieldSeeder.php b/database/seeders/CustomFieldSeeder.php index 8776872644..551e05f40f 100644 --- a/database/seeders/CustomFieldSeeder.php +++ b/database/seeders/CustomFieldSeeder.php @@ -38,24 +38,34 @@ class CustomFieldSeeder extends Seeder [ 'custom_field_id' => '1', 'custom_fieldset_id' => '1', + 'order' => 0, + 'required' => 0, ], [ 'custom_field_id' => '2', 'custom_fieldset_id' => '1', + 'order' => 0, + 'required' => 0, ], [ - 'custom_field_id' => '3', - 'custom_fieldset_id' => '2', + 'custom_field_id' => '3', + 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, ], [ - 'custom_field_id' => '4', - 'custom_fieldset_id' => '2', + 'custom_field_id' => '4', + 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, ], [ - 'custom_field_id' => '5', - 'custom_fieldset_id' => '2', + 'custom_field_id' => '5', + 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, ], - ]); + ]); } } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 2eba6f3721..9c237b169e 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -47,6 +47,13 @@ class UserSeeder extends Seeder ])) ->create(); + User::factory()->count(1)->testAdmin() + ->state(new Sequence(fn($sequence) => [ + 'company_id' => $companyIds->random(), + 'department_id' => $departmentIds->random(), + ])) + ->create(); + User::factory()->count(3)->superuser() ->state(new Sequence(fn($sequence) => [ 'company_id' => $companyIds->random(), diff --git a/package-lock.json b/package-lock.json index 327f8ede3a..7de0d6ac54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1329,9 +1329,9 @@ "dev": true }, "@fortawesome/fontawesome-free": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz", - "integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==" + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", + "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==" }, "@jridgewell/gen-mapping": { "version": "0.1.1", @@ -1346,26 +1346,43 @@ "@jridgewell/resolve-uri": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true + "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==" }, "@jridgewell/set-array": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", - "dev": true + "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==" + }, + "@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } }, "@jridgewell/sourcemap-codec": { "version": "1.4.13", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true + "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" }, "@jridgewell/trace-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", - "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1405,6 +1422,21 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==" }, + "@swc/helpers": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", + "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==", + "requires": { + "tslib": "^2.4.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, "@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -1533,9 +1565,9 @@ } }, "@types/eslint": { - "version": "8.21.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.2.tgz", - "integrity": "sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==", "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -1551,9 +1583,9 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" }, "@types/express": { "version": "4.17.13", @@ -1852,133 +1884,133 @@ } }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -2038,9 +2070,9 @@ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" }, "acorn-node": { "version": "1.8.2", @@ -2155,12 +2187,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "optional": true - }, "ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -2200,11 +2226,6 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=" - }, "array-union": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", @@ -2252,58 +2273,6 @@ } } }, - "ast-transform": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz", - "integrity": "sha1-dJRAWIh9goPhidlUYAlHvJj+AGI=", - "requires": { - "escodegen": "~1.2.0", - "esprima": "~1.0.4", - "through": "~2.3.4" - }, - "dependencies": { - "escodegen": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz", - "integrity": "sha1-Cd55Z3kcyVi3+Jot220jRRrzJ+E=", - "requires": { - "esprima": "~1.0.4", - "estraverse": "~1.5.0", - "esutils": "~1.0.0", - "source-map": "~0.1.30" - } - }, - "esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" - }, - "estraverse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", - "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" - }, - "esutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", - "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "ast-types": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", - "integrity": "sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk=" - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2973,6 +2942,7 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -3157,11 +3127,11 @@ } }, "bootstrap-datepicker": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.9.0.tgz", - "integrity": "sha1-5L/OP8zhlnh2sh3Ggz7FmUqu0JA=", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.10.0.tgz", + "integrity": "sha512-lWxtSYddAQOpbAO8UhYhHLcK6425eWoSjb5JDvZU3ePHEPF6A3eUr51WKaFy4PccU19JRxUG6wEU3KdhtKfvpg==", "requires": { - "jquery": ">=1.7.1 <4.0.0" + "jquery": ">=3.4.0 <4.0.0" } }, "bootstrap-daterangepicker": { @@ -3184,9 +3154,9 @@ "integrity": "sha1-EQPWvADPv6jPyaJZmrUYxVZD2j8=" }, "bootstrap-table": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.20.2.tgz", - "integrity": "sha512-6j9zfjjK6VZyJj8KsH+LnGczqglmMvMctGAoEAKDvrQ92ExQbA3mHGYPQr9iPrzoyeGL8+6Dyx6LqJbWmWmBoA==" + "version": "1.21.4", + "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.21.4.tgz", + "integrity": "sha512-Vp0kwkCsZzBINiA1KJ46LSkIa4oA0r6GSuzERZEqkUU8/0JDij2aG4CdYxiOm/UFCPZ9+KuAE4zSjxJLxg5jWw==" }, "brace-expansion": { "version": "1.1.11", @@ -3205,26 +3175,15 @@ "fill-range": "^7.0.1" } }, - "brfs": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz", - "integrity": "sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ==", - "requires": { - "quote-stream": "^1.0.1", - "resolve": "^1.1.5", - "static-module": "^3.0.2", - "through2": "^2.0.0" - } - }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "brotli": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.2.tgz", - "integrity": "sha1-UlqcrU/LqWR119OI9q7LE+7VL0Y=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", "requires": { "base64-js": "^1.1.2" } @@ -3396,31 +3355,6 @@ "safe-buffer": "^5.1.2" } }, - "browserify-optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz", - "integrity": "sha1-HhNyLP3g2F8SFnbCpyztUzoBiGk=", - "requires": { - "ast-transform": "0.0.0", - "ast-types": "^0.7.0", - "browser-resolve": "^1.8.1" - }, - "dependencies": { - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "requires": { - "resolve": "1.1.7" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" - } - } - }, "browserify-rsa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", @@ -3505,11 +3439,6 @@ "ieee754": "^1.1.4" } }, - "buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=" - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -3626,15 +3555,15 @@ }, "dependencies": { "core-js": { - "version": "3.22.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.5.tgz", - "integrity": "sha512-VP/xYuvJ0MJWRAobcmQ8F2H6Bsn+s7zqAAjFaHGBMc5AQm7zaelhD1LGduFn2EehEcQcU+br6t+fwbpQ5d1ZWA==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", + "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", "optional": true }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "optional": true } } @@ -3950,46 +3879,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "connect-history-api-fallback": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", @@ -4048,6 +3937,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -4075,7 +3965,8 @@ "core-js": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "dev": true }, "core-js-compat": { "version": "3.23.2", @@ -4406,15 +4297,6 @@ "css-tree": "^1.1.2" } }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "dash-ast": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", @@ -4468,23 +4350,127 @@ } }, "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", + "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.2", + "get-intrinsic": "^1.1.3", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "dependencies": { + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + } } }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, "default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -4659,9 +4645,9 @@ } }, "dompurify": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.7.tgz", - "integrity": "sha512-fsVZLywBd3awZIG3qU4JEdw7DCb0uUCajTfWRrLhsgKjTBd5CIIluPoAkNfco05GuNYQGj4/+bQIhlq96eT9eQ==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", + "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==", "optional": true }, "domutils": { @@ -4768,9 +4754,9 @@ } }, "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", + "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -4827,10 +4813,57 @@ "string.prototype.trimstart": "^1.0.3" } }, + "es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==" }, "es-to-primitive": { "version": "1.2.1", @@ -4842,71 +4875,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.61", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", - "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - }, - "dependencies": { - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - } - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -4924,26 +4892,6 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - } - } - }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -4953,11 +4901,6 @@ "estraverse": "^4.1.1" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -4978,15 +4921,11 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, - "estree-is-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-is-function/-/estree-is-function-1.0.0.tgz", - "integrity": "sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==" - }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=" + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "dev": true }, "etag": { "version": "1.8.1", @@ -4999,15 +4938,6 @@ "resolved": "https://registry.npmjs.org/eve-raphael/-/eve-raphael-0.5.0.tgz", "integrity": "sha1-F8dUt5K+7z+maE15z1pHxjxM2jA=" }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -5106,21 +5036,6 @@ } } }, - "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "requires": { - "type": "^2.5.0" - }, - "dependencies": { - "type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" - } - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5143,11 +5058,6 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", @@ -5313,42 +5223,27 @@ "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" }, "fontkit": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.8.1.tgz", - "integrity": "sha512-BsNCjDoYRxmNWFdAuK1y9bQt+igIxGtTC9u/jSFjR9MKhmI00rP1fwSvERt+5ddE82544l0XH5mzXozQVUy2Tw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.9.0.tgz", + "integrity": "sha512-HkW/8Lrk8jl18kzQHvAw9aTHe1cqsyx5sDnxncx652+CIfhawokEPkeM3BoIC+z/Xv7a0yMr0f3pRRwhGH455g==", "requires": { - "babel-runtime": "^6.26.0", - "brfs": "^2.0.0", - "brotli": "^1.2.0", - "browserify-optional": "^1.0.1", - "clone": "^1.0.4", - "deep-equal": "^1.0.0", + "@swc/helpers": "^0.3.13", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "deep-equal": "^2.0.5", "dfa": "^1.2.0", - "restructure": "^0.5.3", - "tiny-inflate": "^1.0.2", - "unicode-properties": "^1.2.2", - "unicode-trie": "^0.3.0" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" - }, - "unicode-trie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz", - "integrity": "sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=", - "requires": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - } + "restructure": "^2.0.1", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.3.1", + "unicode-trie": "^2.0.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" } }, "foreach": { @@ -14872,6 +14767,11 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -14959,6 +14859,31 @@ } } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + } + } + }, "got": { "version": "11.8.5", "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", @@ -15011,17 +14936,45 @@ "ansi-regex": "^2.0.0" } }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=" }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + } + } + }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -15316,11 +15269,6 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, - "icheck": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/icheck/-/icheck-1.0.2.tgz", - "integrity": "sha1-BtCNo9R65EjBU7Jjm4bprX/fcSg=" - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -15513,6 +15461,33 @@ } } }, + "internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "requires": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + } + } + }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -15552,12 +15527,64 @@ "call-bind": "^1.0.0" } }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "dependencies": { + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + } + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -15567,6 +15594,15 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -15620,6 +15656,11 @@ "is-extglob": "^2.1.1" } }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" + }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -15630,6 +15671,14 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -15654,12 +15703,33 @@ "has-symbols": "^1.0.1" } }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -15680,6 +15750,20 @@ "has-symbols": "^1.0.1" } }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "is-what": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", @@ -15855,9 +15939,9 @@ }, "dependencies": { "core-js": { - "version": "3.22.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.5.tgz", - "integrity": "sha512-VP/xYuvJ0MJWRAobcmQ8F2H6Bsn+s7zqAAjFaHGBMc5AQm7zaelhD1LGduFn2EehEcQcU+br6t+fwbpQ5d1ZWA==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", + "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", "optional": true } } @@ -16276,15 +16360,6 @@ } } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lilconfig": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", @@ -16292,19 +16367,18 @@ "dev": true }, "linebreak": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.0.2.tgz", - "integrity": "sha512-bJwSRsJeAmaZYnkcwl5sCQNfSDAhBuXxb6L27tb+qkBRtUQSSTUa5bcgCPD6hFEkRNlpWHfK7nFMmcANU7ZP1w==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", "requires": { "base64-js": "0.0.8", - "brfs": "^2.0.2", - "unicode-trie": "^1.0.0" + "unicode-trie": "^2.0.0" }, "dependencies": { "base64-js": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", - "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==" } } }, @@ -16411,14 +16485,6 @@ "yallist": "^4.0.0" } }, - "magic-string": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz", - "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==", - "requires": { - "sourcemap-codec": "^1.4.1" - } - }, "md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -16756,11 +16822,6 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -17081,19 +17142,6 @@ "is-wsl": "^2.2.0" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -17329,7 +17377,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "optional": true }, "picocolors": { @@ -18002,11 +18050,6 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, "prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", @@ -18129,16 +18172,6 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" }, - "quote-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", - "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", - "requires": { - "buffer-equal": "0.0.1", - "minimist": "^1.1.3", - "through2": "^2.0.0" - } - }, "raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -18289,7 +18322,8 @@ "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=" + "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=", + "dev": true }, "regenerator-transform": { "version": "0.10.1", @@ -18303,12 +18337,13 @@ } }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpu-core": { @@ -18421,12 +18456,9 @@ } }, "restructure": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-0.5.4.tgz", - "integrity": "sha1-9U591WNZD7NP1r9Vh2EJrsyyjeg=", - "requires": { - "browserify-optional": "^1.0.0" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz", + "integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==" }, "retry": { "version": "0.13.1", @@ -18442,7 +18474,7 @@ "rgbcolor": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha1-1lBezbMEplldom+ktDMHMGd1lF0=", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", "optional": true }, "rimraf": { @@ -18496,27 +18528,6 @@ "ajv-keywords": "^3.5.2" } }, - "scope-analyzer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/scope-analyzer/-/scope-analyzer-2.1.2.tgz", - "integrity": "sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ==", - "requires": { - "array-from": "^2.1.1", - "dash-ast": "^2.0.1", - "es6-map": "^0.1.5", - "es6-set": "^0.1.5", - "es6-symbol": "^3.1.1", - "estree-is-function": "^1.0.0", - "get-assigned-identifiers": "^1.1.0" - }, - "dependencies": { - "dash-ast": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", - "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==" - } - } - }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -18576,6 +18587,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, "requires": { "randombytes": "^2.1.0" } @@ -18675,11 +18687,6 @@ "kind-of": "^6.0.2" } }, - "shallow-copy": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", - "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" - }, "shasum-object": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", @@ -18723,7 +18730,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -18801,11 +18807,6 @@ } } }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, "spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -18913,72 +18914,6 @@ "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", "optional": true }, - "static-eval": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", - "integrity": "sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==", - "requires": { - "escodegen": "^1.11.1" - } - }, - "static-module": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/static-module/-/static-module-3.0.4.tgz", - "integrity": "sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw==", - "requires": { - "acorn-node": "^1.3.0", - "concat-stream": "~1.6.0", - "convert-source-map": "^1.5.1", - "duplexer2": "~0.1.4", - "escodegen": "^1.11.1", - "has": "^1.0.1", - "magic-string": "0.25.1", - "merge-source-map": "1.0.4", - "object-inspect": "^1.6.0", - "readable-stream": "~2.3.3", - "scope-analyzer": "^2.0.1", - "shallow-copy": "~0.0.1", - "static-eval": "^2.0.5", - "through2": "~2.0.3" - }, - "dependencies": { - "merge-source-map": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", - "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", - "requires": { - "source-map": "^0.5.6" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -18991,6 +18926,14 @@ "integrity": "sha512-/c645XdExBypL01TpFKiG/3RAa/Qmu+zRi0MwAmrdEkwHNuN0ebo8ccAXBBDa5Z0QOJgBskUIbuCK91x0sCVEw==", "dev": true }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "requires": { + "internal-slot": "^1.0.4" + } + }, "stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -19261,9 +19204,9 @@ } }, "tableexport.jquery.plugin": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/tableexport.jquery.plugin/-/tableexport.jquery.plugin-1.26.0.tgz", - "integrity": "sha512-PMad8npkQviiZG/J0kV6zphmcpNwMglxbxZjGR/7DtcKOTfiOzpcQQFIjHVqpnnKgpHHzOfV6GB9km6EkUAt4A==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/tableexport.jquery.plugin/-/tableexport.jquery.plugin-1.27.0.tgz", + "integrity": "sha512-aJ6XBeqtPqV8P2v8vvKNa54SGp9R4V4CIwaIhA1WTkflvoYaWqCyTmf/O6WH5kKBvDpva+RhLnYwsPHEPwdSsg==", "requires": { "file-saver": ">=2.0.1", "html2canvas": ">=1.0.0", @@ -19282,6 +19225,7 @@ "version": "5.10.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "dev": true, "requires": { "commander": "^2.20.0", "source-map": "~0.7.2", @@ -19291,12 +19235,14 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, @@ -19304,6 +19250,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", + "dev": true, "requires": { "jest-worker": "^27.4.1", "schema-utils": "^3.1.1", @@ -19315,12 +19262,14 @@ "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -19330,7 +19279,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -19454,19 +19404,6 @@ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -19522,28 +19459,12 @@ "dev": true }, "unicode-properties": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.3.1.tgz", - "integrity": "sha512-nIV3Tf3LcUEZttY/2g4ZJtGXhWwSkuLL+rCu0DIAMbjyVPj+8j5gNVz4T/sVbnQybIsd5SFGkPKg/756OY6jlA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", "requires": { "base64-js": "^1.3.0", "unicode-trie": "^2.0.0" - }, - "dependencies": { - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" - }, - "unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "requires": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - } } }, "unicode-property-aliases-ecmascript": { @@ -19553,9 +19474,9 @@ "dev": true }, "unicode-trie": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-1.0.0.tgz", - "integrity": "sha512-v5raLKsobbFbWLMoX9+bChts/VhPPj3XpkNr/HbqkirXR1DPk8eo9IYKyvk0MQZFkaoRsFj2Rmaqgi2rfAZYtA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", "requires": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" @@ -19564,7 +19485,7 @@ "pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" } } }, @@ -19758,21 +19679,21 @@ } }, "webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "version": "5.86.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz", + "integrity": "sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg==", "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.14.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -19781,33 +19702,93 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.1.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "dependencies": { + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.2.0.tgz", + "integrity": "sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ==", "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "terser": { + "version": "5.17.7", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz", + "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==", + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + }, + "terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + } + }, "webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", @@ -20117,6 +20098,29 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, "which-typed-array": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", @@ -20147,11 +20151,6 @@ "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -20224,11 +20223,11 @@ } }, "xmldoc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.1.2.tgz", - "integrity": "sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", + "integrity": "sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==", "requires": { - "sax": "^1.2.1" + "sax": "^1.2.4" } }, "xtend": { diff --git a/package.json b/package.json index 442513a9f9..6f007620de 100644 --- a/package.json +++ b/package.json @@ -24,21 +24,20 @@ "vue-template-compiler": "2.4.4" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.3.0", + "@fortawesome/fontawesome-free": "^6.4.0", "acorn": "^8.8.2", - "acorn-import-assertions": "^1.8.0", + "acorn-import-assertions": "^1.9.0", "admin-lte": "^2.4.18", "ajv": "^6.12.6", "blueimp-file-upload": "^9.34.0", "bootstrap": "^3.4.1", "bootstrap-colorpicker": "^2.5.3", - "bootstrap-datepicker": "^1.9.0", + "bootstrap-datepicker": "^1.10.0", "bootstrap-less": "^3.3.8", - "bootstrap-table": "1.20.2", + "bootstrap-table": "1.21.4", "chart.js": "^2.9.4", "css-loader": "^4.0.0", "ekko-lightbox": "^5.1.1", - "icheck": "^1.0.2", "imagemin": "^8.0.1", "jquery-form-validator": "^2.3.79", "jquery-slimscroll": "^1.3.8", @@ -52,9 +51,9 @@ "papaparse": "^4.3.3", "select2": "4.0.13", "sheetjs": "^2.0.0", - "tableexport.jquery.plugin": "1.26.0", + "tableexport.jquery.plugin": "1.27.0", "tether": "^1.4.0", "vue-resource": "^1.5.2", - "webpack": "^5.76.0" + "webpack": "^5.83.1" } } diff --git a/public/css/build/app.css b/public/css/build/app.css index b67a5f6a1c..c8fc025597 100644 --- a/public/css/build/app.css +++ b/public/css/build/app.css @@ -456,6 +456,9 @@ a.accordion-header { width: 50px; padding-top: 10px; } +.pull-text-right { + text-align: right !important; +} .main-header .sidebar-toggle:before { content: "\f0c9"; font-weight: 900; @@ -498,6 +501,9 @@ a.accordion-header { .btn-info.btn-outline { color: #5bc0de; } +.btn-warning { + background-color: #f39c12 !important; +} .btn-warning.btn-outline { color: #f0ad4e; } @@ -950,10 +956,10 @@ th.css-accessory > .th-inner::before { .select2-container--default .select2-selection--multiple { border-radius: 0px; } -.bs-checkbox input { - zoom: 1.5; -} @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } @@ -973,4 +979,144 @@ th.css-accessory > .th-inner::before { white-space: nowrap; text-overflow: ellipsis; } +/** Form-stuff overrides for checkboxes and stuff **/ +label.form-control { + display: grid; + grid-template-columns: 1.8em auto; + gap: 0.5em; + border: 0px; + padding-left: 0px; + background-color: inherit; + color: inherit; + font-size: inherit; + font-weight: inherit; +} +label.form-control--disabled { + color: #959495; + pointer-events: none; + cursor: not-allowed; +} +/** --------------------------------------- **/ +/** Start checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ +input[type="checkbox"] { + /* Add if not using autoprefixer */ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + /* For iOS < 15 to remove gradient background */ + background-color: #fff; + /* Not removed via appearance */ + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 0em; + transform: translateY(-0.075em); + display: grid; + place-content: center; + /*Windows High Contrast Mode*/ +} +/** This sets the display of a checkbox, and what the "fill" checkmark should look like */ +input[type="checkbox"]::before { + /** If you want to use the non-checkbox, filled square, use this instead **/ + box-shadow: inset 1em 1em #d3d3d3; + content: ""; + width: 1em; + height: 1em; + -webkit-clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + transform: scale(0); + transform-origin: bottom left; + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; + /* Windows High Contrast Mode */ + background-color: CanvasText; +} +/** This sets the size of the scale up for the shape we defined above **/ +input[type="checkbox"]:checked::before { + transform: scale(1); +} +/** This sets the scale and color of the DISABLED but CHECKED checkbox */ +input[type=checkbox]:disabled::before, +input[type=radio]:disabled::before { + content: ""; + width: 1em; + height: 1em; + transform: scale(1); + box-shadow: inset 1em 1em #d3d3d3; +} +/* This sets the scale and style of a DISABLED checkbox that is NOT checked */ +input[type=checkbox]:disabled:not(:checked)::before, +input[type=radio]:disabled:not(:checked)::before { + content: ""; + transform: scale(0); + cursor: not-allowed; + pointer-events: none; +} +/** this is the color of the checkbox and content on a disabled, checked box **/ +input[type=checkbox]:disabled, +input[type=radio]:disabled { + --form-control-color: #d3d3d3; + color: #959495; + cursor: not-allowed; + pointer-events: none; +} +/** Radio styles to replace iCheck **/ +input[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 50%; + transform: translateY(-0.075em); + display: grid; + place-content: center; +} +input[type="radio"]::before { + content: ""; + width: 1em; + height: 1em; + border-radius: 50%; + transform: scale(0); + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; +} +input[type="radio"]:checked::before { + transform: scale(1); +} +/** +* This addresses the column selector in bootstrap-table. Without these two lines, the +* checkbox and the with the label text that BS tables generates will +* end up on two different lines and it looks assy. + */ +.dropdown-item-marker input[type=checkbox] { + font-size: 10px; +} +.bootstrap-table .fixed-table-toolbar li.dropdown-item-marker label { + font-weight: normal; + display: grid; + grid-template-columns: 0.1em auto; + gap: 1.5em; +} +.container.row-striped .col-md-6 { + overflow-wrap: anywhere; +} +.nav-tabs-custom > .nav-tabs > li { + z-index: 1; +} +.select2-container .select2-search--inline .select2-search__field { + padding-left: 15px; +} +/** --------------------------------------- **/ +/** End checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ diff --git a/public/css/build/overrides.css b/public/css/build/overrides.css index 7d0450d520..5111695ac7 100644 --- a/public/css/build/overrides.css +++ b/public/css/build/overrides.css @@ -89,6 +89,9 @@ a.accordion-header { width: 50px; padding-top: 10px; } +.pull-text-right { + text-align: right !important; +} .main-header .sidebar-toggle:before { content: "\f0c9"; font-weight: 900; @@ -131,6 +134,9 @@ a.accordion-header { .btn-info.btn-outline { color: #5bc0de; } +.btn-warning { + background-color: #f39c12 !important; +} .btn-warning.btn-outline { color: #f0ad4e; } @@ -583,10 +589,10 @@ th.css-accessory > .th-inner::before { .select2-container--default .select2-selection--multiple { border-radius: 0px; } -.bs-checkbox input { - zoom: 1.5; -} @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } @@ -606,4 +612,144 @@ th.css-accessory > .th-inner::before { white-space: nowrap; text-overflow: ellipsis; } +/** Form-stuff overrides for checkboxes and stuff **/ +label.form-control { + display: grid; + grid-template-columns: 1.8em auto; + gap: 0.5em; + border: 0px; + padding-left: 0px; + background-color: inherit; + color: inherit; + font-size: inherit; + font-weight: inherit; +} +label.form-control--disabled { + color: #959495; + pointer-events: none; + cursor: not-allowed; +} +/** --------------------------------------- **/ +/** Start checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ +input[type="checkbox"] { + /* Add if not using autoprefixer */ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + /* For iOS < 15 to remove gradient background */ + background-color: #fff; + /* Not removed via appearance */ + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 0em; + transform: translateY(-0.075em); + display: grid; + place-content: center; + /*Windows High Contrast Mode*/ +} +/** This sets the display of a checkbox, and what the "fill" checkmark should look like */ +input[type="checkbox"]::before { + /** If you want to use the non-checkbox, filled square, use this instead **/ + box-shadow: inset 1em 1em #d3d3d3; + content: ""; + width: 1em; + height: 1em; + -webkit-clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + transform: scale(0); + transform-origin: bottom left; + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; + /* Windows High Contrast Mode */ + background-color: CanvasText; +} +/** This sets the size of the scale up for the shape we defined above **/ +input[type="checkbox"]:checked::before { + transform: scale(1); +} +/** This sets the scale and color of the DISABLED but CHECKED checkbox */ +input[type=checkbox]:disabled::before, +input[type=radio]:disabled::before { + content: ""; + width: 1em; + height: 1em; + transform: scale(1); + box-shadow: inset 1em 1em #d3d3d3; +} +/* This sets the scale and style of a DISABLED checkbox that is NOT checked */ +input[type=checkbox]:disabled:not(:checked)::before, +input[type=radio]:disabled:not(:checked)::before { + content: ""; + transform: scale(0); + cursor: not-allowed; + pointer-events: none; +} +/** this is the color of the checkbox and content on a disabled, checked box **/ +input[type=checkbox]:disabled, +input[type=radio]:disabled { + --form-control-color: #d3d3d3; + color: #959495; + cursor: not-allowed; + pointer-events: none; +} +/** Radio styles to replace iCheck **/ +input[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 50%; + transform: translateY(-0.075em); + display: grid; + place-content: center; +} +input[type="radio"]::before { + content: ""; + width: 1em; + height: 1em; + border-radius: 50%; + transform: scale(0); + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; +} +input[type="radio"]:checked::before { + transform: scale(1); +} +/** +* This addresses the column selector in bootstrap-table. Without these two lines, the +* checkbox and the with the label text that BS tables generates will +* end up on two different lines and it looks assy. + */ +.dropdown-item-marker input[type=checkbox] { + font-size: 10px; +} +.bootstrap-table .fixed-table-toolbar li.dropdown-item-marker label { + font-weight: normal; + display: grid; + grid-template-columns: 0.1em auto; + gap: 1.5em; +} +.container.row-striped .col-md-6 { + overflow-wrap: anywhere; +} +.nav-tabs-custom > .nav-tabs > li { + z-index: 1; +} +.select2-container .select2-search--inline .select2-search__field { + padding-left: 15px; +} +/** --------------------------------------- **/ +/** End checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ diff --git a/public/css/dist/all.css b/public/css/dist/all.css index f1f0b46d30..a1d9876340 100644 --- a/public/css/dist/all.css +++ b/public/css/dist/all.css @@ -6833,7 +6833,7 @@ button.close { } /*# sourceMappingURL=bootstrap.css.map */ /*! - * Font Awesome Free 6.3.0 by @fontawesome - https://fontawesome.com + * Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * Copyright 2023 Fonticons, Inc. */ @@ -21545,131 +21545,10 @@ a.ui-button:active, box-shadow: 0px 0px 5px #666666; } -/* iCheck plugin Minimal skin, blue ------------------------------------ */ -.icheckbox_minimal-blue, -.iradio_minimal-blue { - display: inline-block; - *display: inline; - vertical-align: middle; - margin: 0; - padding: 0; - width: 18px; - height: 18px; - background: url(blue.png) no-repeat; - border: none; - cursor: pointer; -} - -.icheckbox_minimal-blue { - background-position: 0 0; -} - .icheckbox_minimal-blue.hover { - background-position: -20px 0; - } - .icheckbox_minimal-blue.checked { - background-position: -40px 0; - } - .icheckbox_minimal-blue.disabled { - background-position: -60px 0; - cursor: default; - } - .icheckbox_minimal-blue.checked.disabled { - background-position: -80px 0; - } - -.iradio_minimal-blue { - background-position: -100px 0; -} - .iradio_minimal-blue.hover { - background-position: -120px 0; - } - .iradio_minimal-blue.checked { - background-position: -140px 0; - } - .iradio_minimal-blue.disabled { - background-position: -160px 0; - cursor: default; - } - .iradio_minimal-blue.checked.disabled { - background-position: -180px 0; - } - -/* Retina support */ -@media only screen and (-webkit-min-device-pixel-ratio: 1.5), - only screen and (-moz-min-device-pixel-ratio: 1.5), - only screen and (-o-min-device-pixel-ratio: 3/2), - only screen and (min-device-pixel-ratio: 1.5) { - .icheckbox_minimal-blue, - .iradio_minimal-blue { - background-image: url(blue@2x.png); - -webkit-background-size: 200px 20px; - background-size: 200px 20px; - } -} -/* iCheck plugin Minimal skin, black ------------------------------------ */ -.icheckbox_minimal, -.iradio_minimal { - display: inline-block; - *display: inline; - vertical-align: middle; - margin: 0; - padding: 0; - width: 18px; - height: 18px; - background: url(minimal.png) no-repeat; - border: none; - cursor: pointer; -} - -.icheckbox_minimal { - background-position: 0 0; -} - .icheckbox_minimal.hover { - background-position: -20px 0; - } - .icheckbox_minimal.checked { - background-position: -40px 0; - } - .icheckbox_minimal.disabled { - background-position: -60px 0; - cursor: default; - } - .icheckbox_minimal.checked.disabled { - background-position: -80px 0; - } - -.iradio_minimal { - background-position: -100px 0; -} - .iradio_minimal.hover { - background-position: -120px 0; - } - .iradio_minimal.checked { - background-position: -140px 0; - } - .iradio_minimal.disabled { - background-position: -160px 0; - cursor: default; - } - .iradio_minimal.checked.disabled { - background-position: -180px 0; - } - -/* HiDPI support */ -@media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) { - .icheckbox_minimal, - .iradio_minimal { - background-image: url(minimal@2x.png); - -webkit-background-size: 200px 20px; - background-size: 200px 20px; - } -} /*! - * Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker) + * Datepicker for Bootstrap v1.10.0 (https://github.com/uxsolutions/bootstrap-datepicker) * - * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * Licensed under the Apache License v2.0 (https://www.apache.org/licenses/LICENSE-2.0) */ .datepicker { @@ -22506,9 +22385,11 @@ a.ui-button:active, /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImVra28tbGlnaHRib3guY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGVBQ0UsOEJBQXlCLEFBQXpCLHVCQUF5QixBQUN6QixzQkFBb0IsQUFBcEIsbUJBQW9CLEFBQ3BCLHFCQUF3QixBQUF4Qix1QkFBd0IsQUFDeEIseUJBQTZCLENBQzlCLEFBQ0QseUJBQ0UsaUJBQW1CLENBQ3BCLEFBQ0QsZ0RBQ0Usa0JBQW1CLEFBQ25CLE1BQU8sQUFDUCxPQUFRLEFBQ1IsU0FBVSxBQUNWLFFBQVMsQUFDVCxVQUFZLENBQ2IsQUFDRCxzQkFDRSxXQUFZLEFBQ1osV0FBYSxDQUNkLEFBQ0QsMkJBQ0UsVUFBYSxBQUNiLGtCQUFtQixBQUNuQixNQUFPLEFBQ1AsT0FBUSxBQUNSLFdBQVksQUFDWixZQUFhLEFBQ2Isb0JBQWMsQUFBZCxZQUFjLENBQ2YsQUFDRCw2QkFDRSxXQUFRLEFBQVIsT0FBUSxBQUNSLG9CQUFjLEFBQWQsYUFBYyxBQUNkLHNCQUFvQixBQUFwQixtQkFBb0IsQUFDcEIsVUFBVyxBQUNYLHVCQUF5QixBQUN6QixXQUFZLEFBQ1osZUFBZ0IsQUFDaEIsU0FBYSxDQUNkLEFBQ0QsK0JBQ0Usb0JBQWEsQUFBYixXQUFhLENBQ2QsQUFDRCxvQ0FDRSxZQUFjLENBQ2YsQUFDRCxrQ0FDRSxjQUFnQixDQUNqQixBQUNELDZDQUNFLGdCQUFrQixDQUNuQixBQUNELG1DQUNFLG9CQUFzQixDQUN2QixBQUNELG1DQUNFLFlBQWMsQ0FDZixBQUNELHNDQUNFLGVBQWdCLEFBQ2hCLGlCQUFtQixDQUNwQixBQUNELHVCQUNFLFVBQVcsQUFDWCxvQkFBc0IsQ0FDdkIsQUFDRCw2QkFDRSxZQUFjLENBQ2YsQUFDRCw2QkFDRSxlQUFpQixDQUNsQixBQUNELHNCQUNFLGtCQUFtQixBQUNuQixNQUFPLEFBQ1AsT0FBUSxBQUNSLFNBQVUsQUFDVixRQUFTLEFBQ1QsV0FBWSxBQUNaLG9CQUFjLEFBQWQsYUFBYyxBQUVkLDBCQUF1QixBQUF2QixzQkFBdUIsQUFFdkIscUJBQXdCLEFBQXhCLHVCQUF3QixBQUV4QixzQkFBb0IsQUFBcEIsa0JBQW9CLENBQ3JCLEFBQ0QsMEJBQ0UsV0FBWSxBQUNaLFlBQWEsQUFDYixrQkFBbUIsQUFDbkIsaUJBQW1CLENBQ3BCLEFBQ0QsOEJBQ0UsV0FBWSxBQUNaLFlBQWEsQUFDYixrQkFBbUIsQUFDbkIsc0JBQXVCLEFBQ3ZCLFdBQWEsQUFDYixrQkFBbUIsQUFDbkIsTUFBTyxBQUNQLE9BQVEsQUFDUixtQ0FBNkMsQ0FDOUMsQUFDRCx5Q0FDRSxtQkFBcUIsQ0FDdEIsQUFDRCw0Q0FDRSxxQkFBdUIsQ0FDeEIsQUFVRCxhQUNFLE1BRUUsbUJBQW9CLEFBQ3BCLDBCQUE0QixDQUM3QixBQUNELElBQ0UsbUJBQW9CLEFBQ3BCLDBCQUE0QixDQUM3QixDQUNGIiwiZmlsZSI6ImVra28tbGlnaHRib3guY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmVra28tbGlnaHRib3gge1xuICBkaXNwbGF5OiBmbGV4ICFpbXBvcnRhbnQ7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICBwYWRkaW5nLXJpZ2h0OiAwcHghaW1wb3J0YW50O1xufVxuLmVra28tbGlnaHRib3gtY29udGFpbmVyIHtcbiAgcG9zaXRpb246IHJlbGF0aXZlO1xufVxuLmVra28tbGlnaHRib3gtY29udGFpbmVyID4gZGl2LmVra28tbGlnaHRib3gtaXRlbSB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiAwO1xuICBsZWZ0OiAwO1xuICBib3R0b206IDA7XG4gIHJpZ2h0OiAwO1xuICB3aWR0aDogMTAwJTtcbn1cbi5la2tvLWxpZ2h0Ym94IGlmcmFtZSB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSB7XG4gIHotaW5kZXg6IDEwMDtcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICB0b3A6IDA7XG4gIGxlZnQ6IDA7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGRpc3BsYXk6IGZsZXg7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhIHtcbiAgZmxleDogMTtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgb3BhY2l0eTogMDtcbiAgdHJhbnNpdGlvbjogb3BhY2l0eSAwLjVzO1xuICBjb2xvcjogI2ZmZjtcbiAgZm9udC1zaXplOiAzMHB4O1xuICB6LWluZGV4OiAxMDA7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhID4gKiB7XG4gIGZsZXgtZ3JvdzogMTtcbn1cbi5la2tvLWxpZ2h0Ym94LW5hdi1vdmVybGF5IGEgPiAqOmZvY3VzIHtcbiAgb3V0bGluZTogbm9uZTtcbn1cbi5la2tvLWxpZ2h0Ym94LW5hdi1vdmVybGF5IGEgc3BhbiB7XG4gIHBhZGRpbmc6IDAgMzBweDtcbn1cbi5la2tvLWxpZ2h0Ym94LW5hdi1vdmVybGF5IGE6bGFzdC1jaGlsZCBzcGFuIHtcbiAgdGV4dC1hbGlnbjogcmlnaHQ7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhOmhvdmVyIHtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xufVxuLmVra28tbGlnaHRib3gtbmF2LW92ZXJsYXkgYTpmb2N1cyB7XG4gIG91dGxpbmU6IG5vbmU7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhLmRpc2FibGVkIHtcbiAgY3Vyc29yOiBkZWZhdWx0O1xuICB2aXNpYmlsaXR5OiBoaWRkZW47XG59XG4uZWtrby1saWdodGJveCBhOmhvdmVyIHtcbiAgb3BhY2l0eTogMTtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xufVxuLmVra28tbGlnaHRib3ggLm1vZGFsLWRpYWxvZyB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG4uZWtrby1saWdodGJveCAubW9kYWwtZm9vdGVyIHtcbiAgdGV4dC1hbGlnbjogbGVmdDtcbn1cbi5la2tvLWxpZ2h0Ym94LWxvYWRlciB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiAwO1xuICBsZWZ0OiAwO1xuICBib3R0b206IDA7XG4gIHJpZ2h0OiAwO1xuICB3aWR0aDogMTAwJTtcbiAgZGlzcGxheTogZmxleDtcbiAgLyogZXN0YWJsaXNoIGZsZXggY29udGFpbmVyICovXG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIC8qIG1ha2UgbWFpbiBheGlzIHZlcnRpY2FsICovXG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICAvKiBjZW50ZXIgaXRlbXMgdmVydGljYWxseSwgaW4gdGhpcyBjYXNlICovXG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG59XG4uZWtrby1saWdodGJveC1sb2FkZXIgPiBkaXYge1xuICB3aWR0aDogNDBweDtcbiAgaGVpZ2h0OiA0MHB4O1xuICBwb3NpdGlvbjogcmVsYXRpdmU7XG4gIHRleHQtYWxpZ246IGNlbnRlcjtcbn1cbi5la2tvLWxpZ2h0Ym94LWxvYWRlciA+IGRpdiA+IGRpdiB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjtcbiAgb3BhY2l0eTogMC42O1xuICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIHRvcDogMDtcbiAgbGVmdDogMDtcbiAgYW5pbWF0aW9uOiBzay1ib3VuY2UgMnMgaW5maW5pdGUgZWFzZS1pbi1vdXQ7XG59XG4uZWtrby1saWdodGJveC1sb2FkZXIgPiBkaXYgPiBkaXY6bGFzdC1jaGlsZCB7XG4gIGFuaW1hdGlvbi1kZWxheTogLTFzO1xufVxuLm1vZGFsLWRpYWxvZyAuZWtrby1saWdodGJveC1sb2FkZXIgPiBkaXYgPiBkaXYge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMzMzO1xufVxuQC13ZWJraXQta2V5ZnJhbWVzIHNrLWJvdW5jZSB7XG4gIDAlLFxuICAxMDAlIHtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogc2NhbGUoMCk7XG4gIH1cbiAgNTAlIHtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogc2NhbGUoMSk7XG4gIH1cbn1cbkBrZXlmcmFtZXMgc2stYm91bmNlIHtcbiAgMCUsXG4gIDEwMCUge1xuICAgIHRyYW5zZm9ybTogc2NhbGUoMCk7XG4gICAgLXdlYmtpdC10cmFuc2Zvcm06IHNjYWxlKDApO1xuICB9XG4gIDUwJSB7XG4gICAgdHJhbnNmb3JtOiBzY2FsZSgxKTtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogc2NhbGUoMSk7XG4gIH1cbn1cbiJdfQ== */ /** * @author zhixin wen - * version: 1.20.2 + * version: 1.21.4 * https://github.com/wenzhixin/bootstrap-table/ */ +/* stylelint-disable annotation-no-unknown, max-line-length */ +/* stylelint-enable annotation-no-unknown, max-line-length */ .bootstrap-table .fixed-table-toolbar::after { content: ""; display: block; @@ -22616,6 +22497,11 @@ a.ui-button:active, padding-right: 30px !important; } +.bootstrap-table .fixed-table-container .table thead th .sortable.sortable-center { + padding-left: 20px !important; + padding-right: 20px !important; +} + .bootstrap-table .fixed-table-container .table thead th .both { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC"); } @@ -22840,7 +22726,7 @@ a.ui-button:active, } .bootstrap-table.bootstrap4 .pagination-lg .page-link, .bootstrap-table.bootstrap5 .pagination-lg .page-link { - padding: .5rem 1rem; + padding: 0.5rem 1rem; } .bootstrap-table.bootstrap5 .float-left { @@ -22873,7 +22759,7 @@ div.fixed-table-scroll-outer { 50% { opacity: 1; } - to { + 100% { opacity: 0; } } @@ -23336,6 +23222,9 @@ a.accordion-header { width: 50px; padding-top: 10px; } +.pull-text-right { + text-align: right !important; +} .main-header .sidebar-toggle:before { content: "\f0c9"; font-weight: 900; @@ -23378,6 +23267,9 @@ a.accordion-header { .btn-info.btn-outline { color: #5bc0de; } +.btn-warning { + background-color: #f39c12 !important; +} .btn-warning.btn-outline { color: #f0ad4e; } @@ -23830,10 +23722,10 @@ th.css-accessory > .th-inner::before { .select2-container--default .select2-selection--multiple { border-radius: 0px; } -.bs-checkbox input { - zoom: 1.5; -} @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } @@ -23853,6 +23745,146 @@ th.css-accessory > .th-inner::before { white-space: nowrap; text-overflow: ellipsis; } +/** Form-stuff overrides for checkboxes and stuff **/ +label.form-control { + display: grid; + grid-template-columns: 1.8em auto; + gap: 0.5em; + border: 0px; + padding-left: 0px; + background-color: inherit; + color: inherit; + font-size: inherit; + font-weight: inherit; +} +label.form-control--disabled { + color: #959495; + pointer-events: none; + cursor: not-allowed; +} +/** --------------------------------------- **/ +/** Start checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ +input[type="checkbox"] { + /* Add if not using autoprefixer */ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + /* For iOS < 15 to remove gradient background */ + background-color: #fff; + /* Not removed via appearance */ + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 0em; + transform: translateY(-0.075em); + display: grid; + place-content: center; + /*Windows High Contrast Mode*/ +} +/** This sets the display of a checkbox, and what the "fill" checkmark should look like */ +input[type="checkbox"]::before { + /** If you want to use the non-checkbox, filled square, use this instead **/ + box-shadow: inset 1em 1em #d3d3d3; + content: ""; + width: 1em; + height: 1em; + -webkit-clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + transform: scale(0); + transform-origin: bottom left; + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; + /* Windows High Contrast Mode */ + background-color: CanvasText; +} +/** This sets the size of the scale up for the shape we defined above **/ +input[type="checkbox"]:checked::before { + transform: scale(1); +} +/** This sets the scale and color of the DISABLED but CHECKED checkbox */ +input[type=checkbox]:disabled::before, +input[type=radio]:disabled::before { + content: ""; + width: 1em; + height: 1em; + transform: scale(1); + box-shadow: inset 1em 1em #d3d3d3; +} +/* This sets the scale and style of a DISABLED checkbox that is NOT checked */ +input[type=checkbox]:disabled:not(:checked)::before, +input[type=radio]:disabled:not(:checked)::before { + content: ""; + transform: scale(0); + cursor: not-allowed; + pointer-events: none; +} +/** this is the color of the checkbox and content on a disabled, checked box **/ +input[type=checkbox]:disabled, +input[type=radio]:disabled { + --form-control-color: #d3d3d3; + color: #959495; + cursor: not-allowed; + pointer-events: none; +} +/** Radio styles to replace iCheck **/ +input[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 50%; + transform: translateY(-0.075em); + display: grid; + place-content: center; +} +input[type="radio"]::before { + content: ""; + width: 1em; + height: 1em; + border-radius: 50%; + transform: scale(0); + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; +} +input[type="radio"]:checked::before { + transform: scale(1); +} +/** +* This addresses the column selector in bootstrap-table. Without these two lines, the +* checkbox and the with the label text that BS tables generates will +* end up on two different lines and it looks assy. + */ +.dropdown-item-marker input[type=checkbox] { + font-size: 10px; +} +.bootstrap-table .fixed-table-toolbar li.dropdown-item-marker label { + font-weight: normal; + display: grid; + grid-template-columns: 0.1em auto; + gap: 1.5em; +} +.container.row-striped .col-md-6 { + overflow-wrap: anywhere; +} +.nav-tabs-custom > .nav-tabs > li { + z-index: 1; +} +.select2-container .select2-search--inline .select2-search__field { + padding-left: 15px; +} +/** --------------------------------------- **/ +/** End checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ .select2-container { @@ -24428,6 +24460,9 @@ a.accordion-header { width: 50px; padding-top: 10px; } +.pull-text-right { + text-align: right !important; +} .main-header .sidebar-toggle:before { content: "\f0c9"; font-weight: 900; @@ -24470,6 +24505,9 @@ a.accordion-header { .btn-info.btn-outline { color: #5bc0de; } +.btn-warning { + background-color: #f39c12 !important; +} .btn-warning.btn-outline { color: #f0ad4e; } @@ -24922,10 +24960,10 @@ th.css-accessory > .th-inner::before { .select2-container--default .select2-selection--multiple { border-radius: 0px; } -.bs-checkbox input { - zoom: 1.5; -} @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } @@ -24945,4 +24983,144 @@ th.css-accessory > .th-inner::before { white-space: nowrap; text-overflow: ellipsis; } +/** Form-stuff overrides for checkboxes and stuff **/ +label.form-control { + display: grid; + grid-template-columns: 1.8em auto; + gap: 0.5em; + border: 0px; + padding-left: 0px; + background-color: inherit; + color: inherit; + font-size: inherit; + font-weight: inherit; +} +label.form-control--disabled { + color: #959495; + pointer-events: none; + cursor: not-allowed; +} +/** --------------------------------------- **/ +/** Start checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ +input[type="checkbox"] { + /* Add if not using autoprefixer */ + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + /* For iOS < 15 to remove gradient background */ + background-color: #fff; + /* Not removed via appearance */ + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 0em; + transform: translateY(-0.075em); + display: grid; + place-content: center; + /*Windows High Contrast Mode*/ +} +/** This sets the display of a checkbox, and what the "fill" checkmark should look like */ +input[type="checkbox"]::before { + /** If you want to use the non-checkbox, filled square, use this instead **/ + box-shadow: inset 1em 1em #d3d3d3; + content: ""; + width: 1em; + height: 1em; + -webkit-clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + transform: scale(0); + transform-origin: bottom left; + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; + /* Windows High Contrast Mode */ + background-color: CanvasText; +} +/** This sets the size of the scale up for the shape we defined above **/ +input[type="checkbox"]:checked::before { + transform: scale(1); +} +/** This sets the scale and color of the DISABLED but CHECKED checkbox */ +input[type=checkbox]:disabled::before, +input[type=radio]:disabled::before { + content: ""; + width: 1em; + height: 1em; + transform: scale(1); + box-shadow: inset 1em 1em #d3d3d3; +} +/* This sets the scale and style of a DISABLED checkbox that is NOT checked */ +input[type=checkbox]:disabled:not(:checked)::before, +input[type=radio]:disabled:not(:checked)::before { + content: ""; + transform: scale(0); + cursor: not-allowed; + pointer-events: none; +} +/** this is the color of the checkbox and content on a disabled, checked box **/ +input[type=checkbox]:disabled, +input[type=radio]:disabled { + --form-control-color: #d3d3d3; + color: #959495; + cursor: not-allowed; + pointer-events: none; +} +/** Radio styles to replace iCheck **/ +input[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + margin: 0; + font: inherit; + color: #959495; + width: 1.8em; + height: 1.8em; + border: 0.05em solid; + border-radius: 50%; + transform: translateY(-0.075em); + display: grid; + place-content: center; +} +input[type="radio"]::before { + content: ""; + width: 1em; + height: 1em; + border-radius: 50%; + transform: scale(0); + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em #428bca; +} +input[type="radio"]:checked::before { + transform: scale(1); +} +/** +* This addresses the column selector in bootstrap-table. Without these two lines, the +* checkbox and the with the label text that BS tables generates will +* end up on two different lines and it looks assy. + */ +.dropdown-item-marker input[type=checkbox] { + font-size: 10px; +} +.bootstrap-table .fixed-table-toolbar li.dropdown-item-marker label { + font-weight: normal; + display: grid; + grid-template-columns: 0.1em auto; + gap: 1.5em; +} +.container.row-striped .col-md-6 { + overflow-wrap: anywhere; +} +.nav-tabs-custom > .nav-tabs > li { + z-index: 1; +} +.select2-container .select2-search--inline .select2-search__field { + padding-left: 15px; +} +/** --------------------------------------- **/ +/** End checkbox styles to replace iCheck **/ +/** --------------------------------------- **/ diff --git a/public/css/dist/bootstrap-table.css b/public/css/dist/bootstrap-table.css index b95d3d2b77..8d409af9cb 100644 --- a/public/css/dist/bootstrap-table.css +++ b/public/css/dist/bootstrap-table.css @@ -1,8 +1,10 @@ /** * @author zhixin wen - * version: 1.20.2 + * version: 1.21.4 * https://github.com/wenzhixin/bootstrap-table/ */ +/* stylelint-disable annotation-no-unknown, max-line-length */ +/* stylelint-enable annotation-no-unknown, max-line-length */ .bootstrap-table .fixed-table-toolbar::after { content: ""; display: block; @@ -110,6 +112,11 @@ padding-right: 30px !important; } +.bootstrap-table .fixed-table-container .table thead th .sortable.sortable-center { + padding-left: 20px !important; + padding-right: 20px !important; +} + .bootstrap-table .fixed-table-container .table thead th .both { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC"); } @@ -334,7 +341,7 @@ } .bootstrap-table.bootstrap4 .pagination-lg .page-link, .bootstrap-table.bootstrap5 .pagination-lg .page-link { - padding: .5rem 1rem; + padding: 0.5rem 1rem; } .bootstrap-table.bootstrap5 .float-left { @@ -367,7 +374,7 @@ div.fixed-table-scroll-outer { 50% { opacity: 1; } - to { + 100% { opacity: 0; } } diff --git a/public/css/dist/skins/skin-black-dark.css b/public/css/dist/skins/skin-black-dark.css index 5ba6c5eadd..3e17735603 100644 --- a/public/css/dist/skins/skin-black-dark.css +++ b/public/css/dist/skins/skin-black-dark.css @@ -201,6 +201,37 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +a:hover > h2.task_menu { + color: var(--header); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu > li.header { + background-color: var(--header); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); diff --git a/public/css/dist/skins/skin-black-dark.min.css b/public/css/dist/skins/skin-black-dark.min.css index 5ba6c5eadd..3e17735603 100644 --- a/public/css/dist/skins/skin-black-dark.min.css +++ b/public/css/dist/skins/skin-black-dark.min.css @@ -201,6 +201,37 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +a:hover > h2.task_menu { + color: var(--header); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu > li.header { + background-color: var(--header); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); diff --git a/public/css/dist/skins/skin-blue-dark.css b/public/css/dist/skins/skin-blue-dark.css index a393ee578c..c4f2672931 100644 --- a/public/css/dist/skins/skin-blue-dark.css +++ b/public/css/dist/skins/skin-blue-dark.css @@ -196,6 +196,33 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -458,6 +485,12 @@ a:hover { a:visited { color: var(--visited-link); } +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; +} .row-striped { vertical-align: top; line-height: 2.6; diff --git a/public/css/dist/skins/skin-blue-dark.min.css b/public/css/dist/skins/skin-blue-dark.min.css index a393ee578c..c4f2672931 100644 --- a/public/css/dist/skins/skin-blue-dark.min.css +++ b/public/css/dist/skins/skin-blue-dark.min.css @@ -196,6 +196,33 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -458,6 +485,12 @@ a:hover { a:visited { color: var(--visited-link); } +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; +} .row-striped { vertical-align: top; line-height: 2.6; diff --git a/public/css/dist/skins/skin-green-dark.css b/public/css/dist/skins/skin-green-dark.css index 3fe0a606d3..8e5430e5cd 100644 --- a/public/css/dist/skins/skin-green-dark.css +++ b/public/css/dist/skins/skin-green-dark.css @@ -196,6 +196,33 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -216,9 +243,6 @@ a:visited { background-color: var(--back-main); color: var(--text-main); } -a:link { - color: var(--link); -} .btn-primary.hover { color: var(--nav-link); } @@ -371,10 +395,6 @@ input[type=search] { .nav-tabs-custom > .tab-content { background-color: var(--back-main); } -.navbar-nav > .tasks-menu > .dropdown-menu > li.header { - background-color: var(--back-main); - color: var(--header); -} .open > .dropdown-toggle.btn-default { background-color: var(--back-sub); color: var(--header); @@ -411,7 +431,7 @@ input[type=search] { color: var(--text-main); } .skin-green-dark .main-header .navbar .dropdown-menu li a { - color: var(--header); + color: var(--link); } .fixed-table-body thead th .th-inner, .skin-green-dark .sidebar-menu > li.active > a, @@ -452,6 +472,12 @@ a:hover { a:visited { color: var(--visited-link); } +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; +} .row-striped { vertical-align: top; line-height: 2.6; diff --git a/public/css/dist/skins/skin-green-dark.min.css b/public/css/dist/skins/skin-green-dark.min.css index 3fe0a606d3..8e5430e5cd 100644 --- a/public/css/dist/skins/skin-green-dark.min.css +++ b/public/css/dist/skins/skin-green-dark.min.css @@ -196,6 +196,33 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -216,9 +243,6 @@ a:visited { background-color: var(--back-main); color: var(--text-main); } -a:link { - color: var(--link); -} .btn-primary.hover { color: var(--nav-link); } @@ -371,10 +395,6 @@ input[type=search] { .nav-tabs-custom > .tab-content { background-color: var(--back-main); } -.navbar-nav > .tasks-menu > .dropdown-menu > li.header { - background-color: var(--back-main); - color: var(--header); -} .open > .dropdown-toggle.btn-default { background-color: var(--back-sub); color: var(--header); @@ -411,7 +431,7 @@ input[type=search] { color: var(--text-main); } .skin-green-dark .main-header .navbar .dropdown-menu li a { - color: var(--header); + color: var(--link); } .fixed-table-body thead th .th-inner, .skin-green-dark .sidebar-menu > li.active > a, @@ -452,6 +472,12 @@ a:hover { a:visited { color: var(--visited-link); } +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; +} .row-striped { vertical-align: top; line-height: 2.6; diff --git a/public/css/dist/skins/skin-orange-dark.css b/public/css/dist/skins/skin-orange-dark.css index fe7af4f628..1ec2c0701a 100644 --- a/public/css/dist/skins/skin-orange-dark.css +++ b/public/css/dist/skins/skin-orange-dark.css @@ -190,6 +190,33 @@ li.dropdown-item-marker { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -446,6 +473,12 @@ input[type=search] { .box-header.with-border { border-bottom: #000; } +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; +} .row-striped { vertical-align: top; line-height: 2.6; diff --git a/public/css/dist/skins/skin-orange-dark.min.css b/public/css/dist/skins/skin-orange-dark.min.css index fe7af4f628..1ec2c0701a 100644 --- a/public/css/dist/skins/skin-orange-dark.min.css +++ b/public/css/dist/skins/skin-orange-dark.min.css @@ -190,6 +190,33 @@ li.dropdown-item-marker { #ldapad_test_results.well.well-sm { color: var(--back-main); } +a.actions { + color: #fff !important; +} +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -446,6 +473,12 @@ input[type=search] { .box-header.with-border { border-bottom: #000; } +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; +} .row-striped { vertical-align: top; line-height: 2.6; diff --git a/public/css/dist/skins/skin-purple-dark.css b/public/css/dist/skins/skin-purple-dark.css index e1bd8edb4a..cec9819d7d 100644 --- a/public/css/dist/skins/skin-purple-dark.css +++ b/public/css/dist/skins/skin-purple-dark.css @@ -196,6 +196,30 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--button-default); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -383,7 +407,7 @@ input[type=search] { } .navbar-nav > .tasks-menu > .dropdown-menu > li.header { background-color: var(--back-main); - color: var(--header); + color: var(--link); } .open > .dropdown-toggle.btn-default { background-color: var(--back-sub); diff --git a/public/css/dist/skins/skin-purple-dark.min.css b/public/css/dist/skins/skin-purple-dark.min.css index e1bd8edb4a..cec9819d7d 100644 --- a/public/css/dist/skins/skin-purple-dark.min.css +++ b/public/css/dist/skins/skin-purple-dark.min.css @@ -196,6 +196,30 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--button-default); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -383,7 +407,7 @@ input[type=search] { } .navbar-nav > .tasks-menu > .dropdown-menu > li.header { background-color: var(--back-main); - color: var(--header); + color: var(--link); } .open > .dropdown-toggle.btn-default { background-color: var(--back-sub); diff --git a/public/css/dist/skins/skin-red-dark.css b/public/css/dist/skins/skin-red-dark.css index cbb5b07d14..17d495cbbb 100644 --- a/public/css/dist/skins/skin-red-dark.css +++ b/public/css/dist/skins/skin-red-dark.css @@ -197,6 +197,30 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -220,12 +244,12 @@ a:visited { a:link { color: var(--link); } -a:visited { - color: var(--nav-link); -} a:hover { color: var(--hover-link); } +a:visited { + color: var(--nav-link); +} .far fa-life-ring { color: var(--link); } @@ -266,7 +290,6 @@ body { } .btn-default { background-color: var(--back-sub); - color: var(--link); } .btn-default dropdown-toggle { background-color: var(--button-default); @@ -439,7 +462,6 @@ input[type=search] { } .table-striped > tbody > tr:nth-of-type(even) { background-color: var(--back-sub-alt); - color: var(--text-alt); } #webui > div > div > div > div > div > table > tbody > tr > td > a > i.fa, .box-body, @@ -450,18 +472,30 @@ input[type=search] { .box-header.with-border { border-bottom: #000; } +#upload-table tbody > tr.warning > td, +#upload-table h3, +#upload-table p { + background-color: #fcf8e3; + color: #000; +} a { color: var(--link); } a:link { - color: var(--link); + color: var(--nav-link); } a:hover { - color: var(--hover-link); + color: var(--nav-link); text-decoration: underline; } a:visited { - color: var(--visited-link); + color: var(--nav-link); +} +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; } .row-striped { vertical-align: top; diff --git a/public/css/dist/skins/skin-red-dark.min.css b/public/css/dist/skins/skin-red-dark.min.css index cbb5b07d14..17d495cbbb 100644 --- a/public/css/dist/skins/skin-red-dark.min.css +++ b/public/css/dist/skins/skin-red-dark.min.css @@ -197,6 +197,30 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +.pagination > li > a { + color: var(--light-link); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--visited-link); + border-color: var(--light-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -220,12 +244,12 @@ a:visited { a:link { color: var(--link); } -a:visited { - color: var(--nav-link); -} a:hover { color: var(--hover-link); } +a:visited { + color: var(--nav-link); +} .far fa-life-ring { color: var(--link); } @@ -266,7 +290,6 @@ body { } .btn-default { background-color: var(--back-sub); - color: var(--link); } .btn-default dropdown-toggle { background-color: var(--button-default); @@ -439,7 +462,6 @@ input[type=search] { } .table-striped > tbody > tr:nth-of-type(even) { background-color: var(--back-sub-alt); - color: var(--text-alt); } #webui > div > div > div > div > div > table > tbody > tr > td > a > i.fa, .box-body, @@ -450,18 +472,30 @@ input[type=search] { .box-header.with-border { border-bottom: #000; } +#upload-table tbody > tr.warning > td, +#upload-table h3, +#upload-table p { + background-color: #fcf8e3; + color: #000; +} a { color: var(--link); } a:link { - color: var(--link); + color: var(--nav-link); } a:hover { - color: var(--hover-link); + color: var(--nav-link); text-decoration: underline; } a:visited { - color: var(--visited-link); + color: var(--nav-link); +} +#customFieldsTable a[href*='/models'] { + color: var(--back-sub); +} +#customFieldsTable a[href*='/fieldsets'] { + background-color: transparent; } .row-striped { vertical-align: top; diff --git a/public/css/dist/skins/skin-yellow-dark.css b/public/css/dist/skins/skin-yellow-dark.css index 5691df2194..1f16540750 100644 --- a/public/css/dist/skins/skin-yellow-dark.css +++ b/public/css/dist/skins/skin-yellow-dark.css @@ -101,10 +101,6 @@ border-bottom-right-radius: 2px; border-bottom-left-radius: 0; } -.btn, -.btn:hover { - color: #000; -} .btn.btn-primary, .btn:hover.btn-primary, .btn .btn-primary:link, @@ -129,6 +125,9 @@ background-color: var(--hover-link); color: #545454; } +a.actions { + color: #fff !important; +} /** The dropdown is white, so use a darker color */ @@ -173,6 +172,31 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +.pagination > li > a { + color: var(--text-main); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--button-default); + border-color: var(--light-link); + color: var(--nav-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -186,7 +210,7 @@ a.btn.btn-default { color: var(--nav-link); } .bootstrap-table .fixed-table-container .table thead th .sortable { - color: var(--nav-link); + color: var(--text-main); } .bootstrap-table .fixed-table-toolbar .columns label { color: #000; @@ -398,14 +422,9 @@ input[type=search] { .skin-yellow-dark .main-header .navbar .dropdown-menu li a { color: var(--header); } -div.th-inner { +tr th div.th-inner { color: var(--text-main); } -.fixed-table-body thead th .th-inner, -.skin-yellow-dark, -.skin-yellow { - background-color: var(--header) !important; -} .tab-content, .tab-pane { background-color: var(--back-main); diff --git a/public/css/dist/skins/skin-yellow-dark.min.css b/public/css/dist/skins/skin-yellow-dark.min.css index 5691df2194..1f16540750 100644 --- a/public/css/dist/skins/skin-yellow-dark.min.css +++ b/public/css/dist/skins/skin-yellow-dark.min.css @@ -101,10 +101,6 @@ border-bottom-right-radius: 2px; border-bottom-left-radius: 0; } -.btn, -.btn:hover { - color: #000; -} .btn.btn-primary, .btn:hover.btn-primary, .btn .btn-primary:link, @@ -129,6 +125,9 @@ background-color: var(--hover-link); color: #545454; } +a.actions { + color: #fff !important; +} /** The dropdown is white, so use a darker color */ @@ -173,6 +172,31 @@ a:visited { #ldapad_test_results.well.well-sm { color: var(--back-main); } +.pagination > li > a { + color: var(--text-main); + background-color: var(--back-main); +} +.pagination > .active > a { + background-color: var(--button-default); + border-color: var(--light-link); + color: var(--nav-link); +} +.pagination > .active > a:hover { + background-color: var(--hover-link); +} +.tasks-menu > .dropdown-menu > li .menu { + background-color: var(--back-main); +} +.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress { + background-color: var(--background); +} +h2.task_menu { + color: var(--link); +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + background-color: var(--back-main); + color: var(--link); +} .main-header .navbar, .main-header .logo { background-color: var(--header); @@ -186,7 +210,7 @@ a.btn.btn-default { color: var(--nav-link); } .bootstrap-table .fixed-table-container .table thead th .sortable { - color: var(--nav-link); + color: var(--text-main); } .bootstrap-table .fixed-table-toolbar .columns label { color: #000; @@ -398,14 +422,9 @@ input[type=search] { .skin-yellow-dark .main-header .navbar .dropdown-menu li a { color: var(--header); } -div.th-inner { +tr th div.th-inner { color: var(--text-main); } -.fixed-table-body thead th .th-inner, -.skin-yellow-dark, -.skin-yellow { - background-color: var(--header) !important; -} .tab-content, .tab-pane { background-color: var(--back-main); diff --git a/public/css/webfonts/fa-brands-400.ttf b/public/css/webfonts/fa-brands-400.ttf index 641a489339..774d51ac4b 100644 Binary files a/public/css/webfonts/fa-brands-400.ttf and b/public/css/webfonts/fa-brands-400.ttf differ diff --git a/public/css/webfonts/fa-brands-400.woff2 b/public/css/webfonts/fa-brands-400.woff2 index 5929101297..71e3185268 100644 Binary files a/public/css/webfonts/fa-brands-400.woff2 and b/public/css/webfonts/fa-brands-400.woff2 differ diff --git a/public/css/webfonts/fa-regular-400.ttf b/public/css/webfonts/fa-regular-400.ttf index 7d634a2ba0..8a9d6344d1 100644 Binary files a/public/css/webfonts/fa-regular-400.ttf and b/public/css/webfonts/fa-regular-400.ttf differ diff --git a/public/css/webfonts/fa-regular-400.woff2 b/public/css/webfonts/fa-regular-400.woff2 index 953d5540b0..7f021680b9 100644 Binary files a/public/css/webfonts/fa-regular-400.woff2 and b/public/css/webfonts/fa-regular-400.woff2 differ diff --git a/public/css/webfonts/fa-solid-900.ttf b/public/css/webfonts/fa-solid-900.ttf index b3a2b64103..993dbe1f95 100644 Binary files a/public/css/webfonts/fa-solid-900.ttf and b/public/css/webfonts/fa-solid-900.ttf differ diff --git a/public/css/webfonts/fa-solid-900.woff2 b/public/css/webfonts/fa-solid-900.woff2 index 83433f4455..5c16cd3e8a 100644 Binary files a/public/css/webfonts/fa-solid-900.woff2 and b/public/css/webfonts/fa-solid-900.woff2 differ diff --git a/public/css/webfonts/fa-v4compatibility.ttf b/public/css/webfonts/fa-v4compatibility.ttf index e4eea68d0f..ab6ae22482 100644 Binary files a/public/css/webfonts/fa-v4compatibility.ttf and b/public/css/webfonts/fa-v4compatibility.ttf differ diff --git a/public/css/webfonts/fa-v4compatibility.woff2 b/public/css/webfonts/fa-v4compatibility.woff2 index e804f18603..9027e38bcd 100644 Binary files a/public/css/webfonts/fa-v4compatibility.woff2 and b/public/css/webfonts/fa-v4compatibility.woff2 differ diff --git a/public/js/build/app.js b/public/js/build/app.js index 3c65e5fa81..cfbed799e6 100644 --- a/public/js/build/app.js +++ b/public/js/build/app.js @@ -1049,14 +1049,6 @@ $(document).ready(function () { } }); /* - * iCheck checkbox plugin - */ - - $('input[type="checkbox"].minimal, input[type="radio"].minimal').iCheck({ - checkboxClass: 'icheckbox_minimal-blue', - radioClass: 'iradio_minimal-blue' - }); - /* * Select2 */ @@ -1431,26 +1423,14 @@ function htmlEntities(str) { }; })(jQuery); /** - * Universal Livewire Select2 and iCheck integration + * Universal Livewire Select2 integration * * How to use: * - * 1. Set the class of your select2 elements to 'livewire-select2' and your icheck elements to 'livewire-icheck' (as appropriate). - * (For iCheck, you may still need to apply the other iCheck classes like 'minimal' or 'iCheck') + * 1. Set the class of your select2 elements to 'livewire-select2'). * 2. Name your element to match a property in your Livewire component * 3. Add an attribute called 'data-livewire-component' that points to $_instance->id (via `{{ }}` if you're in a blade, * or just $_instance->id if not). - * 4. For iCheck, you need to wrap the 'checkbox' element with wire:ignore - perhaps in the