diff --git a/.all-contributorsrc b/.all-contributorsrc index eb052687eb..f5085f95e3 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2907,6 +2907,87 @@ "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" + ] + }, + { + "login": "cram42", + "name": "Grant Le Roux", + "avatar_url": "https://avatars.githubusercontent.com/u/5396871?v=4", + "profile": "https://github.com/cram42", + "contributions": [ + "code" + ] + }, + { + "login": "Singrity", + "name": "Bogdan", + "avatar_url": "https://avatars.githubusercontent.com/u/58479551?v=4", + "profile": "http://@singrity", + "contributions": [ + "code" + ] + }, + { + "login": "mmanjos", + "name": "mmanjos", + "avatar_url": "https://avatars.githubusercontent.com/u/3483684?v=4", + "profile": "https://github.com/mmanjos", + "contributions": [ + "code" + ] + }, + { + "login": "Azooz2014", + "name": "Abdelaziz Faki", + "avatar_url": "https://avatars.githubusercontent.com/u/7429229?v=4", + "profile": "https://azooz2014.github.io/", + "contributions": [ + "code" + ] } ] } diff --git a/.env.docker b/.env.docker index 8c47c801e2..87897b10db 100644 --- a/.env.docker +++ b/.env.docker @@ -159,6 +159,7 @@ LOG_CHANNEL=stderr LOG_MAX_DAYS=10 APP_LOCKED=false APP_CIPHER=AES-256-CBC +APP_FORCE_TLS=false GOOGLE_MAPS_API= LDAP_MEM_LIM=500M LDAP_TIME_LIM=600 diff --git a/.env.example b/.env.example index fbf5c4cfff..2d45ff580e 100644 --- a/.env.example +++ b/.env.example @@ -85,6 +85,7 @@ COOKIE_NAME=snipeit_session COOKIE_DOMAIN=null SECURE_COOKIES=false API_TOKEN_EXPIRATION_YEARS=15 +BS_TABLE_STORAGE=cookieStorage # -------------------------------------------- # OPTIONAL: SECURITY HEADER SETTINGS @@ -148,6 +149,7 @@ AWS_DEFAULT_REGION=null # -------------------------------------------- LOGIN_MAX_ATTEMPTS=5 LOGIN_LOCKOUT_DURATION=60 +LOGIN_AUTOCOMPLETE=false # -------------------------------------------- # OPTIONAL: FORGOTTEN PASSWORD SETTINGS 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 a27d5fe5fc..60968e7cbf 100644 --- a/.github/autolabeler.yml +++ b/.github/autolabeler.yml @@ -18,5 +18,5 @@ importer: ["/app/Importer/*","/app/Http/Livewire/Importer.php", "resources/views cli / artisan: ["/app/Console/*"] LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"] docker: ["*docker/*", "Dockerfile", "Dockerfile.alpine", "Dockerfile.fpm-alpine", ".dockerignore", ".env.docker"] -tests: ["/tests/*", "/stubs"] +tests: ["/tests/*", "/database/factories/*", "/stubs"] config: .github diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1..2b4c26c119 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,5 +2,6 @@ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" + target-branch: "develop" schedule: interval: "weekly" diff --git a/.github/workflows/SA-codeql.yml b/.github/workflows/SA-codeql.yml index acf65d5c16..05efd9118d 100644 --- a/.github/workflows/SA-codeql.yml +++ b/.github/workflows/SA-codeql.yml @@ -26,7 +26,7 @@ jobs: language: [ 'javascript' ] steps: - name: Checkout repository - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml index 758838307d..3184042c62 100644 --- a/.github/workflows/codacy-analysis.yml +++ b/.github/workflows/codacy-analysis.yml @@ -32,7 +32,7 @@ jobs: steps: # Checkout the repository to the GitHub Actions runner - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v4 # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - name: Run Codacy Analysis CLI diff --git a/.github/workflows/crowdin-upload.yml b/.github/workflows/crowdin-upload.yml index c986accf37..3255934534 100644 --- a/.github/workflows/crowdin-upload.yml +++ b/.github/workflows/crowdin-upload.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Crowdin push uses: crowdin/github-action@v1 diff --git a/.github/workflows/docker-alpine.yml b/.github/workflows/docker-alpine.yml index 0a5c28ee53..7223ab30dc 100644 --- a/.github/workflows/docker-alpine.yml +++ b/.github/workflows/docker-alpine.yml @@ -32,6 +32,7 @@ jobs: type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine type=ref,event=tag,suffix=-alpine + type=semver,pattern=v{{major}}-latest-alpine # Define default tag "flavor" for docker/metadata-action per # https://github.com/docker/metadata-action#flavor-input # We turn off 'latest' tag by default. @@ -41,17 +42,17 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/login-action - name: Login to DockerHub # Only login if not a PR, as PRs only trigger a Docker build and not a push if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -63,7 +64,7 @@ jobs: # Get Metadata for docker_build step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image id: meta_build - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: snipe/snipe-it tags: ${{ env.IMAGE_TAGS }} @@ -72,7 +73,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push 'snipe-it' image id: docker_build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.alpine diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5aa2758e79..18d055627f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -32,6 +32,7 @@ jobs: type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }} type=ref,event=tag + type=semver,pattern=v{{major}}-latest # Define default tag "flavor" for docker/metadata-action per # https://github.com/docker/metadata-action#flavor-input # We turn off 'latest' tag by default. @@ -41,17 +42,17 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v4 # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # https://github.com/docker/login-action - name: Login to DockerHub # Only login if not a PR, as PRs only trigger a Docker build and not a push if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} @@ -63,7 +64,7 @@ jobs: # Get Metadata for docker_build step below - name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image id: meta_build - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: snipe/snipe-it tags: ${{ env.IMAGE_TAGS }} @@ -72,7 +73,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push 'snipe-it' image id: docker_build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000000..a82946161d --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,73 @@ +name: Tests + +on: + push: + branches: + - master + - develop + pull_request: + +jobs: + tests: + runs-on: ubuntu-latest + + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: snipeit + ports: + - 33306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + strategy: + fail-fast: false + matrix: + php-version: + - "7.4" + - "8.0" + - "8.1.1" + + name: PHP ${{ matrix.php-version }} + + steps: + - uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php-version }}" + coverage: none + + - uses: actions/checkout@v4 + + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Copy .env + run: | + cp -v .env.testing.example .env + cp -v .env.testing.example .env.testing + + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Generate key + run: php artisan key:generate + + - name: Directory Permissions + run: chmod -R 777 storage bootstrap/cache + + - name: Execute tests (Unit and Feature tests) via PHPUnit + env: + DB_CONNECTION: mysql + DB_DATABASE: snipeit + DB_PORT: ${{ job.services.mysql.ports[3306] }} + DB_USERNAME: root + run: php artisan test --parallel diff --git a/.gitignore b/.gitignore index f0e9bfcec2..bf8360ba24 100755 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ .couscous .DS_Store .env -.env.dusk.* -!.env.dusk.example .env.testing phpstan.neon .idea diff --git a/Dockerfile b/Dockerfile index 4a240d7e83..88de52858b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ gcc \ make \ autoconf \ libc-dev \ +libldap-common \ pkg-config \ libmcrypt-dev \ php8.1-dev \ diff --git a/README.md b/README.md index 040de8e93c..f1ea609749 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-320-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) +[![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-329-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 @@ -144,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") | [
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") | +| [
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") | [
Grant Le Roux](https://github.com/cram42)
[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [
Bogdan](http://@singrity)
[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [
mmanjos](https://github.com/mmanjos)
[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [
Abdelaziz Faki](https://azooz2014.github.io/)
[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/TESTING.md b/TESTING.md index 3f0e588105..3a2f4e5385 100644 --- a/TESTING.md +++ b/TESTING.md @@ -9,7 +9,39 @@ Before starting, follow the [instructions](README.md#installation) for installin Before attempting to run the test suite copy the example environment file for tests and update the values to match your environment: `cp .env.testing.example .env.testing` -> Since the data in the database is flushed after each test it is recommended you create a separate mysql database for specifically for tests + +The following should work for running tests in memory with sqlite: +``` +# -------------------------------------------- +# REQUIRED: BASIC APP SETTINGS +# -------------------------------------------- +APP_ENV=testing +APP_DEBUG=true +APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU= +APP_URL=http://localhost:8000 +APP_TIMEZONE='UTC' +APP_LOCALE=en + +# -------------------------------------------- +# REQUIRED: DATABASE SETTINGS +# -------------------------------------------- +DB_CONNECTION=sqlite_testing +#DB_HOST=127.0.0.1 +#DB_PORT=3306 +#DB_DATABASE=null +#DB_USERNAME=null +#DB_PASSWORD=null +``` + +To use MySQL you should update the `DB_` variables to match your local test database: +``` +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE={} +DB_USERNAME={} +DB_PASSWORD={} +``` Now you are ready to run the entire test suite from your terminal: @@ -18,34 +50,3 @@ Now you are ready to run the entire test suite from your terminal: To run individual test files, you can pass the path to the test that you want to run: `php artisan test tests/Unit/AccessoryTest.php` - -## Browser Tests - -Browser tests are run via [Laravel Dusk](https://laravel.com/docs/8.x/dusk) and require Google Chrome to be installed. - -Before attempting to run Dusk tests copy the example environment file for Dusk and update the values to match your environment: - -`cp .env.dusk.example .env.dusk.local` -> `local` refers to the value of `APP_ENV` in your `.env` so if you have it set to `dev` then the file should be named `.env.dusk.dev`. - -**Important**: Dusk tests cannot be run using an in-memory SQLite database. Additionally, the Dusk test suite uses the `DatabaseMigrations` trait which will leave the database in a fresh state after running. Therefore, it is recommended that you create a test database and point `DB_DATABASE` in `.env.dusk.local` to it. - -### Running Browser Tests - -Your application needs to be configured and up and running in order for the browser tests to actually run. When running the tests locally, you can start the application using the following command: - -`php artisan serve` - -Now you are ready to run the test suite. Use the following command from another terminal tab or window: - -`php artisan dusk` - -To run individual test files, you can pass the path to the test that you want to run: - -`php artisan dusk tests/Browser/LoginTest.php` - -If you get an error when attempting to run Dusk tests that says `Couldn't connect to server` run: - -`php artisan dusk:chrome-driver --detect` - -This command will install the specific ChromeDriver Dusk needs for your operating system and Chrome version. diff --git a/app/Console/Commands/LdapSync.php b/app/Console/Commands/LdapSync.php index dd6fea8b66..594f6f064f 100755 --- a/app/Console/Commands/LdapSync.php +++ b/app/Console/Commands/LdapSync.php @@ -18,7 +18,7 @@ class LdapSync extends Command * * @var string */ - protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--filter=} {--summary} {--json_summary}'; + protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=*} {--base_dn=} {--filter=} {--summary} {--json_summary}'; /** * The console command description. @@ -83,7 +83,16 @@ class LdapSync extends Command $summary = []; try { - if ($this->option('base_dn') != '') { + if ( $this->option('location_id') != '') { + + foreach($this->option('location_id') as $location_id){ + $location_ou= Location::where('id', '=', $location_id)->value('ldap_ou'); + $search_base = $location_ou; + Log::debug('Importing users from specified location OU: \"'.$search_base.'\".'); + } + } + + else if ($this->option('base_dn') != '') { $search_base = $this->option('base_dn'); Log::debug('Importing users from specified base DN: \"'.$search_base.'\".'); } else { @@ -106,17 +115,21 @@ class LdapSync extends Command /* Determine which location to assign users to by default. */ $location = null; // TODO - this would be better called "$default_location", which is more explicit about its purpose + if ($this->option('location') != '') { + if ($location = Location::where('name', '=', $this->option('location'))->first()) { + Log::debug('Location name ' . $this->option('location') . ' passed'); + Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')'); + } - if ($this->option('location') != '') { - $location = Location::where('name', '=', $this->option('location'))->first(); - Log::debug('Location name '.$this->option('location').' passed'); - Log::debug('Importing to '.$location->name.' ('.$location->id.')'); - } elseif ($this->option('location_id') != '') { - $location = Location::where('id', '=', $this->option('location_id'))->first(); - Log::debug('Location ID '.$this->option('location_id').' passed'); - Log::debug('Importing to '.$location->name.' ('.$location->id.')'); + } elseif ($this->option('location_id') != '') { + foreach($this->option('location_id') as $location_id) { + if ($location = Location::where('id', '=', $location_id)->first()) { + Log::debug('Location ID ' . $location_id . ' passed'); + Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')'); + } + + } } - if (! isset($location)) { Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.'); } @@ -180,10 +193,6 @@ class LdapSync extends Command } } - /* Create user account entries in Snipe-IT */ - $tmp_pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 20); - $pass = bcrypt($tmp_pass); - $manager_cache = []; if($ldap_default_group != null) { @@ -212,9 +221,12 @@ class LdapSync extends Command $item['manager'] = $results[$i][$ldap_result_manager][0] ?? ''; $item['location'] = $results[$i][$ldap_result_location][0] ?? ''; - $location = Location::firstOrCreate([ - 'name' => $item['location'], - ]); + // ONLY if you are using the "ldap_location" option *AND* you have an actual result + if ($ldap_result_location && $item['location']) { + $location = Location::firstOrCreate([ + 'name' => $item['location'], + ]); + } $department = Department::firstOrCreate([ 'name' => $item['department'], ]); @@ -226,22 +238,44 @@ class LdapSync extends Command } else { // Creating a new user. $user = new User; - $user->password = $pass; + $user->password = $user->noPassword(); $user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below) $item['createorupdate'] = 'created'; } - $user->first_name = $item['firstname']; - $user->last_name = $item['lastname']; + //If a sync option is not filled in on the LDAP settings don't populate the user field + if($ldap_result_username != null){ $user->username = $item['username']; - $user->email = $item['email']; + } + if($ldap_result_last_name != null){ + $user->last_name = $item['lastname']; + } + if($ldap_result_first_name != null){ + $user->first_name = $item['firstname']; + } + if($ldap_result_emp_num != null){ $user->employee_num = e($item['employee_number']); + } + if($ldap_result_email != null){ + $user->email = $item['email']; + } + if($ldap_result_phone != null){ $user->phone = $item['telephone']; + } + if($ldap_result_jobtitle != null){ $user->jobtitle = $item['jobtitle']; + } + if($ldap_result_country != null){ $user->country = $item['country']; + } + if($ldap_result_dept != null){ $user->department_id = $department->id; - $user->location_id = $location->id; + } + if($ldap_result_location != null){ + $user->location_id = $location ? $location->id : null; + } + if($ldap_result_manager != null){ if($item['manager'] != null) { // Check Cache first if (isset($manager_cache[$item['manager']])) { @@ -281,6 +315,7 @@ class LdapSync extends Command } } + } // Sync activated state for Active Directory. if ( !empty($ldap_result_active_flag)) { // IF we have an 'active' flag set.... diff --git a/app/Console/Commands/SystemBackup.php b/app/Console/Commands/SystemBackup.php index 09f23dd887..3b51bcc736 100644 --- a/app/Console/Commands/SystemBackup.php +++ b/app/Console/Commands/SystemBackup.php @@ -38,7 +38,14 @@ class SystemBackup extends Command public function handle() { if ($this->option('filename')) { - $this->call('backup:run', ['--filename' => $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/Events/CheckoutableCheckedIn.php b/app/Events/CheckoutableCheckedIn.php index 9609f7d415..48aed2a64d 100644 --- a/app/Events/CheckoutableCheckedIn.php +++ b/app/Events/CheckoutableCheckedIn.php @@ -15,18 +15,20 @@ class CheckoutableCheckedIn public $checkedInBy; public $note; public $action_date; // Date setted in the hardware.checkin view at the checkin_at input, for the action log + public $originalValues; /** * Create a new event instance. * * @return void */ - public function __construct($checkoutable, $checkedOutTo, User $checkedInBy, $note, $action_date = null) + public function __construct($checkoutable, $checkedOutTo, User $checkedInBy, $note, $action_date = null, $originalValues = []) { $this->checkoutable = $checkoutable; $this->checkedOutTo = $checkedOutTo; $this->checkedInBy = $checkedInBy; $this->note = $note; $this->action_date = $action_date ?? date('Y-m-d'); + $this->originalValues = $originalValues; } } diff --git a/app/Events/CheckoutableCheckedOut.php b/app/Events/CheckoutableCheckedOut.php index 30f70ca0d8..3f215bd3bc 100644 --- a/app/Events/CheckoutableCheckedOut.php +++ b/app/Events/CheckoutableCheckedOut.php @@ -14,17 +14,19 @@ class CheckoutableCheckedOut public $checkedOutTo; public $checkedOutBy; public $note; + public $originalValues; /** * Create a new event instance. * * @return void */ - public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note) + public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note, $originalValues = []) { $this->checkoutable = $checkoutable; $this->checkedOutTo = $checkedOutTo; $this->checkedOutBy = $checkedOutBy; $this->note = $note; + $this->originalValues = $originalValues; } } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 3b1ffb9b31..e76d8e5dae 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -75,7 +75,12 @@ class Handler extends ExceptionHandler // Handle SCIM exceptions if ($e instanceof SCIMException) { - return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid SCIM Request'), 400); + try { + $e->report(); // logs as 'debug', so shouldn't get too noisy + } catch(\Exception $reportException) { + //do nothing + } + return $e->render($request); // ALL SCIMExceptions have the 'render()' method } // Handle standard requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date) @@ -145,6 +150,11 @@ class Handler extends ExceptionHandler return redirect()->guest('login'); } + protected function invalidJson($request, ValidationException $exception) + { + return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors()), 200); + } + /** * A list of the inputs that are never flashed for validation exceptions. diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index c85564fc3c..800a2491d4 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -2,6 +2,8 @@ namespace App\Helpers; use App\Models\Accessory; +use App\Models\Asset; +use App\Models\AssetModel; use App\Models\Component; use App\Models\Consumable; use App\Models\CustomField; @@ -33,6 +35,16 @@ class Helper } } + public static function parseEscapedMarkedownInline($str = null) + { + $Parsedown = new \Parsedown(); + $Parsedown->setSafeMode(true); + + if ($str) { + return $Parsedown->line($str); + } + } + /** * The importer has formatted number strings since v3, * so the value might be a string, or an integer. @@ -61,10 +73,14 @@ class Helper * * @author [A. Gianotto] [] * @since [v3.3] - * @return array + * @return string */ - public static function defaultChartColors($index = 0) + public static function defaultChartColors(int $index = 0) { + if ($index < 0) { + $index = 0; + } + $colors = [ '#008941', '#FF4A46', @@ -337,7 +353,19 @@ class Helper $total_colors = count($colors); if ($index >= $total_colors) { - $index = $index - $total_colors; + + \Log::error('Status label count is '.$index.' and exceeds the allowed count of 266.'); + //patch fix for array key overflow (color count starts at 1, array starts at 0) + $index = $index - $total_colors - 1; + + //constraints to keep result in 0-265 range. This should never be needed, but if something happens + //to create this many status labels and it DOES happen, this will keep it from failing at least. + if($index < 0) { + $index = 0; + } + elseif($index >($total_colors - 1)) { + $index = $total_colors - 1; + } } return $colors[$index]; @@ -543,8 +571,8 @@ class Helper 'license' => trans('general.license'), ]; - if($selection != null){ - return $category_types[$selection]; + if ($selection != null){ + return $category_types[strtolower($selection)]; } else return $category_types; @@ -633,6 +661,7 @@ class Helper $consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get(); $accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get(); $components = Component::whereNotNull('min_amt')->get(); + $asset_models = AssetModel::where('min_amt', '>', 0)->get(); $avail_consumables = 0; $items_array = []; @@ -695,6 +724,28 @@ class Helper } } + foreach ($asset_models as $asset_model){ + + $asset = new Asset(); + $total_owned = $asset->where('model_id', '=', $asset_model->id)->count(); + $avail = $asset->where('model_id', '=', $asset_model->id)->whereNull('assigned_to')->count(); + + if ($avail < ($asset_model->min_amt)+ \App\Models\Setting::getSettings()->alert_threshold) { + if ($avail > 0) { + $percent = number_format((($avail / $total_owned) * 100), 0); + } else { + $percent = 100; + } + $items_array[$all_count]['id'] = $asset_model->id; + $items_array[$all_count]['name'] = $asset_model->name; + $items_array[$all_count]['type'] = 'models'; + $items_array[$all_count]['percent'] = $percent; + $items_array[$all_count]['remaining'] = $avail; + $items_array[$all_count]['min_amt'] = $asset_model->min_amt; + $all_count++; + } + } + return $items_array; } @@ -1210,10 +1261,60 @@ class Helper return true; \Log::debug('app locked!'); } - + return false; } + + /** + * Conversion between units of measurement + * + * @author Grant Le Roux + * @since 5.0 + * @param float $value Measurement value to convert + * @param string $srcUnit Source unit of measurement + * @param string $dstUnit Destination unit of measurement + * @param int $round Round the result to decimals (Default false - No rounding) + * @return float + */ + public static function convertUnit($value, $srcUnit, $dstUnit, $round=false) { + $srcFactor = static::getUnitConversionFactor($srcUnit); + $dstFactor = static::getUnitConversionFactor($dstUnit); + $output = $value * $srcFactor / $dstFactor; + return ($round !== false) ? round($output, $round) : $output; + } + + /** + * Get conversion factor from unit of measurement to mm + * + * @author Grant Le Roux + * @since 5.0 + * @param string $unit Unit of measurement + * @return float + */ + public static function getUnitConversionFactor($unit) { + switch (strtolower($unit)) { + case 'mm': + return 1.0; + case 'cm': + return 10.0; + case 'm': + return 1000.0; + case 'in': + return 25.4; + case 'ft': + return 12 * static::getUnitConversionFactor('in'); + case 'yd': + return 3 * static::getUnitConversionFactor('ft'); + case 'pt': + return (1 / 72) * static::getUnitConversionFactor('in'); + default: + throw new \InvalidArgumentException('Unit: \'' . $unit . '\' is not supported'); + + return false; + } + } + /* * I know it's gauche to return a shitty HTML string, but this is just a helper and since it will be the same every single time, diff --git a/app/Http/Controllers/Accessories/AccessoriesController.php b/app/Http/Controllers/Accessories/AccessoriesController.php index 111cbb3c8b..6ef6fb9933 100755 --- a/app/Http/Controllers/Accessories/AccessoriesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesController.php @@ -126,12 +126,13 @@ class AccessoriesController extends Controller public function getClone($accessoryId = null) { - $this->authorize('create', Accesory::class); + $this->authorize('create', Accessory::class); // Check if the asset exists if (is_null($accessory_to_clone = Accessory::find($accessoryId))) { // Redirect to the asset management page - return redirect()->route('accessory.index')->with('error', trans('admin/accessories/message.does_not_exist')); + return redirect()->route('accessories.index') + ->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryId])); } $accessory = clone $accessory_to_clone; diff --git a/app/Http/Controllers/Accessories/AccessoriesFilesController.php b/app/Http/Controllers/Accessories/AccessoriesFilesController.php index ef701020d8..6a94a897af 100644 --- a/app/Http/Controllers/Accessories/AccessoriesFilesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesFilesController.php @@ -146,9 +146,8 @@ class AccessoriesFilesController extends Controller $this->authorize('view', $accessory); $this->authorize('accessories.files', $accessory); - if (! $log = Actionlog::find($fileId)) { - return response('No matching record for that asset/file', 500) - ->header('Content-Type', 'text/plain'); + if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) { + return redirect()->route('accessories.index')->with('error', trans('admin/users/message.log_record_not_found')); } $file = 'private_uploads/accessories/'.$log->filename; @@ -161,22 +160,19 @@ class AccessoriesFilesController extends Controller ->header('Content-Type', 'text/plain'); } else { + // Display the file inline + if (request('inline') == 'true') { + $headers = [ + 'Content-Disposition' => 'inline', + ]; + return Storage::download($file, $log->filename, $headers); + } + + // We have to override the URL stuff here, since local defaults in Laravel's Flysystem // won't work, as they're not accessible via the web if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? return StorageHelper::downloader($file); - } else { - if ($download != 'true') { - \Log::debug('display the file'); - if ($contents = file_get_contents(Storage::url($file))) { // TODO - this will fail on private S3 files or large public ones - return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file))); - } - - return JsonResponse::create(['error' => 'Failed validation: '], 500); - } - - return StorageHelper::downloader($file); - } } } diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 7644488a89..030e069bd2 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -69,7 +69,7 @@ class AcceptanceController extends Controller } if (! Company::isCurrentUserHasAccess($acceptance->checkoutable)) { - return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); + return redirect()->route('account.accept')->with('error', trans('general.error_user_company')); } return view('account/accept.create', compact('acceptance')); @@ -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,7 +152,6 @@ class AcceptanceController extends Controller } } - // this is horrible switch($acceptance->checkoutable_type){ case 'App\Models\Asset': @@ -170,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': @@ -247,17 +245,51 @@ class AcceptanceController extends Controller $return_msg = trans('admin/users/message.accepted'); } else { + + /** + * Check for the eula-pdfs directory + */ + if (! Storage::exists('private_uploads/eula-pdfs')) { + Storage::makeDirectory('private_uploads/eula-pdfs', 775); + } + + if (Setting::getSettings()->require_accept_signature == '1') { + + // Check if the signature directory exists, if not create it + if (!Storage::exists('private_uploads/signatures')) { + Storage::makeDirectory('private_uploads/signatures', 775); + } + + // The item was accepted, check for a signature + if ($request->filled('signature_output')) { + $sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png'; + $data_uri = $request->input('signature_output'); + $encoded_image = explode(',', $data_uri); + $decoded_image = base64_decode($encoded_image[1]); + Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image); + + // No image data is present, kick them back. + // This mostly only applies to users on super-duper crapola browsers *cough* IE *cough* + } else { + return redirect()->back()->with('error', trans('general.shitty_browser')); + } + } + // Format the data to send the declined notification $branding_settings = SettingsController::getPDFBranding(); // 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': @@ -269,6 +301,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; } @@ -277,11 +311,18 @@ class AcceptanceController extends Controller 'item_model' => $display_model, 'item_serial' => $item->serial, 'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'), + 'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null, 'assigned_to' => $assigned_to, 'company_name' => $branding_settings->site_name, 'date_settings' => $branding_settings->date_display_format, ]; + if ($pdf_view_route!='') { + \Log::debug($pdf_filename.' is the filename, and the route was specified.'); + $pdf = Pdf::loadView($pdf_view_route, $data); + Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf->output()); + } + $acceptance->decline($sig_filename); $acceptance->notify(new AcceptanceAssetDeclinedNotification($data)); event(new CheckoutDeclined($acceptance)); @@ -292,4 +333,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 91a6c5596b..654f3c2e24 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -150,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); } @@ -331,7 +331,7 @@ class AccessoriesController extends Controller $accessory = Accessory::find($accessory_user->accessory_id); $this->authorize('checkin', $accessory); - $logaction = $accessory->logCheckin(User::find($accessory_user->user_id), $request->input('note')); + $logaction = $accessory->logCheckin(User::find($accessory_user->assigned_to), $request->input('note')); // Was the accessory updated? if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) { diff --git a/app/Http/Controllers/Api/AssetMaintenancesController.php b/app/Http/Controllers/Api/AssetMaintenancesController.php index b2870792f9..6da7ce23a1 100644 --- a/app/Http/Controllers/Api/AssetMaintenancesController.php +++ b/app/Http/Controllers/Api/AssetMaintenancesController.php @@ -71,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'; diff --git a/app/Http/Controllers/Api/AssetModelsController.php b/app/Http/Controllers/Api/AssetModelsController.php index a9ef48adc0..e77c648b34 100644 --- a/app/Http/Controllers/Api/AssetModelsController.php +++ b/app/Http/Controllers/Api/AssetModelsController.php @@ -38,6 +38,7 @@ class AssetModelsController extends Controller 'image', 'name', 'model_number', + 'min_amt', 'eol', 'notes', 'created_at', @@ -45,6 +46,7 @@ class AssetModelsController extends Controller 'requestable', 'assets_count', 'category', + 'fieldset', ]; $assetmodels = AssetModel::select([ @@ -52,6 +54,7 @@ class AssetModelsController extends Controller 'models.image', 'models.name', 'model_number', + 'min_amt', 'eol', 'requestable', 'models.notes', @@ -63,7 +66,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') { @@ -92,6 +95,9 @@ class AssetModelsController extends Controller case 'category': $assetmodels->OrderCategory($order); break; + case 'fieldset': + $assetmodels->OrderFieldset($order); + break; default: $assetmodels->orderBy($sort, $order); break; diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index 6898ccc2ea..954ba3a4d0 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api; use App\Events\CheckoutableCheckedIn; +use App\Http\Requests\StoreAssetRequest; use Illuminate\Support\Facades\Gate; use App\Helpers\Helper; use App\Http\Controllers\Controller; @@ -33,6 +34,7 @@ use TCPDF; use Validator; use Route; + /** * This class controls all actions related to assets for * the Snipe-IT Asset Management application. @@ -48,7 +50,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $assetId * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function index(Request $request, $audit = null) { @@ -115,7 +117,7 @@ class AssetsController extends Controller $allowed_columns[] = $field->db_column_name(); } - $assets = Company::scopeCompanyables(Asset::select('assets.*'), 'company_id', 'assets') + $assets = Asset::select('assets.*') ->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo', 'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users. @@ -125,6 +127,8 @@ class AssetsController extends Controller $assets->InModelList($non_deprecable_models->toArray()); } + + // These are used by the API to query against specific ID numbers. // They are also used by the individual searches on detail pages like // locations, etc. @@ -136,6 +140,101 @@ class AssetsController extends Controller } } + if ((! is_null($filter)) && (count($filter)) > 0) { + $assets->ByFilter($filter); + } elseif ($request->filled('search')) { + $assets->TextSearch($request->input('search')); + } + + // This is used by the audit reporting routes + if (Gate::allows('audit', Asset::class)) { + switch ($audit) { + case 'due': + $assets->DueOrOverdueForAudit($settings); + break; + case 'overdue': + $assets->overdueForAudit($settings); + break; + } + } + + + // This is used by the sidenav, mostly + + // We switched from using query scopes here because of a Laravel bug + // related to fulltext searches on complex queries. + // I am sad. :( + switch ($request->input('status')) { + case 'Deleted': + $assets->onlyTrashed(); + break; + case 'Pending': + $assets->join('status_labels AS status_alias', function ($join) { + $join->on('status_alias.id', '=', 'assets.status_id') + ->where('status_alias.deployable', '=', 0) + ->where('status_alias.pending', '=', 1) + ->where('status_alias.archived', '=', 0); + }); + break; + case 'RTD': + $assets->whereNull('assets.assigned_to') + ->join('status_labels AS status_alias', function ($join) { + $join->on('status_alias.id', '=', 'assets.status_id') + ->where('status_alias.deployable', '=', 1) + ->where('status_alias.pending', '=', 0) + ->where('status_alias.archived', '=', 0); + }); + break; + case 'Undeployable': + $assets->Undeployable(); + break; + case 'Archived': + $assets->join('status_labels AS status_alias', function ($join) { + $join->on('status_alias.id', '=', 'assets.status_id') + ->where('status_alias.deployable', '=', 0) + ->where('status_alias.pending', '=', 0) + ->where('status_alias.archived', '=', 1); + }); + break; + case 'Requestable': + $assets->where('assets.requestable', '=', 1) + ->join('status_labels AS status_alias', function ($join) { + $join->on('status_alias.id', '=', 'assets.status_id') + ->where('status_alias.deployable', '=', 1) + ->where('status_alias.pending', '=', 0) + ->where('status_alias.archived', '=', 0); + }); + + break; + case 'Deployed': + // more sad, horrible workarounds for laravel bugs when doing full text searches + $assets->whereNotNull('assets.assigned_to'); + break; + case 'byod': + // This is kind of redundant, since we already check for byod=1 above, but this keeps the + // sidebar nav links a little less chaotic + $assets->where('assets.byod', '=', '1'); + break; + default: + + if ((! $request->filled('status_id')) && ($settings->show_archived_in_list != '1')) { + // terrible workaround for complex-query Laravel bug in fulltext + $assets->join('status_labels AS status_alias', function ($join) { + $join->on('status_alias.id', '=', 'assets.status_id') + ->where('status_alias.archived', '=', 0); + }); + + // If there is a status ID, don't take show_archived_in_list into consideration + } else { + $assets->join('status_labels AS status_alias', function ($join) { + $join->on('status_alias.id', '=', 'assets.status_id'); + }); + } + + } + + + // 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')); } @@ -197,110 +296,10 @@ class AssetsController extends Controller $assets->where('assets.byod', '=', $request->input('byod')); } - $request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : ''; - - // 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'; - - // This is used by the audit reporting routes - if (Gate::allows('audit', Asset::class)) { - switch ($audit) { - case 'due': - $assets->DueOrOverdueForAudit($settings); - break; - case 'overdue': - $assets->overdueForAudit($settings); - break; - } + if ($request->filled('order_number')) { + $assets->where('assets.order_number', '=', strval($request->get('order_number'))); } - - - // This is used by the sidenav, mostly - - // We switched from using query scopes here because of a Laravel bug - // related to fulltext searches on complex queries. - // I am sad. :( - switch ($request->input('status')) { - case 'Deleted': - $assets->onlyTrashed(); - break; - case 'Pending': - $assets->join('status_labels AS status_alias', function ($join) { - $join->on('status_alias.id', '=', 'assets.status_id') - ->where('status_alias.deployable', '=', 0) - ->where('status_alias.pending', '=', 1) - ->where('status_alias.archived', '=', 0); - }); - break; - case 'RTD': - $assets->whereNull('assets.assigned_to') - ->join('status_labels AS status_alias', function ($join) { - $join->on('status_alias.id', '=', 'assets.status_id') - ->where('status_alias.deployable', '=', 1) - ->where('status_alias.pending', '=', 0) - ->where('status_alias.archived', '=', 0); - }); - break; - case 'Undeployable': - $assets->Undeployable(); - break; - case 'Archived': - $assets->join('status_labels AS status_alias', function ($join) { - $join->on('status_alias.id', '=', 'assets.status_id') - ->where('status_alias.deployable', '=', 0) - ->where('status_alias.pending', '=', 0) - ->where('status_alias.archived', '=', 1); - }); - break; - case 'Requestable': - $assets->where('assets.requestable', '=', 1) - ->join('status_labels AS status_alias', function ($join) { - $join->on('status_alias.id', '=', 'assets.status_id') - ->where('status_alias.deployable', '=', 1) - ->where('status_alias.pending', '=', 0) - ->where('status_alias.archived', '=', 0); - }); - - break; - case 'Deployed': - // more sad, horrible workarounds for laravel bugs when doing full text searches - $assets->where('assets.assigned_to', '>', '0'); - break; - case 'byod': - // This is kind of redundant, since we already check for byod=1 above, but this keeps the - // sidebar nav links a little less chaotic - $assets->where('assets.byod', '=', '1'); - break; - default: - - if ((! $request->filled('status_id')) && ($settings->show_archived_in_list != '1')) { - // terrible workaround for complex-query Laravel bug in fulltext - $assets->join('status_labels AS status_alias', function ($join) { - $join->on('status_alias.id', '=', 'assets.status_id') - ->where('status_alias.archived', '=', 0); - }); - - // If there is a status ID, don't take show_archived_in_list into consideration - } else { - $assets->join('status_labels AS status_alias', function ($join) { - $join->on('status_alias.id', '=', 'assets.status_id'); - }); - } - - } - - - if ((! is_null($filter)) && (count($filter)) > 0) { - $assets->ByFilter($filter); - } elseif ($request->filled('search')) { - $assets->TextSearch($request->input('search')); - } - - // 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 // that out to let the default sorter below order them correctly on the assets table. @@ -310,7 +309,8 @@ class AssetsController extends Controller // in the allowed_columns array) $column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at'; - + $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; + switch ($sort_override) { case 'model': $assets->OrderModels($order); @@ -347,6 +347,10 @@ class AssetsController extends Controller } + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value'); + $limit = app('api_limit_value'); + $total = $assets->count(); $assets = $assets->skip($offset)->take($limit)->get(); @@ -441,7 +445,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $assetId * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function show(Request $request, $id) { @@ -472,12 +476,12 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @since [v4.0.16] * @see \App\Http\Transformers\SelectlistTransformer - * + * @return \Illuminate\Http\JsonResponse */ public function selectlist(Request $request) { - $assets = Company::scopeCompanyables(Asset::select([ + $assets = Asset::select([ 'assets.id', 'assets.name', 'assets.asset_tag', @@ -485,7 +489,7 @@ class AssetsController extends Controller 'assets.assigned_to', 'assets.assigned_type', 'assets.status_id', - ])->with('model', 'assetstatus', 'assignedTo')->NotArchived(), 'company_id', 'assets'); + ])->with('model', 'assetstatus', 'assignedTo')->NotArchived(); if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') { $assets = $assets->RTD(); @@ -528,12 +532,10 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param \App\Http\Requests\ImageUploadRequest $request * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ - public function store(ImageUploadRequest $request) + public function store(StoreAssetRequest $request) { - $this->authorize('create', Asset::class); - $asset = new Asset(); $asset->model()->associate(AssetModel::find((int) $request->get('model_id'))); @@ -543,7 +545,8 @@ class AssetsController extends Controller $asset->model_id = $request->get('model_id'); $asset->order_number = $request->get('order_number'); $asset->notes = $request->get('notes'); - $asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset()); + $asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset()); //yup, problem :/ + // NO IT IS NOT!!! This is never firing; we SHOW the asset_tag you're going to get, so it *will* be filled in! $asset->user_id = Auth::id(); $asset->archived = '0'; $asset->physical = '1'; @@ -573,9 +576,6 @@ 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) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 200); - } if (($model) && ($model->fieldset)) { foreach ($model->fieldset->fields as $field) { @@ -639,7 +639,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param \App\Http\Requests\ImageUploadRequest $request * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function update(ImageUploadRequest $request, $id) { @@ -666,10 +666,11 @@ class AssetsController extends Controller $request->offsetSet('image', $request->offsetGet('image_source')); } - $asset = $request->handleImages($asset); + $asset = $request->handleImages($asset); + $model = AssetModel::find($asset->model_id); // Update custom fields - if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) { + if (($model) && (isset($model->fieldset))) { foreach ($model->fieldset->fields as $field) { if ($request->has($field->db_column)) { if ($field->field_encrypted == '1') { @@ -720,7 +721,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $assetId * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function destroy($id) { @@ -749,7 +750,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $assetId * @since [v5.1.18] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function restore(Request $request, $assetId = null) { @@ -789,7 +790,7 @@ class AssetsController extends Controller * @author [N. Butler] * @param string $tag * @since [v6.0.5] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function checkoutByTag(AssetCheckoutRequest $request, $tag) { @@ -805,7 +806,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $assetId * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function checkout(AssetCheckoutRequest $request, $asset_id) { @@ -889,7 +890,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $assetId * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function checkin(Request $request, $asset_id) { @@ -905,6 +906,7 @@ class AssetsController extends Controller $asset->expected_checkin = null; $asset->last_checkout = null; + $asset->last_checkin = now(); $asset->assigned_to = null; $asset->assignedTo()->disassociate($asset); $asset->accepted = null; @@ -924,10 +926,14 @@ class AssetsController extends Controller } $checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at').' '. date('H:i:s') : date('Y-m-d H:i:s'); + $originalValues = $asset->getRawOriginal(); + if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) { + $originalValues['action_date'] = $checkin_at; + } if ($asset->save()) { - event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at)); + event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues)); return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success'))); } @@ -940,7 +946,7 @@ class AssetsController extends Controller * * @author [A. Janes] [] * @since [v6.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function checkinByTag(Request $request, $tag = null) { @@ -966,7 +972,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @param int $id * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function audit(Request $request) @@ -1027,23 +1033,54 @@ class AssetsController extends Controller * * @author [A. Gianotto] [] * @since [v4.0] - * @return JsonResponse + * @return \Illuminate\Http\JsonResponse */ public function requestable(Request $request) { $this->authorize('viewRequestable', Asset::class); - $assets = Company::scopeCompanyables(Asset::select('assets.*'), 'company_id', 'assets') - ->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo', - 'model.category', 'model.manufacturer', 'model.fieldset', 'supplier')->requestableAssets(); + $allowed_columns = [ + 'name', + 'asset_tag', + 'serial', + 'model_number', + 'image', + 'purchase_cost', + 'expected_checkin', + ]; + + $all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load + + foreach ($all_custom_fields as $field) { + $allowed_columns[] = $field->db_column_name(); + } + + $assets = Asset::select('assets.*') + ->with('location', 'assetstatus', 'assetlog', 'company','assignedTo', + 'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests') + ->requestableAssets(); + + + - $offset = request('offset', 0); - $limit = $request->input('limit', 50); - $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; if ($request->filled('search')) { $assets->TextSearch($request->input('search')); } + // Search custom fields by column name + foreach ($all_custom_fields as $field) { + if ($request->filled($field->db_column_name())) { + $assets->where($field->db_column_name(), '=', $request->input($field->db_column_name())); + } + } + + $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; + $sort_override = str_replace('custom_fields.', '', $request->input('sort')); + + // This handles all the pivot sorting (versus the assets.* fields + // in the allowed_columns array) + $column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at'; + switch ($request->input('sort')) { case 'model': $assets->OrderModels($order); @@ -1051,17 +1088,19 @@ class AssetsController extends Controller case 'model_number': $assets->OrderModelNumber($order); break; - case 'category': - $assets->OrderCategory($order); - break; - case 'manufacturer': - $assets->OrderManufacturer($order); + case 'location': + $assets->OrderLocation($order); break; default: - $assets->orderBy('assets.created_at', $order); + $assets->orderBy($column_sort, $order); break; } + + // Make sure the offset and limit are actually integers and do not exceed system limits + $offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value'); + $limit = app('api_limit_value'); + $total = $assets->count(); $assets = $assets->skip($offset)->take($limit)->get(); diff --git a/app/Http/Controllers/Api/CategoriesController.php b/app/Http/Controllers/Api/CategoriesController.php index 2781fa101f..2aa4b3741c 100644 --- a/app/Http/Controllers/Api/CategoriesController.php +++ b/app/Http/Controllers/Api/CategoriesController.php @@ -92,7 +92,7 @@ class CategoriesController extends Controller } // 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')); + $offset = ($request->input('offset') > $categories->count()) ? $categories->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/CompaniesController.php b/app/Http/Controllers/Api/CompaniesController.php index d6c8f6e76e..580bc5d9b9 100644 --- a/app/Http/Controllers/Api/CompaniesController.php +++ b/app/Http/Controllers/Api/CompaniesController.php @@ -27,6 +27,9 @@ class CompaniesController extends Controller $allowed_columns = [ 'id', 'name', + 'phone', + 'fax', + 'email', 'created_at', 'updated_at', 'users_count', @@ -47,9 +50,13 @@ class CompaniesController extends Controller $companies->where('name', '=', $request->input('name')); } + if ($request->filled('email')) { + $companies->where('email', '=', $request->input('email')); + } + // 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')); + $offset = ($request->input('offset') > $companies->count()) ? $companies->count() : app('api_offset_value'); $limit = app('api_limit_value'); @@ -166,6 +173,7 @@ class CompaniesController extends Controller $companies = Company::select([ 'companies.id', 'companies.name', + 'companies.email', 'companies.image', ]); diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index 24eb1044b8..9202b10b71 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -13,6 +13,7 @@ use App\Events\CheckoutableCheckedIn; use App\Events\ComponentCheckedIn; use App\Models\Asset; use Illuminate\Support\Facades\Validator; +use Illuminate\Database\Query\Builder; class ComponentsController extends Controller { @@ -44,9 +45,8 @@ class ComponentsController extends Controller 'notes', ]; - - $components = Company::scopeCompanyables(Component::select('components.*') - ->with('company', 'location', 'category', 'assets', 'supplier')); + $components = Component::select('components.*') + ->with('company', 'location', 'category', 'assets', 'supplier'); if ($request->filled('search')) { $components = $components->TextSearch($request->input('search')); @@ -77,7 +77,7 @@ class ComponentsController extends Controller } // 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')); + $offset = ($request->input('offset') > $components->count()) ? $components->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; @@ -204,12 +204,29 @@ class ComponentsController extends Controller $this->authorize('view', \App\Models\Asset::class); $component = Component::findOrFail($id); - $assets = $component->assets(); - + $offset = request('offset', 0); $limit = $request->input('limit', 50); - $total = $assets->count(); - $assets = $assets->skip($offset)->take($limit)->get(); + + if ($request->filled('search')) { + $assets = $component->assets() + ->where(function ($query) use ($request) { + $search_str = '%' . $request->input('search') . '%'; + $query->where('name', 'like', $search_str) + ->orWhereIn('model_id', function (Builder $query) use ($request) { + $search_str = '%' . $request->input('search') . '%'; + $query->selectRaw('id')->from('models')->where('name', 'like', $search_str); + }) + ->orWhere('asset_tag', 'like', $search_str); + }) + ->get(); + $total = $assets->count(); + } else { + $assets = $component->assets(); + + $total = $assets->count(); + $assets = $assets->skip($offset)->take($limit)->get(); + } return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total); } @@ -236,7 +253,7 @@ class ComponentsController extends Controller $this->authorize('checkout', $component); $validator = Validator::make($request->all(), [ - 'asset_id' => 'required|exists:assets,id', + 'assigned_to' => 'required|exists:assets,id', 'assigned_qty' => "required|numeric|min:1|digits_between:1,".$component->numRemaining(), ]); @@ -246,7 +263,7 @@ class ComponentsController extends Controller } // Make sure there is at least one available to checkout - if ($component->numRemaining() <= $request->get('assigned_qty')) { + 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')]))); } diff --git a/app/Http/Controllers/Api/ConsumablesController.php b/app/Http/Controllers/Api/ConsumablesController.php index bac9440dca..1f3a347fb5 100644 --- a/app/Http/Controllers/Api/ConsumablesController.php +++ b/app/Http/Controllers/Api/ConsumablesController.php @@ -45,11 +45,8 @@ class ConsumablesController extends Controller 'notes', ]; - - $consumables = Company::scopeCompanyables( - Consumable::select('consumables.*') - ->with('company', 'location', 'category', 'users', 'manufacturer') - ); + $consumables = Consumable::select('consumables.*') + ->with('company', 'location', 'category', 'users', 'manufacturer'); if ($request->filled('search')) { $consumables = $consumables->TextSearch(e($request->input('search'))); @@ -89,7 +86,7 @@ class ConsumablesController extends Controller // 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')); + $offset = ($request->input('offset') > $consumables->count()) ? $consumables->count() : app('api_offset_value'); $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']; @@ -269,6 +266,14 @@ class ConsumablesController extends Controller \Log::debug('No enough remaining'); } + // Make sure there is a valid category + if (!$consumable->category){ + return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.invalid_item_category_single', ['type' => trans('general.consumable')]))); + + return redirect()->route('consumables.index')->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.consumable')])); + } + + // 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 diff --git a/app/Http/Controllers/Api/DepartmentsController.php b/app/Http/Controllers/Api/DepartmentsController.php index cab3991baa..f211089b93 100644 --- a/app/Http/Controllers/Api/DepartmentsController.php +++ b/app/Http/Controllers/Api/DepartmentsController.php @@ -27,16 +27,18 @@ class DepartmentsController extends Controller $this->authorize('view', Department::class); $allowed_columns = ['id', 'name', 'image', 'users_count']; - $departments = Company::scopeCompanyables(Department::select( + $departments = Department::select( 'departments.id', 'departments.name', + 'departments.phone', + 'departments.fax', 'departments.location_id', 'departments.company_id', 'departments.manager_id', 'departments.created_at', 'departments.updated_at', - 'departments.image'), - "company_id", "departments")->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count'); + 'departments.image' + )->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count'); if ($request->filled('search')) { $departments = $departments->TextSearch($request->input('search')); @@ -59,7 +61,7 @@ class DepartmentsController extends Controller } // 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')); + $offset = ($request->input('offset') > $departments->count()) ? $departments->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/DepreciationsController.php b/app/Http/Controllers/Api/DepreciationsController.php index 3d86c1b096..502a0741b7 100644 --- a/app/Http/Controllers/Api/DepreciationsController.php +++ b/app/Http/Controllers/Api/DepreciationsController.php @@ -29,7 +29,7 @@ class DepreciationsController extends Controller } // 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')); + $offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/GroupsController.php b/app/Http/Controllers/Api/GroupsController.php index 7cc5d2d756..8548af0ba5 100644 --- a/app/Http/Controllers/Api/GroupsController.php +++ b/app/Http/Controllers/Api/GroupsController.php @@ -36,7 +36,7 @@ class GroupsController extends Controller } // 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')); + $offset = ($request->input('offset') > $groups->count()) ? $groups->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; @@ -63,7 +63,7 @@ class GroupsController extends Controller $group = new Group; $group->name = $request->input('name'); - $group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here + $group->permissions = json_encode($request->input('permissions')); // Todo - some JSON validation stuff here if ($group->save()) { return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success'))); diff --git a/app/Http/Controllers/Api/ImportController.php b/app/Http/Controllers/Api/ImportController.php index 914c16f8da..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'); } diff --git a/app/Http/Controllers/Api/LabelsController.php b/app/Http/Controllers/Api/LabelsController.php new file mode 100644 index 0000000000..6576ec0373 --- /dev/null +++ b/app/Http/Controllers/Api/LabelsController.php @@ -0,0 +1,71 @@ + + * @return JsonResponse + */ + public function index(Request $request) + { + $this->authorize('view', Label::class); + + $labels = Label::find(); + + if ($request->filled('search')) { + $search = $request->get('search'); + $labels = $labels->filter(function ($label, $index) use ($search) { + return stripos($label->getName(), $search) !== false; + }); + } + + $total = $labels->count(); + + $offset = $request->get('offset', 0); + $offset = ($offset > $total) ? $total : $offset; + + $maxLimit = config('app.max_results'); + $limit = $request->get('limit', $maxLimit); + $limit = ($limit > $maxLimit) ? $maxLimit : $limit; + + $labels = $labels->skip($offset)->take($limit); + + return (new LabelsTransformer)->transformLabels($labels, $total, $request); + } + + /** + * Returns JSON with information about a label for detail view. + * + * @author Grant Le Roux + * @param string $labelName + * @return JsonResponse + */ + public function show(string $labelName) + { + $labelName = str_replace('/', '\\', $labelName); + try { + $label = Label::find($labelName); + } catch(ItemNotFoundException $e) { + return response() + ->json( + Helper::formatStandardApiResponse('error', null, trans('admin/labels/message.does_not_exist')), + 404 + ); + } + $this->authorize('view', $label); + return (new LabelsTransformer)->transformLabel($label); + } + +} diff --git a/app/Http/Controllers/Api/LicenseSeatsController.php b/app/Http/Controllers/Api/LicenseSeatsController.php index 884c9c59c4..5e79c49b23 100644 --- a/app/Http/Controllers/Api/LicenseSeatsController.php +++ b/app/Http/Controllers/Api/LicenseSeatsController.php @@ -41,7 +41,12 @@ class LicenseSeatsController extends Controller $total = $seats->count(); // 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')); + $offset = ($request->input('offset') > $seats->count()) ? $seats->count() : app('api_offset_value'); + + if ($offset >= $total ){ + $offset = 0; + } + $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 df74b60895..d456e3cd64 100644 --- a/app/Http/Controllers/Api/LicensesController.php +++ b/app/Http/Controllers/Api/LicensesController.php @@ -26,8 +26,8 @@ class LicensesController extends Controller public function index(Request $request) { $this->authorize('view', License::class); - $licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'supplier','category')->withCount('freeSeats as free_seats_count')); + $licenses = License::with('company', 'manufacturer', 'supplier','category')->withCount('freeSeats as free_seats_count'); if ($request->filled('company_id')) { $licenses->where('company_id', '=', $request->input('company_id')); @@ -95,7 +95,7 @@ class LicensesController extends Controller } // 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')); + $offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : app('api_offset_value'); $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 4b1feee117..b888493286 100644 --- a/app/Http/Controllers/Api/LocationsController.php +++ b/app/Http/Controllers/Api/LocationsController.php @@ -37,6 +37,8 @@ class LocationsController extends Controller 'locations.city', 'locations.state', 'locations.zip', + 'locations.phone', + 'locations.fax', 'locations.country', 'locations.parent_id', 'locations.manager_id', @@ -79,7 +81,7 @@ class LocationsController extends Controller } // 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')); + $offset = ($request->input('offset') > $locations->count()) ? $locations->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; @@ -251,8 +253,12 @@ class LocationsController extends Controller */ public function selectlist(Request $request) { - - $this->authorize('view.selectlists'); + // If a user is in the process of editing their profile, as determined by the referrer, + // then we check that they have permission to edit their own location. + // Otherwise, we do our normal check that they can view select lists. + $request->headers->get('referer') === route('profile') + ? $this->authorize('self.edit_location') + : $this->authorize('view.selectlists'); $locations = Location::select([ 'locations.id', diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index 9ff0a0c202..dadef87e26 100644 --- a/app/Http/Controllers/Api/ManufacturersController.php +++ b/app/Http/Controllers/Api/ManufacturersController.php @@ -62,7 +62,7 @@ class ManufacturersController extends Controller } // 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')); + $offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/PredefinedKitsController.php b/app/Http/Controllers/Api/PredefinedKitsController.php index 85d05c422e..b398dbfae2 100644 --- a/app/Http/Controllers/Api/PredefinedKitsController.php +++ b/app/Http/Controllers/Api/PredefinedKitsController.php @@ -30,7 +30,7 @@ class PredefinedKitsController extends Controller } // 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')); + $offset = ($request->input('offset') > $kits->count()) ? $kits->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'desc' ? 'desc' : 'asc'; diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index 4f5e3b1bdf..ef56ed5370 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -11,6 +11,7 @@ use Illuminate\Http\Request; use Laravel\Passport\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Illuminate\Support\Facades\Gate; +use App\Models\CustomField; use DB; class ProfileController extends Controller @@ -48,14 +49,23 @@ class ProfileController extends Controller { $checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get(); - $results = []; + $results = array(); + $show_field = array(); + $showable_fields = array(); $results['total'] = $checkoutRequests->count(); + $all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load + foreach ($all_custom_fields as $field) { + if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) { + $showable_fields[] = $field->db_column_name(); + } + } + foreach ($checkoutRequests as $checkoutRequest) { // Make sure the asset and request still exist if ($checkoutRequest && $checkoutRequest->itemRequested()) { - $results['rows'][] = [ + $assets = [ 'image' => e($checkoutRequest->itemRequested()->present()->getImageUrl()), 'name' => e($checkoutRequest->itemRequested()->present()->name()), 'type' => e($checkoutRequest->itemType()), @@ -64,7 +74,16 @@ class ProfileController extends Controller 'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'), 'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'), ]; + + foreach ($showable_fields as $showable_field_name) { + $show_field['custom_fields.'.$showable_field_name] = $checkoutRequest->itemRequested()->{$showable_field_name}; + } + + // Merge the plain asset data and the custom fields data + $results['rows'][] = array_merge($assets, $show_field); } + + } return $results; diff --git a/app/Http/Controllers/Api/ReportsController.php b/app/Http/Controllers/Api/ReportsController.php index 21294c5779..7335e7d8e0 100644 --- a/app/Http/Controllers/Api/ReportsController.php +++ b/app/Http/Controllers/Api/ReportsController.php @@ -56,7 +56,7 @@ class ReportsController extends Controller // 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')); + $offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : app('api_offset_value'); $limit = app('api_limit_value'); $sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at'; diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index a9e44d91b4..7c8e260c2f 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; @@ -51,7 +52,7 @@ class StatuslabelsController extends Controller } // 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')); + $offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; @@ -291,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 a26c33b1f8..3e3d637be0 100644 --- a/app/Http/Controllers/Api/SuppliersController.php +++ b/app/Http/Controllers/Api/SuppliersController.php @@ -41,7 +41,7 @@ class SuppliersController extends Controller ]; $suppliers = Supplier::select( - ['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes']) + ['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes', 'url']) ->withCount('assets as assets_count') ->withCount('licenses as licenses_count') ->withCount('accessories as accessories_count') @@ -94,7 +94,7 @@ class SuppliersController extends Controller } // 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')); + $offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->count() : app('api_offset_value'); $limit = app('api_limit_value'); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index dc444ec9e9..5a2cd7dcf1 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -75,15 +75,8 @@ class UsersController extends Controller ])->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'); - $users = Company::scopeCompanyables($users); - if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) { - $users = $users->onlyTrashed(); - } elseif (($request->filled('all')) && ($request->input('all') == 'true')) { - $users = $users->withTrashed(); - } - if ($request->filled('activated')) { $users = $users->where('users.activated', '=', $request->input('activated')); } @@ -199,7 +192,7 @@ class UsersController extends Controller $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; // 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')); + $offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value'); $limit = app('api_limit_value'); @@ -272,6 +265,14 @@ class UsersController extends Controller break; } + if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) { + $users = $users->onlyTrashed(); + } elseif (($request->filled('all')) && ($request->input('all') == 'true')) { + $users = $users->withTrashed(); + } + + $users = Company::scopeCompanyables($users); + $total = $users->count(); $users = $users->skip($offset)->take($limit)->get(); @@ -362,8 +363,12 @@ class UsersController extends Controller $user->permissions = $permissions_array; } - $tmp_pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40); - $user->password = bcrypt($request->get('password', $tmp_pass)); + // + if ($request->filled('password')) { + $user->password = bcrypt($request->get('password')); + } else { + $user->password = $user->noPassword(); + } app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar'); diff --git a/app/Http/Controllers/AssetModelsController.php b/app/Http/Controllers/AssetModelsController.php index dbefb2e7b7..1783b33921 100755 --- a/app/Http/Controllers/AssetModelsController.php +++ b/app/Http/Controllers/AssetModelsController.php @@ -6,6 +6,7 @@ use App\Helpers\Helper; use App\Http\Requests\ImageUploadRequest; use App\Models\AssetModel; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\Validator; @@ -76,6 +77,7 @@ class AssetModelsController extends Controller $model->depreciation_id = $request->input('depreciation_id'); $model->name = $request->input('name'); $model->model_number = $request->input('model_number'); + $model->min_amt = $request->input('min_amt'); $model->manufacturer_id = $request->input('manufacturer_id'); $model->category_id = $request->input('category_id'); $model->notes = $request->input('notes'); @@ -153,6 +155,7 @@ class AssetModelsController extends Controller $model->eol = $request->input('eol'); $model->name = $request->input('name'); $model->model_number = $request->input('model_number'); + $model->min_amt = $request->input('min_amt'); $model->manufacturer_id = $request->input('manufacturer_id'); $model->category_id = $request->input('category_id'); $model->notes = $request->input('notes'); @@ -171,8 +174,20 @@ class AssetModelsController extends Controller } } } - + + + if ($model->save()) { + if ($model->wasChanged('eol')) { + if ($model->eol > 0) { + $newEol = $model->eol; + $model->assets()->whereNotNull('purchase_date')->where('eol_explicit', false) + ->update(['asset_eol_date' => DB::raw('DATE_ADD(purchase_date, INTERVAL ' . $newEol . ' MONTH)')]); + } elseif ($model->eol == 0) { + $model->assets()->whereNotNull('purchase_date')->where('eol_explicit', false) + ->update(['asset_eol_date' => DB::raw('null')]); + } + } return redirect()->route('models.index')->with('success', trans('admin/models/message.update.success')); } @@ -286,6 +301,7 @@ class AssetModelsController extends Controller return view('models/edit') ->with('depreciation_list', Helper::depreciationList()) ->with('item', $model) + ->with('model_id', $model_to_clone->id) ->with('clone_model', $model_to_clone); } diff --git a/app/Http/Controllers/AssetModelsFilesController.php b/app/Http/Controllers/AssetModelsFilesController.php index a68ef482cc..9889cd29ca 100644 --- a/app/Http/Controllers/AssetModelsFilesController.php +++ b/app/Http/Controllers/AssetModelsFilesController.php @@ -78,7 +78,7 @@ class AssetModelsFilesController extends Controller * @return View * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function show($modelId = null, $fileId = null, $download = true) + public function show($modelId = null, $fileId = null) { $model = AssetModel::find($modelId); // the asset is valid @@ -99,12 +99,13 @@ class AssetModelsFilesController extends Controller ->header('Content-Type', 'text/plain'); } - if ($download != 'true') { - if ($contents = file_get_contents(Storage::url($file))) { - return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file))); - } + if (request('inline') == 'true') { - return JsonResponse::create(['error' => 'Failed validation: '], 500); + $headers = [ + 'Content-Disposition' => 'inline', + ]; + + return Storage::download($file, $log->filename, $headers); } return StorageHelper::downloader($file); diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index 657b02171b..4fe10e895f 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -68,6 +68,7 @@ class AssetCheckinController extends Controller $asset->expected_checkin = null; $asset->last_checkout = null; + $asset->last_checkin = now(); $asset->assigned_to = null; $asset->assignedTo()->disassociate($asset); $asset->assigned_type = null; @@ -94,18 +95,25 @@ class AssetCheckinController extends Controller \Log::debug('Manually override the location IDs'); \Log::debug('Original Location ID: '.$asset->location_id); $asset->location_id = ''; - \Log::debug('New RTD Location ID: '.$asset->location_id); + \Log::debug('New Location ID: '.$asset->location_id); } $asset->location_id = $asset->rtd_location_id; if ($request->filled('location_id')) { \Log::debug('NEW Location ID: '.$request->get('location_id')); - $asset->location_id = e($request->get('location_id')); + $asset->location_id = $request->get('location_id'); + + if ($request->get('update_default_location') == 0){ + $asset->rtd_location_id = $request->get('location_id'); + } } + $originalValues = $asset->getRawOriginal(); + $checkin_at = date('Y-m-d H:i:s'); if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) { + $originalValues['action_date'] = $checkin_at; $checkin_at = $request->get('checkin_at'); } @@ -128,7 +136,7 @@ class AssetCheckinController extends Controller // Was the asset updated? if ($asset->save()) { - event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at)); + event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues)); if ((isset($user)) && ($backto == 'user')) { return redirect()->route('users.show', $user->id)->with('success', trans('admin/hardware/message.checkin.success')); diff --git a/app/Http/Controllers/Assets/AssetCheckoutController.php b/app/Http/Controllers/Assets/AssetCheckoutController.php index e6326da6b1..1fdd0a0cc3 100644 --- a/app/Http/Controllers/Assets/AssetCheckoutController.php +++ b/app/Http/Controllers/Assets/AssetCheckoutController.php @@ -89,6 +89,15 @@ class AssetCheckoutController extends Controller } } + $settings = \App\Models\Setting::getSettings(); + + // We have to check whether $target->company_id is null here since locations don't have a company yet + if (($settings->full_multiple_companies_support) && ((!is_null($target->company_id)) && (!is_null($asset->company_id)))) { + if ($target->company_id != $asset->company_id){ + return redirect()->to("hardware/$assetId/checkout")->with('error', trans('general.error_user_company')); + } + } + if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) { return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success')); } diff --git a/app/Http/Controllers/Assets/AssetFilesController.php b/app/Http/Controllers/Assets/AssetFilesController.php index 2c7d6ff9ec..610705c604 100644 --- a/app/Http/Controllers/Assets/AssetFilesController.php +++ b/app/Http/Controllers/Assets/AssetFilesController.php @@ -79,14 +79,14 @@ class AssetFilesController extends Controller * @return View * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function show($assetId = null, $fileId = null, $download = true) + public function show($assetId = null, $fileId = null) { $asset = Asset::find($assetId); // the asset is valid if (isset($asset->id)) { $this->authorize('view', $asset); - if (! $log = Actionlog::find($fileId)) { + if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) { return response('No matching record for that asset/file', 500) ->header('Content-Type', 'text/plain'); } @@ -103,12 +103,13 @@ class AssetFilesController extends Controller ->header('Content-Type', 'text/plain'); } - if ($download != 'true') { - if ($contents = file_get_contents(Storage::url($file))) { - return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file))); - } + if (request('inline') == 'true') { - return JsonResponse::create(['error' => 'Failed validation: '], 500); + $headers = [ + 'Content-Disposition' => 'inline', + ]; + + return Storage::download($file, $log->filename, $headers); } return StorageHelper::downloader($file); diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 2a93845507..28d7906cd7 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -6,32 +6,27 @@ use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\ImageUploadRequest; use App\Models\Actionlog; +use Illuminate\Support\Facades\Log; use App\Models\Asset; use App\Models\AssetModel; 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 Illuminate\Support\Facades\Auth; +use App\View\Label; use Carbon\Carbon; -use DB; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\Gate; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Facades\Cookie; -use Input; -use Intervention\Image\Facades\Image; +use Illuminate\Support\Facades\Validator; use League\Csv\Reader; -use League\Csv\Statement; -use Paginator; -use Redirect; -use Response; -use Slack; -use Str; -use TCPDF; -use View; +use Illuminate\Support\Facades\Redirect; /** * This class controls all actions related to assets for @@ -142,7 +137,7 @@ class AssetsController extends Controller $asset->warranty_months = request('warranty_months', null); $asset->purchase_cost = request('purchase_cost'); $asset->purchase_date = request('purchase_date', null); - $asset->asset_eol_date = request('asset_eol_date', $asset->present()->eol_date()); + $asset->asset_eol_date = request('asset_eol_date', null); $asset->assigned_to = request('assigned_to', null); $asset->supplier_id = request('supplier_id', null); $asset->requestable = request('requestable', 0); @@ -171,9 +166,9 @@ class AssetsController extends Controller if ($field->field_encrypted == '1') { if (Gate::allows('admin')) { if (is_array($request->input($field->db_column))) { - $asset->{$field->db_column} = \Crypt::encrypt(implode(', ', $request->input($field->db_column))); + $asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column))); } else { - $asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column)); + $asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column)); } } } else { @@ -209,12 +204,8 @@ class AssetsController extends Controller } if ($success) { - // Redirect to the asset listing page - $minutes = 518400; - // dd( $_POST['options']); - // Cookie::queue(Cookie::make('optional_info', json_decode($_POST['options']), $minutes)); return redirect()->route('hardware.index') - ->with('success', trans('admin/hardware/message.create.success')); + ->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => $asset->asset_tag])); } @@ -296,10 +287,10 @@ class AssetsController extends Controller /** * Validate and process asset edit form. * - * @author [A. Gianotto] [] * @param int $assetId - * @since [v1.0] - * @return Redirect + * @return \Illuminate\Http\RedirectResponse|Redirect + *@since [v1.0] + * @author [A. Gianotto] [] */ public function update(ImageUploadRequest $request, $assetId = null) { @@ -313,9 +304,27 @@ class AssetsController extends Controller $asset->status_id = $request->input('status_id', null); $asset->warranty_months = $request->input('warranty_months', 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); + $asset->purchase_date = $request->input('purchase_date', null); + if ($request->filled('purchase_date') && !$request->filled('asset_eol_date') && ($asset->model->eol > 0)) { + $asset->purchase_date = $request->input('purchase_date', null); + $asset->asset_eol_date = Carbon::parse($request->input('purchase_date'))->addMonths($asset->model->eol)->format('Y-m-d'); + $asset->eol_explicit = false; + } elseif ($request->filled('asset_eol_date')) { + $asset->asset_eol_date = $request->input('asset_eol_date', null); + $months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date); + if($asset->model->eol) { + if($months != $asset->model->eol > 0) { + $asset->eol_explicit = true; + } else { + $asset->eol_explicit = false; + } + } else { + $asset->eol_explicit = true; + } + } elseif (!$request->filled('asset_eol_date') && (($asset->model->eol) == 0)) { + $asset->asset_eol_date = null; + $asset->eol_explicit = false; + } $asset->supplier_id = $request->input('supplier_id', null); $asset->expected_checkin = $request->input('expected_checkin', null); @@ -324,6 +333,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); } @@ -334,7 +349,7 @@ class AssetsController extends Controller unlink(public_path().'/uploads/assets/'.$asset->image); $asset->image = ''; } catch (\Exception $e) { - \Log::info($e); + Log::info($e); } } @@ -362,9 +377,9 @@ class AssetsController extends Controller if ($field->field_encrypted == '1') { if (Gate::allows('admin')) { if (is_array($request->input($field->db_column))) { - $asset->{$field->db_column} = \Crypt::encrypt(implode(', ', $request->input($field->db_column))); + $asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column))); } else { - $asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column)); + $asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column)); } } } else { @@ -412,7 +427,7 @@ class AssetsController extends Controller try { Storage::disk('public')->delete('assets'.'/'.$asset->image); } catch (\Exception $e) { - \Log::debug($e); + Log::debug($e); } } @@ -446,11 +461,12 @@ class AssetsController extends Controller * @since [v3.0] * @return Redirect */ - public function getAssetByTag(Request $request) + public function getAssetByTag(Request $request, $tag=null) { + $tag = $tag ? $tag : $request->get('assetTag'); $topsearch = ($request->get('topsearch') == 'true'); - if (! $asset = Asset::where('asset_tag', '=', $request->get('assetTag'))->first()) { + if (! $asset = Asset::where('asset_tag', '=', $tag)->first()) { return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist')); } $this->authorize('view', $asset); @@ -526,7 +542,7 @@ class AssetsController extends Controller return response($barcode_obj->getPngData())->header('Content-type', 'image/png'); } catch (\Exception $e) { - \Log::debug('The barcode format is invalid.'); + Log::debug('The barcode format is invalid.'); return response(file_get_contents(public_path('uploads/barcodes/invalid_barcode.gif')))->header('Content-type', 'image/gif'); } @@ -547,9 +563,11 @@ class AssetsController extends Controller $asset = Asset::find($assetId); $this->authorize('view', $asset); - return view('hardware/labels') - ->with('assets', Asset::find($asset)) + return (new Label()) + ->with('assets', collect([ $asset ])) ->with('settings', Setting::getSettings()) + ->with('template', request()->get('template')) + ->with('offset', request()->get('offset')) ->with('bulkedit', false) ->with('count', 0); } @@ -767,7 +785,7 @@ class AssetsController extends Controller } /** - * Retore a deleted asset. + * Restore a deleted asset. * * @author [A. Gianotto] [] * @param int $assetId @@ -845,7 +863,7 @@ class AssetsController extends Controller 'next_audit_date' => 'date|nullable', ]; - $validator = \Validator::make($request->all(), $rules); + $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all())); @@ -862,7 +880,7 @@ class AssetsController extends Controller // Check to see if they checked the box to update the physical location, // not just note it in the audit notes if ($request->input('update_location') == '1') { - \Log::debug('update location in audit'); + Log::debug('update location in audit'); $asset->location_id = $request->input('location_id'); } diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index 80d17f1b30..45ca5bab7c 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -8,11 +8,13 @@ use App\Http\Controllers\CheckInOutRequest; use App\Http\Controllers\Controller; use App\Models\Asset; use App\Models\Setting; +use App\View\Label; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Session; use App\Http\Requests\AssetCheckoutRequest; +use App\Models\CustomField; class BulkAssetsController extends Controller { @@ -30,7 +32,7 @@ class BulkAssetsController extends Controller public function edit(Request $request) { $this->authorize('view', Asset::class); - + if (! $request->filled('ids')) { return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected')); } @@ -40,13 +42,30 @@ class BulkAssetsController extends Controller session(['bulk_back_url' => $bulk_back_url]); $asset_ids = array_values(array_unique($request->input('ids'))); + + //custom fields logic + $asset_custom_field = Asset::with(['model.fieldset.fields', 'model'])->whereIn('id', $asset_ids)->whereHas('model', function ($query) { + return $query->where('fieldset_id', '!=', null); + })->get(); + + $models = $asset_custom_field->unique('model_id'); + $modelNames = []; + foreach($models as $model) { + $modelNames[] = $model->model->name; + } 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)) + $assets_found = Asset::find($asset_ids); + + if ($assets_found->isEmpty()){ + return redirect()->back(); + } + + return (new Label) + ->with('assets', $assets_found) ->with('settings', Setting::getSettings()) ->with('bulkedit', true) ->with('count', 0); @@ -73,7 +92,9 @@ class BulkAssetsController extends Controller $this->authorize('update', Asset::class); return view('hardware/bulk') ->with('assets', $asset_ids) - ->with('statuslabel_list', Helper::statusLabelList()); + ->with('statuslabel_list', Helper::statusLabelList()) + ->with('models', $models->pluck(['model'])) + ->with('modelNames', $modelNames); } } @@ -91,6 +112,8 @@ class BulkAssetsController extends Controller public function update(Request $request) { $this->authorize('update', Asset::class); + $has_errors = 0; + $error_array = array(); // Get the back url from the session and then destroy the session $bulk_back_url = route('hardware.index'); @@ -98,13 +121,21 @@ class BulkAssetsController extends Controller $bulk_back_url = $request->session()->pull('bulk_back_url'); } - - if (! $request->filled('ids') || count($request->input('ids')) <= 0) { + $custom_field_columns = CustomField::all()->pluck('db_column')->toArray(); + + if (Session::exists('ids')) { + $assets = Session::get('ids'); + } elseif (! $request->filled('ids') || count($request->input('ids')) <= 0) { return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected')); } - + $assets = array_keys($request->input('ids')); - + + if ($request->anyFilled($custom_field_columns)) { + $custom_fields_present = true; + } else { + $custom_fields_present = false; + } if (($request->filled('purchase_date')) || ($request->filled('expected_checkin')) || ($request->filled('purchase_cost')) @@ -120,6 +151,7 @@ class BulkAssetsController extends Controller || ($request->filled('null_purchase_date')) || ($request->filled('null_expected_checkin_date')) || ($request->filled('null_next_audit_date')) + || ($request->anyFilled($custom_field_columns)) ) { foreach ($assets as $assetId) { @@ -128,13 +160,15 @@ class BulkAssetsController extends Controller $this->conditionallyAddItem('purchase_date') ->conditionallyAddItem('expected_checkin') - ->conditionallyAddItem('model_id') ->conditionallyAddItem('order_number') ->conditionallyAddItem('requestable') ->conditionallyAddItem('status_id') ->conditionallyAddItem('supplier_id') ->conditionallyAddItem('warranty_months') ->conditionallyAddItem('next_audit_date'); + foreach ($custom_field_columns as $key => $custom_field_column) { + $this->conditionallyAddItem($custom_field_column); + } if ($request->input('null_purchase_date')=='1') { $this->update_array['purchase_date'] = null; @@ -152,6 +186,7 @@ class BulkAssetsController extends Controller $this->update_array['purchase_cost'] = $request->input('purchase_cost'); } + if ($request->filled('company_id')) { $this->update_array['company_id'] = $request->input('company_id'); if ($request->input('company_id') == 'clear') { @@ -160,40 +195,110 @@ class BulkAssetsController extends Controller } if ($request->filled('rtd_location_id')) { - $this->update_array['rtd_location_id'] = $request->input('rtd_location_id'); + if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '0')) { + $this->update_array['rtd_location_id'] = $request->input('rtd_location_id'); + } if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) { $this->update_array['location_id'] = $request->input('rtd_location_id'); + $this->update_array['rtd_location_id'] = $request->input('rtd_location_id'); + } + if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '2')) { + $this->update_array['location_id'] = $request->input('rtd_location_id'); } } $changed = []; - $asset = Asset::where('id' ,$assetId)->get(); + $asset = Asset::find($assetId); foreach ($this->update_array as $key => $value) { - if ($this->update_array[$key] != $asset->toArray()[0][$key]) { - $changed[$key]['old'] = $asset->toArray()[0][$key]; + if ($this->update_array[$key] != $asset->{$key}) { + $changed[$key]['old'] = $asset->{$key}; $changed[$key]['new'] = $this->update_array[$key]; } } + + if ($custom_fields_present) { - $logAction = new Actionlog(); - $logAction->item_type = Asset::class; - $logAction->item_id = $assetId; - $logAction->created_at = date("Y-m-d H:i:s"); - $logAction->user_id = Auth::id(); - $logAction->log_meta = json_encode($changed); - $logAction->logaction('update'); + $model = $asset->model()->first(); - DB::table('assets') - ->where('id', $assetId) - ->update($this->update_array); - } // endforeach + // Use the rules of the new model fieldsets if the model changed + if ($request->filled('model_id')) { + $this->update_array['model_id'] = $request->input('model_id'); + $model = \App\Models\AssetModel::find($request->input('model_id')); + } + + + // Make sure this model is valid + $assetCustomFields = ($model) ? $model->fieldset : null; + + if ($assetCustomFields && $assetCustomFields->fields) { + + foreach ($assetCustomFields->fields as $field) { + + if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted=='1')) { + $decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column}); + + /* + * Check if the decrypted existing value is different from one we just submitted + * and if not, pull it out of the object since it shouldn't really be updating at all. + * If we don't do this, it will try to re-encrypt it, and the same value encrypted two + * different times will have different values, so it will *look* like it was updated + * but it wasn't. + */ + if ($decrypted_old != $this->update_array[$field->db_column]) { + $asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]); + } else { + /* + * Remove the encrypted custom field from the update_array, since nothing changed + */ + unset($this->update_array[$field->db_column]); + unset($asset->{$field->db_column}); + } + + /* + * These custom fields aren't encrypted, just carry on as usual + */ + } else { + + + if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) { + + // Check if this is an array, and if so, flatten it + if (is_array($this->update_array[$field->db_column])) { + $asset->{$field->db_column} = implode(', ', $this->update_array[$field->db_column]); + } else { + $asset->{$field->db_column} = $this->update_array[$field->db_column]; + } + } + } + + } // endforeach + } // end custom field check + } // end custom fields handler + + + + // Check if it passes validation, and then try to save + if (!$asset->update($this->update_array)) { + + // Build the error array + foreach ($asset->getErrors()->toArray() as $key => $message) { + for ($x = 0; $x < count($message); $x++) { + $error_array[$key][] = trans('general.asset') . ' ' . $asset->id . ': ' . $message[$x]; + $has_errors++; + } + } + + } // end if saved + + } // end asset foreach + + if ($has_errors > 0) { + return redirect($bulk_back_url)->with('bulk_asset_errors', $error_array); + } return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.update.success')); - - } - // no values given, nothing to update return redirect($bulk_back_url)->with('warning', trans('admin/hardware/message.update.nothing_updated')); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 011184881c..319ebd0418 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -191,9 +191,11 @@ class LoginController extends Controller $ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user); + $user->password = $user->noPassword(); if (Setting::getSettings()->ldap_pw_sync=='1') { $user->password = bcrypt($request->input('password')); } + $user->email = $ldap_attr['email']; $user->first_name = $ldap_attr['firstname']; $user->last_name = $ldap_attr['lastname']; //FIXME (or TODO?) - do we need to map additional fields that we now support? E.g. country, phone, etc. diff --git a/app/Http/Controllers/CompaniesController.php b/app/Http/Controllers/CompaniesController.php index 396ec5ef68..6c4072362f 100644 --- a/app/Http/Controllers/CompaniesController.php +++ b/app/Http/Controllers/CompaniesController.php @@ -60,6 +60,9 @@ final class CompaniesController extends Controller $company = new Company; $company->name = $request->input('name'); + $company->phone = $request->input('phone'); + $company->fax = $request->input('fax'); + $company->email = $request->input('email'); $company = $request->handleImages($company); @@ -111,6 +114,9 @@ final class CompaniesController extends Controller $this->authorize('update', $company); $company->name = $request->input('name'); + $company->phone = $request->input('phone'); + $company->fax = $request->input('fax'); + $company->email = $request->input('email'); $company = $request->handleImages($company); @@ -119,8 +125,7 @@ final class CompaniesController extends Controller ->with('success', trans('admin/companies/message.update.success')); } - return redirect()->route('companies.edit', ['company' => $companyId]) - ->with('error', trans('admin/companies/message.update.error')); + return redirect()->back()->withInput()->withErrors($company->getErrors()); } /** diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index 09acee0277..9f4724e353 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -56,10 +56,11 @@ class ComponentCheckinController extends Controller * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function store(Request $request, $component_asset_id) + public function store(Request $request, $component_asset_id, $backto = null) { if ($component_assets = DB::table('components_assets')->find($component_asset_id)) { if (is_null($component = Component::find($component_assets->component_id))) { + return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found')); } @@ -95,6 +96,10 @@ class ComponentCheckinController extends Controller $asset = Asset::find($component_assets->asset_id); event(new CheckoutableCheckedIn($component, $asset, Auth::user(), $request->input('note'), Carbon::now())); + if ($backto == 'asset'){ + return redirect()->route('hardware.show', $asset->id)->with('success', + trans('admin/components/message.checkin.success')); + } return redirect()->route('components.index')->with('success', trans('admin/components/message.checkin.success')); diff --git a/app/Http/Controllers/Components/ComponentsFilesController.php b/app/Http/Controllers/Components/ComponentsFilesController.php index 3fc93b74e5..0f4e782aa8 100644 --- a/app/Http/Controllers/Components/ComponentsFilesController.php +++ b/app/Http/Controllers/Components/ComponentsFilesController.php @@ -132,7 +132,7 @@ class ComponentsFilesController extends Controller * @return \Symfony\Component\HttpFoundation\Response * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function show($componentId = null, $fileId = null, $download = true) + public function show($componentId = null, $fileId = null) { \Log::debug('Private filesystem is: '.config('filesystems.default')); $component = Component::find($componentId); @@ -142,7 +142,7 @@ class ComponentsFilesController extends Controller $this->authorize('view', $component); $this->authorize('components.files', $component); - if (! $log = Actionlog::find($fileId)) { + if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) { return response('No matching record for that asset/file', 500) ->header('Content-Type', 'text/plain'); } @@ -157,21 +157,17 @@ class ComponentsFilesController extends Controller ->header('Content-Type', 'text/plain'); } else { + // Display the file inline + if (request('inline') == 'true') { + $headers = [ + 'Content-Disposition' => 'inline', + ]; + return Storage::download($file, $log->filename, $headers); + } + if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? return StorageHelper::downloader($file); - } else { - if ($download != 'true') { - \Log::debug('display the file'); - if ($contents = file_get_contents(Storage::url($file))) { // TODO - this will fail on private S3 files or large public ones - return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file))); - } - - return JsonResponse::create(['error' => 'Failed validation: '], 500); - } - - return StorageHelper::downloader($file); - - } + } } } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index 6585624d82..5723c2c7ed 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -34,6 +34,11 @@ class ConsumableCheckoutController extends Controller return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable')); } + // Make sure there is a valid category + if (!$consumable->category){ + return redirect()->route('consumables.edit', ['consumable' => $consumable->id])->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.consumable')])); + } + $this->authorize('checkout', $consumable); return view('consumables/checkout', compact('consumable')); diff --git a/app/Http/Controllers/Consumables/ConsumablesFilesController.php b/app/Http/Controllers/Consumables/ConsumablesFilesController.php index 9b4007a43b..6053e82cca 100644 --- a/app/Http/Controllers/Consumables/ConsumablesFilesController.php +++ b/app/Http/Controllers/Consumables/ConsumablesFilesController.php @@ -131,7 +131,7 @@ class ConsumablesFilesController extends Controller * @return \Symfony\Consumable\HttpFoundation\Response * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function show($consumableId = null, $fileId = null, $download = true) + public function show($consumableId = null, $fileId = null) { $consumable = Consumable::find($consumableId); @@ -140,7 +140,7 @@ class ConsumablesFilesController extends Controller $this->authorize('view', $consumable); $this->authorize('consumables.files', $consumable); - if (! $log = Actionlog::find($fileId)) { + if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) { return response('No matching record for that asset/file', 500) ->header('Content-Type', 'text/plain'); } @@ -155,22 +155,19 @@ class ConsumablesFilesController extends Controller ->header('Content-Type', 'text/plain'); } else { + // Display the file inline + if (request('inline') == 'true') { + $headers = [ + 'Content-Disposition' => 'inline', + ]; + return Storage::download($file, $log->filename, $headers); + } + + // We have to override the URL stuff here, since local defaults in Laravel's Flysystem // won't work, as they're not accessible via the web if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? return StorageHelper::downloader($file); - } else { - if ($download != 'true') { - \Log::debug('display the file'); - if ($contents = file_get_contents(Storage::url($file))) { // TODO - this will fail on private S3 files or large public ones - return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file))); - } - - return JsonResponse::create(['error' => 'Failed validation: '], 500); - } - - return StorageHelper::downloader($file); - } } } diff --git a/app/Http/Controllers/CustomFieldsController.php b/app/Http/Controllers/CustomFieldsController.php index bbc3790d63..ffe5eceec2 100644 --- a/app/Http/Controllers/CustomFieldsController.php +++ b/app/Http/Controllers/CustomFieldsController.php @@ -109,6 +109,8 @@ class CustomFieldsController extends Controller "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), + "show_in_listview" => $request->get("show_in_listview", 0), + "show_in_requestable_list" => $request->get("show_in_requestable_list", 0), "user_id" => Auth::id() ]); @@ -265,6 +267,8 @@ class CustomFieldsController extends Controller $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); + $field->show_in_listview = $request->get("show_in_listview", 0); + $field->show_in_requestable_list = $request->get("show_in_requestable_list", 0); if ($request->get('format') == 'CUSTOM REGEX') { $field->format = e($request->get('custom_format')); diff --git a/app/Http/Controllers/CustomFieldsetsController.php b/app/Http/Controllers/CustomFieldsetsController.php index dbee97b776..abf7c1d18e 100644 --- a/app/Http/Controllers/CustomFieldsetsController.php +++ b/app/Http/Controllers/CustomFieldsetsController.php @@ -94,7 +94,7 @@ class CustomFieldsetsController extends Controller $this->authorize('create', CustomField::class); $fieldset = new CustomFieldset([ - 'name' => e($request->get('name')), + 'name' => $request->get('name'), 'user_id' => Auth::user()->id, ]); diff --git a/app/Http/Controllers/DepartmentsController.php b/app/Http/Controllers/DepartmentsController.php index 4b2b97ce7f..2d456c0a4a 100644 --- a/app/Http/Controllers/DepartmentsController.php +++ b/app/Http/Controllers/DepartmentsController.php @@ -170,6 +170,8 @@ class DepartmentsController extends Controller $department->manager_id = ($request->filled('manager_id') ? $request->input('manager_id') : null); $department->location_id = ($request->filled('location_id') ? $request->input('location_id') : null); $department->company_id = ($request->filled('company_id') ? $request->input('company_id') : null); + $department->phone = $request->input('phone'); + $department->fax = $request->input('fax'); $department = $request->handleImages($department); 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/LabelsController.php b/app/Http/Controllers/LabelsController.php new file mode 100755 index 0000000000..950094bc45 --- /dev/null +++ b/app/Http/Controllers/LabelsController.php @@ -0,0 +1,85 @@ + + * @param string $labelName + * @return \Illuminate\Contracts\View\View + */ + public function show(string $labelName) + { + $labelName = str_replace('/', '\\', $labelName); + $template = Label::find($labelName); + + $exampleAsset = new Asset(); + + $exampleAsset->id = 999999; + $exampleAsset->name = 'JEN-867-5309'; + $exampleAsset->asset_tag = '100001'; + $exampleAsset->serial = 'SN9876543210'; + $exampleAsset->asset_eol_date = '2025-01-01'; + $exampleAsset->order_number = '12345'; + $exampleAsset->purchase_date = '2023-01-01'; + $exampleAsset->status_id = 1; + + $exampleAsset->company = new Company([ + 'name' => 'Test Company Limited', + 'phone' => '1-555-555-5555', + 'email' => 'company@example.com', + ]); + + $exampleAsset->setRelation('assignedTo', new User(['first_name' => 'Luke', 'last_name' => 'Skywalker'])); + $exampleAsset->defaultLoc = new Location(['name' => 'Building 1', 'phone' => '1-555-555-5555']); + $exampleAsset->location = new Location(['name' => 'Building 2', 'phone' => '1-555-555-5555']); + + $exampleAsset->model = new AssetModel(); + $exampleAsset->model->id = 999999; + $exampleAsset->model->name = 'Test Model'; + $exampleAsset->model->model_number = 'MDL5678'; + $exampleAsset->model->manufacturer = new Manufacturer(); + $exampleAsset->model->manufacturer->id = 999999; + $exampleAsset->model->manufacturer->name = 'Test Manufacturing Inc.'; + $exampleAsset->model->manufacturer->support_email = 'support@test.com'; + $exampleAsset->model->manufacturer->support_phone = '1-555-555-5555'; + $exampleAsset->model->manufacturer->support_url = 'https://example.com'; + $exampleAsset->supplier = new Supplier(['name' => 'Test Company Limited']); + $exampleAsset->model->category = new Category(); + $exampleAsset->model->category->id = 999999; + $exampleAsset->model->category->name = 'Test Category'; + + $settings = Setting::getSettings(); + if (request()->has('settings')) { + $overrides = request()->get('settings'); + foreach ($overrides as $key => $value) { + $settings->$key = $value; + } + } + + return (new LabelView()) + ->with('assets', collect([$exampleAsset])) + ->with('settings', $settings) + ->with('template', $template) + ->with('bulkedit', false) + ->with('count', 0); + + return redirect()->route('home')->with('error', trans('admin/labels/message.does_not_exist')); + } +} diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 50e20c7985..367ff3f1d9 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -76,7 +76,7 @@ class LicenseCheckinController extends Controller // Declare the rules for the form validation $rules = [ - 'note' => 'string|nullable', + 'notes' => 'string|nullable', ]; // Create a new validator instance from our validation rules @@ -97,10 +97,11 @@ class LicenseCheckinController extends Controller // Update the asset data $licenseSeat->assigned_to = null; $licenseSeat->asset_id = null; + $licenseSeat->notes = $request->input('notes'); // Was the asset updated? if ($licenseSeat->save()) { - event(new CheckoutableCheckedIn($licenseSeat, $return_to, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($licenseSeat, $return_to, Auth::user(), $request->input('notes'))); if ($backTo == 'user') { return redirect()->route('users.show', $return_to->id)->with('success', trans('admin/licenses/message.checkin.success')); @@ -128,6 +129,13 @@ class LicenseCheckinController extends Controller $license = License::findOrFail($licenseId); $this->authorize('checkin', $license); + if (! $license->reassignable) { + // Not allowed to checkin + Session::flash('error', 'License not reassignable.'); + + return redirect()->back()->withInput(); + } + $licenseSeatsByUser = LicenseSeat::where('license_id', '=', $licenseId) ->whereNotNull('assigned_to') ->with('user') diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index a710497692..07ca8bbd58 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -30,15 +30,17 @@ class LicenseCheckoutController extends Controller // Check that the license is valid if ($license = License::find($licenseId)) { + $this->authorize('checkout', $license); // If the license is valid, check that there is an available seat if ($license->avail_seats_count < 1) { return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license'); } + return view('licenses/checkout', compact('license')); } - $this->authorize('checkout', $license); + return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found')); + - return view('licenses/checkout', compact('license')); } /** @@ -61,6 +63,7 @@ class LicenseCheckoutController extends Controller $licenseSeat = $this->findLicenseSeatToCheckout($license, $seatId); $licenseSeat->user_id = Auth::id(); + $licenseSeat->notes = $request->input('notes'); $checkoutMethod = 'checkoutTo'.ucwords(request('checkout_to_type')); @@ -102,7 +105,7 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = $target->assigned_to; } if ($licenseSeat->save()) { - event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('notes'))); return true; } @@ -119,7 +122,7 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = request('assigned_to'); if ($licenseSeat->save()) { - event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('notes'))); return true; } diff --git a/app/Http/Controllers/Licenses/LicenseFilesController.php b/app/Http/Controllers/Licenses/LicenseFilesController.php index d457d4983a..f6f7c1ad0c 100644 --- a/app/Http/Controllers/Licenses/LicenseFilesController.php +++ b/app/Http/Controllers/Licenses/LicenseFilesController.php @@ -137,7 +137,7 @@ class LicenseFilesController extends Controller $this->authorize('view', $license); $this->authorize('licenses.files', $license); - if (! $log = Actionlog::find($fileId)) { + if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) { return response('No matching record for that asset/file', 500) ->header('Content-Type', 'text/plain'); } @@ -152,21 +152,19 @@ class LicenseFilesController extends Controller ->header('Content-Type', 'text/plain'); } else { + if (request('inline') == 'true') { + + $headers = [ + 'Content-Disposition' => 'inline', + ]; + + return Storage::download($file, $log->filename, $headers); + } + // We have to override the URL stuff here, since local defaults in Laravel's Flysystem // won't work, as they're not accessible via the web if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? return StorageHelper::downloader($file); - } else { - if ($download != 'true') { - \Log::debug('display the file'); - if ($contents = file_get_contents(Storage::url($file))) { // TODO - this will fail on private S3 files or large public ones - return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file))); - } - - return JsonResponse::create(['error' => 'Failed validation: '], 500); - } - - return StorageHelper::downloader($file); } } diff --git a/app/Http/Controllers/Licenses/LicensesController.php b/app/Http/Controllers/Licenses/LicensesController.php index 26cba33281..02e2163207 100755 --- a/app/Http/Controllers/Licenses/LicensesController.php +++ b/app/Http/Controllers/Licenses/LicensesController.php @@ -207,7 +207,7 @@ class LicensesController extends Controller if ($license->assigned_seats_count == 0) { // Delete the license and the associated license seats DB::table('license_seats') - ->where('id', $license->id) + ->where('license_id', $license->id) ->update(['assigned_to' => null, 'asset_id' => null]); $licenseSeats = $license->licenseseats(); diff --git a/app/Http/Controllers/LocationsController.php b/app/Http/Controllers/LocationsController.php index 39b73a9797..08dc38b3ac 100755 --- a/app/Http/Controllers/LocationsController.php +++ b/app/Http/Controllers/LocationsController.php @@ -79,6 +79,8 @@ class LocationsController extends Controller $location->ldap_ou = $request->input('ldap_ou'); $location->manager_id = $request->input('manager_id'); $location->user_id = Auth::id(); + $location->phone = request('phone'); + $location->fax = request('fax'); $location = $request->handleImages($location); @@ -139,6 +141,8 @@ class LocationsController extends Controller $location->state = $request->input('state'); $location->country = $request->input('country'); $location->zip = $request->input('zip'); + $location->phone = request('phone'); + $location->fax = request('fax'); $location->ldap_ou = $request->input('ldap_ou'); $location->manager_id = $request->input('manager_id'); diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 9bb4a420c0..ea036e8d82 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -23,6 +23,7 @@ use Input; use League\Csv\Reader; use Symfony\Component\HttpFoundation\StreamedResponse; use League\Csv\EscapeFormula; +use App\Http\Requests\CustomAssetReportRequest; /** @@ -246,6 +247,9 @@ class ReportsController extends Controller trans('general.action'), trans('general.type'), trans('general.item'), + trans('general.license_serial'), + trans('general.model_name'), + trans('general.model_no'), 'To', trans('general.notes'), 'Changed', @@ -288,6 +292,9 @@ class ReportsController extends Controller $actionlog->present()->actionType(), e($actionlog->itemType()), ($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name, + ($actionlog->item->serial) ? $actionlog->item->serial : null, + ($actionlog->item->model) ? htmlspecialchars($actionlog->item->model->name, ENT_NOQUOTES) : null, + ($actionlog->item->model) ? $actionlog->item->model->model_number : null, $target_name, ($actionlog->note) ? e($actionlog->note) : '', $actionlog->log_meta, @@ -403,11 +410,12 @@ class ReportsController extends Controller * @since [v1.0] * @return \Illuminate\Http\Response */ - public function postCustom(Request $request) + public function postCustom(CustomAssetReportRequest $request) { ini_set('max_execution_time', env('REPORT_TIME_LIMIT', 12000)); //12000 seconds = 200 minutes $this->authorize('reports.view'); + \Debugbar::disable(); $customfields = CustomField::get(); $response = new StreamedResponse(function () use ($customfields, $request) { @@ -501,7 +509,6 @@ class ReportsController extends Controller $header[] = trans('general.zip'); } - if ($request->filled('assigned_to')) { $header[] = trans('admin/hardware/table.checkoutto'); $header[] = trans('general.type'); @@ -527,24 +534,53 @@ class ReportsController extends Controller $header[] = trans('admin/users/table.title'); } + if ($request->filled('phone')) { + $header[] = trans('admin/users/table.phone'); + } + + if ($request->filled('user_address')) { + $header[] = trans('admin/reports/general.custom_export.user_address'); + } + + if ($request->filled('user_city')) { + $header[] = trans('admin/reports/general.custom_export.user_city'); + } + + if ($request->filled('user_state')) { + $header[] = trans('admin/reports/general.custom_export.user_state'); + } + + if ($request->filled('user_country')) { + $header[] = trans('admin/reports/general.custom_export.user_country'); + } + + if ($request->filled('user_zip')) { + $header[] = trans('admin/reports/general.custom_export.user_zip'); + } + if ($request->filled('status')) { $header[] = trans('general.status'); } if ($request->filled('warranty')) { - $header[] = 'Warranty'; - $header[] = 'Warranty Expires'; + $header[] = trans('admin/hardware/form.warranty'); + $header[] = trans('admin/hardware/form.warranty_expires'); } + if ($request->filled('depreciation')) { - $header[] = 'Value'; - $header[] = 'Diff'; - $header[] = 'Fully Depreciated'; + $header[] = trans('admin/hardware/table.book_value'); + $header[] = trans('admin/hardware/table.diff'); + $header[] = trans('admin/hardware/form.fully_depreciated'); } if ($request->filled('checkout_date')) { $header[] = trans('admin/hardware/table.checkout_date'); } + if ($request->filled('checkin_date')) { + $header[] = trans('admin/hardware/table.last_checkin_date'); + } + if ($request->filled('expected_checkin')) { $header[] = trans('admin/hardware/form.expected_checkin'); } @@ -590,28 +626,28 @@ class ReportsController extends Controller $executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']; \Log::debug('Added headers: '.$executionTime); - $assets = \App\Models\Company::scopeCompanyables(Asset::select('assets.*'))->with( + $assets = Asset::select('assets.*')->with( 'location', 'assetstatus', 'company', 'defaultLoc', 'assignedTo', '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 +667,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'))) { @@ -639,14 +675,28 @@ 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')]); + $created_start = \Carbon::parse($request->input('created_start'))->startOfDay(); + $created_end = \Carbon::parse($request->input('created_end'))->endOfDay(); + + $assets->whereBetween('assets.created_at', [$created_start, $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')]); + $checkout_start = \Carbon::parse($request->input('checkout_date_start'))->startOfDay(); + $checkout_end = \Carbon::parse($request->input('checkout_date_end'))->endOfDay(); + + $assets->whereBetween('assets.last_checkout', [$checkout_start, $checkout_end]); + } + + if (($request->filled('checkin_date_start'))) { + $assets->whereBetween('last_checkin', [ + Carbon::parse($request->input('checkin_date_start'))->startOfDay(), + // use today's date is `checkin_date_end` is not provided + Carbon::parse($request->input('checkin_date_end', now()))->endOfDay(), + ]); } 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')]); + $assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]); } if (($request->filled('asset_eol_date_start')) && ($request->filled('asset_eol_date_end'))) { @@ -654,7 +704,10 @@ class ReportsController extends Controller } if (($request->filled('last_audit_start')) && ($request->filled('last_audit_end'))) { - $assets->whereBetween('assets.last_audit_date', [$request->input('last_audit_start'), $request->input('last_audit_end')]); + $last_audit_start = \Carbon::parse($request->input('last_audit_start'))->startOfDay(); + $last_audit_end = \Carbon::parse($request->input('last_audit_end'))->endOfDay(); + + $assets->whereBetween('assets.last_audit_date', [$last_audit_start, $last_audit_end]); } if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) { @@ -670,6 +723,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']; @@ -808,6 +862,54 @@ class ReportsController extends Controller } } + if ($request->filled('phone')) { + if ($asset->checkedOutToUser()) { + $row[] = ($asset->assignedto) ? $asset->assignedto->phone : ''; + } else { + $row[] = ''; // Empty string if unassigned + } + } + + if ($request->filled('user_address')) { + if ($asset->checkedOutToUser()) { + $row[] = ($asset->assignedto) ? $asset->assignedto->address : ''; + } else { + $row[] = ''; // Empty string if unassigned + } + } + + if ($request->filled('user_city')) { + if ($asset->checkedOutToUser()) { + $row[] = ($asset->assignedto) ? $asset->assignedto->city : ''; + } else { + $row[] = ''; // Empty string if unassigned + } + } + + if ($request->filled('user_state')) { + if ($asset->checkedOutToUser()) { + $row[] = ($asset->assignedto) ? $asset->assignedto->state : ''; + } else { + $row[] = ''; // Empty string if unassigned + } + } + + if ($request->filled('user_country')) { + if ($asset->checkedOutToUser()) { + $row[] = ($asset->assignedto) ? $asset->assignedto->country : ''; + } else { + $row[] = ''; // Empty string if unassigned + } + } + + if ($request->filled('user_zip')) { + if ($asset->checkedOutToUser()) { + $row[] = ($asset->assignedto) ? $asset->assignedto->zip : ''; + } else { + $row[] = ''; // Empty string if unassigned + } + } + if ($request->filled('status')) { $row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : ''; } @@ -829,6 +931,12 @@ class ReportsController extends Controller $row[] = ($asset->last_checkout) ? $asset->last_checkout : ''; } + if ($request->filled('checkin_date')) { + $row[] = ($asset->last_checkin) + ? Carbon::parse($asset->last_checkin)->format('Y-m-d') + : ''; + } + if ($request->filled('expected_checkin')) { $row[] = ($asset->expected_checkin) ? $asset->expected_checkin : ''; } @@ -997,7 +1105,12 @@ class ReportsController extends Controller $assetsForReport = $acceptances ->filter(function ($acceptance) { - return $acceptance->checkoutable_type == 'App\Models\Asset'; + $acceptance_checkoutable_flag = false; + if ($acceptance->checkoutable){ + $acceptance_checkoutable_flag = $acceptance->checkoutable->checkedOutToUser(); + } + + return $acceptance->checkoutable_type == 'App\Models\Asset' && $acceptance_checkoutable_flag; }) ->map(function($acceptance) { return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance]; @@ -1014,27 +1127,34 @@ class ReportsController extends Controller * @throws \Illuminate\Auth\Access\AuthorizationException * @version v1.0 */ - public function sentAssetAcceptanceReminder($acceptanceId = null) + public function sentAssetAcceptanceReminder(Request $request) { $this->authorize('reports.view'); - if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) { + if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) { + \Log::debug('No pending acceptances'); // Redirect to the unaccepted assets report page with error return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data')); } + $assetItem = $acceptance->checkoutable; + \Log::debug(print_r($assetItem, true)); + if (is_null($acceptance->created_at)){ + \Log::debug('No acceptance created_at'); return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data')); } else { $logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get(); + if ($logItem_res->isEmpty()){ + \Log::debug('Acceptance date mismatch'); return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data')); } $logItem = $logItem_res[0]; } - if(!$assetItem->assignedTo->locale){ + if (!$assetItem->assignedTo->locale){ Notification::locale(Setting::getSettings()->locale)->send( $assetItem->assignedTo, new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note) diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index c65dbc7d27..989fe8c494 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -7,6 +7,7 @@ use App\Helpers\StorageHelper; use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\SettingsSamlRequest; use App\Http\Requests\SetupUserRequest; +use App\Models\CustomField; use App\Models\Group; use App\Models\Setting; use App\Models\Asset; @@ -26,7 +27,7 @@ use Response; use App\Http\Requests\SlackSettingsRequest; use Illuminate\Support\Str; use Illuminate\Support\Facades\Artisan; -use Validator; +use Illuminate\Support\Facades\Validator; /** * This controller handles all actions related to Settings for @@ -590,6 +591,7 @@ class SettingsController extends Controller $setting->date_display_format = $request->input('date_display_format'); $setting->time_display_format = $request->input('time_display_format'); $setting->digit_separator = $request->input('digit_separator'); + $setting->name_display_format = $request->input('name_display_format'); if ($setting->save()) { return redirect()->route('settings.index') @@ -808,9 +810,10 @@ class SettingsController extends Controller */ public function getLabels() { - $setting = Setting::getSettings(); - - return view('settings.labels', compact('setting')); + return view('settings.labels', [ + 'setting' => Setting::getSettings(), + 'customFields' => CustomField::all(), + ]); } /** @@ -827,6 +830,14 @@ class SettingsController extends Controller if (is_null($setting = Setting::getSettings())) { return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error')); } + $setting->label2_enable = $request->input('label2_enable'); + $setting->label2_template = $request->input('label2_template'); + $setting->label2_title = $request->input('label2_title'); + $setting->label2_asset_logo = $request->input('label2_asset_logo'); + $setting->label2_1d_type = $request->input('label2_1d_type'); + $setting->label2_2d_type = $request->input('label2_2d_type'); + $setting->label2_2d_target = $request->input('label2_2d_target'); + $setting->label2_fields = $request->input('label2_fields'); $setting->labels_per_page = $request->input('labels_per_page'); $setting->labels_width = $request->input('labels_width'); $setting->labels_height = $request->input('labels_height'); @@ -875,7 +886,7 @@ class SettingsController extends Controller } if ($setting->save()) { - return redirect()->route('settings.index') + return redirect()->route('settings.labels.index') ->with('success', trans('admin/settings/message.update.success')); } @@ -1039,6 +1050,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. * @@ -1094,7 +1147,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 @@ -1197,13 +1250,11 @@ class SettingsController extends Controller if (!$request->hasFile('file')) { return redirect()->route('settings.backups.index')->with('error', 'No file uploaded'); } else { + $max_file_size = Helper::file_upload_max_size(); - - $rules = [ + $validator = Validator::make($request->all(), [ 'file' => 'required|mimes:zip|max:'.$max_file_size, - ]; - - $validator = \Validator::make($request->all(), $rules); + ]); if ($validator->passes()) { @@ -1214,7 +1265,7 @@ class SettingsController extends Controller return redirect()->route('settings.backups.index')->with('success', 'File uploaded'); } - return redirect()->route('settings.backups.index')->withErrors($request->getErrors()); + return redirect()->route('settings.backups.index')->withErrors($validator); } diff --git a/app/Http/Controllers/Users/BulkUsersController.php b/app/Http/Controllers/Users/BulkUsersController.php index a2d3d496da..ca1e2a4897 100644 --- a/app/Http/Controllers/Users/BulkUsersController.php +++ b/app/Http/Controllers/Users/BulkUsersController.php @@ -125,10 +125,26 @@ class BulkUsersController extends Controller ]; } + /** + * Check to see if the user wants to actually blank out the values vs skip them + */ if ($request->input('null_location_id')=='1') { $this->update_array['location_id'] = null; } + if ($request->input('null_department_id')=='1') { + $this->update_array['department_id'] = null; + } + + if ($request->input('null_manager_id')=='1') { + $this->update_array['manager_id'] = null; + } + + if ($request->input('null_company_id')=='1') { + $this->update_array['company_id'] = null; + } + + if (! $manager_conflict) { $this->conditionallyAddItem('manager_id'); } diff --git a/app/Http/Controllers/Users/LDAPImportController.php b/app/Http/Controllers/Users/LDAPImportController.php index 88a6b207df..e535a171a0 100644 --- a/app/Http/Controllers/Users/LDAPImportController.php +++ b/app/Http/Controllers/Users/LDAPImportController.php @@ -49,15 +49,19 @@ class LDAPImportController extends Controller { $this->authorize('update', User::class); // Call Artisan LDAP import command. - $location_id = $request->input('location_id'); - Artisan::call('snipeit:ldap-sync', ['--location_id' => $location_id, '--json_summary' => true]); + + Artisan::call('snipeit:ldap-sync', ['--location_id' => $request->input('location_id'), '--json_summary' => true]); // Collect and parse JSON summary. $ldap_results_json = Artisan::output(); $ldap_results = json_decode($ldap_results_json, true); + if (!$ldap_results) { + return redirect()->back()->withInput()->with('error', trans('general.no_results')); + } // Direct user to appropriate status page. if ($ldap_results['error']) { + return redirect()->back()->withInput()->with('error', $ldap_results['error_message']); } diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index cb49396324..0b787306f9 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Users; +use App\Helpers\StorageHelper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetFileRequest; use App\Models\Actionlog; @@ -135,22 +136,36 @@ class UserFilesController extends Controller */ public function show($userId = null, $fileId = null) { + + if (empty($fileId)) { + return redirect()->route('users.show')->with('error', 'Invalid file request'); + } + $user = User::find($userId); // the license is valid if (isset($user->id)) { + $this->authorize('view', $user); - $log = Actionlog::find($fileId); - $file = $log->get_src('users'); + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) { - return Response::download($file); //FIXME this doesn't use the new StorageHelper yet, but it's complicated... + // Display the file inline + if (request('inline') == 'true') { + $headers = [ + 'Content-Disposition' => 'inline', + ]; + return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers); + } + + return Storage::download('private_uploads/users/'.$log->filename); + } + + return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found')); } - // Prepare the error message - $error = trans('admin/users/message.user_not_found', ['id' => $userId]); - // Redirect to the licence management page - return redirect()->route('users.index')->with('error', $error); + // Redirect to the user management page if the user doesn't exist + return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId])); } } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index ee3e4b14c6..c9aafd2c78 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -82,7 +82,7 @@ class ViewAssetsController extends Controller return view('account/requestable-assets', compact('assets', 'models')); } - public function getRequestItem(Request $request, $itemType, $itemId = null) + public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null) { $item = null; $fullItemType = 'App\\Models\\'.studly_case($itemType); @@ -119,16 +119,16 @@ class ViewAssetsController extends Controller $settings = Setting::getSettings(); - if ($item_request = $item->isRequestedBy($user)) { - $item->cancelRequest(); - $data['item_quantity'] = $item_request->qty; + if (($item_request = $item->isRequestedBy($user)) || $cancel_by_admin) { + $item->cancelRequest($requestingUser); + $data['item_quantity'] = ($item_request) ? $item_request->qty : 1; $logaction->logaction('request_canceled'); if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) { $settings->notify(new RequestAssetCancelation($data)); } - return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); + return redirect()->back()->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); } else { $item->request(); if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) { diff --git a/app/Http/Livewire/CategoryEditForm.php b/app/Http/Livewire/CategoryEditForm.php new file mode 100644 index 0000000000..05a1fe9d45 --- /dev/null +++ b/app/Http/Livewire/CategoryEditForm.php @@ -0,0 +1,67 @@ +originalSendCheckInEmailValue = $this->sendCheckInEmail; + + if ($this->eulaText || $this->useDefaultEula) { + $this->sendCheckInEmail = 1; + } + } + + public function render() + { + return view('livewire.category-edit-form'); + } + + public function updated($property, $value) + { + if (! in_array($property, ['eulaText', 'useDefaultEula'])) { + return; + } + + $this->sendCheckInEmail = $this->eulaText || $this->useDefaultEula ? 1 : $this->originalSendCheckInEmailValue; + } + + public function getShouldDisplayEmailMessageProperty(): bool + { + return $this->eulaText || $this->useDefaultEula; + } + + public function getEmailMessageProperty(): string + { + if ($this->useDefaultEula) { + return trans('admin/categories/general.email_will_be_sent_due_to_global_eula'); + } + + return trans('admin/categories/general.email_will_be_sent_due_to_category_eula'); + } + + public function getEulaTextDisabledProperty() + { + return (bool)$this->useDefaultEula; + } + + public function getSendCheckInEmailDisabledProperty() + { + return $this->eulaText || $this->useDefaultEula; + } +} diff --git a/app/Http/Livewire/Importer.php b/app/Http/Livewire/Importer.php index e11dd060e9..07f3a9f16c 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,182 +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', - 'last_name' => 'Last Name', - 'jobtitle' => 'Job Title', - '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', - 'zip' => 'Zip', - 'vip' => 'VIP', - 'remote' => 'Remote', - ]; - - static $locations = [ - 'name' => 'Name', - 'address' => 'Address', - 'address2' => 'Address 2', - 'city' => 'City', - 'state' => 'State', - 'country' => 'Country', - 'zip' => 'Zip', - 'currency' => 'Currency', - 'ldap_ou' => 'LDAP OU', - 'manager_username' => 'Manager Username', - 'manager' => 'Manager', - 'parent_location' => 'Parent Location', - ]; - - //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' - ], - 'zip' => - [ - 'Postal Code', - 'Post Code' - ], - 'min_amt' => - [ - 'Min Amount', - 'Min QTY' - ], - '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', - ], - - - ]; 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 = self::$general + self::$locations; + $results = $this->locations_fields; break; default: - $results = self::$general; + $results = []; } asort($results, SORT_FLAG_CASE | SORT_STRING); if ($type == "asset") { @@ -261,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. } @@ -272,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 - @@ -312,6 +174,306 @@ class Importer extends Component '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'), + 'image' => trans('general.importer.image_filename'), + 'asset_eol_date' => trans('admin/hardware/form.eol_date'), + /** + * 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'), + 'order_number' => trans('general.order_number'), + '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'), + 'email' => trans('general.importer.checked_out_to_email'), + 'username' => trans('general.importer.checked_out_to_username'), + 'manufacturer' => trans('general.manufacturer'), + ]; + + $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_num' => 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 foreach($this->importTypes AS $type => $name) { $this->columnOptions[$type] = $this->getColumns($type); @@ -323,8 +485,17 @@ class Importer extends Component public function selectFile($id) { + $this->clearMessage(); $this->activeFile = Import::find($id); + + if (!$this->activeFile) { + $this->message = trans('admin/hardware/message.import.file_missing'); + $this->message_type = 'danger'; + + return; + } + $this->field_map = null; foreach($this->activeFile->header_row as $element) { if(isset($this->activeFile->field_map[$element])) { @@ -359,6 +530,12 @@ class Importer extends Component } } + public function clearMessage() + { + $this->message = null; + $this->message_type = null; + } + public function render() { $this->files = Import::orderBy('id','desc')->get(); //HACK - slows down renders. diff --git a/app/Http/Livewire/SlackSettingsForm.php b/app/Http/Livewire/SlackSettingsForm.php index bb3a7bd7f6..7fc53c7818 100644 --- a/app/Http/Livewire/SlackSettingsForm.php +++ b/app/Http/Livewire/SlackSettingsForm.php @@ -12,7 +12,7 @@ class SlackSettingsForm extends Component public $webhook_endpoint; public $webhook_channel; public $webhook_botname; - public $isDisabled ='' ; + public $isDisabled ='disabled' ; public $webhook_name; public $webhook_link; public $webhook_placeholder; @@ -22,11 +22,17 @@ class SlackSettingsForm extends Component public Setting $setting; + public $webhook_endpoint_rules; + + protected $rules = [ - 'webhook_endpoint' => 'url|required_with:webhook_channel|starts_with:https://hooks.slack.com/services|nullable', + 'webhook_endpoint' => 'required_with:webhook_channel|starts_with:http://,https://,ftp://,irc://,https://hooks.slack.com/services/|url|nullable', 'webhook_channel' => 'required_with:webhook_endpoint|starts_with:#|nullable', 'webhook_botname' => 'string|nullable', ]; + public $messages = [ + 'webhook_endpoint.starts_with' => 'your webhook endpoint should begin with http://, https:// or other protocol.', + ]; public function mount() { $this->webhook_text= [ @@ -55,9 +61,7 @@ class SlackSettingsForm extends Component $this->webhook_botname = $this->setting->webhook_botname; $this->webhook_options = $this->setting->webhook_selected; - if($this->setting->webhook_selected == 'general'){ - $this->isDisabled=''; - } + if($this->setting->webhook_endpoint != null && $this->setting->webhook_channel != null){ $this->isDisabled= ''; } @@ -65,9 +69,8 @@ class SlackSettingsForm extends Component } public function updated($field) { - if($this->webhook_selected != 'general') { $this->validateOnly($field, $this->rules); - } + } public function updatedWebhookSelected() { @@ -82,7 +85,6 @@ class SlackSettingsForm extends Component } private function isButtonDisabled() { - if($this->webhook_selected == 'slack') { if (empty($this->webhook_endpoint)) { $this->isDisabled = 'disabled'; $this->save_button = trans('admin/settings/general.webhook_presave'); @@ -91,8 +93,6 @@ class SlackSettingsForm extends Component $this->isDisabled = 'disabled'; $this->save_button = trans('admin/settings/general.webhook_presave'); } - } - } public function render() @@ -108,6 +108,7 @@ class SlackSettingsForm extends Component 'defaults' => [ 'exceptions' => false, ], + 'allow_redirects' => false, ]); $payload = json_encode( @@ -116,18 +117,23 @@ class SlackSettingsForm extends Component 'text' => trans('general.webhook_test_msg', ['app' => $this->webhook_name]), 'username' => e($this->webhook_botname), 'icon_emoji' => ':heart:', + ]); try { + $test = $webhook->post($this->webhook_endpoint, ['body' => $payload]); - $webhook->post($this->webhook_endpoint, ['body' => $payload]); + if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){ + return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint])); + } $this->isDisabled=''; $this->save_button = trans('general.save'); - return session()->flash('success' , 'Your '.$this->webhook_name.' Integration works!'); + return session()->flash('success' , trans('admin/settings/message.webhook.success', ['webhook_name' => $this->webhook_name])); } catch (\Exception $e) { - $this->isDisabled= 'disabled'; + $this->isDisabled='disabled'; + $this->save_button = trans('admin/settings/general.webhook_presave'); return session()->flash('error' , trans('admin/settings/message.webhook.error', ['error_message' => $e->getMessage(), 'app' => $this->webhook_name])); } @@ -158,9 +164,7 @@ class SlackSettingsForm extends Component if (Helper::isDemoMode()) { session()->flash('error',trans('general.feature_disabled')); } else { - if ($this->webhook_selected != 'general') { - $this->validate($this->rules); - } + $this->validate($this->rules); $this->setting->webhook_selected = $this->webhook_selected; $this->setting->webhook_endpoint = $this->webhook_endpoint; diff --git a/app/Http/Requests/CustomAssetReportRequest.php b/app/Http/Requests/CustomAssetReportRequest.php new file mode 100644 index 0000000000..2a8fbe7fab --- /dev/null +++ b/app/Http/Requests/CustomAssetReportRequest.php @@ -0,0 +1,46 @@ + 'date|date_format:Y-m-d|nullable', + 'purchase_end' => 'date|date_format:Y-m-d|nullable', + 'created_start' => 'date|date_format:Y-m-d|nullable', + 'created_end' => 'date|date_format:Y-m-d|nullable', + 'checkout_date_start' => 'date|date_format:Y-m-d|nullable', + 'checkout_date_end' => 'date|date_format:Y-m-d|nullable', + 'expected_checkin_start' => 'date|date_format:Y-m-d|nullable', + 'expected_checkin_end' => 'date|date_format:Y-m-d|nullable', + 'checkin_date_start' => 'date|date_format:Y-m-d|nullable', + 'checkin_date_end' => 'date|date_format:Y-m-d|nullable', + 'last_audit_start' => 'date|date_format:Y-m-d|nullable', + 'last_audit_end' => 'date|date_format:Y-m-d|nullable', + 'next_audit_start' => 'date|date_format:Y-m-d|nullable', + 'next_audit_end' => 'date|date_format:Y-m-d|nullable', + ]; + } + + public function response(array $errors) + { + return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag); + } +} diff --git a/app/Http/Requests/SaveUserRequest.php b/app/Http/Requests/SaveUserRequest.php index 98e561549e..b38193c15a 100644 --- a/app/Http/Requests/SaveUserRequest.php +++ b/app/Http/Requests/SaveUserRequest.php @@ -32,6 +32,7 @@ class SaveUserRequest extends FormRequest public function rules() { $rules = [ + 'department_id' => 'nullable|exists:departments,id', 'manager_id' => 'nullable|exists:users,id', ]; diff --git a/app/Http/Requests/StoreAssetRequest.php b/app/Http/Requests/StoreAssetRequest.php new file mode 100644 index 0000000000..254895f134 --- /dev/null +++ b/app/Http/Requests/StoreAssetRequest.php @@ -0,0 +1,40 @@ +getRules(), + parent::rules(), + ); + + return $rules; + } +} diff --git a/app/Http/Traits/UniqueSerialTrait.php b/app/Http/Traits/UniqueSerialTrait.php deleted file mode 100644 index b5529d7cf8..0000000000 --- a/app/Http/Traits/UniqueSerialTrait.php +++ /dev/null @@ -1,25 +0,0 @@ -unique_serial == '1') { - return 'unique_undeleted:'.$this->table.','.$this->getKey(); - } - } - } -} diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index cdc94ebed9..7b79973a9a 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -32,7 +32,7 @@ class AccessoriesTransformer 'model_number' => ($accessory->model_number) ? e($accessory->model_number) : null, 'category' => ($accessory->category) ? ['id' => $accessory->category->id, 'name'=> e($accessory->category->name)] : null, 'location' => ($accessory->location) ? ['id' => $accessory->location->id, 'name'=> e($accessory->location->name)] : null, - 'notes' => ($accessory->notes) ? e($accessory->notes) : null, + 'notes' => ($accessory->notes) ? Helper::parseEscapedMarkedownInline($accessory->notes) : null, 'qty' => ($accessory->qty) ? (int) $accessory->qty : null, 'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null, 'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost), diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index cd2ce586d1..1de9143329 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -3,8 +3,17 @@ namespace App\Http\Transformers; use App\Helpers\Helper; use App\Models\Actionlog; +use App\Models\Asset; +use App\Models\CustomField; use App\Models\Setting; +use App\Models\Company; +use App\Models\Supplier; +use App\Models\Location; +use App\Models\AssetModel; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Contracts\Encryption\DecryptException; +use Illuminate\Support\Facades\Crypt; +use Illuminate\Support\Facades\Gate; class ActionlogsTransformer { @@ -38,21 +47,80 @@ class ActionlogsTransformer public function transformActionlog (Actionlog $actionlog, $settings = null) { $icon = $actionlog->present()->icon(); + $custom_fields = CustomField::all(); + if ($actionlog->filename!='') { - $icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename)); + $icon = Helper::filetype_icon($actionlog->filename); } // This is necessary since we can't escape special characters within a JSON object if (($actionlog->log_meta) && ($actionlog->log_meta!='')) { $meta_array = json_decode($actionlog->log_meta); + $clean_meta = []; + if ($meta_array) { + foreach ($meta_array as $fieldname => $fieldata) { + $clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old); $clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new); + + // this is a custom field + if (str_starts_with($fieldname, '_snipeit_')) { + + foreach ($custom_fields as $custom_field) { + + if ($custom_field->db_column == $fieldname) { + + if ($custom_field->field_encrypted == '1') { + + // Unset these fields. We need to decrypt them, since even if the decrypted value + // didn't change, their value in the DB will, so we have to compare the unencrypted version + // to see if the values actually did change + unset($clean_meta[$fieldname]); + unset($clean_meta[$fieldname]); + + $enc_old = ''; + $enc_new = ''; + + try { + $enc_old = \Crypt::decryptString($this->clean_field($fieldata->old)); + } catch (\Exception $e) { + \Log::debug('Could not decrypt field - maybe the key changed?'); + } + + try { + $enc_new = \Crypt::decryptString($this->clean_field($fieldata->new)); + } catch (\Exception $e) { + \Log::debug('Could not decrypt field - maybe the key changed?'); + } + + if ($enc_old != $enc_new) { + \Log::debug('custom fields do not match'); + $clean_meta[$fieldname]['old'] = "************"; + $clean_meta[$fieldname]['new'] = "************"; + + // Display the changes if the user is an admin or superadmin + if (Gate::allows('admin')) { + $clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old): ''; + $clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new): ''; + } + + } + + + } + + } + + } + } + } } + $clean_meta= $this->changedInfo($clean_meta); } $file_url = ''; @@ -110,14 +178,15 @@ class ActionlogsTransformer 'type' => e($actionlog->targetType()), ] : null, - 'note' => ($actionlog->note) ? e($actionlog->note): null, + 'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null, 'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null, 'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null, 'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'), ]; - //\Log::info("Clean Meta is: ".print_r($clean_meta,true)); +// \Log::info("Clean Meta is: ".print_r($clean_meta,true)); //dd($array); + return $array; } @@ -132,6 +201,98 @@ class ActionlogsTransformer } return (new DatatablesTransformer)->transformDatatables($array, $total); } + /** + * This takes the ids of the changed attributes and returns the names instead for the history view of an Asset + * + * @param array $clean_meta + * @return array + */ + + public function changedInfo(array $clean_meta) + { $location = Location::withTrashed()->get(); + $supplier = Supplier::withTrashed()->get(); + $model = AssetModel::withTrashed()->get(); + $company = Company::get(); + + + if(array_key_exists('rtd_location_id',$clean_meta)) { + + $oldRtd = $location->find($clean_meta['rtd_location_id']['old']); + $oldRtdName = $oldRtd ? e($oldRtd->name) : trans('general.deleted'); + + $newRtd = $location->find($clean_meta['rtd_location_id']['new']); + $newRtdName = $newRtd ? e($newRtd->name) : trans('general.deleted'); + + $clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $oldRtdName : ''; + $clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $newRtdName : ''; + $clean_meta['Default Location'] = $clean_meta['rtd_location_id']; + unset($clean_meta['rtd_location_id']); + } + + + if (array_key_exists('location_id', $clean_meta)) { + + $oldLocation = $location->find($clean_meta['location_id']['old']); + $oldLocationName = $oldLocation ? e($oldLocation->name) : trans('general.deleted'); + + $newLocation = $location->find($clean_meta['location_id']['new']); + $newLocationName = $newLocation ? e($newLocation->name) : trans('general.deleted'); + + + $clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ". $oldLocationName : ''; + $clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ". $newLocationName : ''; + $clean_meta['Current Location'] = $clean_meta['location_id']; + unset($clean_meta['location_id']); + } + + if(array_key_exists('model_id', $clean_meta)) { + + $oldModel = $model->find($clean_meta['model_id']['old']); + $oldModelName = $oldModel ? e($oldModel->name) : trans('admin/models/message.deleted'); + + $newModel = $model->find($clean_meta['model_id']['new']); + $newModelName = $newModel ? e($newModel->name) : trans('admin/models/message.deleted'); + + $clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$oldModelName; + $clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$newModelName; /** model is required at asset creation */ + + $clean_meta['Model'] = $clean_meta['model_id']; + unset($clean_meta['model_id']); + } + if(array_key_exists('company_id', $clean_meta)) { + + $oldCompany = $company->find($clean_meta['company_id']['old']); + $oldCompanyName = $oldCompany ? e($oldCompany->name) : trans('admin/company/message.deleted'); + + $newCompany = $company->find($clean_meta['company_id']['new']); + $newCompanyName = $newCompany ? e($newCompany->name) : trans('admin/company/message.deleted'); + + $clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."] ". $oldCompanyName : trans('general.unassigned'); + $clean_meta['company_id']['new'] = $clean_meta['company_id']['new'] ? "[id: ".$clean_meta['company_id']['new']."] ". $newCompanyName : trans('general.unassigned'); + $clean_meta['Company'] = $clean_meta['company_id']; + unset($clean_meta['company_id']); + } + if(array_key_exists('supplier_id', $clean_meta)) { + + $oldSupplier = $supplier->find($clean_meta['supplier_id']['old']); + $oldSupplierName = $oldSupplier ? e($oldSupplier->name) : trans('admin/suppliers/message.deleted'); + + $newSupplier = $supplier->find($clean_meta['supplier_id']['new']); + $newSupplierName = $newSupplier ? e($newSupplier->name) : trans('admin/suppliers/message.deleted'); + + $clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ". $oldSupplierName : trans('general.unassigned'); + $clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ". $newSupplierName : trans('general.unassigned'); + $clean_meta['Supplier'] = $clean_meta['supplier_id']; + unset($clean_meta['supplier_id']); + } + if(array_key_exists('asset_eol_date', $clean_meta)) { + $clean_meta['EOL date'] = $clean_meta['asset_eol_date']; + unset($clean_meta['asset_eol_date']); + } + + return $clean_meta; + + } diff --git a/app/Http/Transformers/AssetMaintenancesTransformer.php b/app/Http/Transformers/AssetMaintenancesTransformer.php index 7766d7cd3a..fa92640823 100644 --- a/app/Http/Transformers/AssetMaintenancesTransformer.php +++ b/app/Http/Transformers/AssetMaintenancesTransformer.php @@ -49,7 +49,7 @@ class AssetMaintenancesTransformer 'id' => (int) $assetmaintenance->asset->defaultLoc->id, 'name'=> e($assetmaintenance->asset->defaultLoc->name), ] : null, - 'notes' => ($assetmaintenance->notes) ? e($assetmaintenance->notes) : null, + 'notes' => ($assetmaintenance->notes) ? Helper::parseEscapedMarkedownInline($assetmaintenance->notes) : null, 'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null, 'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost), 'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type), @@ -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/AssetModelsTransformer.php b/app/Http/Transformers/AssetModelsTransformer.php index 5725e55930..ff0a13048b 100644 --- a/app/Http/Transformers/AssetModelsTransformer.php +++ b/app/Http/Transformers/AssetModelsTransformer.php @@ -47,6 +47,7 @@ class AssetModelsTransformer ] : null, 'image' => ($assetmodel->image != '') ? Storage::disk('public')->url('models/'.e($assetmodel->image)) : null, 'model_number' => e($assetmodel->model_number), + 'min_amt' => ($assetmodel->min_amt) ? (int) $assetmodel->min_amt : null, 'depreciation' => ($assetmodel->depreciation) ? [ 'id' => (int) $assetmodel->depreciation->id, 'name'=> e($assetmodel->depreciation->name), @@ -63,7 +64,7 @@ class AssetModelsTransformer 'default_fieldset_values' => $default_field_values, 'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None', 'requestable' => ($assetmodel->requestable == '1') ? true : false, - 'notes' => e($assetmodel->notes), + 'notes' => Helper::parseEscapedMarkedownInline($assetmodel->notes), 'created_at' => Helper::getFormattedDateObject($assetmodel->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($assetmodel->updated_at, 'datetime'), 'deleted_at' => Helper::getFormattedDateObject($assetmodel->deleted_at, 'datetime'), diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index d431ec890d..d38eac2419 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -7,7 +7,8 @@ use App\Models\Asset; use App\Models\Setting; use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; - +use Carbon\Carbon; +use Auth; class AssetsTransformer { @@ -38,7 +39,7 @@ class AssetsTransformer 'byod' => ($asset->byod ? true : false), 'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null, - 'eol' => ($asset->model->eol != '') ? $asset->model->eol : null, + 'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date).' months' : null, 'asset_eol_date' => ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date') : null, 'status_label' => ($asset->assetstatus) ? [ 'id' => (int) $asset->assetstatus->id, @@ -58,7 +59,7 @@ class AssetsTransformer 'id' => (int) $asset->supplier->id, 'name'=> e($asset->supplier->name), ] : null, - 'notes' => ($asset->notes) ? e($asset->notes) : null, + 'notes' => ($asset->notes) ? Helper::parseEscapedMarkedownInline($asset->notes) : null, 'order_number' => ($asset->order_number) ? e($asset->order_number) : null, 'company' => ($asset->company) ? [ 'id' => (int) $asset->company->id, @@ -92,6 +93,7 @@ class AssetsTransformer 'checkout_counter' => (int) $asset->checkout_counter, 'requests_counter' => (int) $asset->requests_counter, 'user_can_checkout' => (bool) $asset->availableForCheckout(), + 'book_value' => Helper::formatCurrencyOutput($asset->getLinearDepreciatedValue()), ]; @@ -101,10 +103,10 @@ class AssetsTransformer foreach ($asset->model->fieldset->fields as $field) { if ($field->isFieldDecryptable($asset->{$field->db_column})) { $decrypted = Helper::gracefulDecrypt($field, $asset->{$field->db_column}); - $value = (Gate::allows('superadmin')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted')); + $value = (Gate::allows('assets.view.encrypted_custom_fields')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted')); if ($field->format == 'DATE'){ - if (Gate::allows('superadmin')){ + if (Gate::allows('assets.view.encrypted_custom_fields')){ $value = Helper::getFormattedDateObject($value, 'date', false); } else { $value = strtoupper(trans('admin/custom_fields/general.encrypted')); @@ -230,6 +232,29 @@ class AssetsTransformer 'assigned_to_self' => ($asset->assigned_to == \Auth::user()->id), ]; + if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) { + $fields_array = []; + + foreach ($asset->model->fieldset->fields as $field) { + + // Only display this if it's allowed via the custom field setting + if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) { + + $value = $asset->{$field->db_column}; + if (($field->format == 'DATE') && (!is_null($value)) && ($value != '')) { + $value = Helper::getFormattedDateObject($value, 'date', false); + } + + $fields_array[$field->db_column] = e($value); + } + + $array['custom_fields'] = $fields_array; + } + } else { + $array['custom_fields'] = new \stdClass; // HACK to force generation of empty object instead of empty list + } + + $permissions_array['available_actions'] = [ 'cancel' => ($asset->isRequestedBy(\Auth::user())) ? true : false, 'request' => ($asset->isRequestedBy(\Auth::user())) ? false : true, diff --git a/app/Http/Transformers/CompaniesTransformer.php b/app/Http/Transformers/CompaniesTransformer.php index 4f1de75dec..fe8befc27a 100644 --- a/app/Http/Transformers/CompaniesTransformer.php +++ b/app/Http/Transformers/CompaniesTransformer.php @@ -26,6 +26,9 @@ class CompaniesTransformer $array = [ 'id' => (int) $company->id, 'name' => e($company->name), + 'phone' => ($company->phone!='') ? e($company->phone): null, + 'fax' => ($company->fax!='') ? e($company->fax): null, + 'email' => ($company->email!='') ? e($company->email): null, 'image' => ($company->image) ? Storage::disk('public')->url('companies/'.e($company->image)) : null, 'created_at' => Helper::getFormattedDateObject($company->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($company->updated_at, 'datetime'), diff --git a/app/Http/Transformers/ComponentsTransformer.php b/app/Http/Transformers/ComponentsTransformer.php index 97677af283..d18870bdc3 100644 --- a/app/Http/Transformers/ComponentsTransformer.php +++ b/app/Http/Transformers/ComponentsTransformer.php @@ -46,7 +46,7 @@ class ComponentsTransformer 'id' => (int) $component->company->id, 'name' => e($component->company->name), ] : null, - 'notes' => ($component->notes) ? e($component->notes) : null, + 'notes' => ($component->notes) ? Helper::parseEscapedMarkedownInline($component->notes) : null, 'created_at' => Helper::getFormattedDateObject($component->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($component->updated_at, 'datetime'), 'user_can_checkout' => ($component->numRemaining() > 0) ? 1 : 0, diff --git a/app/Http/Transformers/ConsumablesTransformer.php b/app/Http/Transformers/ConsumablesTransformer.php index b92f843b7f..f23ebc0382 100644 --- a/app/Http/Transformers/ConsumablesTransformer.php +++ b/app/Http/Transformers/ConsumablesTransformer.php @@ -39,7 +39,7 @@ class ConsumablesTransformer 'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost), 'purchase_date' => Helper::getFormattedDateObject($consumable->purchase_date, 'date'), 'qty' => (int) $consumable->qty, - 'notes' => ($consumable->notes) ? e($consumable->notes) : null, + 'notes' => ($consumable->notes) ? Helper::parseEscapedMarkedownInline($consumable->notes) : null, 'created_at' => Helper::getFormattedDateObject($consumable->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($consumable->updated_at, 'datetime'), ]; diff --git a/app/Http/Transformers/CustomFieldsTransformer.php b/app/Http/Transformers/CustomFieldsTransformer.php index db467be0bd..d6401a3e5e 100644 --- a/app/Http/Transformers/CustomFieldsTransformer.php +++ b/app/Http/Transformers/CustomFieldsTransformer.php @@ -49,6 +49,7 @@ class CustomFieldsTransformer '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, + 'show_in_listview' => ($field->show_in_listview == '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/DepartmentsTransformer.php b/app/Http/Transformers/DepartmentsTransformer.php index 7a0d7647d1..546cedf04f 100644 --- a/app/Http/Transformers/DepartmentsTransformer.php +++ b/app/Http/Transformers/DepartmentsTransformer.php @@ -26,6 +26,8 @@ class DepartmentsTransformer $array = [ 'id' => (int) $department->id, 'name' => e($department->name), + 'phone' => ($department->phone!='') ? e($department->phone): null, + 'fax' => ($department->fax!='') ? e($department->fax): null, 'image' => ($department->image) ? Storage::disk('public')->url(app('departments_upload_url').e($department->image)) : null, 'company' => ($department->company) ? [ 'id' => (int) $department->company->id, diff --git a/app/Http/Transformers/LabelsTransformer.php b/app/Http/Transformers/LabelsTransformer.php new file mode 100644 index 0000000000..0ed5a09686 --- /dev/null +++ b/app/Http/Transformers/LabelsTransformer.php @@ -0,0 +1,71 @@ +transformDatatables($array, $total); + } + + public function transformLabel(Label $label) + { + $array = [ + 'name' => $label->getName(), + 'unit' => $label->getUnit(), + + 'width' => number_format($label->getWidth(), 2), + 'height' => number_format($label->getHeight(), 2), + + 'margin_top' => $label->getMarginTop(), + 'margin_bottom' => $label->getMarginBottom(), + 'margin_left' => $label->getMarginLeft(), + 'margin_right' => $label->getMarginRight(), + + 'support_asset_tag' => $label->getSupportAssetTag(), + 'support_1d_barcode' => $label->getSupport1DBarcode(), + 'support_2d_barcode' => $label->getSupport2DBarcode(), + 'support_fields' => $label->getSupportFields(), + 'support_logo' => $label->getSupportLogo(), + 'support_title' => $label->getSupportTitle(), + ]; + + if ($label instanceof Sheet) { + $array['sheet_info'] = [ + 'label_width' => $label->getLabelWidth(), + 'label_height' => $label->getLabelHeight(), + + 'label_margin_top' => $label->getLabelMarginTop(), + 'label_margin_bottom' => $label->getLabelMarginBottom(), + 'label_margin_left' => $label->getLabelMarginLeft(), + 'label_margin_right' => $label->getLabelMarginRight(), + + 'labels_per_page' => $label->getLabelsPerPage(), + 'label_border' => $label->getLabelBorder(), + ]; + } + + if ($label instanceof RectangleSheet) { + $array['rectanglesheet_info'] = [ + 'columns' => $label->getColumns(), + 'rows' => $label->getRows(), + 'column_spacing' => $label->getLabelColumnSpacing(), + 'row_spacing' => $label->getLabelRowSpacing(), + ]; + } + + return $array; + } +} diff --git a/app/Http/Transformers/LicenseSeatsTransformer.php b/app/Http/Transformers/LicenseSeatsTransformer.php index f82fd3a49f..62614db4d3 100644 --- a/app/Http/Transformers/LicenseSeatsTransformer.php +++ b/app/Http/Transformers/LicenseSeatsTransformer.php @@ -45,6 +45,7 @@ class LicenseSeatsTransformer 'name'=> e($seat->location()->name), ] : null, 'reassignable' => (bool) $seat->license->reassignable, + 'notes' => e($seat->notes), 'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')), ]; diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php index 8df6b89f19..f68ad738d8 100644 --- a/app/Http/Transformers/LicensesTransformer.php +++ b/app/Http/Transformers/LicensesTransformer.php @@ -34,7 +34,7 @@ class LicensesTransformer 'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null, 'purchase_cost' => Helper::formatCurrencyOutput($license->purchase_cost), 'purchase_cost_numeric' => $license->purchase_cost, - 'notes' => e($license->notes), + 'notes' => Helper::parseEscapedMarkedownInline($license->notes), 'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'), 'seats' => (int) $license->seats, 'free_seats_count' => (int) $license->free_seats_count, diff --git a/app/Http/Transformers/LocationsTransformer.php b/app/Http/Transformers/LocationsTransformer.php index 22eade5d6e..635a90cbc7 100644 --- a/app/Http/Transformers/LocationsTransformer.php +++ b/app/Http/Transformers/LocationsTransformer.php @@ -43,6 +43,8 @@ class LocationsTransformer 'state' => ($location->state) ? e($location->state) : null, 'country' => ($location->country) ? e($location->country) : null, 'zip' => ($location->zip) ? e($location->zip) : null, + 'phone' => ($location->phone!='') ? e($location->phone): null, + 'fax' => ($location->fax!='') ? e($location->fax): null, 'assigned_assets_count' => (int) $location->assigned_assets_count, 'assets_count' => (int) $location->assets_count, 'rtd_assets_count' => (int) $location->rtd_assets_count, diff --git a/app/Http/Transformers/SuppliersTransformer.php b/app/Http/Transformers/SuppliersTransformer.php index e7546bfd15..1fdc93c193 100644 --- a/app/Http/Transformers/SuppliersTransformer.php +++ b/app/Http/Transformers/SuppliersTransformer.php @@ -43,7 +43,7 @@ class SuppliersTransformer '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, + 'notes' => ($supplier->notes) ? Helper::parseEscapedMarkedownInline($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 867a884619..45de50fa7d 100644 --- a/app/Http/Transformers/UsersTransformer.php +++ b/app/Http/Transformers/UsersTransformer.php @@ -24,7 +24,7 @@ class UsersTransformer $array = [ 'id' => (int) $user->id, 'avatar' => e($user->present()->gravatar), - 'name' => e($user->first_name).' '.e($user->last_name), + 'name' => e($user->getFullNameAttribute()), 'first_name' => e($user->first_name), 'last_name' => e($user->last_name), 'username' => e($user->username), @@ -53,7 +53,7 @@ class UsersTransformer 'id' => (int) $user->userloc->id, 'name'=> e($user->userloc->name), ] : null, - 'notes'=> e($user->notes), + 'notes'=> Helper::parseEscapedMarkedownInline($user->notes), 'permissions' => $user->decodePermissions(), 'activated' => ($user->activated == '1') ? true : false, 'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false, diff --git a/app/Importer/AccessoryImporter.php b/app/Importer/AccessoryImporter.php index 417075ef31..9901fb70d7 100644 --- a/app/Importer/AccessoryImporter.php +++ b/app/Importer/AccessoryImporter.php @@ -34,7 +34,7 @@ class AccessoryImporter extends ItemImporter } $this->log('Updating Accessory'); - $this->item['model_number'] = $this->findCsvMatch($row, "model_number"); + $this->item['model_number'] = trim($this->findCsvMatch($row, "model_number")); $accessory->update($this->sanitizeItemForUpdating($accessory)); $accessory->save(); diff --git a/app/Importer/AssetImporter.php b/app/Importer/AssetImporter.php index 0fcbf1166c..cf762a8fd4 100644 --- a/app/Importer/AssetImporter.php +++ b/app/Importer/AssetImporter.php @@ -3,7 +3,12 @@ namespace App\Importer; use App\Models\Asset; +use App\Models\AssetModel; use App\Models\Statuslabel; +use App\Models\User; +use App\Events\CheckoutableCheckedIn; +use Illuminate\Support\Facades\Auth; +use Carbon\Carbon; class AssetImporter extends ItemImporter { @@ -12,7 +17,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,6 +68,7 @@ class AssetImporter extends ItemImporter $asset_tag = Asset::autoincrement_asset(); } + $asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first(); if ($asset) { if (! $this->updating) { @@ -74,13 +83,13 @@ class AssetImporter extends ItemImporter $this->log('No Matching Asset, Creating a new one'); $asset = new Asset; } - $this->item['notes'] = $this->findCsvMatch($row, 'asset_notes'); - $this->item['image'] = $this->findCsvMatch($row, 'image'); - $this->item['requestable'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable')) == 1) ? '1' : 0; + $this->item['notes'] = trim($this->findCsvMatch($row, 'asset_notes')); + $this->item['image'] = trim($this->findCsvMatch($row, 'image')); + $this->item['requestable'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'))) == 1) ? '1' : 0; $asset->requestable = $this->item['requestable']; - $this->item['warranty_months'] = intval($this->findCsvMatch($row, 'warranty_months')); + $this->item['warranty_months'] = intval(trim($this->findCsvMatch($row, 'warranty_months'))); $this->item['model_id'] = $this->createOrFetchAssetModel($row); - $this->item['byod'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'byod')) == 1) ? '1' : 0; + $this->item['byod'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'byod'))) == 1) ? '1' : 0; // If no status ID is found @@ -113,7 +122,7 @@ class AssetImporter extends ItemImporter if (isset($this->item['next_audit_date'])) { $item['next_audit_date'] = $this->item['next_audit_date']; } - + if ($editingAsset) { $asset->update($item); } else { @@ -126,17 +135,22 @@ class AssetImporter extends ItemImporter $asset->{$custom_field} = $val; } } - - //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. //-- user_id is a property of the abstract class Importer, which this class inherits from and it's setted by //-- the class that needs to use it (command importer or GUI importer inside the project). - if (isset($target)) { + if (isset($target) && ($target !== false)) { + if (!is_null($asset->assigned_to)){ + if ($asset->assigned_to != $target->id){ + event(new CheckoutableCheckedIn($asset, User::find($asset->assigned_to), Auth::user(), $asset->notes, date('Y-m-d H:i:s'))); + } + } + $asset->fresh()->checkOut($target, $this->user_id, date('Y-m-d H:i:s'), null, $asset->notes, $asset->name); } diff --git a/app/Importer/ComponentImporter.php b/app/Importer/ComponentImporter.php index de3ee14d1c..71ded1b0e5 100644 --- a/app/Importer/ComponentImporter.php +++ b/app/Importer/ComponentImporter.php @@ -28,8 +28,8 @@ class ComponentImporter extends ItemImporter { $component = null; $this->log('Creating Component'); - $component = Component::where('name', $this->item['name']) - ->where('serial', $this->item['serial']) + $component = Component::where('name', trim($this->item['name'])) + ->where('serial', trim($this->item['serial'])) ->first(); if ($component) { diff --git a/app/Importer/ConsumableImporter.php b/app/Importer/ConsumableImporter.php index b0dea1f514..5a65514deb 100644 --- a/app/Importer/ConsumableImporter.php +++ b/app/Importer/ConsumableImporter.php @@ -26,7 +26,7 @@ class ConsumableImporter extends ItemImporter */ public function createConsumableIfNotExists($row) { - $consumable = Consumable::where('name', $this->item['name'])->first(); + $consumable = Consumable::where('name', trim($this->item['name']))->first(); if ($consumable) { if (! $this->updating) { $this->log('A matching Consumable '.$this->item['name'].' already exists. '); @@ -41,9 +41,9 @@ class ConsumableImporter extends ItemImporter } $this->log('No matching consumable, creating one'); $consumable = new Consumable(); - $this->item['model_number'] = $this->findCsvMatch($row, 'model_number'); - $this->item['item_no'] = $this->findCsvMatch($row, 'item_number'); - $this->item['min_amt'] = $this->findCsvMatch($row, "min_amt"); + $this->item['model_number'] = trim($this->findCsvMatch($row, 'model_number')); + $this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number')); + $this->item['min_amt'] = trim($this->findCsvMatch($row, "min_amt")); $consumable->fill($this->sanitizeItemForStoring($consumable)); //FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything. $consumable->unsetEventDispatcher(); diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php index dfc8084666..961f4af52c 100644 --- a/app/Importer/Importer.php +++ b/app/Importer/Importer.php @@ -19,14 +19,22 @@ abstract class Importer * Id of User performing import * @var */ + protected $user_id; /** * Are we updating items in the import * @var bool */ + protected $updating; + /** * Default Map of item fields->csv names + * + * This has been moved into app/Http/Livewire/Importer.php to be more granular. + * This private variable is ONLY used for the cli-importer. + * + * @todo - find a way to make this less duplicative * @var array */ private $defaultFieldMap = [ @@ -288,6 +296,8 @@ abstract class Importer $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' => '', @@ -296,7 +306,6 @@ abstract class Importer 'remote' => $this->fetchHumanBoolean(($this->findCsvMatch($row, 'remote'))), ]; - if ($type == 'manager') { $user_array['full_name'] = $this->findCsvMatch($row, 'manager'); $user_array['username'] = $this->findCsvMatch($row, 'manager_username'); @@ -312,8 +321,9 @@ abstract class Importer // 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']))) { - $this->log('Insufficient user data provided (Full name or username is required) - skipping user creation.'); + 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; @@ -325,10 +335,14 @@ abstract class Importer $user_array['email'] = User::generateEmailFromFullName($user_array['full_name']); } - // Get some fields for first name and last name based off of full name + // Get some variables for $user_formatted_array in case we need them later $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_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']; diff --git a/app/Importer/ItemImporter.php b/app/Importer/ItemImporter.php index f989457dc6..e1b0f1c289 100644 --- a/app/Importer/ItemImporter.php +++ b/app/Importer/ItemImporter.php @@ -10,6 +10,8 @@ use App\Models\Manufacturer; use App\Models\Statuslabel; use App\Models\Supplier; use App\Models\User; +use Carbon\CarbonImmutable; +use Illuminate\Support\Facades\Log; class ItemImporter extends Importer { @@ -87,6 +89,17 @@ 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') != '') { + $csvMatch = $this->findCsvMatch($row, 'asset_eol_date'); + try { + $this->item['asset_eol_date'] = CarbonImmutable::parse($csvMatch)->format('Y-m-d'); + } catch (\Exception $e) { + Log::info($e->getMessage()); + $this->log('Unable to parse date: '.$csvMatch); + } + } + $this->item['qty'] = $this->findCsvMatch($row, 'quantity'); $this->item['requestable'] = $this->findCsvMatch($row, 'requestable'); $this->item['user_id'] = $this->user_id; @@ -103,16 +116,12 @@ 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); - } - - if (get_class($this) != LocationImporter::class) { + // Locations don't get checked out to anyone/anything + if (get_class($this) == LocationImporter::class) { return; } @@ -363,7 +372,7 @@ class ItemImporter extends Importer if (empty($asset_statuslabel_name)) { return null; } - $status = Statuslabel::where(['name' => $asset_statuslabel_name])->first(); + $status = Statuslabel::where(['name' => trim($asset_statuslabel_name)])->first(); if ($status) { $this->log('A matching Status '.$asset_statuslabel_name.' already exists'); @@ -372,7 +381,7 @@ class ItemImporter extends Importer } $this->log('Creating a new status'); $status = new Statuslabel(); - $status->name = $asset_statuslabel_name; + $status->name = trim($asset_statuslabel_name); $status->deployable = 1; $status->pending = 0; @@ -411,7 +420,7 @@ class ItemImporter extends Importer //Otherwise create a manufacturer. $manufacturer = new Manufacturer(); - $manufacturer->name = $item_manufacturer; + $manufacturer->name = trim($item_manufacturer); $manufacturer->user_id = $this->user_id; if ($manufacturer->save()) { diff --git a/app/Importer/LicenseImporter.php b/app/Importer/LicenseImporter.php index 3bfbf1ee26..393d00367d 100644 --- a/app/Importer/LicenseImporter.php +++ b/app/Importer/LicenseImporter.php @@ -27,15 +27,24 @@ class LicenseImporter extends ItemImporter * @since 4.0 * @param array $row * @return License|mixed|null + * updated @author Jes Vinsmoke + * @since 6.1 + * */ public function createLicenseIfNotExists(array $row) { $editingLicense = false; - $license = License::where('name', $this->item['name']) + $license = License::where('serial', $this->item['serial'])->where('name', $this->item['name']) ->first(); if ($license) { if (! $this->updating) { - $this->log('A matching License '.$this->item['name'].' with serial '.$this->item['serial'].' already exists'); + + if($this->item['serial'] != "") { + $this->log('A matching License ' . $this->item['name'] . ' with serial ' . $this->item['serial'] . ' already exists'); + } + else { + $this->log('A matching License ' . $this->item['name'] . ' with no serial number already exists'); + } return; } @@ -46,17 +55,24 @@ class LicenseImporter extends ItemImporter $this->log('No Matching License, Creating a new one'); $license = new License; } - $asset_tag = $this->item['asset_tag'] = $this->findCsvMatch($row, 'asset_tag'); // used for checkout out to an asset. + $asset_tag = $this->item['asset_tag'] = trim($this->findCsvMatch($row, 'asset_tag')); // used for checkout out to an asset. $this->item["expiration_date"] = null; if ($this->findCsvMatch($row, "expiration_date")!='') { - $this->item["expiration_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "expiration_date"))); + $this->item["expiration_date"] = date("Y-m-d 00:00:01", strtotime(trim($this->findCsvMatch($row, "expiration_date")))); + } + $this->item['license_email'] = trim($this->findCsvMatch($row, 'license_email')); + $this->item['license_name'] = trim($this->findCsvMatch($row, 'license_name')); + $this->item['maintained'] = trim($this->findCsvMatch($row, 'maintained')); + $this->item['purchase_order'] = trim($this->findCsvMatch($row, 'purchase_order')); + $this->item['order_number'] = trim($this->findCsvMatch($row, 'order_number')); + $this->item['reassignable'] = trim($this->findCsvMatch($row, 'reassignable')); + $this->item['manufacturer'] = $this->createOrFetchManufacturer(trim($this->findCsvMatch($row, 'manufacturer'))); + + if($this->item['reassignable'] == "") + { + $this->item['reassignable'] = 1; } - $this->item['license_email'] = $this->findCsvMatch($row, 'license_email'); - $this->item['license_name'] = $this->findCsvMatch($row, 'license_name'); - $this->item['maintained'] = $this->findCsvMatch($row, 'maintained'); - $this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order'); - $this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable'); $this->item['seats'] = $this->findCsvMatch($row, 'seats'); $this->item["termination_date"] = null; diff --git a/app/Importer/LocationImporter.php b/app/Importer/LocationImporter.php index 25140abe00..47a157aa7f 100644 --- a/app/Importer/LocationImporter.php +++ b/app/Importer/LocationImporter.php @@ -53,21 +53,21 @@ class LocationImporter extends ItemImporter } // 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['name'] = trim($this->findCsvMatch($row, 'name')); + $this->item['address'] = trim($this->findCsvMatch($row, 'address')); + $this->item['address2'] = trim($this->findCsvMatch($row, 'address2')); + $this->item['city'] = trim($this->findCsvMatch($row, 'city')); + $this->item['state'] = trim($this->findCsvMatch($row, 'state')); + $this->item['country'] = trim($this->findCsvMatch($row, 'country')); + $this->item['zip'] = trim($this->findCsvMatch($row, 'zip')); + $this->item['currency'] = trim($this->findCsvMatch($row, 'currency')); + $this->item['ldap_ou'] = trim($this->findCsvMatch($row, 'ldap_ou')); + $this->item['manager'] = trim($this->findCsvMatch($row, 'manager')); + $this->item['manager_username'] = trim($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')); + $this->item['parent_id'] = $this->createOrFetchLocation(trim($this->findCsvMatch($row, 'parent_location'))); } if (!empty($this->item['manager'])) { diff --git a/app/Importer/UserImporter.php b/app/Importer/UserImporter.php index 9f2c1c5f43..263287e3b3 100644 --- a/app/Importer/UserImporter.php +++ b/app/Importer/UserImporter.php @@ -42,27 +42,32 @@ class UserImporter extends ItemImporter public function createUserIfNotExists(array $row) { // Pull the records from the CSV to determine their values - $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['phone'] = $this->findCsvMatch($row, 'phone_number'); - $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['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['autoassign_licenses'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'autoassign_licenses')) ==1 ) ? '1' : 0; + $this->item['id'] = trim($this->findCsvMatch($row, 'id')); + $this->item['username'] = trim($this->findCsvMatch($row, 'username')); + $this->item['first_name'] = trim($this->findCsvMatch($row, 'first_name')); + $this->item['last_name'] = trim($this->findCsvMatch($row, 'last_name')); + $this->item['email'] = trim($this->findCsvMatch($row, 'email')); + $this->item['gravatar'] = trim($this->findCsvMatch($row, 'gravatar')); + $this->item['phone'] = trim($this->findCsvMatch($row, 'phone_number')); + $this->item['website'] = trim($this->findCsvMatch($row, 'website')); + $this->item['jobtitle'] = trim($this->findCsvMatch($row, 'jobtitle')); + $this->item['address'] = trim($this->findCsvMatch($row, 'address')); + $this->item['city'] = trim($this->findCsvMatch($row, 'city')); + $this->item['state'] = trim($this->findCsvMatch($row, 'state')); + $this->item['country'] = trim($this->findCsvMatch($row, 'country')); + $this->item['start_date'] = trim($this->findCsvMatch($row, 'start_date')); + $this->item['end_date'] = trim($this->findCsvMatch($row, 'end_date')); + $this->item['zip'] = trim($this->findCsvMatch($row, 'zip')); + $this->item['activated'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'activated'))) == 1) ? '1' : 0; + $this->item['employee_num'] = trim($this->findCsvMatch($row, 'employee_num')); + $this->item['department_id'] = trim($this->createOrFetchDepartment(trim($this->findCsvMatch($row, 'department')))); + $this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name'))); + $this->item['remote'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'remote'))) == 1 ) ? '1' : 0; + $this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0; + $this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0; - $user_department = $this->findCsvMatch($row, 'department'); + $user_department = trim($this->findCsvMatch($row, 'department')); if ($this->shouldUpdateField($user_department)) { $this->item['department_id'] = $this->createOrFetchDepartment($user_department); } @@ -72,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'); @@ -105,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 09cb3ae8f2..17a8a6c1bf 100644 --- a/app/Listeners/CheckoutableListener.php +++ b/app/Listeners/CheckoutableListener.php @@ -18,6 +18,7 @@ use App\Notifications\CheckoutAccessoryNotification; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseSeatNotification; +use GuzzleHttp\Exception\ClientException; use Illuminate\Support\Facades\Notification; use Exception; use Log; @@ -41,14 +42,9 @@ class CheckoutableListener /** * Make a checkout acceptance and attach it in the notification */ - $acceptance = $this->getCheckoutAcceptance($event); + $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), @@ -60,8 +56,15 @@ class CheckoutableListener $this->getCheckoutNotification($event, $acceptance) ); } + + if ($this->shouldSendWebhookNotification()) { + Notification::route('slack', Setting::getSettings()->webhook_endpoint) + ->notify($this->getCheckoutNotification($event)); + } + } catch (ClientException $e) { + Log::debug("Exception caught during checkout notification: " . $e->getMessage()); } catch (Exception $e) { - Log::error("Exception caught during checkout notification: ".$e->getMessage()); + Log::error("Exception caught during checkout notification: " . $e->getMessage()); } } @@ -79,22 +82,19 @@ class CheckoutableListener /** * Send the appropriate notification */ - $acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id) - ->where('assigned_to_id', $event->checkedOutTo->id) - ->get(); + if ($event->checkedOutTo && $event->checkoutable){ + $acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id) + ->where('assigned_to_id', $event->checkedOutTo->id) + ->get(); - foreach($acceptances as $acceptance){ - if($acceptance->isPending()){ - $acceptance->delete(); + foreach($acceptances as $acceptance){ + if($acceptance->isPending()){ + $acceptance->delete(); + } } } 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( @@ -107,8 +107,15 @@ class CheckoutableListener $this->getCheckinNotification($event) ); } + + if ($this->shouldSendWebhookNotification()) { + Notification::route('slack', Setting::getSettings()->webhook_endpoint) + ->notify($this->getCheckinNotification($event)); + } + } catch (ClientException $e) { + Log::debug("Exception caught during checkout notification: " . $e->getMessage()); } catch (Exception $e) { - Log::error("Exception caught during checkin notification: ".$e->getMessage()); + Log::error("Exception caught during checkin notification: " . $e->getMessage()); } } @@ -142,9 +149,11 @@ class CheckoutableListener $notifiables = collect(); /** - * Notify the user who checked out the item + * Notify who checked out the item as long as the model can route notifications */ - $notifiables->push($event->checkedOutTo); + if (method_exists($event->checkedOutTo, 'routeNotificationFor')) { + $notifiables->push($event->checkedOutTo); + } /** * Notify Admin users if the settings is activated diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index d14b674364..2ffee3e3c3 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -33,7 +33,7 @@ class LogListener */ public function onCheckoutableCheckedIn(CheckoutableCheckedIn $event) { - $event->checkoutable->logCheckin($event->checkedOutTo, $event->note, $event->action_date); + $event->checkoutable->logCheckin($event->checkedOutTo, $event->note, $event->action_date, $event->originalValues); } /** @@ -46,7 +46,7 @@ class LogListener */ public function onCheckoutableCheckedOut(CheckoutableCheckedOut $event) { - $event->checkoutable->logCheckout($event->note, $event->checkedOutTo, $event->checkoutable->last_checkout); + $event->checkoutable->logCheckout($event->note, $event->checkedOutTo, $event->checkoutable->last_checkout, $event->originalValues); } /** diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index 7576cc644f..86502dc7e7 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -349,6 +349,22 @@ class Accessory extends SnipeModel return (int) $remaining; } + /** + * Run after the checkout acceptance was declined by the user + * + * @param User $acceptedBy + * @param string $signature + */ + public function declinedCheckout(User $declinedBy, $signature) + { + if (is_null($accessory_user = \DB::table('accessories_users')->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) { + // Redirect to the accessory management page with error + return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist')); + } + + $accessory_user->limit(1)->delete(); + } + /** * Query builder scope to order on company * diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 8c92f8c78e..4e5a25974e 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -6,7 +6,6 @@ use App\Events\AssetCheckedOut; use App\Events\CheckoutableCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Helpers\Helper; -use App\Http\Traits\UniqueSerialTrait; use App\Http\Traits\UniqueUndeletedTrait; use App\Models\Traits\Acceptable; use App\Models\Traits\Searchable; @@ -32,7 +31,7 @@ class Asset extends Depreciable protected $presenter = \App\Presenters\AssetPresenter::class; use CompanyableTrait; - use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait; + use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait; public const LOCATION = 'location'; public const ASSET = 'asset'; @@ -72,7 +71,9 @@ class Asset extends Depreciable protected $casts = [ 'purchase_date' => 'date', + 'eol_explicit' => 'boolean', 'last_checkout' => 'datetime', + 'last_checkin' => 'datetime', 'expected_checkin' => 'date', 'last_audit_date' => 'datetime', 'next_audit_date' => 'date', @@ -82,7 +83,6 @@ class Asset extends Depreciable 'location_id' => 'integer', 'rtd_company_id' => 'integer', 'supplier_id' => 'integer', - 'byod' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', @@ -90,7 +90,7 @@ class Asset extends Depreciable protected $rules = [ 'name' => 'max:255|nullable', - 'model_id' => 'required|integer|exists:models,id', + 'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array', 'status_id' => 'required|integer|exists:status_labels,id', 'company_id' => 'integer|nullable', 'warranty_months' => 'numeric|nullable|digits_between:0,240', @@ -99,14 +99,17 @@ class Asset extends Depreciable 'expected_checkin' => 'date|nullable', 'location_id' => 'exists:locations,id|nullable', 'rtd_location_id' => 'exists:locations,id|nullable', - 'asset_tag' => 'required|min:1|max:255|unique_undeleted', + 'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array', 'purchase_date' => 'date|date_format:Y-m-d|nullable', - 'serial' => 'unique_serial|nullable', + 'serial' => 'unique_undeleted:assets,serial|nullable', 'purchase_cost' => 'numeric|nullable|gte:0', 'supplier_id' => 'exists:suppliers,id|nullable', - 'asset_eol_date' => 'date|max:10|min:10|nullable', + 'asset_eol_date' => 'date|nullable', + 'eol_explicit' => 'boolean|nullable', + 'byod' => 'boolean', ]; + /** * The attributes that are mass assignable. * @@ -135,8 +138,10 @@ class Asset extends Depreciable 'expected_checkin', 'byod', 'asset_eol_date', + 'eol_explicit', 'last_audit_date', 'next_audit_date', + 'asset_eol_date', ]; use Searchable; @@ -207,16 +212,16 @@ class Asset extends Depreciable $this->rules += $model->fieldset->validation_rules(); - foreach ($this->model->fieldset->fields as $field){ - if($field->format == 'BOOLEAN'){ - $this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN); + if ($this->model->fieldset){ + foreach ($this->model->fieldset->fields as $field){ + if($field->format == 'BOOLEAN'){ + $this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN); + } } } } } - - return parent::save($params); } @@ -332,6 +337,13 @@ class Asset extends Depreciable } } + $originalValues = $this->getRawOriginal(); + + // attempt to detect change in value if different from today's date + if ($checkout_at && strpos($checkout_at, date('Y-m-d')) === false) { + $originalValues['action_date'] = date('Y-m-d H:i:s'); + } + if ($this->save()) { if (is_int($admin)) { $checkedOutBy = User::findOrFail($admin); @@ -340,7 +352,7 @@ class Asset extends Depreciable } else { $checkedOutBy = Auth::user(); } - event(new CheckoutableCheckedOut($this, $target, $checkedOutBy, $note)); + event(new CheckoutableCheckedOut($this, $target, $checkedOutBy, $note, $originalValues)); $this->increment('checkout_counter', 1); @@ -390,7 +402,7 @@ class Asset extends Depreciable */ public function depreciation() { - return $this->model->belongsTo(\App\Models\Depreciation::class, 'depreciation_id'); + return $this->hasOneThrough(\App\Models\Depreciation::class,\App\Models\AssetModel::class,'id','id','model_id','depreciation_id'); } @@ -403,7 +415,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'); } @@ -777,7 +789,6 @@ class Asset extends Depreciable } - /** * Get the next autoincremented asset tag * @@ -785,24 +796,17 @@ class Asset extends Depreciable * @since [v4.0] * @return string | false */ - public static function autoincrement_asset() + public static function autoincrement_asset(int $additional_increment = 0) { $settings = \App\Models\Setting::getSettings(); if ($settings->auto_increment_assets == '1') { - $temp_asset_tag = \DB::table('assets') - ->where('physical', '=', '1') - ->max('asset_tag'); - - $asset_tag_digits = preg_replace('/\D/', '', $temp_asset_tag); - $asset_tag = preg_replace('/^0*/', '', $asset_tag_digits); - if ($settings->zerofill_count > 0) { - return $settings->auto_increment_prefix.self::zerofill($settings->next_auto_tag_base, $settings->zerofill_count); + return $settings->auto_increment_prefix.self::zerofill($settings->next_auto_tag_base + $additional_increment, $settings->zerofill_count); } - return $settings->auto_increment_prefix.$settings->next_auto_tag_base; + return $settings->auto_increment_prefix.($settings->next_auto_tag_base + $additional_increment); } else { return false; } @@ -947,8 +951,11 @@ 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%"]); - + ->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%') + ->orWhereMultipleColumns([ + 'assets_users.first_name', + 'assets_users.last_name', + ], $term); } /** @@ -1343,7 +1350,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.'%'); @@ -1435,6 +1445,12 @@ class Asset extends Depreciable }); } + if ($fieldname == 'rtd_location') { + $query->whereHas('defaultLoc', function ($query) use ($search_val) { + $query->where('locations.name', 'LIKE', '%'.$search_val.'%'); + }); + } + if ($fieldname =='assigned_to') { $query->whereHasMorph('assignedTo', [User::class], function ($query) use ($search_val) { $query->where(function ($query) use ($search_val) { @@ -1561,7 +1577,7 @@ class Asset extends Depreciable */ public function scopeOrderModelNumber($query, $order) { - return $query->join('models', 'assets.model_id', '=', 'models.id')->orderBy('models.model_number', $order); + return $query->leftJoin('models as model_number_sort', 'assets.model_id', '=', 'model_number_sort.id')->orderBy('model_number_sort.model_number', $order); } @@ -1662,7 +1678,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/AssetModel.php b/app/Models/AssetModel.php index e4e5ac720a..f94c6f8eac 100755 --- a/app/Models/AssetModel.php +++ b/app/Models/AssetModel.php @@ -29,6 +29,7 @@ class AssetModel extends SnipeModel protected $rules = [ 'name' => 'required|min:1|max:255', 'model_number' => 'max:255|nullable', + 'min_amt' => 'integer|min:0|nullable', 'category_id' => 'required|integer|exists:categories,id', 'manufacturer_id' => 'integer|exists:manufacturers,id|nullable', 'eol' => 'integer:min:0|max:240|nullable', @@ -65,6 +66,7 @@ class AssetModel extends SnipeModel 'fieldset_id', 'image', 'manufacturer_id', + 'min_amt', 'model_number', 'name', 'notes', @@ -150,6 +152,11 @@ class AssetModel extends SnipeModel { return $this->belongsTo(\App\Models\CustomFieldset::class, 'fieldset_id'); } + + public function customFields() + { + return $this->fieldset()->first()->fields(); + } /** * Establishes the model -> custom field default values relationship @@ -284,4 +291,9 @@ class AssetModel extends SnipeModel { return $query->leftJoin('categories', 'models.category_id', '=', 'categories.id')->orderBy('categories.name', $order); } + + public function scopeOrderFieldset($query, $order) + { + return $query->leftJoin('custom_fieldsets', 'models.fieldset_id', '=', 'custom_fieldsets.id')->orderBy('custom_fieldsets.name', $order); + } } 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/CheckoutRequest.php b/app/Models/CheckoutRequest.php index 26abdd301e..b717a332aa 100644 --- a/app/Models/CheckoutRequest.php +++ b/app/Models/CheckoutRequest.php @@ -18,7 +18,7 @@ class CheckoutRequest extends Model public function requestingUser() { - return $this->user()->first(); + return $this->user()->withTrashed()->first(); } public function requestedItem() diff --git a/app/Models/Company.php b/app/Models/Company.php index 413011b9a7..c3a2fdae7f 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -24,6 +24,9 @@ final class Company extends SnipeModel // Declare the rules for the model validation protected $rules = [ 'name' => 'required|min:1|max:255|unique:companies,name', + 'fax' => 'min:7|max:35|nullable', + 'phone' => 'min:7|max:35|nullable', + 'email' => 'email|max:150|nullable', ]; protected $presenter = \App\Presenters\CompanyPresenter::class; @@ -45,7 +48,7 @@ final class Company extends SnipeModel * * @var array */ - protected $searchableAttributes = ['name', 'created_at', 'updated_at']; + protected $searchableAttributes = ['name', 'phone', 'fax', 'email', 'created_at', 'updated_at']; /** * The relations and their attributes that should be included when searching the model. @@ -59,7 +62,12 @@ final class Company extends SnipeModel * * @var array */ - protected $fillable = ['name']; + protected $fillable = [ + 'name', + 'phone', + 'fax', + 'email', + ]; private static function isFullMultipleCompanySupportEnabled() { diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php index b39650e74b..c1826a94d8 100644 --- a/app/Models/CustomField.php +++ b/app/Models/CustomField.php @@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Validation\Rule; use Schema; use Watson\Validating\ValidatingTrait; - class CustomField extends Model { use HasFactory; @@ -53,6 +52,13 @@ class CustomField extends Model 'element' => 'required|in:text,listbox,textarea,checkbox,radio', 'field_encrypted' => 'nullable|boolean', 'auto_add_to_fieldsets' => 'boolean', + 'show_in_listview' => 'boolean', + 'show_in_requestable_list' => 'boolean', + 'show_in_email' => 'boolean', + ]; + + protected $casts = [ + 'show_in_requestable_list' => 'boolean', ]; /** @@ -71,7 +77,9 @@ class CustomField extends Model 'is_unique', 'display_in_user_view', 'auto_add_to_fieldsets', - + 'show_in_listview', + 'show_in_email', + 'show_in_requestable_list', ]; /** @@ -180,6 +188,11 @@ class CustomField extends Model { return $this->belongsToMany(\App\Models\CustomFieldset::class); } + + public function assetModels() + { + return $this->fieldset()->with('models')->get()->pluck('models')->flatten()->unique('id'); + } /** * Establishes the customfield -> admin user relationship @@ -237,8 +250,6 @@ class CustomField extends Model /** * Gets the DB column name. * - * @todo figure out if this is still needed? I don't know WTF it's for. - * * @author [A. Gianotto] [] * @since [v3.0] * @return string @@ -306,9 +317,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/CustomFieldset.php b/app/Models/CustomFieldset.php index a2698d818c..a62f96d631 100644 --- a/app/Models/CustomFieldset.php +++ b/app/Models/CustomFieldset.php @@ -92,6 +92,8 @@ class CustomFieldset extends Model array_push($rule, $field->attributes['format']); $rules[$field->db_column_name()] = $rule; + //add not_array to rules for all fields + $rules[$field->db_column_name()][] = 'not_array'; } return $rules; diff --git a/app/Models/Department.php b/app/Models/Department.php index 74e2b23df9..62755d2aa0 100644 --- a/app/Models/Department.php +++ b/app/Models/Department.php @@ -9,6 +9,7 @@ use Watson\Validating\ValidatingTrait; class Department extends SnipeModel { + use CompanyableTrait; use HasFactory; /** @@ -43,6 +44,8 @@ class Department extends SnipeModel protected $fillable = [ 'user_id', 'name', + 'phone', + 'fax', 'location_id', 'company_id', 'manager_id', @@ -56,7 +59,7 @@ class Department extends SnipeModel * * @var array */ - protected $searchableAttributes = ['name', 'notes']; + protected $searchableAttributes = ['name', 'notes', 'phone', 'fax']; /** * The relations and their attributes that should be included when searching the model. diff --git a/app/Models/Depreciable.php b/app/Models/Depreciable.php index 250894820c..cfc42aa945 100644 --- a/app/Models/Depreciable.php +++ b/app/Models/Depreciable.php @@ -68,7 +68,7 @@ class Depreciable extends SnipeModel */ public function getLinearDepreciatedValue() // TODO - for testing it might be nice to have an optional $relative_to param here, defaulted to 'now' { - if ($this->purchase_date) { + if (($this->get_depreciation()) && ($this->purchase_date)) { $months_passed = ($this->purchase_date->diff(now())->m)+($this->purchase_date->diff(now())->y*12); } else { return null; @@ -127,7 +127,7 @@ class Depreciable extends SnipeModel $yearsPast = 0; } - return round($yearsPast / $deprecationYears * $this->purchase_cost, 2); + return $this->purchase_cost - round($yearsPast / $deprecationYears * $this->purchase_cost, 2); } /** diff --git a/app/Models/Labels/DefaultLabel.php b/app/Models/Labels/DefaultLabel.php new file mode 100644 index 0000000000..f06c4582f9 --- /dev/null +++ b/app/Models/Labels/DefaultLabel.php @@ -0,0 +1,234 @@ +textSize = Helper::convertUnit($settings->labels_fontsize, 'pt', 'in'); + + $this->labelWidth = $settings->labels_width; + $this->labelHeight = $settings->labels_height; + + $this->labelSpacingH = $settings->labels_display_sgutter; + $this->labelSpacingV = $settings->labels_display_bgutter; + + $this->pageMarginTop = $settings->labels_pmargin_top; + $this->pageMarginBottom = $settings->labels_pmargin_bottom; + $this->pageMarginLeft = $settings->labels_pmargin_left; + $this->pageMarginRight = $settings->labels_pmargin_right; + + $this->pageWidth = $settings->labels_pagewidth; + $this->pageHeight = $settings->labels_pageheight; + + $usableWidth = $this->pageWidth - $this->pageMarginLeft - $this->pageMarginRight; + $usableHeight = $this->pageHeight - $this->pageMarginTop - $this->pageMarginBottom; + + $this->columns = ($usableWidth + $this->labelSpacingH) / ($this->labelWidth + $this->labelSpacingH); + $this->rows = ($usableHeight + $this->labelSpacingV) / ($this->labelHeight + $this->labelSpacingV); + + // Make sure the columns and rows are never zero, since that scenario should never happen + if ($this->columns == 0) { + $this->columns = 1; + } + + if ($this->rows == 0) { + $this->rows = 1; + } + + } + + public function getUnit() { return 'in'; } + + public function getPageWidth() { return $this->pageWidth; } + public function getPageHeight() { return $this->pageHeight; } + + public function getPageMarginTop() { return $this->pageMarginTop; } + public function getPageMarginBottom() { return $this->pageMarginBottom; } + public function getPageMarginLeft() { return $this->pageMarginLeft; } + public function getPageMarginRight() { return $this->pageMarginRight; } + + public function getColumns() { return $this->columns; } + public function getRows() { return $this->rows; } + public function getLabelBorder() { return 0; } + + public function getLabelWidth() { return $this->labelWidth; } + public function getLabelHeight() { return $this->labelHeight; } + + public function getLabelMarginTop() { return 0; } + public function getLabelMarginBottom() { return 0; } + public function getLabelMarginLeft() { return 0; } + public function getLabelMarginRight() { return 0; } + + public function getLabelColumnSpacing() { return $this->labelSpacingH; } + public function getLabelRowSpacing() { return $this->labelSpacingV; } + + public function getSupportAssetTag() { return false; } + public function getSupport1DBarcode() { return true; } + public function getSupport2DBarcode() { return true; } + public function getSupportFields() { return 4; } + public function getSupportTitle() { return true; } + public function getSupportLogo() { return true; } + + public function preparePDF($pdf) {} + + public function write($pdf, $record) { + + $asset = $record->get('asset'); + $settings = Setting::getSettings(); + + $textY = 0; + $textX1 = 0; + $textX2 = $this->getLabelWidth(); + + // 1D Barcode + if ($record->get('barcode1d')) { + static::write1DBarcode( + $pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type, + 0.05, $this->getLabelHeight() - self::BARCODE1D_SIZE, + $this->getLabelWidth() - 0.1, self::BARCODE1D_SIZE + ); + } + + // 2D Barcode + if ($record->get('barcode2d')) { + static::write2DBarcode( + $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type, + 0, 0, self::BARCODE2D_SIZE, self::BARCODE2D_SIZE + ); + $textX1 += self::BARCODE2D_SIZE + self::BARCODE2D_MARGIN; + } + + // Logo + if ($record->get('logo')) { + $logoSize = static::writeImage( + $pdf, $record->get('logo'), + $this->labelWidth - self::LOGO_SIZE[0], 0, + self::LOGO_SIZE[0], self::LOGO_SIZE[1], + 'R', 'T', 300, true, false, 0 + ); + $textX2 -= ($logoSize[0] + self::LOGO_MARGIN); + } + + $textW = $textX2 - $textX1; + + // Title + if ($record->get('title')) { + static::writeText( + $pdf, $record->get('title'), + $textX1, 0, + 'freesans', 'b', $this->textSize, 'L', + $textW, $this->textSize, + true, 0 + ); + $textY += $this->textSize + self::TEXT_MARGIN; + } + + // Fields + $fieldsDone = 0; + if ($settings->labels_display_name && $fieldsDone < $this->getSupportFields()) { + if ($asset->name) { + static::writeText( + $pdf, 'N: '.$asset->name, + $textX1, $textY, + 'freesans', '', $this->textSize, 'L', + $textW, $this->textSize, + true, 0 + ); + $textY += $this->textSize + self::TEXT_MARGIN; + $fieldsDone++; + } + } + if ($settings->labels_display_company_name && $fieldsDone < $this->getSupportFields()) { + if ($asset->company) { + static::writeText( + $pdf, 'C: '.$asset->company->name, + $textX1, $textY, + 'freesans', '', $this->textSize, 'L', + $textW, $this->textSize, + true, 0 + ); + $textY += $this->textSize + self::TEXT_MARGIN; + $fieldsDone++; + } + } + if ($settings->labels_display_tag && $fieldsDone < $this->getSupportFields()) { + if ($asset->asset_tag) { + static::writeText( + $pdf, 'T: '.$asset->asset_tag, + $textX1, $textY, + 'freesans', '', $this->textSize, 'L', + $textW, $this->textSize, + true, 0 + ); + $textY += $this->textSize + self::TEXT_MARGIN; + $fieldsDone++; + } + } + if ($settings->labels_display_serial && $fieldsDone < $this->getSupportFields()) { + if ($asset->serial) { + static::writeText( + $pdf, 'S: '.$asset->serial, + $textX1, $textY, + 'freesans', '', $this->textSize, 'L', + $textW, $this->textSize, + true, 0 + ); + $textY += $this->textSize + self::TEXT_MARGIN; + $fieldsDone++; + } + } + if ($settings->labels_display_model && $fieldsDone < $this->getSupportFields()) { + if ($asset->model) { + static::writeText( + $pdf, 'M: '.$asset->model->name, + $textX1, $textY, + 'freesans', '', $this->textSize, 'L', + $textW, $this->textSize, + true, 0 + ); + $textY += $this->textSize + self::TEXT_MARGIN; + $fieldsDone++; + } + } + + } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Field.php b/app/Models/Labels/Field.php new file mode 100644 index 0000000000..c023f54175 --- /dev/null +++ b/app/Models/Labels/Field.php @@ -0,0 +1,41 @@ +options; } + public function setOptions($options) { + $tempCollect = collect($options); + if (!$tempCollect->contains(fn($o) => !is_subclass_of($o, FieldOption::class))) { + $this->options = $options; + } + } + + public function toArray(Asset $asset) { return Field::makeArray($this, $asset); } + + /* Statics */ + + public static function makeArray(Field $field, Asset $asset) { + return $field->getOptions() + // filter out any FieldOptions that are accidentally null + ->filter() + ->map(fn($option) => $option->toArray($asset)) + ->filter(fn($result) => $result['value'] != null); + } + + public static function makeString(Field $option) { + return implode('|', $option->getOptions()); + } + + public static function fromString(string $theString) { + $field = new Field(); + $field->options = collect(explode('|', $theString)) + ->filter(fn($optionString) => !empty($optionString)) + ->map(fn($optionString) => FieldOption::fromString($optionString)); + return $field; + } +} diff --git a/app/Models/Labels/FieldOption.php b/app/Models/Labels/FieldOption.php new file mode 100644 index 0000000000..7e45cc0ce7 --- /dev/null +++ b/app/Models/Labels/FieldOption.php @@ -0,0 +1,57 @@ +label; } + + protected string $dataSource; + public function getDataSource() { return $this->dataSource; } + + public function getValue(Asset $asset) { + $dataPath = collect(explode('.', $this->dataSource)); + + // assignedTo directly on the asset is a special case where + // we want to avoid returning the property directly + // and instead return the entity's presented name. + if ($dataPath[0] === 'assignedTo'){ + return $asset->assignedTo ? $asset->assignedTo->present()->fullName() : null; + } + + return $dataPath->reduce(function ($myValue, $path) { + try { return $myValue ? $myValue->{$path} : ${$myValue}; } + catch (\Exception $e) { return $myValue; } + }, $asset); + } + + public function toArray(Asset $asset=null) { return FieldOption::makeArray($this, $asset); } + public function toString() { return FieldOption::makeString($this); } + + /* Statics */ + + public static function makeArray(FieldOption $option, Asset $asset=null) { + return [ + 'label' => $option->getLabel(), + 'dataSource' => $option->getDataSource(), + 'value' => $asset ? $option->getValue($asset) : null + ]; + } + + public static function makeString(FieldOption $option) { + return $option->getLabel() . '=' . $option->getDataSource(); + } + + public static function fromString(string $theString) { + $parts = explode('=', $theString); + if (count($parts) == 2) { + $option = new FieldOption(); + $option->label = $parts[0]; + $option->dataSource = $parts[1]; + return $option; + } + } +} diff --git a/app/Models/Labels/Label.php b/app/Models/Labels/Label.php new file mode 100644 index 0000000000..b727c1cb18 --- /dev/null +++ b/app/Models/Labels/Label.php @@ -0,0 +1,603 @@ +each(function ($record, $index) use ($pdf) { + $pdf->AddPage(); + $this->write($pdf, $record); + }); + } + + /** + * Returns the qualified class name relative to the Label class's namespace. + * + * @return string + */ + public final function getName() { + $refClass = new \ReflectionClass(Label::class); + return str_replace($refClass->getNamespaceName() . '\\', '', get_class($this)); + } + + /** + * Returns the label's orientation as a string. + * 'L' = Landscape + * 'P' = Portrait + * + * @return string + */ + public final function getOrientation() { + return ($this->getWidth() >= $this->getHeight()) ? 'L' : 'P'; + } + + /** + * Returns the label's printable area as an object. + * + * @return object [ 'x1'=>0.00, 'y1'=>0.00, 'x2'=>0.00, 'y2'=>0.00, 'w'=>0.00, 'h'=>0.00 ] + */ + public final function getPrintableArea() { + return (object)[ + 'x1' => $this->getMarginLeft(), + 'y1' => $this->getMarginTop(), + 'x2' => $this->getWidth() - $this->getMarginRight(), + 'y2' => $this->getHeight() - $this->getMarginBottom(), + 'w' => $this->getWidth() - $this->getMarginLeft() - $this->getMarginRight(), + 'h' => $this->getHeight() - $this->getMarginTop() - $this->getMarginBottom(), + ]; + } + + /** + * Write a text cell. + * + * @param TCPDF $pdf The TCPDF instance + * @param string $text The text to write. Supports 'some **bold** text'. + * @param float $x X position of top-left + * @param float $y Y position of top-left + * @param string $font The font family + * @param string $style The font style + * @param int $size The font size in getUnit() units + * @param string $align Align text in the box. 'L' left, 'R' right, 'C' center. + * @param float $width Force text box width. NULL to auto-fit. + * @param float $height Force text box height. NULL to auto-fit. + * @param bool $squash Squash text if it's too big + * @param int $border Thickness of border. Default = 0. + * @param int $spacing Letter spacing. Default = 0. + */ + public final function writeText(TCPDF $pdf, $text, $x, $y, $font=null, $style=null, $size=null, $align='L', $width=null, $height=null, $squash=false, $border=0, $spacing=0) { + $prevFamily = $pdf->getFontFamily(); + $prevStyle = $pdf->getFontStyle(); + $prevSizePt = $pdf->getFontSizePt(); + + $text = !empty($text) ? $text : ''; + + $fontFamily = !empty($font) ? $font : $prevFamily; + $fontStyle = !empty($style) ? $style : $prevStyle; + if ($size) $fontSizePt = Helper::convertUnit($size, $this->getUnit(), 'pt', true); + else $fontSizePt = $prevSizePt; + + $pdf->SetFontSpacing($spacing); + + $parts = collect(explode('**', $text)) + ->map(function ($part, $index) use ($pdf, $fontFamily, $fontStyle, $fontSizePt) { + $modStyle = ($index % 2 == 1) ? 'B' : $fontStyle; + $pdf->setFont($fontFamily, $modStyle, $fontSizePt); + return [ + 'text' => $part, + 'text_width' => $pdf->GetStringWidth($part), + 'font_family' => $fontFamily, + 'font_style' => $modStyle, + 'font_size' => $fontSizePt, + ]; + }); + + $textWidth = $parts->reduce(function ($carry, $part) { return $carry += $part['text_width']; }); + $cellWidth = !empty($width) ? $width : $textWidth; + + if ($squash && ($textWidth > 0)) { + $scaleFactor = min(1.0, $cellWidth / $textWidth); + $parts = $parts->map(function ($part, $index) use ($scaleFactor) { + $part['text_width'] = $part['text_width'] * $scaleFactor; + return $part; + }); + } + $cellHeight = !empty($height) ? $height : Helper::convertUnit($fontSizePt, 'pt', $this->getUnit()); + + if ($border) { + $prevLineWidth = $pdf->getLineWidth(); + $pdf->setLineWidth($border); + $pdf->Rect($x, $y, $cellWidth, $cellHeight); + $pdf->setLineWidth($prevLineWidth); + } + + switch($align) { + case 'R': $startX = ($x + $cellWidth) - min($cellWidth, $textWidth); break; + case 'C': $startX = ($x + ($cellWidth / 2)) - (min($cellWidth, $textWidth) / 2); break; + case 'L': + default: $startX = $x; break; + } + + $parts->reduce(function ($currentX, $part) use ($pdf, $y, $cellHeight) { + $pdf->SetXY($currentX, $y); + $pdf->setFont($part['font_family'], $part['font_style'], $part['font_size']); + $pdf->Cell($part['text_width'], $cellHeight, $part['text'], 0, 0, '', false, '', 1, true); + return $currentX += $part['text_width']; + }, $startX); + + $pdf->SetFont($prevFamily, $prevStyle, $prevSizePt); + $pdf->SetFontSpacing(0); + } + + /** + * Write an image. + * + * @param TCPDF $pdf The TCPDF instance + * @param string $image The image to write + * @param float $x X position of top-left + * @param float $y Y position of top-left + * @param float $width The container width + * @param float $height The container height + * @param string $halign Align text in the box. 'L' left, 'R' right, 'C' center. Default 'L'. + * @param string $valign Align text in the box. 'T' top, 'B' bottom, 'C' center. Default 'T'. + * @param int $dpi Pixels per inch + * @param bool $resize Resize to fit container + * @param bool $stretch Stretch (vs Scale) to fit container + * @param int $border Thickness of border. Default = 0. + * + * @return array Returns the final calculated size [w,h] + */ + public final function writeImage(TCPDF $pdf, $image, $x, $y, $width=null, $height=null, $halign='L', $valign='L', $dpi=300, $resize=false, $stretch=false, $border=0) { + + if (empty($image)) return [0,0]; + + $imageInfo = getimagesize($image); + if (!$imageInfo) return [0,0]; // TODO: SVG or other + + $imageWidthPx = $imageInfo[0]; + $imageHeightPx = $imageInfo[1]; + $imageType = image_type_to_extension($imageInfo[2], false); + + $imageRatio = $imageWidthPx / $imageHeightPx; + $dpu = Helper::convertUnit($dpi, $this->getUnit(), 'in'); + $imageWidth = $imageWidthPx / $dpu; + $imageHeight = $imageHeightPx / $dpu; + + $outputWidth = $imageWidth; + $outputHeight = $imageHeight; + + if ($resize) { + // Assign specified parameters + $limitWidth = $width; + $limitHeight = $height; + + // If not, try calculating from the other dimension + $limitWidth = ($limitWidth > 0) ? $limitWidth : ($limitHeight / $imageRatio); + $limitHeight = ($limitHeight > 0) ? $limitHeight : ($limitWidth * $imageRatio); + + // If not, just use the image size + $limitWidth = ($limitWidth > 0) ? $limitWidth : $imageWidth; + $limitHeight = ($limitHeight > 0) ? $limitHeight : $imageHeight; + + $scaleWidth = $limitWidth / $imageWidth; + $scaleHeight = $limitHeight / $imageHeight; + + // If non-stretch, make both scales factors equal + if (!$stretch) { + // Do we need to scale down at all? That's most important. + if (($scaleWidth < 1.0) || ($scaleHeight < 1.0)) { + // Choose largest scale-down + $scaleWidth = min($scaleWidth, $scaleHeight); + } else { + // Choose smallest scale-up + $scaleWidth = min(max($scaleWidth, 1.0), max($scaleHeight, 1.0)); + } + $scaleHeight = $scaleWidth; + } + + $outputWidth = $imageWidth * $scaleWidth; + $outputHeight = $imageHeight * $scaleHeight; + } + + // Container + $containerWidth = !empty($width) ? $width : $outputWidth; + $containerHeight = !empty($height) ? $height : $outputHeight; + + // Horizontal Position + switch ($halign) { + case 'R': $originX = ($x + $containerWidth) - $outputWidth; break; + case 'C': $originX = ($x + ($containerWidth / 2)) - ($outputWidth / 2); break; + case 'L': + default: $originX = $x; break; + } + + // Vertical Position + switch ($valign) { + case 'B': $originY = ($y + $containerHeight) - $outputHeight; break; + case 'C': $originY = ($y + ($containerHeight / 2)) - ($outputHeight / 2); break; + case 'T': + default: $originY = $y; break; + } + + // Actual Image + $pdf->Image($image, $originX, $originY, $outputWidth, $outputHeight, $imageType, '', '', true); + + // Border + if ($border) { + $prevLineWidth = $pdf->getLineWidth(); + $pdf->setLineWidth($border); + $pdf->Rect($x, $y, $containerWidth, $containerHeight); + $pdf->setLineWidth($prevLineWidth); + } + + return [ $outputWidth, $outputHeight ]; + } + + /** + * Write a 1D barcode. + * + * @param TCPDF $pdf The TCPDF instance + * @param string $value The barcode content + * @param string $type The barcode type + * @param float $x X position of top-left + * @param float $y Y position of top-left + * @param float $width The container width + * @param float $height The container height + */ + public final function write1DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height) { + if (empty($value)) return; + try { + $pdf->write1DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]); + } catch (\Exception|TypeError $e) { + \Log::debug('The 1D barcode ' . $value . ' is not compliant with the barcode type '. $type); + } + } + + /** + * Write a 2D barcode. + * + * @param TCPDF $pdf The TCPDF instance + * @param string $value The barcode content + * @param string $type The barcode type + * @param float $x X position of top-left + * @param float $y Y position of top-left + * @param float $width The container width + * @param float $height The container height + */ + public final function write2DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height) { + if (empty($value)) return; + $pdf->write2DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]); + } + + + + /** + * Checks the template is internally valid + */ + public final function validate() { + $this->validateUnits(); + $this->validateSize(); + $this->validateMargins(); + $this->validateSupport(); + } + + private function validateUnits() { + $validUnits = [ 'pt', 'mm', 'cm', 'in' ]; + $unit = $this->getUnit(); + if (!in_array(strtolower($unit), $validUnits)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_value', [ + 'name' => 'getUnit()', + 'expected' => '[ \''.implode('\', \'', $validUnits).'\' ]', + 'actual' => '\''.$unit.'\'' + ])); + } + } + + private function validateSize() { + $width = $this->getWidth(); + if (!is_numeric($width) || is_string($width)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getWidth()', + 'expected' => 'float', + 'actual' => gettype($width) + ])); + } + + $height = $this->getHeight(); + if (!is_numeric($height) || is_string($height)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getHeight()', + 'expected' => 'float', + 'actual' => gettype($height) + ])); + } + } + + private function validateMargins() { + $marginTop = $this->getMarginTop(); + if (!is_numeric($marginTop) || is_string($marginTop)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getMarginTop()', + 'expected' => 'float', + 'actual' => gettype($marginTop) + ])); + } + + $marginBottom = $this->getMarginBottom(); + if (!is_numeric($marginBottom) || is_string($marginBottom)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getMarginBottom()', + 'expected' => 'float', + 'actual' => gettype($marginBottom) + ])); + } + + $marginLeft = $this->getMarginLeft(); + if (!is_numeric($marginLeft) || is_string($marginLeft)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getMarginLeft()', + 'expected' => 'float', + 'actual' => gettype($marginLeft) + ])); + } + + $marginRight = $this->getMarginRight(); + if (!is_numeric($marginRight) || is_string($marginRight)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getMarginRight()', + 'expected' => 'float', + 'actual' => gettype($marginRight) + ])); + } + } + + private function validateSupport() { + $support1D = $this->getSupport1DBarcode(); + if (!is_bool($support1D)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getSupport1DBarcode()', + 'expected' => 'boolean', + 'actual' => gettype($support1D) + ])); + } + + $support2D = $this->getSupport2DBarcode(); + if (!is_bool($support2D)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getSupport2DBarcode()', + 'expected' => 'boolean', + 'actual' => gettype($support2D) + ])); + } + + $supportFields = $this->getSupportFields(); + if (!is_int($supportFields)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getSupportFields()', + 'expected' => 'integer', + 'actual' => gettype($supportFields) + ])); + } + + $supportLogo = $this->getSupportLogo(); + if (!is_bool($supportLogo)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getSupportLogo()', + 'expected' => 'boolean', + 'actual' => gettype($supportLogo) + ])); + } + + $supportTitle = $this->getSupportTitle(); + if (!is_bool($supportTitle)) { + throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [ + 'name' => 'getSupportTitle()', + 'expected' => 'boolean', + 'actual' => gettype($supportTitle) + ])); + } + } + + + + /** + * Public Static Functions + */ + + /** + * Find size of a page by its format. + * + * @param string $format Format name (eg: 'A4', 'LETTER', etc.) + * @param string $orientation 'L' for Landscape, 'P' for Portrait ('L' default) + * @param string $unit Unit of measure to return in ('mm' default) + * + * @return object (object)[ 'width' => (float)123.4, 'height' => (float)123.4 ] + */ + public static function fromFormat($format, $orientation='L', $unit='mm', $round=false) { + $size = collect(TCPDF_STATIC::getPageSizeFromFormat(strtoupper($format))) + ->sort() + ->map(function ($value) use ($unit) { + return Helper::convertUnit($value, 'pt', $unit); + }) + ->toArray(); + $width = ($orientation == 'L') ? $size[1] : $size[0]; + $height = ($orientation == 'L') ? $size[0] : $size[1]; + return (object)[ + 'width' => ($round !== false) ? round($width, $round) : $width, + 'height' => ($round !== false) ? round($height, $round) : $height, + ]; + } + + /** + * Find a Label by its path (or just return them all). + * + * Unlike most Models, these are defined by their existence as non- + * abstract classes stored in Models\Labels. + * + * @param string|Arrayable|array|null $path Label path[s] + * @return Collection|Label|null + */ + public static function find($name=null) { + // Find many + if (is_array($name) || $name instanceof Arrayable) { + $labels = collect($name) + ->map(function ($thisname) { + return static::find($thisname); + }) + ->whereNotNull(); + return ($labels->count() > 0) ? $labels : null; + } + + // Find one + if ($name !== null) { + return static::find() + ->sole(function ($label) use ($name) { + return $label->getName() == $name; + }); + } + + // Find all + return collect(File::allFiles(__DIR__)) + ->map(function ($file) { + preg_match_all('/\/*(.+?)(?:\/|\.)/', $file->getRelativePathName(), $matches); + return __NAMESPACE__ . '\\' . implode('\\', $matches[1]); + }) + ->filter(function ($name) { + if (!class_exists($name)) return false; + $refClass = new \ReflectionClass($name); + if ($refClass->isAbstract()) return false; + return $refClass->isSubclassOf(Label::class); + }) + ->map(function ($name) { + return new $name(); + }); + } + + + +} diff --git a/app/Models/Labels/RectangleSheet.php b/app/Models/Labels/RectangleSheet.php new file mode 100644 index 0000000000..f5fe5cda98 --- /dev/null +++ b/app/Models/Labels/RectangleSheet.php @@ -0,0 +1,48 @@ +getColumns() * $this->getRows(); } + + public function getLabelPosition($index) { + $printIndex = $index + $this->getLabelIndexOffset(); + $row = (int)($printIndex / $this->getColumns()); + $col = $printIndex - ($row * $this->getColumns()); + $x = $this->getPageMarginLeft() + (($this->getLabelWidth() + $this->getLabelColumnSpacing()) * $col); + $y = $this->getPageMarginTop() + (($this->getLabelHeight() + $this->getLabelRowSpacing()) * $row); + return [ $x, $y ]; + } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheet.php b/app/Models/Labels/Sheet.php new file mode 100644 index 0000000000..83e363591a --- /dev/null +++ b/app/Models/Labels/Sheet.php @@ -0,0 +1,209 @@ +getPageWidth(); } + public function getHeight() { return $this->getPageHeight(); } + public function getMarginTop() { return $this->getPageMarginTop(); } + public function getMarginBottom() { return $this->getPageMarginBottom(); } + public function getMarginLeft() { return $this->getPageMarginLeft(); } + public function getMarginRight() { return $this->getPageMarginRight(); } + + /** + * Returns the page width in getUnit() units + * + * @return float + */ + public abstract function getPageWidth(); + + /** + * Returns the page height in getUnit() units + * + * @return float + */ + public abstract function getPageHeight(); + + /** + * Returns the page top margin in getUnit() units + * + * @return float + */ + public abstract function getPageMarginTop(); + + /** + * Returns the page bottom margin in getUnit() units + * + * @return float + */ + public abstract function getPageMarginBottom(); + + /** + * Returns the page left margin in getUnit() units + * + * @return float + */ + public abstract function getPageMarginLeft(); + + /** + * Returns the page right margin in getUnit() units + * + * @return float + */ + public abstract function getPageMarginRight(); + + /** + * Returns the page width in getUnit() units + * + * @return float + */ + public abstract function getLabelWidth(); + + /** + * Returns each label's height in getUnit() units + * + * @return float + */ + public abstract function getLabelHeight(); + + /** + * Returns each label's top margin in getUnit() units + * + * @return float + */ + public abstract function getLabelMarginTop(); + + /** + * Returns each label's bottom margin in getUnit() units + * + * @return float + */ + public abstract function getLabelMarginBottom(); + + /** + * Returns each label's left margin in getUnit() units + * + * @return float + */ + public abstract function getLabelMarginLeft(); + + /** + * Returns each label's right margin in getUnit() units + * + * @return float + */ + public abstract function getLabelMarginRight(); + + /** + * Returns the number of labels each page supports + * + * @return int + */ + public abstract function getLabelsPerPage(); + + /** + * Returns label position based on its index + * + * @param int $index + * + * @return array [x,y] + */ + public abstract function getLabelPosition(int $index); + + /** + * Returns the border to draw around labels + * + * @return int + */ + public abstract function getLabelBorder(); + + /** + * Handle the data here. Override for multiple-per-page handling + * + * @param TCPDF $pdf The TCPDF instance + * @param Collection $data The data + */ + public function writeAll($pdf, $data) { + $prevPageNumber = -1; + + foreach ($data->toArray() as $recordIndex => $record) { + + $pageNumber = (int)($recordIndex / $this->getLabelsPerPage()); + if ($pageNumber != $prevPageNumber) { + $pdf->AddPage(); + $prevPageNumber = $pageNumber; + } + + $pageIndex = $recordIndex - ($this->getLabelsPerPage() * $pageNumber); + $position = $this->getLabelPosition($pageIndex); + + $pdf->StartTemplate(); + $this->write($pdf, $data->get($recordIndex)); + $template = $pdf->EndTemplate(); + + $pdf->printTemplate($template, $position[0], $position[1]); + + if ($this->getLabelBorder()) { + $prevLineWidth = $pdf->GetLineWidth(); + + $borderThickness = $this->getLabelBorder(); + $borderOffset = $borderThickness / 2; + $borderX = $position[0]- $borderOffset; + $borderY = $position[1] - $borderOffset; + $borderW = $this->getLabelWidth() + $borderThickness; + $borderH = $this->getLabelHeight() + $borderThickness; + + $pdf->setLineWidth($borderThickness); + $pdf->Rect($borderX, $borderY, $borderW, $borderH); + $pdf->setLineWidth($prevLineWidth); + } + } + } + + /** + * Returns each label's orientation as a string. + * 'L' = Landscape + * 'P' = Portrait + * + * @return string + */ + public final function getLabelOrientation() { + return ($this->getLabelWidth() >= $this->getLabelHeight()) ? 'L' : 'P'; + } + + /** + * Returns each label's printable area as an object. + * + * @return object [ 'x1'=>0.00, 'y1'=>0.00, 'x2'=>0.00, 'y2'=>0.00, 'w'=>0.00, 'h'=>0.00 ] + */ + public final function getLabelPrintableArea() { + return (object)[ + 'x1' => $this->getLabelMarginLeft(), + 'y1' => $this->getLabelMarginTop(), + 'x2' => $this->getLabelWidth() - $this->getLabelMarginRight(), + 'y2' => $this->getLabelHeight() - $this->getLabelMarginBottom(), + 'w' => $this->getLabelWidth() - $this->getLabelMarginLeft() - $this->getLabelMarginRight(), + 'h' => $this->getLabelHeight() - $this->getLabelMarginTop() - $this->getLabelMarginBottom(), + ]; + } + + /** + * Returns label index offset (skip positions) + * + * @return int + */ + public function getLabelIndexOffset() { return $this->indexOffset; } + + /** + * Sets label index offset (skip positions) + * + * @param int $offset + * + */ + public function setLabelIndexOffset(int $offset) { $this->indexOffset = $offset; } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/L7162.php b/app/Models/Labels/Sheets/Avery/L7162.php new file mode 100644 index 0000000000..e1097db9b2 --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/L7162.php @@ -0,0 +1,71 @@ +getUnit(), 0); + $this->pageWidth = $paperSize->width; + $this->pageHeight = $paperSize->height; + + $this->pageMarginLeft = Helper::convertUnit(self::COLUMN1_X, 'pt', $this->getUnit()); + $this->pageMarginTop = Helper::convertUnit(self::ROW1_Y, 'pt', $this->getUnit()); + + $columnSpacingPt = self::COLUMN2_X - self::COLUMN1_X - self::LABEL_W; + $this->columnSpacing = Helper::convertUnit($columnSpacingPt, 'pt', $this->getUnit()); + $rowSpacingPt = self::ROW2_Y - self::ROW1_Y - self::LABEL_H; + $this->rowSpacing = Helper::convertUnit($rowSpacingPt, 'pt', $this->getUnit()); + + $this->labelWidth = Helper::convertUnit(self::LABEL_W, 'pt', $this->getUnit()); + $this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit()); + } + + public function getPageWidth() { return $this->pageWidth; } + public function getPageHeight() { return $this->pageHeight; } + + public function getPageMarginTop() { return $this->pageMarginTop; } + public function getPageMarginBottom() { return $this->pageMarginTop; } + public function getPageMarginLeft() { return $this->pageMarginLeft; } + public function getPageMarginRight() { return $this->pageMarginLeft; } + + public function getColumns() { return 2; } + public function getRows() { return 8; } + + public function getLabelColumnSpacing() { return $this->columnSpacing; } + public function getLabelRowSpacing() { return $this->rowSpacing; } + + public function getLabelWidth() { return $this->labelWidth; } + public function getLabelHeight() { return $this->labelHeight; } + + public function getLabelBorder() { return 0; } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/L7162_A.php b/app/Models/Labels/Sheets/Avery/L7162_A.php new file mode 100644 index 0000000000..0b3312ba7c --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/L7162_A.php @@ -0,0 +1,100 @@ +getLabelPrintableArea(); + + $usableWidth = $pa->w; + $usableHeight = $pa->h; + $currentX = $pa->x1; + $currentY = $pa->y1; + $titleShiftX = 0; + + $barcodeSize = $pa->h - self::TAG_SIZE; + + if ($record->has('barcode2d')) { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'C', + $barcodeSize, self::TAG_SIZE, true, 0 + ); + static::write2DBarcode( + $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type, + $pa->x1, $pa->y1, + $barcodeSize, $barcodeSize + ); + $currentX += $barcodeSize + self::BARCODE_MARGIN; + $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; + } else { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y1, + 'freemono', 'b', self::TITLE_SIZE, 'L', + $barcodeSize, self::TITLE_SIZE, true, 0 + ); + $titleShiftX = $barcodeSize; + } + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $currentX + $titleShiftX, $currentY, + 'freesans', '', self::TITLE_SIZE, 'L', + $usableWidth, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.3 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + + } +} + + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/L7162_B.php b/app/Models/Labels/Sheets/Avery/L7162_B.php new file mode 100644 index 0000000000..268754e04f --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/L7162_B.php @@ -0,0 +1,103 @@ +getLabelPrintableArea(); + + $usableWidth = $pa->w; + $usableHeight = $pa->h; + $currentX = $pa->x1; + $currentY = $pa->y1; + + if ($record->has('barcode1d')) { + static::write1DBarcode( + $pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type, + $pa->x1, $pa->y2 - self::BARCODE_SIZE, + $usableWidth, self::BARCODE_SIZE + ); + $usableHeight -= self::BARCODE_SIZE + self::BARCODE_MARGIN; + } + + if ($record->has('logo')) { + $logoSize = static::writeImage( + $pdf, $record->get('logo'), + $pa->x1, $pa->y1, + self::LOGO_MAX_WIDTH, $usableHeight, + 'L', 'T', 300, true, false, 0.1 + ); + $currentX += $logoSize[0] + self::LOGO_MARGIN; + $usableWidth -= $logoSize[0] + self::LOGO_MARGIN; + } + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $currentX, $currentY, + 'freesans', '', self::TITLE_SIZE, 'L', + $usableWidth, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.3 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + + static::writeText( + $pdf, $record->get('tag'), + $currentX, $pa->y2 - self::BARCODE_SIZE - self::BARCODE_MARGIN - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'R', + $usableWidth, self::TAG_SIZE, true, 0, 0.3 + ); + + } +} + + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/L7163.php b/app/Models/Labels/Sheets/Avery/L7163.php new file mode 100644 index 0000000000..f143260336 --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/L7163.php @@ -0,0 +1,71 @@ +getUnit(), 0); + $this->pageWidth = $paperSize->width; + $this->pageHeight = $paperSize->height; + + $this->pageMarginLeft = Helper::convertUnit(self::COLUMN1_X, 'pt', $this->getUnit()); + $this->pageMarginTop = Helper::convertUnit(self::ROW1_Y, 'pt', $this->getUnit()); + + $columnSpacingPt = self::COLUMN2_X - self::COLUMN1_X - self::LABEL_W; + $this->columnSpacing = Helper::convertUnit($columnSpacingPt, 'pt', $this->getUnit()); + $rowSpacingPt = self::ROW2_Y - self::ROW1_Y - self::LABEL_H; + $this->rowSpacing = Helper::convertUnit($rowSpacingPt, 'pt', $this->getUnit()); + + $this->labelWidth = Helper::convertUnit(self::LABEL_W, 'pt', $this->getUnit()); + $this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit()); + } + + public function getPageWidth() { return $this->pageWidth; } + public function getPageHeight() { return $this->pageHeight; } + + public function getPageMarginTop() { return $this->pageMarginTop; } + public function getPageMarginBottom() { return $this->pageMarginTop; } + public function getPageMarginLeft() { return $this->pageMarginLeft; } + public function getPageMarginRight() { return $this->pageMarginLeft; } + + public function getColumns() { return 2; } + public function getRows() { return 7; } + + public function getLabelColumnSpacing() { return $this->columnSpacing; } + public function getLabelRowSpacing() { return $this->rowSpacing; } + + public function getLabelWidth() { return $this->labelWidth; } + public function getLabelHeight() { return $this->labelHeight; } + + public function getLabelBorder() { return 0; } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/L7163_A.php b/app/Models/Labels/Sheets/Avery/L7163_A.php new file mode 100644 index 0000000000..6dc33f64dd --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/L7163_A.php @@ -0,0 +1,98 @@ +getLabelPrintableArea(); + + $usableWidth = $pa->w; + $usableHeight = $pa->h; + $currentX = $pa->x1; + $currentY = $pa->y1; + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $currentX, $currentY, + 'freesans', '', self::TITLE_SIZE, 'C', + $usableWidth, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + } + + $barcodeSize = $pa->h - self::TITLE_SIZE - self::TITLE_MARGIN - self::TAG_SIZE; + + if ($record->has('barcode2d')) { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'C', + $barcodeSize, self::TAG_SIZE, true, 0 + ); + static::write2DBarcode( + $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type, + $pa->x1, $currentY, + $barcodeSize, $barcodeSize + ); + $currentX += $barcodeSize + self::BARCODE_MARGIN; + $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; + } else { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'R', + $usableWidth, self::TAG_SIZE, true, 0 + ); + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.5 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + + } +} + + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/_5267.php b/app/Models/Labels/Sheets/Avery/_5267.php new file mode 100644 index 0000000000..f5f2f13557 --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/_5267.php @@ -0,0 +1,71 @@ +getUnit(), 2); + $this->pageWidth = $paperSize->width; + $this->pageHeight = $paperSize->height; + + $this->pageMarginLeft = Helper::convertUnit(self::COLUMN1_X, 'pt', $this->getUnit()); + $this->pageMarginTop = Helper::convertUnit(self::ROW1_Y, 'pt', $this->getUnit()); + + $columnSpacingPt = self::COLUMN2_X - self::COLUMN1_X - self::LABEL_W; + $this->columnSpacing = Helper::convertUnit($columnSpacingPt, 'pt', $this->getUnit()); + $rowSpacingPt = self::ROW2_Y - self::ROW1_Y - self::LABEL_H; + $this->rowSpacing = Helper::convertUnit($rowSpacingPt, 'pt', $this->getUnit()); + + $this->labelWidth = Helper::convertUnit(self::LABEL_W, 'pt', $this->getUnit()); + $this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit()); + } + + public function getPageWidth() { return $this->pageWidth; } + public function getPageHeight() { return $this->pageHeight; } + + public function getPageMarginTop() { return $this->pageMarginTop; } + public function getPageMarginBottom() { return $this->pageMarginTop; } + public function getPageMarginLeft() { return $this->pageMarginLeft; } + public function getPageMarginRight() { return $this->pageMarginLeft; } + + public function getColumns() { return 4; } + public function getRows() { return 20; } + + public function getLabelColumnSpacing() { return $this->columnSpacing; } + public function getLabelRowSpacing() { return $this->rowSpacing; } + + public function getLabelWidth() { return $this->labelWidth; } + public function getLabelHeight() { return $this->labelHeight; } + + public function getLabelBorder() { return 0; } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/_5267_A.php b/app/Models/Labels/Sheets/Avery/_5267_A.php new file mode 100644 index 0000000000..efe0855d5e --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/_5267_A.php @@ -0,0 +1,68 @@ +getLabelPrintableArea(); + + if ($record->has('barcode1d')) { + static::write1DBarcode( + $pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type, + $pa->x1, $pa->y2 - self::BARCODE_SIZE, + $pa->w, self::BARCODE_SIZE + ); + } + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $pa->x1, $pa->y1, + 'freesans', '', self::TITLE_SIZE, 'L', + $pa->w, self::TITLE_SIZE, true, 0 + ); + } + + $fieldY = $pa->y2 - self::BARCODE_SIZE - self::BARCODE_MARGIN - self::FIELD_SIZE; + if ($record->has('fields')) { + if ($record->get('fields')->count() >= 1) { + $field = $record->get('fields')->first(); + static::writeText( + $pdf, $field['value'], + $pa->x1, $fieldY, + 'freemono', 'B', self::FIELD_SIZE, 'C', + $pa->w, self::FIELD_SIZE, true, 0, 0.01 + ); + } + } + + } +} + + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/_5520.php b/app/Models/Labels/Sheets/Avery/_5520.php new file mode 100644 index 0000000000..00cb0e0687 --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/_5520.php @@ -0,0 +1,71 @@ +getUnit(), 2); + $this->pageWidth = $paperSize->width; + $this->pageHeight = $paperSize->height; + + $this->pageMarginLeft = Helper::convertUnit(self::COLUMN1_X, 'pt', $this->getUnit()); + $this->pageMarginTop = Helper::convertUnit(self::ROW1_Y, 'pt', $this->getUnit()); + + $columnSpacingPt = self::COLUMN2_X - self::COLUMN1_X - self::LABEL_W; + $this->columnSpacing = Helper::convertUnit($columnSpacingPt, 'pt', $this->getUnit()); + $rowSpacingPt = self::ROW2_Y - self::ROW1_Y - self::LABEL_H; + $this->rowSpacing = Helper::convertUnit($rowSpacingPt, 'pt', $this->getUnit()); + + $this->labelWidth = Helper::convertUnit(self::LABEL_W, 'pt', $this->getUnit()); + $this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit()); + } + + public function getPageWidth() { return $this->pageWidth; } + public function getPageHeight() { return $this->pageHeight; } + + public function getPageMarginTop() { return $this->pageMarginTop; } + public function getPageMarginBottom() { return $this->pageMarginTop; } + public function getPageMarginLeft() { return $this->pageMarginLeft; } + public function getPageMarginRight() { return $this->pageMarginLeft; } + + public function getColumns() { return 3; } + public function getRows() { return 10; } + + public function getLabelColumnSpacing() { return $this->columnSpacing; } + public function getLabelRowSpacing() { return $this->rowSpacing; } + + public function getLabelWidth() { return $this->labelWidth; } + public function getLabelHeight() { return $this->labelHeight; } + + public function getLabelBorder() { return 0; } +} + +?> \ No newline at end of file diff --git a/app/Models/Labels/Sheets/Avery/_5520_A.php b/app/Models/Labels/Sheets/Avery/_5520_A.php new file mode 100644 index 0000000000..199566d248 --- /dev/null +++ b/app/Models/Labels/Sheets/Avery/_5520_A.php @@ -0,0 +1,85 @@ +getLabelPrintableArea(); + + $currentX = $pa->x1; + $currentY = $pa->y1; + $usableWidth = $pa->w; + $usableHeight = $pa->h; + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $pa->x1, $pa->y1, + 'freesans', '', self::TITLE_SIZE, 'C', + $pa->w, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + $usableHeight -= self::TITLE_SIZE + self::TITLE_MARGIN; + } + + $barcodeSize = $usableHeight; + if ($record->has('barcode2d')) { + static::write2DBarcode( + $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type, + $currentX, $currentY, + $barcodeSize, $barcodeSize + ); + $currentX += $barcodeSize + self::BARCODE_MARGIN; + $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.01 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + + } +} + + +?> \ No newline at end of file diff --git a/app/Models/Labels/Tapes/Brother/TZe_12mm.php b/app/Models/Labels/Tapes/Brother/TZe_12mm.php new file mode 100644 index 0000000000..f9196847ce --- /dev/null +++ b/app/Models/Labels/Tapes/Brother/TZe_12mm.php @@ -0,0 +1,19 @@ +getUnit()); } + public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); } + public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());} + public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); } + public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); } +} \ No newline at end of file diff --git a/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php b/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php new file mode 100644 index 0000000000..f89cfc5d47 --- /dev/null +++ b/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php @@ -0,0 +1,56 @@ +getPrintableArea(); + + if ($record->has('barcode1d')) { + static::write1DBarcode( + $pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type, + $pa->x1, $pa->y1, $pa->w, self::BARCODE_SIZE + ); + } + + $currentY = $pa->y1 + self::BARCODE_SIZE + self::BARCODE_MARGIN; + $usableHeight = $pa->h - self::BARCODE_SIZE - self::BARCODE_MARGIN; + $fontSize = $usableHeight + self::TEXT_SIZE_MOD; + + $tagWidth = $pa->w / 3; + $fieldWidth = $pa->w / 3 * 2; + + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $currentY, + 'freemono', 'b', $fontSize, 'L', + $tagWidth, $usableHeight, true, 0, 0 + ); + + if ($record->get('fields')->count() >= 1) { + static::writeText( + $pdf, $record->get('fields')->values()->get(0)['value'], + $pa->x1 + ($tagWidth), $currentY, + 'freemono', 'b', $fontSize, 'R', + $fieldWidth, $usableHeight, true, 0, 0 + ); + } + + } +} \ No newline at end of file diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm.php b/app/Models/Labels/Tapes/Brother/TZe_24mm.php new file mode 100644 index 0000000000..3c67bc1614 --- /dev/null +++ b/app/Models/Labels/Tapes/Brother/TZe_24mm.php @@ -0,0 +1,19 @@ +getUnit()); } + public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); } + public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());} + public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); } + public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); } +} \ No newline at end of file diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php b/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php new file mode 100644 index 0000000000..ea4c6c9dfb --- /dev/null +++ b/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php @@ -0,0 +1,87 @@ +getPrintableArea(); + + $currentX = $pa->x1; + $currentY = $pa->y1; + $usableWidth = $pa->w; + + $barcodeSize = $pa->h - self::TAG_SIZE; + + if ($record->has('barcode2d')) { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'C', + $barcodeSize, self::TAG_SIZE, true, 0 + ); + static::write2DBarcode( + $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type, + $currentX, $currentY, + $barcodeSize, $barcodeSize + ); + $currentX += $barcodeSize + self::BARCODE_MARGIN; + $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; + } else { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'R', + $usableWidth, self::TAG_SIZE, true, 0 + ); + } + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $currentX, $currentY, + 'freesans', '', self::TITLE_SIZE, 'L', + $usableWidth, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.3 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + } +} \ No newline at end of file diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter.php b/app/Models/Labels/Tapes/Dymo/LabelWriter.php new file mode 100644 index 0000000000..fa427fd213 --- /dev/null +++ b/app/Models/Labels/Tapes/Dymo/LabelWriter.php @@ -0,0 +1,19 @@ +getUnit()); } + public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'in', $this->getUnit()); } + public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'in', $this->getUnit());} + public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'in', $this->getUnit()); } + public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'in', $this->getUnit()); } +} \ No newline at end of file diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php new file mode 100644 index 0000000000..1b34eb113a --- /dev/null +++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php @@ -0,0 +1,90 @@ +getPrintableArea(); + + $currentX = $pa->x1; + $currentY = $pa->y1; + $usableWidth = $pa->w; + + $barcodeSize = $pa->h - self::TAG_SIZE; + + if ($record->has('barcode2d')) { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'C', + $barcodeSize, self::TAG_SIZE, true, 0 + ); + static::write2DBarcode( + $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type, + $currentX, $currentY, + $barcodeSize, $barcodeSize + ); + $currentX += $barcodeSize + self::BARCODE_MARGIN; + $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; + } else { + static::writeText( + $pdf, $record->get('tag'), + $pa->x1, $pa->y2 - self::TAG_SIZE, + 'freemono', 'b', self::TAG_SIZE, 'R', + $usableWidth, self::TAG_SIZE, true, 0 + ); + } + + if ($record->has('title')) { + static::writeText( + $pdf, $record->get('title'), + $currentX, $currentY, + 'freesans', '', self::TITLE_SIZE, 'L', + $usableWidth, self::TITLE_SIZE, true, 0 + ); + $currentY += self::TITLE_SIZE + self::TITLE_MARGIN; + } + + foreach ($record->get('fields') as $field) { + static::writeText( + $pdf, $field['label'], + $currentX, $currentY, + 'freesans', '', self::LABEL_SIZE, 'L', + $usableWidth, self::LABEL_SIZE, true, 0, 0 + ); + $currentY += self::LABEL_SIZE + self::LABEL_MARGIN; + + static::writeText( + $pdf, $field['value'], + $currentX, $currentY, + 'freemono', 'B', self::FIELD_SIZE, 'L', + $usableWidth, self::FIELD_SIZE, true, 0, 0.3 + ); + $currentY += self::FIELD_SIZE + self::FIELD_MARGIN; + } + } +} \ No newline at end of file diff --git a/app/Models/Ldap.php b/app/Models/Ldap.php index 4eb496a2ab..ae1f163dda 100644 --- a/app/Models/Ldap.php +++ b/app/Models/Ldap.php @@ -252,13 +252,10 @@ class Ldap extends Model $user->last_name = $item['lastname']; $user->username = $item['username']; $user->email = $item['email']; + $user->password = $user->noPassword(); if (Setting::getSettings()->ldap_pw_sync == '1') { - $user->password = bcrypt($password); - } else { - $pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 25); - $user->password = bcrypt($pass); } $user->activated = 1; @@ -268,7 +265,7 @@ class Ldap extends Model if ($user->save()) { return $user; } else { - LOG::debug('Could not create user.'.$user->getErrors()); + \Log::debug('Could not create user.'.$user->getErrors()); throw new Exception('Could not create user: '.$user->getErrors()); } } diff --git a/app/Models/License.php b/app/Models/License.php index 162b3d662a..2ea10939fa 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -32,6 +32,7 @@ class License extends Depreciable protected $guarded = 'id'; protected $table = 'licenses'; + protected $casts = [ 'purchase_date' => 'date', 'expiration_date' => 'date', @@ -323,7 +324,10 @@ class License extends Depreciable */ public function checkin_email() { - return $this->category->checkin_email; + if ($this->category) { + return $this->category->checkin_email; + } + return false; } /** @@ -335,7 +339,11 @@ class License extends Depreciable */ public function requireAcceptance() { - return $this->category->require_acceptance; + if ($this->category) { + return $this->category->require_acceptance; + } + + return false; } /** @@ -348,14 +356,16 @@ class License extends Depreciable */ public function getEula() { - - if ($this->category->eula_text) { - return Helper::parseEscapedMarkedown($this->category->eula_text); - } elseif ($this->category->use_default_eula == '1') { - return Helper::parseEscapedMarkedown(Setting::getSettings()->default_eula_text); - } else { - return false; + if ($this->category){ + if ($this->category->eula_text) { + return Helper::parseEscapedMarkedown($this->category->eula_text); + } elseif ($this->category->use_default_eula == '1') { + return Helper::parseEscapedMarkedown(Setting::getSettings()->default_eula_text); + } } + + return false; + } /** diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index d2a99d3c56..8a51c0c9cf 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -48,7 +48,10 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild */ public function requireAcceptance() { - return $this->license->category->require_acceptance; + if ($this->license && $this->license->category) { + return $this->license->category->require_acceptance; + } + return false; } public function getEula() diff --git a/app/Models/Location.php b/app/Models/Location.php index c3ddaa7598..145d6cef9a 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:12|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', ]; @@ -65,6 +66,8 @@ class Location extends SnipeModel 'state', 'country', 'zip', + 'phone', + 'fax', 'ldap_ou', 'currency', 'manager_id', @@ -79,7 +82,7 @@ class Location extends SnipeModel * * @var array */ - protected $searchableAttributes = ['name', 'address', 'city', 'state', 'zip', 'created_at', 'ldap_ou']; + protected $searchableAttributes = ['name', 'address', 'city', 'state', 'zip', 'created_at', 'ldap_ou', 'phone', 'fax']; /** * The relations and their attributes that should be included when searching the model. diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index d0bbd10733..ce3a07f159 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -23,7 +23,7 @@ trait Loggable * @since [v3.4] * @return \App\Models\Actionlog */ - public function logCheckout($note, $target, $action_date = null) + public function logCheckout($note, $target, $action_date = null, $originalValues = []) { $log = new Actionlog; $log = $this->determineLogItemType($log); @@ -62,6 +62,23 @@ trait Loggable $log->action_date = date('Y-m-d H:i:s'); } + $changed = []; + $originalValues = array_intersect_key($originalValues, array_flip(['action_date','name','status_id','location_id','expected_checkin'])); + + foreach ($originalValues as $key => $value) { + if ($key == 'action_date' && $value != $action_date) { + $changed[$key]['old'] = $value; + $changed[$key]['new'] = is_string($action_date) ? $action_date : $action_date->format('Y-m-d H:i:s'); + } elseif ($value != $this->getAttributes()[$key]) { + $changed[$key]['old'] = $value; + $changed[$key]['new'] = $this->getAttributes()[$key]; + } + } + + if (!empty($changed)){ + $log->log_meta = json_encode($changed); + } + $log->logaction('checkout'); return $log; @@ -89,7 +106,7 @@ trait Loggable * @since [v3.4] * @return \App\Models\Actionlog */ - public function logCheckin($target, $note, $action_date = null) + public function logCheckin($target, $note, $action_date = null, $originalValues = []) { $settings = Setting::getSettings(); $log = new Actionlog; @@ -114,13 +131,9 @@ trait Loggable } } - $log->location_id = null; $log->note = $note; $log->action_date = $action_date; - if (! $log->action_date) { - $log->action_date = date('Y-m-d H:i:s'); - } if (! $log->action_date) { $log->action_date = date('Y-m-d H:i:s'); @@ -130,6 +143,23 @@ trait Loggable $log->user_id = Auth::user()->id; } + $changed = []; + $originalValues = array_intersect_key($originalValues, array_flip(['action_date','name','status_id','location_id','rtd_location_id','expected_checkin'])); + + foreach ($originalValues as $key => $value) { + if ($key == 'action_date' && $value != $action_date) { + $changed[$key]['old'] = $value; + $changed[$key]['new'] = is_string($action_date) ? $action_date : $action_date->format('Y-m-d H:i:s'); + } elseif ($value != $this->getAttributes()[$key]) { + $changed[$key]['old'] = $value; + $changed[$key]['new'] = $this->getAttributes()[$key]; + } + } + + if (!empty($changed)){ + $log->log_meta = json_encode($changed); + } + $log->logaction('checkin from'); // $params = [ diff --git a/app/Models/Requestable.php b/app/Models/Requestable.php index 3983254e41..bf5c9c427b 100644 --- a/app/Models/Requestable.php +++ b/app/Models/Requestable.php @@ -38,8 +38,12 @@ trait Requestable $this->requests()->where('user_id', Auth::id())->delete(); } - public function cancelRequest() + public function cancelRequest($user_id = null) { - $this->requests()->where('user_id', Auth::id())->update(['canceled_at' => \Carbon\Carbon::now()]); + if (!$user_id){ + $user_id = Auth::id(); + } + + $this->requests()->where('user_id', $user_id)->update(['canceled_at' => \Carbon\Carbon::now()]); } } diff --git a/app/Models/SCIMUser.php b/app/Models/SCIMUser.php index 71bd9169ae..fcf34c0d5d 100644 --- a/app/Models/SCIMUser.php +++ b/app/Models/SCIMUser.php @@ -9,8 +9,7 @@ class SCIMUser extends User protected $throwValidationExceptions = true; // we want model-level validation to fully THROW, not just return false public function __construct(array $attributes = []) { - $attributes['password'] = "*NO PASSWORD*"; - // $attributes['activated'] = 1; + $attributes['password'] = $this->noPassword(); parent::__construct($attributes); } } \ No newline at end of file diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 61be790e00..caf142cbdf 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,13 @@ class Setting extends Model 'webhook_endpoint', 'webhook_channel', 'webhook_botname', + 'google_login', + 'google_client_id', + 'google_client_secret', + ]; + + protected $casts = [ + 'label2_asset_logo' => 'boolean', ]; /** diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index fa00fbad2a..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', ]; 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 36e1c8ac47..8011f94ff4 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -69,15 +69,12 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo ]; protected $casts = [ - 'activated' => 'boolean', 'manager_id' => 'integer', 'location_id' => 'integer', 'company_id' => 'integer', - 'vip' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', - 'autoassign_licenses' => 'boolean', ]; /** @@ -98,6 +95,14 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo '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', + 'vip' => 'boolean', + 'remote' => 'boolean', + 'activated' => 'boolean', ]; /** @@ -242,21 +247,12 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo */ public function getFullNameAttribute() { - return $this->first_name.' '.$this->last_name; - } + $setting = Setting::getSettings(); - /** - * Returns the complete name attribute with username - * - * @todo refactor this so it's less repetitive and dumb - * - * @author A. Gianotto - * @since [v2.0] - * @return string - */ - public function getCompleteNameAttribute() - { - return $this->last_name.', '.$this->first_name.' ('.$this->username.')'; + if ($setting->name_display_format=='last_first') { + return ($this->last_name) ? $this->last_name.' '.$this->first_name : $this->first_name; + } + return $this->last_name ? $this->first_name.' '.$this->last_name : $this->first_name; } @@ -460,6 +456,22 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at'); } + /** + * Set a common string when the user has been imported/synced from: + * + * - LDAP without password syncing + * - SCIM + * - CSV import where no password was provided + * + * @author A. Gianotto + * @since [v6.2.0] + * @return string + */ + public function noPassword() + { + return "*** NO PASSWORD ***"; + } + /** * Query builder scope to return NOT-deleted users @@ -639,14 +651,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. * @@ -655,9 +667,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; @@ -752,4 +766,26 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo { return $this->locale; } + public function getUserTotalCost(){ + $asset_cost= 0; + $license_cost= 0; + $accessory_cost= 0; + foreach ($this->assets as $asset){ + $asset_cost += $asset->purchase_cost; + $this->asset_cost = $asset_cost; + } + foreach ($this->licenses as $license){ + $license_cost += $license->purchase_cost; + $this->license_cost = $license_cost; + } + foreach ($this->accessories as $accessory){ + $accessory_cost += $accessory->purchase_cost; + $this->accessory_cost = $accessory_cost; + } + + $this->total_user_cost = ($asset_cost + $accessory_cost + $license_cost); + + + return $this; + } } 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..7735f7dc11 100644 --- a/app/Notifications/CheckinAccessoryNotification.php +++ b/app/Notifications/CheckinAccessoryNotification.php @@ -26,7 +26,6 @@ class CheckinAccessoryNotification extends Notification $this->admin = $checkedInby; $this->note = $note; $this->settings = Setting::getSettings(); - \Log::debug('Constructor for notification fired'); } /** @@ -92,6 +91,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 +101,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..05e56a9619 100644 --- a/app/Notifications/CheckinAssetNotification.php +++ b/app/Notifications/CheckinAssetNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Helpers\Helper; use App\Models\Asset; use App\Models\Setting; use App\Models\User; @@ -65,6 +66,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 +77,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/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/Observers/AssetObserver.php b/app/Observers/AssetObserver.php index a399e1d2a6..f42b041fad 100644 --- a/app/Observers/AssetObserver.php +++ b/app/Observers/AssetObserver.php @@ -6,11 +6,12 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\Setting; use Auth; +use Carbon\Carbon; class AssetObserver { /** - * Listen to the User created event. + * Listen to the Asset updating event. This fires automatically every time an existing asset is saved. * * @param Asset $asset * @return void @@ -71,12 +72,33 @@ class AssetObserver public function created(Asset $asset) { if ($settings = Setting::getSettings()) { - $settings->increment('next_auto_tag_base'); - $settings->save(); + $tag = $asset->asset_tag; + $prefix = $settings->auto_increment_prefix; + $number = substr($tag, strlen($prefix)); + // IF - auto_increment_assets is on, AND (there is no prefix OR the prefix matches the start of the tag) + // AND the rest of the string after the prefix is all digits, THEN... + if ($settings->auto_increment_assets && ($prefix=='' || strpos($tag, $prefix) === 0) && preg_match('/\d+/',$number) === 1) { + // new way of auto-trueing-up auto_increment ID's + $next_asset_tag = intval($number, 10) + 1; + // we had to use 'intval' because the $number could be '01234' and + // might get interpreted in Octal instead of decimal + + // only modify the 'next' one if it's *bigger* than the stored base + // + if($next_asset_tag > $settings->next_auto_tag_base) { + $settings->next_auto_tag_base = $next_asset_tag; + $settings->save(); + } + + } else { + // legacy method + $settings->increment('next_auto_tag_base'); + $settings->save(); + } } $logAction = new Actionlog(); - $logAction->item_type = Asset::class; + $logAction->item_type = Asset::class; // can we instead say $logAction->item = $asset ? $logAction->item_id = $asset->id; $logAction->created_at = date('Y-m-d H:i:s'); $logAction->user_id = Auth::id(); @@ -98,4 +120,42 @@ class AssetObserver $logAction->user_id = Auth::id(); $logAction->logaction('delete'); } + + /** + * Executes every time an asset is saved. + * + * This matters specifically because any database fields affected here MUST already exist on + * the assets table (and/or any related models), or related migrations WILL fail. + * + * For example, if there is a database migration that's a bit older and modifies an asset, if the save + * fires before a field gets created in a later migration and that field in the later migration + * is used in this observer, it doesn't actually exist yet and the migration will break unless we + * use saveQuietly() in the migration which skips this observer. + * + * @see https://github.com/snipe/snipe-it/issues/13723#issuecomment-1761315938 + */ + public function saving(Asset $asset) + { + // determine if calculated eol and then calculate it - this should only happen on a new asset + if (is_null($asset->asset_eol_date) && !is_null($asset->purchase_date) && ($asset->model->eol > 0)){ + $asset->asset_eol_date = $asset->purchase_date->addMonths($asset->model->eol)->format('Y-m-d'); + $asset->eol_explicit = false; + } + + // determine if explicit and set eol_explicit to true + if (!is_null($asset->asset_eol_date) && !is_null($asset->purchase_date)) { + if($asset->model->eol > 0) { + $months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date); + if($months != $asset->model->eol) { + $asset->eol_explicit = true; + } + } + } elseif (!is_null($asset->asset_eol_date) && is_null($asset->purchase_date)) { + $asset->eol_explicit = true; + } + if ((!is_null($asset->asset_eol_date)) && (!is_null($asset->purchase_date)) && (is_null($asset->model->eol) || ($asset->model->eol == 0))) { + $asset->eol_explicit = true; + } + + } } 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 8e3d109045..85a0fa58ec 100644 --- a/app/Presenters/AssetModelPresenter.php +++ b/app/Presenters/AssetModelPresenter.php @@ -65,6 +65,14 @@ class AssetModelPresenter extends Presenter 'title' => trans('admin/models/table.modelnumber'), 'visible' => true, ], + [ + 'field' => 'min_amt', + 'searchable' => false, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('mail.min_QTY'), + 'visible' => true, + ], [ 'field' => 'assets_count', 'searchable' => false, @@ -96,7 +104,7 @@ class AssetModelPresenter extends Presenter 'searchable' => false, 'sortable' => true, 'switchable' => true, - 'title' => trans('general.eol'), + 'title' => trans('admin/hardware/form.eol_rate'), 'visible' => true, ], [ @@ -123,6 +131,7 @@ class AssetModelPresenter extends Presenter 'switchable' => true, 'title' => trans('general.notes'), 'visible' => false, + 'formatter' => 'notesFormatter', ], [ 'field' => 'created_at', diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php index ce476d082c..de7c2c7709 100644 --- a/app/Presenters/AssetPresenter.php +++ b/app/Presenters/AssetPresenter.php @@ -3,6 +3,7 @@ namespace App\Presenters; use App\Models\CustomField; +use Carbon\CarbonImmutable; use DateTime; /** @@ -142,8 +143,8 @@ class AssetPresenter extends Presenter 'formatter' => 'dateDisplayFormatter', ], [ 'field' => 'age', - 'searchable' => true, - 'sortable' => true, + 'searchable' => false, + 'sortable' => false, 'visible' => false, 'title' => trans('general.age'), ], [ @@ -154,6 +155,13 @@ class AssetPresenter extends Presenter 'footerFormatter' => 'sumFormatter', 'class' => 'text-right', ], [ + "field" => "book_value", + "searchable" => false, + "sortable" => false, + "title" => trans('admin/hardware/table.book_value'), + "footerFormatter" => 'sumFormatter', + "class" => "text-right", + ],[ 'field' => 'order_number', 'searchable' => true, 'sortable' => true, @@ -165,7 +173,7 @@ class AssetPresenter extends Presenter 'searchable' => false, 'sortable' => true, 'visible' => false, - 'title' => trans('general.eol'), + 'title' => trans('admin/hardware/form.eol_rate'), ], [ 'field' => 'asset_eol_date', @@ -292,7 +300,7 @@ class AssetPresenter extends Presenter 'formatter'=> 'customFieldsFormatter', 'escape' => true, 'class' => ($field->field_encrypted == '1') ? 'css-padlock' : '', - 'visible' => true, + 'visible' => ($field->show_in_listview == '1') ? true : false, ]; } @@ -422,10 +430,7 @@ class AssetPresenter extends Presenter public function eol_date() { if (($this->purchase_date) && ($this->model->model) && ($this->model->model->eol)) { - $date = date_create($this->purchase_date); - date_add($date, date_interval_create_from_date_string($this->model->model->eol.' months')); - - return date_format($date, 'Y-m-d'); + return CarbonImmutable::parse($this->purchase_date)->addMonths($this->model->model->eol)->format('Y-m-d'); } } @@ -541,8 +546,10 @@ class AssetPresenter extends Presenter 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)); + $url = (str_replace('{LOCALE}',\App\Models\Setting::getSettings()->locale, $warranty_lookup_url)); + $url = (str_replace('{SERIAL}', urlencode($this->model->serial), $url)); + $url = (str_replace('{MODEL_NAME}', urlencode($this->model->model->name), $url)); + $url = (str_replace('{MODEL_NUMBER}', urlencode($this->model->model->model_number), $url)); return $url; } diff --git a/app/Presenters/CompanyPresenter.php b/app/Presenters/CompanyPresenter.php index a6aaebf7e4..ec2e7cfc5a 100644 --- a/app/Presenters/CompanyPresenter.php +++ b/app/Presenters/CompanyPresenter.php @@ -29,6 +29,30 @@ class CompanyPresenter extends Presenter 'title' => trans('admin/companies/table.name'), 'visible' => true, 'formatter' => 'companiesLinkFormatter', + ], [ + 'field' => 'phone', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('admin/users/table.phone'), + 'visible' => false, + 'formatter' => 'phoneFormatter', + ], [ + 'field' => 'fax', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('admin/suppliers/table.fax'), + 'visible' => false, + 'formatter' => 'phoneFormatter', + ], [ + 'field' => 'email', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('admin/suppliers/table.email'), + 'visible' => true, + 'formatter' => 'emailFormatter', ], [ 'field' => 'image', 'searchable' => false, diff --git a/app/Presenters/LabelPresenter.php b/app/Presenters/LabelPresenter.php new file mode 100644 index 0000000000..db919e659a --- /dev/null +++ b/app/Presenters/LabelPresenter.php @@ -0,0 +1,96 @@ + 'radio', + 'radio' => true, + 'formatter' => 'labelRadioFormatter' + ], [ + 'field' => 'name', + 'searchable' => true, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('general.name'), + 'visible' => true, + ], [ + 'field' => 'size', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/settings/table.size'), + 'visible' => true, + 'formatter' => 'labelSizeFormatter' + ], [ + 'field' => 'labels_per_page', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.labels_per_page'), + 'visible' => true, + 'formatter' => 'labelPerPageFormatter' + ], [ + 'field' => 'support_fields', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.support_fields'), + 'visible' => true + ], [ + 'field' => 'support_asset_tag', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.support_asset_tag'), + 'visible' => true, + 'formatter' => 'trueFalseFormatter' + ], [ + 'field' => 'support_1d_barcode', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.support_1d_barcode'), + 'visible' => true, + 'formatter' => 'trueFalseFormatter' + ], [ + 'field' => 'support_2d_barcode', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.support_2d_barcode'), + 'visible' => true, + 'formatter' => 'trueFalseFormatter' + ], [ + 'field' => 'support_logo', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.support_logo'), + 'visible' => true, + 'formatter' => 'trueFalseFormatter' + ], [ + 'field' => 'support_title', + 'searchable' => false, + 'sortable' => false, + 'switchable' => true, + 'title' => trans('admin/labels/table.support_title'), + 'visible' => true, + 'formatter' => 'trueFalseFormatter' + ] + ]; + + return json_encode($layout); + } +} diff --git a/app/Presenters/LicensePresenter.php b/app/Presenters/LicensePresenter.php index f2d54549e1..e76c9152cb 100644 --- a/app/Presenters/LicensePresenter.php +++ b/app/Presenters/LicensePresenter.php @@ -254,6 +254,14 @@ class LicensePresenter extends Presenter 'visible' => true, 'formatter' => 'locationsLinkObjFormatter', ], + [ + 'field' => 'notes', + 'searchable' => false, + 'sortable' => false, + 'visible' => false, + 'title' => trans('general.notes'), + 'formatter' => 'notesFormatter' + ], [ 'field' => 'checkincheckout', 'searchable' => false, diff --git a/app/Presenters/LocationPresenter.php b/app/Presenters/LocationPresenter.php index d4281839a1..86e82c1220 100644 --- a/app/Presenters/LocationPresenter.php +++ b/app/Presenters/LocationPresenter.php @@ -106,7 +106,7 @@ class LocationPresenter extends Presenter 'searchable' => true, 'sortable' => true, 'switchable' => true, - 'title' => trans('admin/locations/table.address'), + 'title' => trans('admin/locations/table.address2'), 'visible' => false, ], [ @@ -141,6 +141,24 @@ class LocationPresenter extends Presenter 'title' => trans('admin/locations/table.country'), 'visible' => false, ], + [ + 'field' => 'phone', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('admin/users/table.phone'), + 'visible' => false, + 'formatter' => 'phoneFormatter', + ], + [ + 'field' => 'fax', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('admin/suppliers/table.fax'), + 'visible' => false, + 'formatter' => 'phoneFormatter', + ], [ 'field' => 'ldap_ou', 'searchable' => true, diff --git a/app/Presenters/UserPresenter.php b/app/Presenters/UserPresenter.php index fbb41a4d51..f70ddf8af6 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 @@ -399,17 +400,25 @@ 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; } } @@ -424,7 +433,7 @@ class UserPresenter extends Presenter */ public function nameUrl() { - return (string) link_to_route('users.show', $this->fullName(), $this->id); + return (string) link_to_route('users.show', $this->getFullNameAttribute(), $this->id); } /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 607d206a67..325fb8ad12 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -75,12 +75,7 @@ class AppServiceProvider extends ServiceProvider // Only load rollbar if there is a rollbar key and the app is in production if (($this->app->environment('production')) && (config('logging.channels.rollbar.access_token'))) { $this->app->register(\Rollbar\Laravel\RollbarServiceProvider::class); - } - - // Only load dusk's service provider if the app is in local or develop mode - if ($this->app->environment(['local', 'develop'])) { - $this->app->register(\Laravel\Dusk\DuskServiceProvider::class); - } + } $this->app->singleton('ArieTimmerman\Laravel\SCIMServer\SCIMConfig', SnipeSCIMConfig::class); // this overrides the default SCIM configuration with our own diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index f1e817ca16..9d493e85bb 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -146,6 +146,11 @@ class AuthServiceProvider extends ServiceProvider } }); + Gate::define('assets.view.encrypted_custom_fields', function ($user) { + if($user->hasAccess('assets.view.encrypted_custom_fields')){ + return true; + } + }); // ----------------------------------------- // Reports diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php index 371fc234de..41dd80b4fc 100644 --- a/app/Providers/SettingsServiceProvider.php +++ b/app/Providers/SettingsServiceProvider.php @@ -33,18 +33,33 @@ class SettingsServiceProvider extends ServiceProvider // 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'); + $int_limit = intval(request('limit')); - if ((abs(intval(request('limit'))) > 0) && (abs(request('limit')) <= config('app.max_results'))) { - $limit = abs(request('limit')); + if ((abs($int_limit) > 0) && ($int_limit <= config('app.max_results'))) { + $limit = abs($int_limit); } - \Log::debug('Max in env: '.config('app.max_results')); - \Log::debug('Original requested limit: '.request('limit')); - \Log::debug('Modified limit: '.$limit); - \Log::debug('------------------------------'); + +// \Log::debug('Max in env: '.config('app.max_results')); +// \Log::debug('Original requested limit: '.request('limit')); +// \Log::debug('Int limit: '.$int_limit); +// \Log::debug('Modified limit: '.$limit); +// \Log::debug('------------------------------'); + return $limit; }); + // Make sure the offset is actually set and is an integer + \App::singleton('api_offset_value', function () { + $offset = intval(request('offset')); +// \Log::debug('Original requested offset: '.request('offset')); +// \Log::debug('Modified offset: '.$offset); +// \Log::debug('------------------------------'); + + + return $offset; + }); + /** * Set some common variables so that they're globally available. diff --git a/app/Providers/ValidationServiceProvider.php b/app/Providers/ValidationServiceProvider.php index d7a3c03778..50468c8d72 100644 --- a/app/Providers/ValidationServiceProvider.php +++ b/app/Providers/ValidationServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use App\Models\Department; +use App\Models\Setting; use DB; use Illuminate\Support\ServiceProvider; use Illuminate\Validation\Rule; @@ -45,30 +46,87 @@ class ValidationServiceProvider extends ServiceProvider return $validator->passes(); }); - // Unique only if undeleted - // This works around the use case where multiple deleted items have the same unique attribute. - // (I think this is a bug in Laravel's validator?) + + /** + * Unique only if undeleted. + * + * This works around the use case where multiple deleted items have the same unique attribute. + * (I think this is a bug in Laravel's validator?) + * + * $attribute is the FIELDNAME you're checking against + * $value is the VALUE of the item you're checking against the existing values in the fieldname + * $parameters[0] is the TABLE NAME you're querying + * $parameters[1] is the ID of the item you're querying - this makes it work on saving, checkout, etc, + * since it defaults to 0 if there is no item created yet (new item), but populates the ID if editing + * + * The UniqueUndeletedTrait prefills these parameters, so you can just use + * `unique_undeleted:table,fieldname` in your rules out of the box + */ Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) { + if (count($parameters)) { - $count = DB::table($parameters[0])->select('id')->where($attribute, '=', $value)->whereNull('deleted_at')->where('id', '!=', $parameters[1])->count(); + + // This is a bit of a shim, but serial doesn't have any other rules around it other than that it's nullable + if (($parameters[0]=='assets') && ($attribute == 'serial') && (Setting::getSettings()->unique_serial != '1')) { + return true; + } + + $count = DB::table($parameters[0]) + ->select('id') + ->where($attribute, '=', $value) + ->whereNull('deleted_at') + ->where('id', '!=', $parameters[1])->count(); + + return $count < 1; + } + }); + + /** + * Unique if undeleted for two columns + * + * Same as unique_undeleted but taking the combination of two columns as unique constrain. + * This uses the Validator::replacer('two_column_unique_undeleted') below for nicer translations. + * + * $parameters[0] - the name of the first table we're looking at + * $parameters[1] - the ID (this will be 0 on new creations) + * $parameters[2] - the name of the second table we're looking at + * $parameters[3] - the value that the request is passing for the second table we're + * checking for uniqueness across + * + */ + Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) { + if (count($parameters)) { + $count = DB::table($parameters[0]) + ->select('id')->where($attribute, '=', $value) + ->whereNull('deleted_at') + ->where('id', '!=', $parameters[1]) + ->where($parameters[2], $parameters[3])->count(); return $count < 1; } }); - // Unique if undeleted for two columns - // Same as unique_undeleted but taking the combination of two columns as unique constrain. - Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) { - if (count($parameters)) { - $count = DB::table($parameters[0]) - ->select('id')->where($attribute, '=', $value) - ->whereNull('deleted_at') - ->where('id', '!=', $parameters[1]) - ->where($parameters[2], $parameters[3])->count(); - return $count < 1; - } - }); + /** + * This is the validator replace static method that allows us to pass the $parameters of the table names + * into the translation string in validation.two_column_unique_undeleted for two_column_unique_undeleted + * validation messages. + * + * This is invoked automatically by Validator::extend('two_column_unique_undeleted') above and + * produces a translation like: "The name value must be unique across categories and category type." + * + * The $parameters passed coincide with the ones the two_column_unique_undeleted custom validator above + * uses, so $parameter[0] is the first table and so $parameter[2] is the second table. + */ + Validator::replacer('two_column_unique_undeleted', function($message, $attribute, $rule, $parameters) { + $message = str_replace(':table1', $parameters[0], $message); + $message = str_replace(':table2', $parameters[2], $message); + + // Change underscores to spaces for a friendlier display + $message = str_replace('_', ' ', $message); + return $message; + }); + // Prevent circular references // @@ -232,6 +290,10 @@ class ValidationServiceProvider extends ServiceProvider return true; } }); + + Validator::extend('not_array', function ($attribute, $value, $parameters, $validator) { + return !is_array($value); + }); } /** diff --git a/app/View/Label.php b/app/View/Label.php new file mode 100644 index 0000000000..83184e4b04 --- /dev/null +++ b/app/View/Label.php @@ -0,0 +1,219 @@ +data = new Collection(); + } + + /** + * Render the PDF label. + * + * @param callable|null $callback + */ + public function render(callable $callback = null) + { + $settings = $this->data->get('settings'); + $assets = $this->data->get('assets'); + $offset = $this->data->get('offset'); + $template = $this->data->get('template'); + + // If disabled, pass to legacy view + if ((!$settings->label2_enable) && (!$template)) { + return view('hardware/labels') + ->with('assets', $assets) + ->with('settings', $settings) + ->with('bulkedit', $this->data->get('bulkedit')) + ->with('count', $this->data->get('count')); + } + + // If a specific template was set, use it, otherwise fall back to default + if (empty($template)) { + $template = LabelModel::find($settings->label2_template); + } elseif (is_string($template)) { + $template = LabelModel::find($template); + } + + $template->validate(); + + $pdf = new TCPDF( + $template->getOrientation(), + $template->getUnit(), + [ $template->getWidth(), $template->getHeight() ] + ); + + // Reset parameters + $pdf->SetPrintHeader(false); + $pdf->SetPrintFooter(false); + $pdf->SetAutoPageBreak(false); + $pdf->SetMargins(0, 0, null, true); + $pdf->SetCellMargins(0, 0, 0, 0); + $pdf->SetCellPaddings(0, 0, 0, 0); + $pdf->setCreator('Snipe-IT'); + $pdf->SetSubject('Asset Labels'); + $template->preparePDF($pdf); + + // Get fields from settings + $fieldDefinitions = collect(explode(';', $settings->label2_fields)) + ->filter(fn($fieldString) => !empty($fieldString)) + ->map(fn($fieldString) => Field::fromString($fieldString)); + + // Prepare data + $data = $assets + ->map(function ($asset) use ($template, $settings, $fieldDefinitions) { + + $assetData = new Collection(); + + $assetData->put('asset', $asset); + $assetData->put('id', $asset->id); + $assetData->put('tag', $asset->asset_tag); + + if ($template->getSupportTitle()) { + + if ($asset->company && !empty($settings->label2_title)) { + $title = str_replace('{COMPANY}', $asset->company->name, $settings->label2_title); + $settings->qr_text; + $assetData->put('title', $title); + } + } + + if ($template->getSupportLogo()) { + + $logo = null; + + // Should we use the assets assigned company logo? (A.K.A. "Is `Labels > Use Asset Logo` enabled?"), and do we have a company logo? + if ($settings->label2_asset_logo && $asset->company && $asset->company->image!='') { + $logo = Storage::disk('public')->path('companies/'.e($asset->company->image)); + } elseif (!empty($settings->label_logo)) { + // Use the general site label logo, if available + $logo = Storage::disk('public')->path('/'.e($settings->label_logo)); + } + + if (!empty($logo)) { + $assetData->put('logo', $logo); + } + } + + if ($template->getSupport1DBarcode()) { + $barcode1DType = $settings->label2_1d_type; + $barcode1DType = ($barcode1DType == 'default') ? + (($settings->alt_barcode_enabled) ? $settings->alt_barcode : null) : + $barcode1DType; + if ($barcode1DType != 'none') { + $assetData->put('barcode1d', (object)[ + 'type' => $barcode1DType, + 'content' => $asset->asset_tag, + ]); + } + } + + if ($template->getSupport2DBarcode()) { + $barcode2DType = $settings->label2_2d_type; + $barcode2DType = ($barcode2DType == 'default') ? + $settings->barcode_type : + $barcode2DType; + if (($barcode2DType != 'none') && (!is_null($barcode2DType))) { + switch ($settings->label2_2d_target) { + case 'ht_tag': $barcode2DTarget = route('ht/assetTag', $asset->asset_tag); break; + case 'hardware_id': + default: $barcode2DTarget = route('hardware.show', $asset->id); break; + } + $assetData->put('barcode2d', (object)[ + 'type' => $barcode2DType, + 'content' => $barcode2DTarget, + ]); + } + } + + $fields = $fieldDefinitions + ->map(fn($field) => $field->toArray($asset)) + ->filter(fn($field) => $field != null) + ->reduce(function($myFields, $field) { + // Remove Duplicates + $toAdd = $field + ->filter(fn($o) => !$myFields->contains('dataSource', $o['dataSource'])) + ->first(); + + return $toAdd ? $myFields->push($toAdd) : $myFields; + }, new Collection()); + + $assetData->put('fields', $fields->take($template->getSupportFields())); + + return $assetData; + }); + + if ($template instanceof Sheet) { + $template->setLabelIndexOffset($offset ?? 0); + } + $template->writeAll($pdf, $data); + + $filename = $assets->count() > 1 ? 'assets.pdf' : $assets->first()->asset_tag.'.pdf'; + $pdf->Output($filename, 'I'); + } + + /** + * Add a piece of data. + * + * @param string|array $key + * @param mixed $value + * @return $this + */ + public function with($key, $value = null) + { + $this->data->put($key, $value); + return $this; + } + + /** + * Get the array of view data. + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Get the name of the view. + * + * @return string + */ + public function name() + { + return $this->getName(); + } + + /** + * Get the name of the view. + * + * @return string + */ + public function getName() + { + return self::NAME; + } + +} \ No newline at end of file diff --git a/composer.json b/composer.json index a66aa22df0..020b2f9ca7 100644 --- a/composer.json +++ b/composer.json @@ -46,10 +46,11 @@ "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", - "lcobucci/clock": "1.2.0|2.0.0", + "lcobucci/clock": "^1.2.0|^2.0.0", "lcobucci/jwt": "^3.4.5|^4.0.4", "league/csv": "^9.7", "league/flysystem-aws-s3-v3": "^1.0", @@ -69,12 +70,13 @@ "spatie/laravel-backup": "^6.16", "symfony/polyfill-mbstring": "^1.22", "tecnickcom/tc-lib-barcode": "^1.15", + "tecnickcom/tcpdf": "^6.5", "unicodeveloper/laravel-password": "^1.0", "watson/validating": "^6.1" }, "require-dev": { + "brianium/paratest": "^6.6", "fakerphp/faker": "^1.16", - "laravel/dusk": "^6.25", "mockery/mockery": "^1.4", "nunomaduro/larastan": "^1.0", "nunomaduro/phpinsights": "^2.7", @@ -83,7 +85,7 @@ "squizlabs/php_codesniffer": "^3.5", "symfony/css-selector": "^4.4", "symfony/dom-crawler": "^4.4", - "vimeo/psalm": "^5.6" + "vimeo/psalm": "^5.13" }, "extra": { "laravel": { @@ -104,7 +106,6 @@ }, "autoload-dev": { "classmap": [ - "tests/DuskTestCase.php", "tests/TestCase.php" ], "psr-4": { diff --git a/composer.lock b/composer.lock index 620c7c6855..7ca88da10f 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": "c0444af6de1d16a70c7e5520db745170", + "content-hash": "348f96db24a0f8dfb595ee38b38b34eb", "packages": [ { "name": "alek13/slack", @@ -78,25 +78,25 @@ "source": { "type": "git", "url": "https://github.com/grokability/laravel-scim-server.git", - "reference": "9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419" + "reference": "dda6dfb60d70fb6cca4b8d4ce1c5f4c19deaab2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419", - "reference": "9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419", + "url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/dda6dfb60d70fb6cca4b8d4ce1c5f4c19deaab2d", + "reference": "dda6dfb60d70fb6cca4b8d4ce1c5f4c19deaab2d", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0", - "illuminate/database": "^6.0|^7.0|^8.0|^9.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", "php": "^7.0|^8.0", "tmilos/scim-filter-parser": "^1.3", "tmilos/scim-schema": "^0.1.0" }, "require-dev": { "laravel/legacy-factories": "*", - "orchestra/testbench": "^5.0|^6.0" + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0" }, "default-branch": true, "type": "library", @@ -133,7 +133,7 @@ "support": { "source": "https://github.com/grokability/laravel-scim-server/tree/master" }, - "time": "2023-01-12T00:32:07+00:00" + "time": "2023-09-07T16:45:26+00:00" }, { "name": "asm89/stack-cors", @@ -2948,16 +2948,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.4.0", + "version": "2.4.5", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "13388f00956b1503577598873fffb5ae994b5737" + "reference": "0454e12ef0cd597ccd2adb036f7bda4e7fface66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737", - "reference": "13388f00956b1503577598873fffb5ae994b5737", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/0454e12ef0cd597ccd2adb036f7bda4e7fface66", + "reference": "0454e12ef0cd597ccd2adb036f7bda4e7fface66", "shasum": "" }, "require": { @@ -2971,17 +2971,18 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.1", "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.8 || ^9.3.10" + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.4-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { @@ -3043,7 +3044,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.0" + "source": "https://github.com/guzzle/psr7/tree/2.4.5" }, "funding": [ { @@ -3059,7 +3060,7 @@ "type": "tidelift" } ], - "time": "2022-06-20T21:43:11+00:00" + "time": "2023-04-17T16:00:45+00:00" }, { "name": "intervention/image", @@ -3605,6 +3606,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", @@ -4534,6 +4604,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", @@ -5985,16 +6131,16 @@ }, { "name": "nyholm/psr7", - "version": "1.5.1", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a" + "reference": "e874c8c4286a1e010fb4f385f3a55ac56a05cc93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/e874c8c4286a1e010fb4f385f3a55ac56a05cc93", + "reference": "e874c8c4286a1e010fb4f385f3a55ac56a05cc93", "shasum": "" }, "require": { @@ -6004,6 +6150,7 @@ "psr/http-message": "^1.0" }, "provide": { + "php-http/message-factory-implementation": "1.0", "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, @@ -6016,7 +6163,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.6-dev" } }, "autoload": { @@ -6046,7 +6193,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.5.1" + "source": "https://github.com/Nyholm/psr7/tree/1.6.1" }, "funding": [ { @@ -6058,7 +6205,7 @@ "type": "github" } ], - "time": "2022-06-22T07:13:36+00:00" + "time": "2023-04-17T16:03:48+00:00" }, { "name": "onelogin/php-saml", @@ -11233,6 +11380,78 @@ ], "time": "2022-06-14T09:09:40+00:00" }, + { + "name": "tecnickcom/tcpdf", + "version": "6.6.2", + "source": { + "type": "git", + "url": "https://github.com/tecnickcom/TCPDF.git", + "reference": "e3cffc9bcbc76e89e167e9eb0bbda0cab7518459" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/e3cffc9bcbc76e89e167e9eb0bbda0cab7518459", + "reference": "e3cffc9bcbc76e89e167e9eb0bbda0cab7518459", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "config", + "include", + "tcpdf.php", + "tcpdf_parser.php", + "tcpdf_import.php", + "tcpdf_barcodes_1d.php", + "tcpdf_barcodes_2d.php", + "include/tcpdf_colors.php", + "include/tcpdf_filters.php", + "include/tcpdf_font_data.php", + "include/tcpdf_fonts.php", + "include/tcpdf_images.php", + "include/tcpdf_static.php", + "include/barcodes/datamatrix.php", + "include/barcodes/pdf417.php", + "include/barcodes/qrcode.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-only" + ], + "authors": [ + { + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "role": "lead" + } + ], + "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", + "homepage": "http://www.tcpdf.org/", + "keywords": [ + "PDFD32000-2008", + "TCPDF", + "barcodes", + "datamatrix", + "pdf", + "pdf417", + "qrcode" + ], + "support": { + "issues": "https://github.com/tecnickcom/TCPDF/issues", + "source": "https://github.com/tecnickcom/TCPDF/tree/6.6.2" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project", + "type": "custom" + } + ], + "time": "2022-12-17T10:28:59+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "2.2.4", @@ -11978,17 +12197,176 @@ "time": "2021-03-30T17:13:30+00:00" }, { - "name": "composer/ca-bundle", - "version": "1.3.5", + "name": "brianium/paratest", + "version": "v6.6.2", "source": { "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd" + "url": "https://github.com/paratestphp/paratest.git", + "reference": "5249af4e25e79da66d1ec3b54b474047999c10b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/74780ccf8c19d6acb8d65c5f39cd72110e132bbd", - "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/5249af4e25e79da66d1ec3b54b474047999c10b8", + "reference": "5249af4e25e79da66d1ec3b54b474047999c10b8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "jean85/pretty-package-versions": "^2.0.5", + "php": "^7.3 || ^8.0", + "phpunit/php-code-coverage": "^9.2.15", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-timer": "^5.0.3", + "phpunit/phpunit": "^9.5.21", + "sebastian/environment": "^5.1.4", + "symfony/console": "^5.4.9 || ^6.1.2", + "symfony/polyfill-php80": "^v1.26.0", + "symfony/process": "^5.4.8 || ^6.1.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "infection/infection": "^0.26.13", + "malukenho/mcbumpface": "^1.1.5", + "squizlabs/php_codesniffer": "^3.7.1", + "symfony/filesystem": "^5.4.9 || ^6.1.0", + "vimeo/psalm": "^4.26.0" + }, + "bin": [ + "bin/paratest", + "bin/paratest.bat", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v6.6.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2022-08-22T10:45:51+00:00" + }, + { + "name": "cmgmyr/phploc", + "version": "8.0.2", + "source": { + "type": "git", + "url": "https://github.com/cmgmyr/phploc.git", + "reference": "35e308033e02264a59cb1b56cc2abb1a22483ca8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cmgmyr/phploc/zipball/35e308033e02264a59cb1b56cc2abb1a22483ca8", + "reference": "35e308033e02264a59cb1b56cc2abb1a22483ca8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "php": "^7.4 || ^8.0", + "phpunit/php-file-iterator": "^3.0|^4.0", + "sebastian/cli-parser": "^1.0|^2.0", + "sebastian/version": "^3.0|^4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpunit/phpunit": "^9.0|^10.0", + "vimeo/psalm": "^5.7" + }, + "bin": [ + "phploc" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Chris Gmyr", + "email": "cmgmyr@gmail.com", + "role": "lead" + } + ], + "description": "A tool for quickly measuring the size of a PHP project.", + "homepage": "https://github.com/cmgmyr/phploc", + "support": { + "issues": "https://github.com/cmgmyr/phploc/issues", + "source": "https://github.com/cmgmyr/phploc/tree/8.0.2" + }, + "funding": [ + { + "url": "https://github.com/cmgmyr", + "type": "github" + } + ], + "time": "2023-03-19T10:37:20+00:00" + }, + { + "name": "composer/ca-bundle", + "version": "1.3.6", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "90d087e988ff194065333d16bc5cf649872d9cdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/90d087e988ff194065333d16bc5cf649872d9cdb", + "reference": "90d087e988ff194065333d16bc5cf649872d9cdb", "shasum": "" }, "require": { @@ -12035,7 +12413,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.3.5" + "source": "https://github.com/composer/ca-bundle/tree/1.3.6" }, "funding": [ { @@ -12051,7 +12429,7 @@ "type": "tidelift" } ], - "time": "2023-01-11T08:27:00+00:00" + "time": "2023-06-06T12:02:59+00:00" }, { "name": "composer/composer", @@ -12232,79 +12610,6 @@ ], "time": "2021-04-07T13:37:33+00:00" }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, { "name": "composer/pcre", "version": "3.1.0", @@ -12888,16 +13193,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "0.4.1", + "version": "0.5.1", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2" + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623", "shasum": "" }, "require": { @@ -12937,7 +13242,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/0.4.1" + "source": "https://github.com/theofidry/cpu-core-counter/tree/0.5.1" }, "funding": [ { @@ -12945,7 +13250,7 @@ "type": "github" } ], - "time": "2022-12-16T22:01:02+00:00" + "time": "2022-12-24T12:35:10+00:00" }, { "name": "friendsofphp/php-cs-fixer", @@ -13087,6 +13392,65 @@ }, "time": "2020-07-09T08:09:16+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, { "name": "justinrainbow/json-schema", "version": "5.2.12", @@ -13157,79 +13521,6 @@ }, "time": "2022-04-13T08:02:27+00:00" }, - { - "name": "laravel/dusk", - "version": "v6.25.2", - "source": { - "type": "git", - "url": "https://github.com/laravel/dusk.git", - "reference": "25a595ac3dc82089a91af10dd23b0d58fd3f6d0b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/dusk/zipball/25a595ac3dc82089a91af10dd23b0d58fd3f6d0b", - "reference": "25a595ac3dc82089a91af10dd23b0d58fd3f6d0b", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-zip": "*", - "illuminate/console": "^6.0|^7.0|^8.0|^9.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0", - "nesbot/carbon": "^2.0", - "php": "^7.2|^8.0", - "php-webdriver/webdriver": "^1.9.0", - "symfony/console": "^4.3|^5.0|^6.0", - "symfony/finder": "^4.3|^5.0|^6.0", - "symfony/process": "^4.3|^5.0|^6.0", - "vlucas/phpdotenv": "^3.0|^4.0|^5.2" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "orchestra/testbench": "^4.16|^5.17.1|^6.12.1|^7.0", - "phpunit/phpunit": "^7.5.15|^8.4|^9.0" - }, - "suggest": { - "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Dusk\\DuskServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Dusk\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", - "keywords": [ - "laravel", - "testing", - "webdriver" - ], - "support": { - "issues": "https://github.com/laravel/dusk/issues", - "source": "https://github.com/laravel/dusk/tree/v6.25.2" - }, - "time": "2022-09-29T09:37:07+00:00" - }, { "name": "league/container", "version": "4.2.0", @@ -13445,16 +13736,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.1.0", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", "shasum": "" }, "require": { @@ -13490,9 +13781,9 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" }, - "time": "2022-12-08T20:46:14+00:00" + "time": "2023-04-09T17:37:40+00:00" }, { "name": "nunomaduro/larastan", @@ -13594,19 +13885,20 @@ }, { "name": "nunomaduro/phpinsights", - "version": "v2.7.0", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/phpinsights.git", - "reference": "3a2f02cadcd1be920eed19814798810944698c51" + "reference": "a701b7acfda9940ef0140c7276319df9026824c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/phpinsights/zipball/3a2f02cadcd1be920eed19814798810944698c51", - "reference": "3a2f02cadcd1be920eed19814798810944698c51", + "url": "https://api.github.com/repos/nunomaduro/phpinsights/zipball/a701b7acfda9940ef0140c7276319df9026824c4", + "reference": "a701b7acfda9940ef0140c7276319df9026824c4", "shasum": "" }, "require": { + "cmgmyr/phploc": "^8.0", "composer/semver": "^3.3", "ext-iconv": "*", "ext-json": "*", @@ -13615,29 +13907,28 @@ "friendsofphp/php-cs-fixer": "^3.0.0", "justinrainbow/json-schema": "^5.1", "league/container": "^3.2|^4.2", - "php": "^7.4 || ^8.0", + "php": "^7.4 || ^8.0 || ^8.1", "php-parallel-lint/php-parallel-lint": "^1.3", - "phploc/phploc": "^5.0|^6.0|^7.0", "psr/container": "^1.0|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "sebastian/diff": "^4.0", + "sebastian/diff": "^4.0|^5.0", "slevomat/coding-standard": "^7.0.8|^8.0", "squizlabs/php_codesniffer": "^3.5", "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/console": "^4.2|^5.0|^6.0", - "symfony/finder": "^4.2|^5.0|^6.0", - "symfony/http-client": "^4.3|^5.0|^6.0", + "symfony/console": "^4.2.12|^5.0|^6.0", + "symfony/finder": "^4.2.12|^5.0|^6.0", + "symfony/http-client": "^4.3.8|^5.0|^6.0", "symfony/process": "^5.4|^6.0" }, "require-dev": { "ergebnis/phpstan-rules": "^0.15.0", - "illuminate/console": "^5.8|^6.0|^7.0|^8.0|^9.0", - "illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0", + "illuminate/console": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0", "mockery/mockery": "^1.0", "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^8.0|^9.0", + "phpunit/phpunit": "^8.0|^9.0|^10.0", "rector/rector": "0.11.56", - "symfony/var-dumper": "^4.2|^5.0|^6.0", + "symfony/var-dumper": "^4.2.12|^5.0|^6.0", "thecodingmachine/phpstan-strict-rules": "^0.12.0" }, "suggest": { @@ -13680,7 +13971,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/phpinsights/issues", - "source": "https://github.com/nunomaduro/phpinsights/tree/v2.7.0" + "source": "https://github.com/nunomaduro/phpinsights/tree/v2.8.0" }, "funding": [ { @@ -13696,7 +13987,7 @@ "type": "github" } ], - "time": "2023-01-30T20:28:59+00:00" + "time": "2023-03-18T18:38:03+00:00" }, { "name": "phar-io/manifest", @@ -13866,151 +14157,26 @@ }, "time": "2022-02-21T12:50:22+00:00" }, - { - "name": "php-webdriver/webdriver", - "version": "1.12.1", - "source": { - "type": "git", - "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "b27ddf458d273c7d4602106fcaf978aa0b7fe15a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/b27ddf458d273c7d4602106fcaf978aa0b7fe15a", - "reference": "b27ddf458d273c7d4602106fcaf978aa0b7fe15a", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0 || ^8.0", - "symfony/polyfill-mbstring": "^1.12", - "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0 || ^6.0" - }, - "replace": { - "facebook/webdriver": "*" - }, - "require-dev": { - "ondram/ci-detector": "^2.1 || ^3.5 || ^4.0", - "php-coveralls/php-coveralls": "^2.4", - "php-mock/php-mock-phpunit": "^1.1 || ^2.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9", - "squizlabs/php_codesniffer": "^3.5", - "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "autoload": { - "files": [ - "lib/Exception/TimeoutException.php" - ], - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", - "homepage": "https://github.com/php-webdriver/php-webdriver", - "keywords": [ - "Chromedriver", - "geckodriver", - "php", - "selenium", - "webdriver" - ], - "support": { - "issues": "https://github.com/php-webdriver/php-webdriver/issues", - "source": "https://github.com/php-webdriver/php-webdriver/tree/1.12.1" - }, - "time": "2022-05-03T12:16:34+00:00" - }, - { - "name": "phploc/phploc", - "version": "7.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phploc.git", - "reference": "af0d5fc84f3f7725513ba59cdcbe670ac2a4532a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/af0d5fc84f3f7725513ba59cdcbe670ac2a4532a", - "reference": "af0d5fc84f3f7725513ba59cdcbe670ac2a4532a", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0", - "sebastian/cli-parser": "^1.0", - "sebastian/version": "^3.0" - }, - "bin": [ - "phploc" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "A tool for quickly measuring the size of a PHP project.", - "homepage": "https://github.com/sebastianbergmann/phploc", - "support": { - "issues": "https://github.com/sebastianbergmann/phploc/issues", - "source": "https://github.com/sebastianbergmann/phploc/tree/7.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2020-12-07T05:51:20+00:00" - }, { "name": "phpstan/phpdoc-parser", - "version": "1.15.3", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "61800f71a5526081d1b5633766aa88341f1ade76" + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76", - "reference": "61800f71a5526081d1b5633766aa88341f1ade76", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -14034,9 +14200,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" }, - "time": "2022-12-20T20:56:55+00:00" + "time": "2023-06-29T20:46:06+00:00" }, { "name": "phpstan/phpstan", @@ -14579,23 +14745,23 @@ }, { "name": "react/promise", - "version": "v2.9.0", + "version": "v2.10.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910" + "reference": "f913fb8cceba1e6644b7b90c4bfb678ed8a3ef38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/234f8fd1023c9158e2314fa9d7d0e6a83db42910", - "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f913fb8cceba1e6644b7b90c4bfb678ed8a3ef38", + "reference": "f913fb8cceba1e6644b7b90c4bfb678ed8a3ef38", "shasum": "" }, "require": { "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { @@ -14639,19 +14805,15 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.9.0" + "source": "https://github.com/reactphp/promise/tree/v2.10.0" }, "funding": [ { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2022-02-11T10:27:51+00:00" + "time": "2023-05-02T15:15:43+00:00" }, { "name": "sebastian/cli-parser", @@ -15339,16 +15501,16 @@ }, { "name": "seld/jsonlint", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "4211420d25eba80712bff236a98960ef68b866b7" + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", - "reference": "4211420d25eba80712bff236a98960ef68b866b7", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", "shasum": "" }, "require": { @@ -15387,7 +15549,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" }, "funding": [ { @@ -15399,7 +15561,7 @@ "type": "tidelift" } ], - "time": "2022-04-01T13:37:23+00:00" + "time": "2023-05-11T13:16:46+00:00" }, { "name": "seld/phar-utils", @@ -15451,32 +15613,32 @@ }, { "name": "slevomat/coding-standard", - "version": "8.8.0", + "version": "8.13.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89" + "reference": "a13c15e20f2d307a1ca8dec5313ec462a4466470" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/59e25146a4ef0a7b194c5bc55b32dd414345db89", - "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/a13c15e20f2d307a1ca8dec5313ec462a4466470", + "reference": "a13c15e20f2d307a1ca8dec5313ec462a4466470", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": ">=1.15.2 <1.16.0", + "phpstan/phpdoc-parser": "^1.22.0", "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.9.6", - "phpstan/phpstan-deprecation-rules": "1.1.1", - "phpstan/phpstan-phpunit": "1.0.0|1.3.3", - "phpstan/phpstan-strict-rules": "1.4.4", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.27" + "phpstan/phpstan": "1.10.21", + "phpstan/phpstan-deprecation-rules": "1.1.3", + "phpstan/phpstan-phpunit": "1.3.13", + "phpstan/phpstan-strict-rules": "1.5.1", + "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.2.2" }, "type": "phpcodesniffer-standard", "extra": { @@ -15486,7 +15648,7 @@ }, "autoload": { "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard" + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" } }, "notification-url": "https://packagist.org/downloads/", @@ -15500,7 +15662,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.8.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.13.1" }, "funding": [ { @@ -15512,7 +15674,7 @@ "type": "tidelift" } ], - "time": "2023-01-09T10:46:13+00:00" + "time": "2023-06-25T12:52:34+00:00" }, { "name": "spatie/array-to-xml", @@ -15580,16 +15742,16 @@ }, { "name": "symfony/cache", - "version": "v5.4.19", + "version": "v5.4.25", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "e9147c89fdfdc5d5ef798bb7193f23726ad609f5" + "reference": "e2013521c0f07473ae69a01fce0af78fc3ec0f23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/e9147c89fdfdc5d5ef798bb7193f23726ad609f5", - "reference": "e9147c89fdfdc5d5ef798bb7193f23726ad609f5", + "url": "https://api.github.com/repos/symfony/cache/zipball/e2013521c0f07473ae69a01fce0af78fc3ec0f23", + "reference": "e2013521c0f07473ae69a01fce0af78fc3ec0f23", "shasum": "" }, "require": { @@ -15657,7 +15819,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v5.4.19" + "source": "https://github.com/symfony/cache/tree/v5.4.25" }, "funding": [ { @@ -15673,7 +15835,7 @@ "type": "tidelift" } ], - "time": "2023-01-19T09:49:58+00:00" + "time": "2023-06-22T08:06:06+00:00" }, { "name": "symfony/cache-contracts", @@ -15830,22 +15992,23 @@ }, { "name": "symfony/filesystem", - "version": "v6.0.19", + "version": "v5.4.25", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" + "reference": "0ce3a62c9579a53358d3a7eb6b3dfb79789a6364" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", - "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/0ce3a62c9579a53358d3a7eb6b3dfb79789a6364", + "reference": "0ce3a62c9579a53358d3a7eb6b3dfb79789a6364", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -15873,7 +16036,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.19" + "source": "https://github.com/symfony/filesystem/tree/v5.4.25" }, "funding": [ { @@ -15889,33 +16052,36 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2023-05-31T13:04:02+00:00" }, { "name": "symfony/http-client", - "version": "v6.0.20", + "version": "v5.4.25", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "541c04560da1875f62c963c3aab6ea12a7314e11" + "reference": "ccbb572627466f03a3d7aa1b23483787f5969afc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/541c04560da1875f62c963c3aab6ea12a7314e11", - "reference": "541c04560da1875f62c963c3aab6ea12a7314e11", + "url": "https://api.github.com/repos/symfony/http-client/zipball/ccbb572627466f03a3d7aa1b23483787f5969afc", + "reference": "ccbb572627466f03a3d7aa1b23483787f5969afc", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=7.2.5", "psr/log": "^1|^2|^3", - "symfony/http-client-contracts": "^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-client-contracts": "^2.4", + "symfony/polyfill-php73": "^1.11", + "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.0|^2|^3" }, "provide": { "php-http/async-client-implementation": "*", "php-http/client-implementation": "*", "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" + "symfony/http-client-implementation": "2.4" }, "require-dev": { "amphp/amp": "^2.5", @@ -15925,11 +16091,12 @@ "guzzlehttp/promises": "^1.4", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", + "php-http/message-factory": "^1.0", "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0" + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0" }, "type": "library", "autoload": { @@ -15956,8 +16123,11 @@ ], "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", + "keywords": [ + "http" + ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.0.20" + "source": "https://github.com/symfony/http-client/tree/v5.4.25" }, "funding": [ { @@ -15973,24 +16143,24 @@ "type": "tidelift" } ], - "time": "2023-01-30T15:41:07+00:00" + "time": "2023-06-21T14:44:30+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.0.2", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "4184b9b63af1edaf35b6a7974c6f1f9f33294129" + "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4184b9b63af1edaf35b6a7974c6f1f9f33294129", - "reference": "4184b9b63af1edaf35b6a7974c6f1f9f33294129", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", + "reference": "ba6a9f0e8f3edd190520ee3b9a958596b6ca2e70", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=7.2.5" }, "suggest": { "symfony/http-client-implementation": "" @@ -15998,7 +16168,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -16035,7 +16205,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.2" }, "funding": [ { @@ -16051,25 +16221,27 @@ "type": "tidelift" } ], - "time": "2022-04-12T16:11:42+00:00" + "time": "2022-04-12T15:48:08+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.0.19", + "version": "v5.4.21", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3" + "reference": "4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6a180d1c45e0d9797470ca9eb46215692de00fa3", - "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9", + "reference": "4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -16102,7 +16274,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.0.19" + "source": "https://github.com/symfony/options-resolver/tree/v5.4.21" }, "funding": [ { @@ -16118,24 +16290,24 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-02-14T08:03:56+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.0.19", + "version": "v5.4.21", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "011e781839dd1d2eb8119f65ac516a530f60226d" + "reference": "f83692cd869a6f2391691d40a01e8acb89e76fee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/011e781839dd1d2eb8119f65ac516a530f60226d", - "reference": "011e781839dd1d2eb8119f65ac516a530f60226d", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f83692cd869a6f2391691d40a01e8acb89e76fee", + "reference": "f83692cd869a6f2391691d40a01e8acb89e76fee", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=7.2.5", "symfony/service-contracts": "^1|^2|^3" }, "type": "library", @@ -16164,7 +16336,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.0.19" + "source": "https://github.com/symfony/stopwatch/tree/v5.4.21" }, "funding": [ { @@ -16180,27 +16352,28 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-02-14T08:03:56+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.0.19", + "version": "v5.4.21", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "df56f53818c2d5d9f683f4ad2e365ba73a3b69d2" + "reference": "be74908a6942fdd331554b3cec27ff41b45ccad4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/df56f53818c2d5d9f683f4ad2e365ba73a3b69d2", - "reference": "df56f53818c2d5d9f683f4ad2e365ba73a3b69d2", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/be74908a6942fdd331554b3cec27ff41b45ccad4", + "reference": "be74908a6942fdd331554b3cec27ff41b45ccad4", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0" }, "type": "library", "autoload": { @@ -16236,7 +16409,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.0.19" + "source": "https://github.com/symfony/var-exporter/tree/v5.4.21" }, "funding": [ { @@ -16252,7 +16425,7 @@ "type": "tidelift" } ], - "time": "2023-01-13T08:34:10+00:00" + "time": "2023-02-21T19:46:44+00:00" }, { "name": "theseer/tokenizer", @@ -16306,22 +16479,22 @@ }, { "name": "vimeo/psalm", - "version": "5.6.0", + "version": "5.13.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5" + "reference": "086b94371304750d1c673315321a55d15fc59015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/e784128902dfe01d489c4123d69918a9f3c1eac5", - "reference": "e784128902dfe01d489c4123d69918a9f3c1eac5", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/086b94371304750d1c673315321a55d15fc59015", + "reference": "086b94371304750d1c673315321a55d15fc59015", "shasum": "" }, "require": { "amphp/amp": "^2.4.2", "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.10.0", + "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^2.0 || ^3.0", "dnoegel/php-xdg-base-dir": "^0.1.1", @@ -16334,12 +16507,12 @@ "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.1", "felixfbecker/language-server-protocol": "^1.5.2", - "fidry/cpu-core-counter": "^0.4.0", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.13", + "nikic/php-parser": "^4.14", "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", "sebastian/diff": "^4.0 || ^5.0", - "spatie/array-to-xml": "^2.17.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", "symfony/console": "^4.1.6 || ^5.0 || ^6.0", "symfony/filesystem": "^5.4 || ^6.0" }, @@ -16347,14 +16520,15 @@ "psalm/psalm": "self.version" }, "require-dev": { + "amphp/phpunit-util": "^2.0", "bamarni/composer-bin-plugin": "^1.4", - "brianium/paratest": "^6.0", + "brianium/paratest": "^6.9", "ext-curl": "*", "mockery/mockery": "^1.5", "nunomaduro/mock-final-classes": "^1.1", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpdoc-parser": "^1.6", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^9.6", "psalm/plugin-mockery": "^1.1", "psalm/plugin-phpunit": "^0.18", "slevomat/coding-standard": "^8.4", @@ -16400,13 +16574,14 @@ "keywords": [ "code", "inspection", - "php" + "php", + "static analysis" ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.6.0" + "source": "https://github.com/vimeo/psalm/tree/5.13.1" }, - "time": "2023-01-23T20:32:47+00:00" + "time": "2023-06-27T16:39:49+00:00" } ], "aliases": [], diff --git a/config/app.php b/config/app.php index 8833ea2ae7..cca6b85e57 100755 --- a/config/app.php +++ b/config/app.php @@ -216,7 +216,8 @@ return [ */ 'require_saml' => env('REQUIRE_SAML', false), - + + /* |-------------------------------------------------------------------------- | Demo Mode Lockdown @@ -238,7 +239,7 @@ return [ | */ - 'min_php' => '7.2.5', + 'min_php' => '7.4.0', /* @@ -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/auth.php b/config/auth.php index 7f580d68b1..1603c93337 100644 --- a/config/auth.php +++ b/config/auth.php @@ -132,4 +132,16 @@ return [ 'password_timeout' => env('PASSWORD_CONFIRM_TIMEOUT', 10800), + + /* + |-------------------------------------------------------------------------- + | Login form autocomplete + |-------------------------------------------------------------------------- + | + | Determine whether to include autocomplete="off" on the login form. Some users may want to disable + | autocomplete for compliance with security requirements. + | + */ + 'login_autocomplete' => env('LOGIN_AUTOCOMPLETE', false), + ]; diff --git a/config/backup.php b/config/backup.php index 479b1db6aa..b95129b7a6 100644 --- a/config/backup.php +++ b/config/backup.php @@ -35,6 +35,13 @@ return [ 'files' => [ + /* + * This path is used to make directories in resulting zip-file relative + * Set to false to include complete absolute path + * Example: base_path() + */ + 'relative_path' => base_path(), + /* * The list of directories and files that will be included in the backup. */ diff --git a/config/database.php b/config/database.php index 36440b2127..bb62cf0d3a 100755 --- a/config/database.php +++ b/config/database.php @@ -69,7 +69,7 @@ return [ 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), - 'port' => env('DB_PORT', '3306'), + 'port' => env('DB_PORT', 3306), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 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/permissions.php b/config/permissions.php index 0b65a4e26b..10c44a1896 100644 --- a/config/permissions.php +++ b/config/permissions.php @@ -106,6 +106,13 @@ return [ 'display' => true, ], + [ + 'permission' => 'assets.view.encrypted_custom_fields', + 'label' => 'View and Modify Encrypted Custom Fields', + 'note' => '', + 'display' => true, + ], + ], 'Accessories' => [ diff --git a/config/services.php b/config/services.php index de8c4ed71a..21acb6778a 100644 --- a/config/services.php +++ b/config/services.php @@ -25,6 +25,7 @@ return [ 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), ], 'mandrill' => [ diff --git a/config/session.php b/config/session.php index 688340c9e2..a47294a8cb 100644 --- a/config/session.php +++ b/config/session.php @@ -158,4 +158,20 @@ return [ 'secure' => env('SECURE_COOKIES', false), + /* + |-------------------------------------------------------------------------- + | Bootstrap Table Storage Type + |-------------------------------------------------------------------------- + | + | Set the storage that this Bootstrap Table will use. + | Valid options are: + | - cookieStorage + | - localStorage: use this if you have a LOT of custom fields and are getting a REQUEST TOO LARGE error + | - sessionStorage + | + | More info: https://bootstrap-table.com/docs/extensions/cookie/#cookiestorage + */ + + 'bs_table_storage' => env('BS_TABLE_STORAGE', 'cookieStorage'), + ]; diff --git a/config/version.php b/config/version.php index 4f60d60906..311cfdb336 100644 --- a/config/version.php +++ b/config/version.php @@ -1,10 +1,10 @@ 'v6.1.1-pre', - 'full_app_version' => 'v6.1.1-pre - build 10534-g609b1646e', - 'build_version' => '10534', + 'app_version' => 'v6.2.3', + 'full_app_version' => 'v6.2.3 - build 11936-gb47e734b3', + 'build_version' => '11936', 'prerelease_version' => '', - 'hash_version' => 'g609b1646e', - 'full_hash' => 'v6.1.1-pre-292-g609b1646e', + 'hash_version' => 'gb47e734b3', + 'full_hash' => 'v6.2.3-175-gb47e734b3', 'branch' => 'develop', ); \ No newline at end of file diff --git a/database/factories/ActionlogFactory.php b/database/factories/ActionlogFactory.php index c25fdcc70a..1a4007888c 100644 --- a/database/factories/ActionlogFactory.php +++ b/database/factories/ActionlogFactory.php @@ -38,7 +38,7 @@ class ActionlogFactory extends Factory { return $this->state(function () { $target = User::inRandomOrder()->first(); - $asset = Asset::RTD()->inRandomOrder()->first(); + $asset = Asset::inRandomOrder()->RTD()->first(); $asset->update( [ diff --git a/database/factories/AssetFactory.php b/database/factories/AssetFactory.php index cd3e0f8391..ccb6474e5a 100644 --- a/database/factories/AssetFactory.php +++ b/database/factories/AssetFactory.php @@ -8,6 +8,8 @@ use App\Models\Location; use App\Models\Statuslabel; use App\Models\Supplier; use App\Models\User; +use Carbon\Carbon; +use Carbon\CarbonImmutable; use Illuminate\Database\Eloquent\Factories\Factory; class AssetFactory extends Factory @@ -48,6 +50,18 @@ class AssetFactory extends Factory 'last_checkout' => null, ]; } + + + public function configure() + { + return $this->afterMaking(function (Asset $asset) { + // calculates the EOL date most of the time, but sometimes sets a random date so we have some explicits + // the explicit boolean gets set in the saving() method on the observer + $asset->asset_eol_date = $this->faker->boolean(5) + ? CarbonImmutable::parse($asset->purchase_date)->addMonths(rand(0, 20))->format('Y-m-d') + : CarbonImmutable::parse($asset->purchase_date)->addMonths($asset->model->eol)->format('Y-m-d'); + }); + } public function laptopMbp() { @@ -328,4 +342,14 @@ class AssetFactory extends Factory ]; }); } + + public function requestable() + { + return $this->state(['requestable' => true]); + } + + public function nonrequestable() + { + return $this->state(['requestable' => false]); + } } 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/CompanyFactory.php b/database/factories/CompanyFactory.php index bf4b7ce242..607822fef1 100644 --- a/database/factories/CompanyFactory.php +++ b/database/factories/CompanyFactory.php @@ -22,7 +22,7 @@ class CompanyFactory extends Factory public function definition() { return [ - 'name' => $this->faker->company(), + 'name' => $this->faker->unique()->company(), ]; } } diff --git a/database/factories/ConsumableFactory.php b/database/factories/ConsumableFactory.php index 5444999711..18a116418b 100644 --- a/database/factories/ConsumableFactory.php +++ b/database/factories/ConsumableFactory.php @@ -27,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), diff --git a/database/factories/CustomFieldFactory.php b/database/factories/CustomFieldFactory.php index adcca9cae1..9407f16b0a 100644 --- a/database/factories/CustomFieldFactory.php +++ b/database/factories/CustomFieldFactory.php @@ -22,10 +22,11 @@ class CustomFieldFactory extends Factory public function definition() { return [ - 'name' => $this->faker->catchPhrase(), + 'name' => $this->faker->unique()->catchPhrase(), 'format' => '', 'element' => 'text', 'auto_add_to_fieldsets' => '0', + 'show_in_requestable_list' => '0', ]; } @@ -66,6 +67,7 @@ class CustomFieldFactory extends Factory return [ 'name' => 'CPU', 'help_text' => 'The speed of the processor on this device.', + 'show_in_requestable_list' => '1', ]; }); } @@ -79,4 +81,28 @@ class CustomFieldFactory extends Factory ]; }); } + + public function testEncrypted() + { + return $this->state(function () { + return [ + 'name' => 'Test Encrypted', + 'field_encrypted' => '1', + 'help_text' => 'This is a sample encrypted field.', + ]; + }); + } + + public function testCheckbox() + { + return $this->state(function () { + return [ + 'name' => 'Test Checkbox', + 'help_text' => 'This is a sample checkbox.', + 'field_values' => "One\nTwo\nThree", + 'element' => 'checkbox', + ]; + }); + } + } diff --git a/database/factories/CustomFieldsetFactory.php b/database/factories/CustomFieldsetFactory.php index f369121c8f..e651b5c8d3 100644 --- a/database/factories/CustomFieldsetFactory.php +++ b/database/factories/CustomFieldsetFactory.php @@ -22,7 +22,7 @@ class CustomFieldsetFactory extends Factory public function definition() { return [ - 'name' => $this->faker->catchPhrase(), + 'name' => $this->faker->unique()->catchPhrase(), ]; } diff --git a/database/factories/DepreciationFactory.php b/database/factories/DepreciationFactory.php index 6a648d7ade..6359e2326b 100644 --- a/database/factories/DepreciationFactory.php +++ b/database/factories/DepreciationFactory.php @@ -23,7 +23,7 @@ class DepreciationFactory extends Factory public function definition() { return [ - 'name' => $this->faker->catchPhrase(), + 'name' => $this->faker->unique()->catchPhrase(), 'user_id' => User::factory()->superuser(), 'months' => 36, ]; diff --git a/database/factories/ManufacturerFactory.php b/database/factories/ManufacturerFactory.php index 4e736b8d8a..7d6892426d 100644 --- a/database/factories/ManufacturerFactory.php +++ b/database/factories/ManufacturerFactory.php @@ -23,7 +23,7 @@ class ManufacturerFactory extends Factory public function definition() { return [ - 'name' => $this->faker->company(), + 'name' => $this->faker->unique()->company(), 'user_id' => User::factory()->superuser(), 'support_phone' => $this->faker->phoneNumber(), 'url' => $this->faker->url(), diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index f40301753b..c8fe69e0db 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -30,14 +30,14 @@ class UserFactory extends Factory 'locale' => 'en', 'notes' => 'Created by DB seeder', 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password - 'permissions' => '{"user":"0"}', + 'permissions' => '{}', 'phone' => $this->faker->phoneNumber(), 'state' => $this->faker->stateAbbr(), - 'username' => $this->faker->username(), + 'username' => $this->faker->unique()->username(), 'zip' => $this->faker->postcode(), ]; } - + public function firstAdmin() { return $this->state(function () { @@ -65,15 +65,25 @@ class UserFactory extends Factory }); } - public function superuser() + 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->appendPermission(['superuser' => '1']); + } + public function admin() { return $this->state(function () { @@ -88,317 +98,205 @@ class UserFactory extends Factory public function viewAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.view":"1"}', - ]; - }); + return $this->appendPermission(['assets.view' => '1']); } public function createAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.create":"1"}', - ]; - }); + return $this->appendPermission(['assets.create' => '1']); } public function editAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.edit":"1"}', - ]; - }); + return $this->appendPermission(['assets.edit' => '1']); } public function deleteAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.delete":"1"}', - ]; - }); + return $this->appendPermission(['assets.delete' => '1']); } public function checkinAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.checkin":"1"}', - ]; - }); + return $this->appendPermission(['assets.checkin' => '1']); } public function checkoutAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.checkout":"1"}', - ]; - }); + return $this->appendPermission(['assets.checkout' => '1']); } public function viewRequestableAssets() { - return $this->state(function () { - return [ - 'permissions' => '{"assets.view.requestable":"1"}', - ]; - }); + return $this->appendPermission(['assets.view.requestable' => '1']); } public function viewAccessories() { - return $this->state(function () { - return [ - 'permissions' => '{"accessories.view":"1"}', - ]; - }); + return $this->appendPermission(['accessories.view' => '1']); } public function createAccessories() { - return $this->state(function () { - return [ - 'permissions' => '{"accessories.create":"1"}', - ]; - }); + return $this->appendPermission(['accessories.create' => '1']); } public function editAccessories() { - return $this->state(function () { - return [ - 'permissions' => '{"accessories.edit":"1"}', - ]; - }); + return $this->appendPermission(['accessories.edit' => '1']); } public function deleteAccessories() { - return $this->state(function () { - return [ - 'permissions' => '{"accessories.delete":"1"}', - ]; - }); + return $this->appendPermission(['accessories.delete' => '1']); } public function checkinAccessories() { - return $this->state(function () { - return [ - 'permissions' => '{"accessories.checkin":"1"}', - ]; - }); + return $this->appendPermission(['accessories.checkin' => '1']); } public function checkoutAccessories() { - return $this->state(function () { - return [ - 'permissions' => '{"accessories.checkout":"1"}', - ]; - }); + return $this->appendPermission(['accessories.checkout' => '1']); } public function viewConsumables() { - return $this->state(function () { - return [ - 'permissions' => '{"consumables.view":"1"}', - ]; - }); + return $this->appendPermission(['consumables.view' => '1']); } public function createConsumables() { - return $this->state(function () { - return [ - 'permissions' => '{"consumables.create":"1"}', - ]; - }); + return $this->appendPermission(['consumables.create' => '1']); } public function editConsumables() { - return $this->state(function () { - return [ - 'permissions' => '{"consumables.edit":"1"}', - ]; - }); + return $this->appendPermission(['consumables.edit' => '1']); } public function deleteConsumables() { - return $this->state(function () { - return [ - 'permissions' => '{"consumables.delete":"1"}', - ]; - }); + return $this->appendPermission(['consumables.delete' => '1']); } public function checkinConsumables() { - return $this->state(function () { - return [ - 'permissions' => '{"consumables.checkin":"1"}', - ]; - }); + return $this->appendPermission(['consumables.checkin' => '1']); } public function checkoutConsumables() { - return $this->state(function () { - return [ - 'permissions' => '{"consumables.checkout":"1"}', - ]; - }); + return $this->appendPermission(['consumables.checkout' => '1']); + } + + public function viewDepartments() + { + return $this->appendPermission(['departments.view' => '1']); } public function viewLicenses() { - return $this->state(function () { - return [ - 'permissions' => '{"licenses.view":"1"}', - ]; - }); + return $this->appendPermission(['licenses.view' => '1']); } public function createLicenses() { - return $this->state(function () { - return [ - 'permissions' => '{"licenses.create":"1"}', - ]; - }); + return $this->appendPermission(['licenses.create' => '1']); } public function editLicenses() { - return $this->state(function () { - return [ - 'permissions' => '{"licenses.edit":"1"}', - ]; - }); + return $this->appendPermission(['licenses.edit' => '1']); } public function deleteLicenses() { - return $this->state(function () { - return [ - 'permissions' => '{"licenses.delete":"1"}', - ]; - }); + return $this->appendPermission(['licenses.delete' => '1']); } public function checkoutLicenses() { - return $this->state(function () { - return [ - 'permissions' => '{"licenses.checkout":"1"}', - ]; - }); + return $this->appendPermission(['licenses.checkout' => '1']); } public function viewKeysLicenses() { - return $this->state(function () { - return [ - 'permissions' => '{"licenses.keys":"1"}', - ]; - }); + return $this->appendPermission(['licenses.keys' => '1']); } public function viewComponents() { - return $this->state(function () { - return [ - 'permissions' => '{"components.view":"1"}', - ]; - }); + return $this->appendPermission(['components.view' => '1']); } public function createComponents() { - return $this->state(function () { - return [ - 'permissions' => '{"components.create":"1"}', - ]; - }); + return $this->appendPermission(['components.create' => '1']); } public function editComponents() { - return $this->state(function () { - return [ - 'permissions' => '{"components.edit":"1"}', - ]; - }); + return $this->appendPermission(['components.edit' => '1']); } public function deleteComponents() { - return $this->state(function () { - return [ - 'permissions' => '{"components.delete":"1"}', - ]; - }); + return $this->appendPermission(['components.delete' => '1']); } public function checkinComponents() { - return $this->state(function () { - return [ - 'permissions' => '{"components.checkin":"1"}', - ]; - }); + return $this->appendPermission(['components.checkin' => '1']); } public function checkoutComponents() { - return $this->state(function () { - return [ - 'permissions' => '{"components.checkout":"1"}', - ]; - }); + return $this->appendPermission(['components.checkout' => '1']); } public function viewUsers() { - return $this->state(function () { - return [ - 'permissions' => '{"users.view":"1"}', - ]; - }); + return $this->appendPermission(['users.view' => '1']); } public function createUsers() { - return $this->state(function () { - return [ - 'permissions' => '{"users.create":"1"}', - ]; - }); + return $this->appendPermission(['users.create' => '1']); } public function editUsers() { - return $this->state(function () { - return [ - 'permissions' => '{"users.edit":"1"}', - ]; - }); + return $this->appendPermission(['users.edit' => '1']); } public function deleteUsers() { - return $this->state(function () { + return $this->appendPermission(['users.delete' => '1']); + } + + public function canEditOwnLocation() + { + return $this->appendPermission(['self.edit_location' => '1']); + } + + public function canViewReports() + { + return $this->appendPermission(['reports.view' => '1']); + } + + private function appendPermission(array $permission) + { + return $this->state(function ($currentState) use ($permission) { return [ - 'permissions' => '{"users.delete":"1"}', + 'permissions' => json_encode( + array_merge( + json_decode($currentState['permissions'], true), + $permission + ) + ), ]; }); } - } diff --git a/database/migrations/2022_10_25_215520_add_label2_settings.php b/database/migrations/2022_10_25_215520_add_label2_settings.php new file mode 100644 index 0000000000..2b07651e67 --- /dev/null +++ b/database/migrations/2022_10_25_215520_add_label2_settings.php @@ -0,0 +1,46 @@ +boolean('label2_enable')->default(false); + $table->string('label2_template')->nullable()->default('DefaultLabel'); + $table->string('label2_title')->nullable()->default(null); + $table->boolean('label2_asset_logo')->default(false); + $table->string('label2_1d_type')->default('default'); + $table->string('label2_2d_type')->default('default'); + $table->string('label2_2d_target')->default('hardware_id'); + $table->string('label2_fields')->default('name=name;serial=serial;model=model.name;'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + if (Schema::hasColumn('settings', 'label2_enable')) $table->dropColumn('label2_enable'); + if (Schema::hasColumn('settings', 'label2_template')) $table->dropColumn('label2_template'); + if (Schema::hasColumn('settings', 'label2_title')) $table->dropColumn('label2_title'); + if (Schema::hasColumn('settings', 'label2_asset_logo')) $table->dropColumn('label2_asset_logo'); + if (Schema::hasColumn('settings', 'label2_1d_type')) $table->dropColumn('label2_1d_type'); + if (Schema::hasColumn('settings', 'label2_2d_type')) $table->dropColumn('label2_2d_type'); + if (Schema::hasColumn('settings', 'label2_2d_target')) $table->dropColumn('label2_2d_target'); + if (Schema::hasColumn('settings', 'label2_fields')) $table->dropColumn('label2_fields'); + }); + } +} diff --git a/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php b/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php index 9f5c5aa1e5..aa9086a7c7 100644 --- a/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php +++ b/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php @@ -17,19 +17,30 @@ class AddEolDateOnAssetsTable extends Migration { Schema::table('assets', function (Blueprint $table) { + if (!Schema::hasColumn('assets', 'asset_eol_date')) { $table->date('asset_eol_date')->after('purchase_date')->nullable()->default(null); } + + // This is a temporary shim so we don't have to modify the asset observer for migrations where + // there is a large version difference. (See the AssetObserver notes). This column gets created + // later in 2023_07_13_052204_denormalized_eol_and_add_column_for_explicit_date_to_assets.php + // but we have to temporarily create it now so the save method below doesn't break + if (!Schema::hasColumn('assets', 'eol_explicit')) { + $table->boolean('eol_explicit')->default(false)->after('asset_eol_date'); + } }); // Chunk the model query to get the models that do have an EOL date + // We use saveQuietly() here to skip the AssetObserver, since it modifies fields + // that do not yet exist on the assets table. AssetModel::whereNotNull('eol')->chunk(10, function ($models) { foreach ($models as $model) { foreach ($model->assets as $asset) { if ($asset->purchase_date!='') { $asset->asset_eol_date = $asset->present()->eol_date(); - $asset->save(); + $asset->saveQuietly(); } } 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/migrations/2023_07_05_092237_change_settings_table_increase_saml_idp_metadata_size.php b/database/migrations/2023_07_05_092237_change_settings_table_increase_saml_idp_metadata_size.php new file mode 100644 index 0000000000..0e200e11b5 --- /dev/null +++ b/database/migrations/2023_07_05_092237_change_settings_table_increase_saml_idp_metadata_size.php @@ -0,0 +1,36 @@ +mediumText('saml_idp_metadata')->nullable()->default(null)->change(); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->text('saml_idp_metadata')->nullable()->default(null)->change(); + }); + } +} diff --git a/database/migrations/2023_07_06_092507_add_phone_fax_to_locations.php b/database/migrations/2023_07_06_092507_add_phone_fax_to_locations.php new file mode 100644 index 0000000000..d6835ac295 --- /dev/null +++ b/database/migrations/2023_07_06_092507_add_phone_fax_to_locations.php @@ -0,0 +1,54 @@ +string('phone', 20)->after('zip')->nullable()->default(null); + $table->string('fax', 20)->after('zip')->nullable()->default(null); + }); + + Schema::table('companies', function (Blueprint $table) { + $table->string('phone', 20)->after('name')->nullable()->default(null); + $table->string('fax', 20)->after('name')->nullable()->default(null); + }); + + Schema::table('departments', function (Blueprint $table) { + $table->string('phone', 20)->after('name')->nullable()->default(null); + $table->string('fax', 20)->after('name')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('locations', function (Blueprint $table) { + $table->dropColumn('phone'); + $table->dropColumn('fax'); + }); + + Schema::table('companies', function (Blueprint $table) { + $table->dropColumn('phone'); + $table->dropColumn('fax'); + }); + + Schema::table('departments', function (Blueprint $table) { + $table->dropColumn('phone'); + $table->dropColumn('fax'); + }); + } +} diff --git a/database/migrations/2023_07_13_052204_denormalized_eol_and_add_column_for_explicit_date_to_assets.php b/database/migrations/2023_07_13_052204_denormalized_eol_and_add_column_for_explicit_date_to_assets.php new file mode 100644 index 0000000000..982bd8ac0d --- /dev/null +++ b/database/migrations/2023_07_13_052204_denormalized_eol_and_add_column_for_explicit_date_to_assets.php @@ -0,0 +1,69 @@ +boolean('eol_explicit')->default(false)->after('asset_eol_date'); + } + }); + + + // Update the eol_explicit column with the value from asset_eol_date if it exists and is different from the calculated value + Asset::whereNotNull('asset_eol_date')->with('model')->chunkById(500, function ($assetsWithEolDates) { + foreach ($assetsWithEolDates as $asset) { + if ($asset->asset_eol_date && $asset->purchase_date) { + try { + $months = CarbonImmutable::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date); + } catch (\Exception $e) { + Log::info('asset_eol_date invalid for asset ' . $asset->id); + } + if ($asset->model->eol) { + if ($months != $asset->model->eol) { + $asset->update(['eol_explicit' => true]); + } + } else { + $asset->update(['eol_explicit' => true]); + } + } + } + }); + + DB::table('assets') + ->whereNull('asset_eol_date') + ->whereNotNull('purchase_date') + ->whereNotNull('model_id') + ->join('models', 'assets.model_id', '=', 'models.id') + ->update([ + 'asset_eol_date' => DB::raw('DATE_ADD(purchase_date, INTERVAL ' . DB::getTablePrefix() . 'models.eol MONTH)') + ]); + } + + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('assets', function (Blueprint $table) { + $table->dropColumn('eol_explicit'); + }); + } +} diff --git a/database/migrations/2023_07_14_004221_add_show_in_list_view_to_custom_fields.php b/database/migrations/2023_07_14_004221_add_show_in_list_view_to_custom_fields.php new file mode 100644 index 0000000000..227a8bd6c3 --- /dev/null +++ b/database/migrations/2023_07_14_004221_add_show_in_list_view_to_custom_fields.php @@ -0,0 +1,34 @@ +boolean('show_in_listview')->nullable()->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('custom_fields', function (Blueprint $table) { + if (Schema::hasColumn('custom_fields', 'show_in_listview')) { + $table->dropColumn('show_in_listview'); + } + }); + } +} diff --git a/database/migrations/2023_08_01_174150_change_webhook_settings_variable_type.php b/database/migrations/2023_08_01_174150_change_webhook_settings_variable_type.php new file mode 100644 index 0000000000..59c9728e2e --- /dev/null +++ b/database/migrations/2023_08_01_174150_change_webhook_settings_variable_type.php @@ -0,0 +1,33 @@ +text('webhook_endpoint')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->varchar('webhook_endpoint')->change(); + }); + + } +} diff --git a/database/migrations/2023_08_13_172600_add_email_to_companies.php b/database/migrations/2023_08_13_172600_add_email_to_companies.php new file mode 100644 index 0000000000..a1369d9ade --- /dev/null +++ b/database/migrations/2023_08_13_172600_add_email_to_companies.php @@ -0,0 +1,32 @@ +string('email', 150)->after('fax')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('companies', function (Blueprint $table) { + $table->dropColumn('email'); + }); + } +} diff --git a/database/migrations/2023_08_17_202638_add_last_checkin_to_assets.php b/database/migrations/2023_08_17_202638_add_last_checkin_to_assets.php new file mode 100644 index 0000000000..74048ce941 --- /dev/null +++ b/database/migrations/2023_08_17_202638_add_last_checkin_to_assets.php @@ -0,0 +1,36 @@ +dateTime('last_checkin')->after('last_checkout')->nullable(); + }); + + DB::statement( + "UPDATE " . DB::getTablePrefix() . "assets SET last_checkin=(SELECT MAX(" . DB::getTablePrefix() . "action_logs.action_date) FROM " . DB::getTablePrefix() . "action_logs WHERE item_type='App\\\Models\\\Asset' AND " . DB::getTablePrefix() . "action_logs.item_id=" . DB::getTablePrefix() . "assets.id AND " . DB::getTablePrefix() . "action_logs.action_type='checkin from')" + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('assets', function (Blueprint $table) { + $table->dropColumn('last_checkin'); + }); + } +} diff --git a/database/migrations/2023_08_21_064609_add_name_ordering_to_settings.php b/database/migrations/2023_08_21_064609_add_name_ordering_to_settings.php new file mode 100644 index 0000000000..7a0afc5456 --- /dev/null +++ b/database/migrations/2023_08_21_064609_add_name_ordering_to_settings.php @@ -0,0 +1,32 @@ +string('name_display_format', 10)->after('alert_threshold')->nullable()->default('first_last'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('name_display_format'); + }); + } +} diff --git a/database/migrations/2023_08_21_181742_add_min_amt_to_models_table.php b/database/migrations/2023_08_21_181742_add_min_amt_to_models_table.php new file mode 100644 index 0000000000..5f0bb85f9b --- /dev/null +++ b/database/migrations/2023_08_21_181742_add_min_amt_to_models_table.php @@ -0,0 +1,32 @@ +integer('min_amt')->after('model_number')->default(null);; + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('models', function (Blueprint $table) { + $table->dropColumn('min_amt'); + }); + } +} diff --git a/database/migrations/2023_09_13_200913_fix_asset_model_min_qty_nullability.php b/database/migrations/2023_09_13_200913_fix_asset_model_min_qty_nullability.php new file mode 100644 index 0000000000..c3b3d1e0d7 --- /dev/null +++ b/database/migrations/2023_09_13_200913_fix_asset_model_min_qty_nullability.php @@ -0,0 +1,32 @@ +integer('min_amt')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('models', function (Blueprint $table) { + $table->integer('min_amt')->nullable(false)->change(); + }); + } +} diff --git a/database/migrations/2023_10_25_064324_add_show_in_requestable_to_custom_fields.php b/database/migrations/2023_10_25_064324_add_show_in_requestable_to_custom_fields.php new file mode 100644 index 0000000000..710a56e819 --- /dev/null +++ b/database/migrations/2023_10_25_064324_add_show_in_requestable_to_custom_fields.php @@ -0,0 +1,34 @@ +boolean('show_in_requestable_list')->after('show_in_email')->nullable()->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('custom_fields', function (Blueprint $table) { + if (Schema::hasColumn('custom_fields', 'show_in_requestable_list')) { + $table->dropColumn('show_in_requestable_list'); + } + }); + } +} diff --git a/database/seeders/CustomFieldSeeder.php b/database/seeders/CustomFieldSeeder.php index 8776872644..e51ca510f9 100644 --- a/database/seeders/CustomFieldSeeder.php +++ b/database/seeders/CustomFieldSeeder.php @@ -33,29 +33,69 @@ class CustomFieldSeeder extends Seeder CustomField::factory()->count(1)->ram()->create(); CustomField::factory()->count(1)->cpu()->create(); CustomField::factory()->count(1)->macAddress()->create(); + CustomField::factory()->count(1)->testEncrypted()->create(); + CustomField::factory()->count(1)->testCheckbox()->create(); + DB::table('custom_field_custom_fieldset')->insert([ [ '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, ], - ]); + [ + 'custom_field_id' => '6', + 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, + ], + + [ + 'custom_field_id' => '6', + 'custom_fieldset_id' => '1', + 'order' => 0, + 'required' => 0, + ], + + [ + 'custom_field_id' => '7', + 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, + ], + [ + 'custom_field_id' => '7', + 'custom_fieldset_id' => '1', + 'order' => 0, + 'required' => 0, + ], + + ]); } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 1429604139..5e26a9a257 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -38,12 +38,13 @@ class DatabaseSeeder extends Seeder $this->call(DepreciationSeeder::class); $this->call(StatuslabelSeeder::class); $this->call(AccessorySeeder::class); + $this->call(CustomFieldSeeder::class); $this->call(AssetSeeder::class); $this->call(LicenseSeeder::class); $this->call(ComponentSeeder::class); $this->call(ConsumableSeeder::class); $this->call(ActionlogSeeder::class); - $this->call(CustomFieldSeeder::class); + Artisan::call('snipeit:sync-asset-locations', ['--output' => 'all']); $output = Artisan::output(); 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/docker-compose.yml b/docker-compose.yml index 101c15d3b5..15272ce5c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: ports: - "8000:80" volumes: - - ./logs:/var/www/html/storage/logs + - ./storage/logs:/var/www/html/storage/logs depends_on: - mariadb - redis diff --git a/package-lock.json b/package-lock.json index cd722a04b2..b6254682a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1329,9 +1329,9 @@ "dev": true }, "@fortawesome/fontawesome-free": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", - "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==" + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", + "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==" }, "@jridgewell/gen-mapping": { "version": "0.1.1", @@ -1354,9 +1354,9 @@ "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==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -1431,9 +1431,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" } } }, @@ -1565,9 +1565,9 @@ } }, "@types/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", + "version": "8.44.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", + "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -1583,9 +1583,9 @@ } }, "@types/estree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", - "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + "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", @@ -1883,134 +1883,147 @@ } } }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@vue/shared": "3.1.5" + } + }, + "@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" + }, + "@webassemblyjs/ast": { + "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.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" } }, @@ -2065,14 +2078,14 @@ } }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" }, "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", @@ -2187,6 +2200,14 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, + "alpinejs": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz", + "integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==", + "requires": { + "@vue/reactivity": "~3.1.1" + } + }, "ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -2215,6 +2236,15 @@ "picomatch": "^2.0.4" } }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, "array-filter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", @@ -3127,11 +3157,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": { @@ -3154,9 +3184,9 @@ "integrity": "sha1-EQPWvADPv6jPyaJZmrUYxVZD2j8=" }, "bootstrap-table": { - "version": "1.21.4", - "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.21.4.tgz", - "integrity": "sha512-Vp0kwkCsZzBINiA1KJ46LSkIa4oA0r6GSuzERZEqkUU8/0JDij2aG4CdYxiOm/UFCPZ9+KuAE4zSjxJLxg5jWw==" + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.22.1.tgz", + "integrity": "sha512-Nw8p+BmaiMDSfoer/p49YeI3vJQAWhudxhyKMuqnJBb3NRvCRewMk7JDgiN9SQO3YeSejOirKtcdWpM0dtddWg==" }, "brace-expansion": { "version": "1.1.11", @@ -3516,11 +3546,6 @@ "tslib": "^2.0.3" } }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -3555,9 +3580,9 @@ }, "dependencies": { "core-js": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", - "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", + "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==", "optional": true }, "regenerator-runtime": { @@ -3694,6 +3719,16 @@ "string-width": "^4.2.0" } }, + "clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -3955,11 +3990,11 @@ "dev": true }, "copy-anything": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz", - "integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "requires": { - "is-what": "^3.12.0" + "is-what": "^3.14.1" } }, "core-js": { @@ -4135,24 +4170,27 @@ } }, "css-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.3.0.tgz", - "integrity": "sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg==", + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", "requires": { - "camelcase": "^6.0.0", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", + "icss-utils": "^5.1.0", "loader-utils": "^2.0.0", - "postcss": "^7.0.32", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.3", - "postcss-modules-scope": "^2.2.0", - "postcss-modules-values": "^3.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.1", - "semver": "^7.3.2" + "schema-utils": "^3.0.0", + "semver": "^7.3.5" }, "dependencies": { + "@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==" + }, "loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", @@ -4163,32 +4201,23 @@ "json5": "^2.1.2" } }, - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -4350,15 +4379,16 @@ } }, "deep-equal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", - "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", "requires": { + "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.2", - "get-intrinsic": "^1.1.3", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.1", + "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", @@ -4366,7 +4396,7 @@ "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", @@ -4388,12 +4418,13 @@ } }, "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -4428,18 +4459,6 @@ "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", @@ -4457,16 +4476,15 @@ } }, "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==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "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" + "has-tostringtag": "^1.0.0" } } } @@ -4510,6 +4528,11 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4645,9 +4668,9 @@ } }, "dompurify": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", - "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==", + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", + "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", "optional": true }, "domutils": { @@ -4754,9 +4777,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.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -4830,12 +4853,13 @@ }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -4861,9 +4885,9 @@ } }, "es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" }, "es-to-primitive": { "version": "1.2.1", @@ -14859,6 +14883,14 @@ } } }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "requires": { + "delegate": "^3.1.2" + } + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -14868,12 +14900,13 @@ }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -14955,6 +14988,11 @@ "get-intrinsic": "^1.1.1" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", @@ -15273,38 +15311,15 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } }, "icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "requires": { - "postcss": "^7.0.14" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" }, "ieee754": { "version": "1.2.1", @@ -15472,12 +15487,13 @@ }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -15543,12 +15559,13 @@ "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -15558,9 +15575,17 @@ "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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "requires": { + "which-typed-array": "^1.1.11" + } + }, + "which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -15939,17 +15964,17 @@ }, "dependencies": { "core-js": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", - "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", + "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==", "optional": true } } }, "jspdf-autotable": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.5.25.tgz", - "integrity": "sha512-BIbDd/cilRbVm5PmR+ZonolSqRtm0AvZDpTz+rrWed7BnFj5mqF7x7lkxDAMzPudLapktHUk6cxipcvUzal8cg==" + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.5.31.tgz", + "integrity": "sha512-Lc1KuLGDQWW/5t57Z/+c2E94XQV3jV2QVU3xMRiwvcm/nMx79aCkpPCsxLzJZVFneZvz4XoA8+egQR1QajYiWw==" }, "junk": { "version": "3.1.0", @@ -16297,9 +16322,9 @@ } }, "less": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", - "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "requires": { "copy-anything": "^2.0.1", "errno": "^0.1.1", @@ -16307,34 +16332,12 @@ "image-size": "~0.5.0", "make-dir": "^2.1.0", "mime": "^1.4.1", - "needle": "^2.5.2", + "needle": "^3.1.0", "parse-node-version": "^1.0.1", "source-map": "~0.6.0", "tslib": "^2.3.0" }, "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "optional": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "optional": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "optional": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16485,6 +16488,24 @@ "yallist": "^4.0.0" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "optional": true + } + } + }, "md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -16780,17 +16801,16 @@ "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "needle": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", - "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", "optional": true, "requires": { "debug": "^3.2.6", - "iconv-lite": "^0.4.4", + "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, "dependencies": { @@ -16803,6 +16823,15 @@ "ms": "^2.1.1" } }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -17390,6 +17419,12 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "optional": true + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -17408,7 +17443,6 @@ "version": "8.4.5", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", - "dev": true, "requires": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", @@ -17686,124 +17720,34 @@ } }, "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", - "requires": { - "postcss": "^7.0.5" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" }, "postcss-modules-local-by-default": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", - "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", "requires": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.32", + "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", "postcss-value-parser": "^4.1.0" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "postcss-modules-scope": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", - "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "postcss-selector-parser": "^6.0.4" } }, "postcss-modules-values": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "requires": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "icss-utils": "^5.0.0" } }, "postcss-normalize-charset": { @@ -18337,13 +18281,24 @@ } }, "regexp.prototype.flags": { - "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==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "dependencies": { + "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" + } + } } }, "regexpu-core": { @@ -18522,12 +18477,18 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, "requires": { "@types/json-schema": "^7.0.5", "ajv": "^6.12.4", "ajv-keywords": "^3.5.2" } }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -18788,8 +18749,7 @@ "source-map-js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true + "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==" }, "source-map-support": { "version": "0.5.21", @@ -18909,9 +18869,9 @@ "dev": true }, "stackblur-canvas": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", - "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.6.0.tgz", + "integrity": "sha512-8S1aIA+UoF6erJYnglGPug6MaHYGo1Ot7h5fuXx4fUPvcvQfcdw2o/ppCse63+eZf8PPidSu4v1JnmEVtEDnpg==", "optional": true }, "statuses": { @@ -19204,11 +19164,11 @@ } }, "tableexport.jquery.plugin": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/tableexport.jquery.plugin/-/tableexport.jquery.plugin-1.27.0.tgz", - "integrity": "sha512-aJ6XBeqtPqV8P2v8vvKNa54SGp9R4V4CIwaIhA1WTkflvoYaWqCyTmf/O6WH5kKBvDpva+RhLnYwsPHEPwdSsg==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/tableexport.jquery.plugin/-/tableexport.jquery.plugin-1.28.0.tgz", + "integrity": "sha512-ydDjOhw8A+LOu+801zPXDeMF8MoU1q2HtS2msphCuny0tdXgbXG9GJfA4ll1hBs0ABiAnOaVVZaRuxBmW/qHtw==", "requires": { - "file-saver": ">=2.0.1", + "file-saver": ">=2.0.4", "html2canvas": ">=1.0.0", "jquery": ">=3.2.1", "jspdf": ">=2.0.0", @@ -19354,6 +19314,11 @@ "process": "~0.11.0" } }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, "tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -19679,20 +19644,20 @@ } }, "webpack": { - "version": "5.79.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.79.0.tgz", - "integrity": "sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@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", + "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -19702,7 +19667,7 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", @@ -19710,28 +19675,28 @@ }, "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==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" }, "@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==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@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==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@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==" }, "commander": { "version": "2.20.3", @@ -19744,9 +19709,9 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -19762,26 +19727,26 @@ } }, "terser": { - "version": "5.16.9", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.9.tgz", - "integrity": "sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", + "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@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.7", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", - "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "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.5" + "terser": "^5.16.8" } }, "webpack-sources": { diff --git a/package.json b/package.json index 0026531e77..2e220c597a 100644 --- a/package.json +++ b/package.json @@ -24,19 +24,21 @@ "vue-template-compiler": "2.4.4" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.4.0", - "acorn": "^8.8.2", - "acorn-import-assertions": "^1.8.0", + "@fortawesome/fontawesome-free": "^6.4.2", + "acorn": "^8.9.0", + "acorn-import-assertions": "^1.9.0", "admin-lte": "^2.4.18", "ajv": "^6.12.6", + "alpinejs": "^3.10.5", "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.21.4", + "bootstrap-table": "1.22.1", "chart.js": "^2.9.4", - "css-loader": "^4.0.0", + "clipboard": "^2.0.11", + "css-loader": "^5.0.0", "ekko-lightbox": "^5.1.1", "imagemin": "^8.0.1", "jquery-form-validator": "^2.3.79", @@ -44,16 +46,16 @@ "jquery-ui": "^1.13.2", "jquery-ui-bundle": "^1.12.1", "jquery.iframe-transport": "^1.0.0", - "jspdf-autotable": "^3.5.24", - "less": "^4.1.2", + "jspdf-autotable": "^3.5.30", + "less": "^4.2.0", "less-loader": "^5.0", "list.js": "^1.5.0", "papaparse": "^4.3.3", "select2": "4.0.13", "sheetjs": "^2.0.0", - "tableexport.jquery.plugin": "1.27.0", + "tableexport.jquery.plugin": "1.28.0", "tether": "^1.4.0", "vue-resource": "^1.5.2", - "webpack": "^5.78.0" + "webpack": "^5.88.2" } } diff --git a/public/css/build/app.css b/public/css/build/app.css index 623ae2df1d..b82e27befc 100644 --- a/public/css/build/app.css +++ b/public/css/build/app.css @@ -957,18 +957,21 @@ th.css-accessory > .th-inner::before { border-radius: 0px; } @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } } -@media screen and (max-width: 771px) and (min-width: 512px) { +@media screen and (max-width: 912px) and (min-width: 512px) { .sidebar-menu { - margin-top: 160px; + margin-top: 100px; } } -@media screen and (max-width: 1098px) and (min-width: 772px) { +@media screen and (max-width: 1268px) and (min-width: 912px) { .sidebar-menu { - margin-top: 98px; + margin-top: 50px; } } .ellipsis { @@ -1104,6 +1107,15 @@ input[type="radio"]:checked::before { 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 2c4f57f858..4020194f84 100644 --- a/public/css/build/overrides.css +++ b/public/css/build/overrides.css @@ -590,18 +590,21 @@ th.css-accessory > .th-inner::before { border-radius: 0px; } @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } } -@media screen and (max-width: 771px) and (min-width: 512px) { +@media screen and (max-width: 912px) and (min-width: 512px) { .sidebar-menu { - margin-top: 160px; + margin-top: 100px; } } -@media screen and (max-width: 1098px) and (min-width: 772px) { +@media screen and (max-width: 1268px) and (min-width: 912px) { .sidebar-menu { - margin-top: 98px; + margin-top: 50px; } } .ellipsis { @@ -737,6 +740,15 @@ input[type="radio"]:checked::before { 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 6db24038e1..0786a8b30d 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.4.0 by @fontawesome - https://fontawesome.com + * Font Awesome Free 6.4.2 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. */ @@ -13384,6 +13384,9 @@ readers do not read off random characters that represent icons */ .fa-edge:before { content: "\f282"; } +.fa-threads:before { + content: "\e618"; } + .fa-napster:before { content: "\f3d2"; } @@ -13552,6 +13555,9 @@ readers do not read off random characters that represent icons */ .fa-scribd:before { content: "\f28a"; } +.fa-debian:before { + content: "\e60b"; } + .fa-openid:before { content: "\f19b"; } @@ -13792,6 +13798,9 @@ readers do not read off random characters that represent icons */ .fa-neos:before { content: "\f612"; } +.fa-square-threads:before { + content: "\e619"; } + .fa-hackerrank:before { content: "\f5f7"; } @@ -14134,6 +14143,9 @@ readers do not read off random characters that represent icons */ .fa-erlang:before { content: "\f39d"; } +.fa-x-twitter:before { + content: "\e61b"; } + .fa-cotton-bureau:before { content: "\f89e"; } @@ -14629,6 +14641,9 @@ readers do not read off random characters that represent icons */ .fa-quora:before { content: "\f2c4"; } +.fa-square-x-twitter:before { + content: "\e61a"; } + .fa-reacteurope:before { content: "\f75d"; } @@ -21546,9 +21561,9 @@ a.ui-button:active, } /*! - * 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 { @@ -22385,7 +22400,7 @@ a.ui-button:active, /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImVra28tbGlnaHRib3guY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGVBQ0UsOEJBQXlCLEFBQXpCLHVCQUF5QixBQUN6QixzQkFBb0IsQUFBcEIsbUJBQW9CLEFBQ3BCLHFCQUF3QixBQUF4Qix1QkFBd0IsQUFDeEIseUJBQTZCLENBQzlCLEFBQ0QseUJBQ0UsaUJBQW1CLENBQ3BCLEFBQ0QsZ0RBQ0Usa0JBQW1CLEFBQ25CLE1BQU8sQUFDUCxPQUFRLEFBQ1IsU0FBVSxBQUNWLFFBQVMsQUFDVCxVQUFZLENBQ2IsQUFDRCxzQkFDRSxXQUFZLEFBQ1osV0FBYSxDQUNkLEFBQ0QsMkJBQ0UsVUFBYSxBQUNiLGtCQUFtQixBQUNuQixNQUFPLEFBQ1AsT0FBUSxBQUNSLFdBQVksQUFDWixZQUFhLEFBQ2Isb0JBQWMsQUFBZCxZQUFjLENBQ2YsQUFDRCw2QkFDRSxXQUFRLEFBQVIsT0FBUSxBQUNSLG9CQUFjLEFBQWQsYUFBYyxBQUNkLHNCQUFvQixBQUFwQixtQkFBb0IsQUFDcEIsVUFBVyxBQUNYLHVCQUF5QixBQUN6QixXQUFZLEFBQ1osZUFBZ0IsQUFDaEIsU0FBYSxDQUNkLEFBQ0QsK0JBQ0Usb0JBQWEsQUFBYixXQUFhLENBQ2QsQUFDRCxvQ0FDRSxZQUFjLENBQ2YsQUFDRCxrQ0FDRSxjQUFnQixDQUNqQixBQUNELDZDQUNFLGdCQUFrQixDQUNuQixBQUNELG1DQUNFLG9CQUFzQixDQUN2QixBQUNELG1DQUNFLFlBQWMsQ0FDZixBQUNELHNDQUNFLGVBQWdCLEFBQ2hCLGlCQUFtQixDQUNwQixBQUNELHVCQUNFLFVBQVcsQUFDWCxvQkFBc0IsQ0FDdkIsQUFDRCw2QkFDRSxZQUFjLENBQ2YsQUFDRCw2QkFDRSxlQUFpQixDQUNsQixBQUNELHNCQUNFLGtCQUFtQixBQUNuQixNQUFPLEFBQ1AsT0FBUSxBQUNSLFNBQVUsQUFDVixRQUFTLEFBQ1QsV0FBWSxBQUNaLG9CQUFjLEFBQWQsYUFBYyxBQUVkLDBCQUF1QixBQUF2QixzQkFBdUIsQUFFdkIscUJBQXdCLEFBQXhCLHVCQUF3QixBQUV4QixzQkFBb0IsQUFBcEIsa0JBQW9CLENBQ3JCLEFBQ0QsMEJBQ0UsV0FBWSxBQUNaLFlBQWEsQUFDYixrQkFBbUIsQUFDbkIsaUJBQW1CLENBQ3BCLEFBQ0QsOEJBQ0UsV0FBWSxBQUNaLFlBQWEsQUFDYixrQkFBbUIsQUFDbkIsc0JBQXVCLEFBQ3ZCLFdBQWEsQUFDYixrQkFBbUIsQUFDbkIsTUFBTyxBQUNQLE9BQVEsQUFDUixtQ0FBNkMsQ0FDOUMsQUFDRCx5Q0FDRSxtQkFBcUIsQ0FDdEIsQUFDRCw0Q0FDRSxxQkFBdUIsQ0FDeEIsQUFVRCxhQUNFLE1BRUUsbUJBQW9CLEFBQ3BCLDBCQUE0QixDQUM3QixBQUNELElBQ0UsbUJBQW9CLEFBQ3BCLDBCQUE0QixDQUM3QixDQUNGIiwiZmlsZSI6ImVra28tbGlnaHRib3guY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmVra28tbGlnaHRib3gge1xuICBkaXNwbGF5OiBmbGV4ICFpbXBvcnRhbnQ7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICBwYWRkaW5nLXJpZ2h0OiAwcHghaW1wb3J0YW50O1xufVxuLmVra28tbGlnaHRib3gtY29udGFpbmVyIHtcbiAgcG9zaXRpb246IHJlbGF0aXZlO1xufVxuLmVra28tbGlnaHRib3gtY29udGFpbmVyID4gZGl2LmVra28tbGlnaHRib3gtaXRlbSB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiAwO1xuICBsZWZ0OiAwO1xuICBib3R0b206IDA7XG4gIHJpZ2h0OiAwO1xuICB3aWR0aDogMTAwJTtcbn1cbi5la2tvLWxpZ2h0Ym94IGlmcmFtZSB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSB7XG4gIHotaW5kZXg6IDEwMDtcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICB0b3A6IDA7XG4gIGxlZnQ6IDA7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGRpc3BsYXk6IGZsZXg7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhIHtcbiAgZmxleDogMTtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgb3BhY2l0eTogMDtcbiAgdHJhbnNpdGlvbjogb3BhY2l0eSAwLjVzO1xuICBjb2xvcjogI2ZmZjtcbiAgZm9udC1zaXplOiAzMHB4O1xuICB6LWluZGV4OiAxMDA7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhID4gKiB7XG4gIGZsZXgtZ3JvdzogMTtcbn1cbi5la2tvLWxpZ2h0Ym94LW5hdi1vdmVybGF5IGEgPiAqOmZvY3VzIHtcbiAgb3V0bGluZTogbm9uZTtcbn1cbi5la2tvLWxpZ2h0Ym94LW5hdi1vdmVybGF5IGEgc3BhbiB7XG4gIHBhZGRpbmc6IDAgMzBweDtcbn1cbi5la2tvLWxpZ2h0Ym94LW5hdi1vdmVybGF5IGE6bGFzdC1jaGlsZCBzcGFuIHtcbiAgdGV4dC1hbGlnbjogcmlnaHQ7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhOmhvdmVyIHtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xufVxuLmVra28tbGlnaHRib3gtbmF2LW92ZXJsYXkgYTpmb2N1cyB7XG4gIG91dGxpbmU6IG5vbmU7XG59XG4uZWtrby1saWdodGJveC1uYXYtb3ZlcmxheSBhLmRpc2FibGVkIHtcbiAgY3Vyc29yOiBkZWZhdWx0O1xuICB2aXNpYmlsaXR5OiBoaWRkZW47XG59XG4uZWtrby1saWdodGJveCBhOmhvdmVyIHtcbiAgb3BhY2l0eTogMTtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xufVxuLmVra28tbGlnaHRib3ggLm1vZGFsLWRpYWxvZyB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG4uZWtrby1saWdodGJveCAubW9kYWwtZm9vdGVyIHtcbiAgdGV4dC1hbGlnbjogbGVmdDtcbn1cbi5la2tvLWxpZ2h0Ym94LWxvYWRlciB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiAwO1xuICBsZWZ0OiAwO1xuICBib3R0b206IDA7XG4gIHJpZ2h0OiAwO1xuICB3aWR0aDogMTAwJTtcbiAgZGlzcGxheTogZmxleDtcbiAgLyogZXN0YWJsaXNoIGZsZXggY29udGFpbmVyICovXG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIC8qIG1ha2UgbWFpbiBheGlzIHZlcnRpY2FsICovXG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICAvKiBjZW50ZXIgaXRlbXMgdmVydGljYWxseSwgaW4gdGhpcyBjYXNlICovXG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG59XG4uZWtrby1saWdodGJveC1sb2FkZXIgPiBkaXYge1xuICB3aWR0aDogNDBweDtcbiAgaGVpZ2h0OiA0MHB4O1xuICBwb3NpdGlvbjogcmVsYXRpdmU7XG4gIHRleHQtYWxpZ246IGNlbnRlcjtcbn1cbi5la2tvLWxpZ2h0Ym94LWxvYWRlciA+IGRpdiA+IGRpdiB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjtcbiAgb3BhY2l0eTogMC42O1xuICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIHRvcDogMDtcbiAgbGVmdDogMDtcbiAgYW5pbWF0aW9uOiBzay1ib3VuY2UgMnMgaW5maW5pdGUgZWFzZS1pbi1vdXQ7XG59XG4uZWtrby1saWdodGJveC1sb2FkZXIgPiBkaXYgPiBkaXY6bGFzdC1jaGlsZCB7XG4gIGFuaW1hdGlvbi1kZWxheTogLTFzO1xufVxuLm1vZGFsLWRpYWxvZyAuZWtrby1saWdodGJveC1sb2FkZXIgPiBkaXYgPiBkaXYge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMzMzO1xufVxuQC13ZWJraXQta2V5ZnJhbWVzIHNrLWJvdW5jZSB7XG4gIDAlLFxuICAxMDAlIHtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogc2NhbGUoMCk7XG4gIH1cbiAgNTAlIHtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogc2NhbGUoMSk7XG4gIH1cbn1cbkBrZXlmcmFtZXMgc2stYm91bmNlIHtcbiAgMCUsXG4gIDEwMCUge1xuICAgIHRyYW5zZm9ybTogc2NhbGUoMCk7XG4gICAgLXdlYmtpdC10cmFuc2Zvcm06IHNjYWxlKDApO1xuICB9XG4gIDUwJSB7XG4gICAgdHJhbnNmb3JtOiBzY2FsZSgxKTtcbiAgICAtd2Via2l0LXRyYW5zZm9ybTogc2NhbGUoMSk7XG4gIH1cbn1cbiJdfQ== */ /** * @author zhixin wen - * version: 1.21.4 + * version: 1.22.1 * https://github.com/wenzhixin/bootstrap-table/ */ /* stylelint-disable annotation-no-unknown, max-line-length */ @@ -23723,18 +23738,21 @@ th.css-accessory > .th-inner::before { border-radius: 0px; } @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } } -@media screen and (max-width: 771px) and (min-width: 512px) { +@media screen and (max-width: 912px) and (min-width: 512px) { .sidebar-menu { - margin-top: 160px; + margin-top: 100px; } } -@media screen and (max-width: 1098px) and (min-width: 772px) { +@media screen and (max-width: 1268px) and (min-width: 912px) { .sidebar-menu { - margin-top: 98px; + margin-top: 50px; } } .ellipsis { @@ -23870,6 +23888,15 @@ input[type="radio"]:checked::before { 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 **/ /** --------------------------------------- **/ @@ -24949,18 +24976,21 @@ th.css-accessory > .th-inner::before { border-radius: 0px; } @media screen and (max-width: 511px) { + .tab-content .tab-pane .alert-block { + margin-top: 120px; + } .sidebar-menu { margin-top: 160px; } } -@media screen and (max-width: 771px) and (min-width: 512px) { +@media screen and (max-width: 912px) and (min-width: 512px) { .sidebar-menu { - margin-top: 160px; + margin-top: 100px; } } -@media screen and (max-width: 1098px) and (min-width: 772px) { +@media screen and (max-width: 1268px) and (min-width: 912px) { .sidebar-menu { - margin-top: 98px; + margin-top: 50px; } } .ellipsis { @@ -25096,6 +25126,15 @@ input[type="radio"]:checked::before { 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 8d409af9cb..b3304f6aeb 100644 --- a/public/css/dist/bootstrap-table.css +++ b/public/css/dist/bootstrap-table.css @@ -1,6 +1,6 @@ /** * @author zhixin wen - * version: 1.21.4 + * version: 1.22.1 * https://github.com/wenzhixin/bootstrap-table/ */ /* stylelint-disable annotation-no-unknown, max-line-length */ @@ -400,3 +400,44 @@ div.fixed-table-scroll-outer { .fix-sticky table thead.thead-dark { background: #212529; } + +/* + * dragtable + * + * @Version 2.0.15 + * + * default css + * + */ +/*##### the dragtable stuff #####*/ +.dragtable-sortable { + list-style-type: none; margin: 0; padding: 0; -moz-user-select: none; +} +.dragtable-sortable li { + margin: 0; padding: 0; float: left; font-size: 1em; background: white; +} + +.dragtable-sortable th, .dragtable-sortable td{ + border-left: 0px; +} + +.dragtable-sortable li:first-child th, .dragtable-sortable li:first-child td { + border-left: 1px solid #CCC; +} + +.ui-sortable-helper { + opacity: 0.7;filter: alpha(opacity=70); +} +.ui-sortable-placeholder { + -moz-box-shadow: 4px 5px 4px #C6C6C6 inset; + -webkit-box-shadow: 4px 5px 4px #C6C6C6 inset; + box-shadow: 4px 5px 4px #C6C6C6 inset; + border-bottom: 1px solid #CCCCCC; + border-top: 1px solid #CCCCCC; + visibility: visible !important; + background: #EFEFEF !important; + visibility: visible !important; +} +.ui-sortable-placeholder * { + opacity: 0.0; visibility: hidden; +} \ No newline at end of file diff --git a/public/css/dist/skins/skin-red-dark.css b/public/css/dist/skins/skin-red-dark.css index 5ae17e15da..17d495cbbb 100644 --- a/public/css/dist/skins/skin-red-dark.css +++ b/public/css/dist/skins/skin-red-dark.css @@ -472,6 +472,12 @@ 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); } diff --git a/public/css/dist/skins/skin-red-dark.min.css b/public/css/dist/skins/skin-red-dark.min.css index 5ae17e15da..17d495cbbb 100644 --- a/public/css/dist/skins/skin-red-dark.min.css +++ b/public/css/dist/skins/skin-red-dark.min.css @@ -472,6 +472,12 @@ 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); } diff --git a/public/css/dist/skins/skin-yellow-dark.css b/public/css/dist/skins/skin-yellow-dark.css index 1f16540750..09babaa6ab 100644 --- a/public/css/dist/skins/skin-yellow-dark.css +++ b/public/css/dist/skins/skin-yellow-dark.css @@ -128,6 +128,10 @@ a.actions { color: #fff !important; } +a:visited.label-default, +a:link.label-default { + color: #444; +} /** The dropdown is white, so use a darker color */ @@ -203,9 +207,6 @@ h2.task_menu { background: linear-gradient(to bottom, var(--header) 0%, var(--header) 100%); border-color: var(--header); } -.label-default { - background-color: var(--back-sub); -} a.btn.btn-default { color: var(--nav-link); } diff --git a/public/css/dist/skins/skin-yellow-dark.min.css b/public/css/dist/skins/skin-yellow-dark.min.css index 1f16540750..09babaa6ab 100644 --- a/public/css/dist/skins/skin-yellow-dark.min.css +++ b/public/css/dist/skins/skin-yellow-dark.min.css @@ -128,6 +128,10 @@ a.actions { color: #fff !important; } +a:visited.label-default, +a:link.label-default { + color: #444; +} /** The dropdown is white, so use a darker color */ @@ -203,9 +207,6 @@ h2.task_menu { background: linear-gradient(to bottom, var(--header) 0%, var(--header) 100%); border-color: var(--header); } -.label-default { - background-color: var(--back-sub); -} a.btn.btn-default { color: var(--nav-link); } diff --git a/public/css/webfonts/fa-brands-400.ttf b/public/css/webfonts/fa-brands-400.ttf index 774d51ac4b..30f55b7435 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 71e3185268..8a480d9b1f 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 8a9d6344d1..c79589d83d 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 7f021680b9..059a94e2fd 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 993dbe1f95..e479fb2934 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 5c16cd3e8a..88b0367aae 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 ab6ae22482..ba6cb258e0 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 9027e38bcd..23b1c47ba2 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 000ab0bf37..2332ec657f 100644 --- a/public/js/build/app.js +++ b/public/js/build/app.js @@ -1295,12 +1295,16 @@ $(document).ready(function () { $('#assigned_user').hide(); $('#assigned_location').hide(); $('.notification-callout').fadeOut(); + $('[name="assigned_location"]').val('').trigger('change.select2'); + $('[name="assigned_user"]').val('').trigger('change.select2'); } else if (assignto_type == 'location') { $('#current_assets_box').fadeOut(); $('#assigned_asset').hide(); $('#assigned_user').hide(); $('#assigned_location').show(); $('.notification-callout').fadeOut(); + $('[name="assigned_asset"]').val('').trigger('change.select2'); + $('[name="assigned_user"]').val('').trigger('change.select2'); } else { $('#assigned_asset').hide(); $('#assigned_user').show(); @@ -1311,6 +1315,8 @@ $(document).ready(function () { } $('.notification-callout').fadeIn(); + $('[name="assigned_asset"]').val('').trigger('change.select2'); + $('[name="assigned_location"]').val('').trigger('change.select2'); } }); }); // ------------------------------------------------ @@ -1496,7 +1502,9 @@ $(function () { select = link.data("select"); refreshSelector = link.data("refresh"); $('#createModal').load(link.attr('href'), function () { - //do we need to re-select2 this, after load? Probably. + // this sets the focus to be the name field + $('#modal-name').focus(); //do we need to re-select2 this, after load? Probably. + $('#createModal').find('select.select2').select2(); // Initialize the ajaxy select2 with images. // This is a copy/paste of the code from snipeit.js, would be great to only have this in one place. @@ -33915,8 +33923,8 @@ __webpack_require__.r(__webpack_exports__); "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "render": () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_AuthorizedClients_vue_vue_type_template_id_2ee9fe67_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render), -/* harmony export */ "staticRenderFns": () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_AuthorizedClients_vue_vue_type_template_id_2ee9fe67_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns) +/* harmony export */ render: () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_AuthorizedClients_vue_vue_type_template_id_2ee9fe67_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render), +/* harmony export */ staticRenderFns: () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_AuthorizedClients_vue_vue_type_template_id_2ee9fe67_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns) /* harmony export */ }); /* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_AuthorizedClients_vue_vue_type_template_id_2ee9fe67_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./AuthorizedClients.vue?vue&type=template&id=2ee9fe67&scoped=true& */ "./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/assets/js/components/passport/AuthorizedClients.vue?vue&type=template&id=2ee9fe67&scoped=true&"); @@ -33932,8 +33940,8 @@ __webpack_require__.r(__webpack_exports__); "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "render": () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_Clients_vue_vue_type_template_id_5d1d7d82_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render), -/* harmony export */ "staticRenderFns": () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_Clients_vue_vue_type_template_id_5d1d7d82_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns) +/* harmony export */ render: () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_Clients_vue_vue_type_template_id_5d1d7d82_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render), +/* harmony export */ staticRenderFns: () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_Clients_vue_vue_type_template_id_5d1d7d82_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns) /* harmony export */ }); /* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_Clients_vue_vue_type_template_id_5d1d7d82_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Clients.vue?vue&type=template&id=5d1d7d82&scoped=true& */ "./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/assets/js/components/passport/Clients.vue?vue&type=template&id=5d1d7d82&scoped=true&"); @@ -33949,8 +33957,8 @@ __webpack_require__.r(__webpack_exports__); "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "render": () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_PersonalAccessTokens_vue_vue_type_template_id_89c53f18_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render), -/* harmony export */ "staticRenderFns": () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_PersonalAccessTokens_vue_vue_type_template_id_89c53f18_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns) +/* harmony export */ render: () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_PersonalAccessTokens_vue_vue_type_template_id_89c53f18_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render), +/* harmony export */ staticRenderFns: () => (/* reexport safe */ _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_PersonalAccessTokens_vue_vue_type_template_id_89c53f18_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns) /* harmony export */ }); /* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_PersonalAccessTokens_vue_vue_type_template_id_89c53f18_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./PersonalAccessTokens.vue?vue&type=template&id=89c53f18&scoped=true& */ "./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib/index.js??vue-loader-options!./resources/assets/js/components/passport/PersonalAccessTokens.vue?vue&type=template&id=89c53f18&scoped=true&"); @@ -33966,8 +33974,8 @@ __webpack_require__.r(__webpack_exports__); "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "render": () => (/* binding */ render), -/* harmony export */ "staticRenderFns": () => (/* binding */ staticRenderFns) +/* harmony export */ render: () => (/* binding */ render), +/* harmony export */ staticRenderFns: () => (/* binding */ staticRenderFns) /* harmony export */ }); var render = function() { var _vm = this @@ -34080,8 +34088,8 @@ render._withStripped = true "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "render": () => (/* binding */ render), -/* harmony export */ "staticRenderFns": () => (/* binding */ staticRenderFns) +/* harmony export */ render: () => (/* binding */ render), +/* harmony export */ staticRenderFns: () => (/* binding */ staticRenderFns) /* harmony export */ }); var render = function() { var _vm = this @@ -34656,8 +34664,8 @@ render._withStripped = true "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "render": () => (/* binding */ render), -/* harmony export */ "staticRenderFns": () => (/* binding */ staticRenderFns) +/* harmony export */ render: () => (/* binding */ render), +/* harmony export */ staticRenderFns: () => (/* binding */ staticRenderFns) /* harmony export */ }); var render = function() { var _vm = this @@ -35180,9 +35188,9 @@ function normalizeComponent ( "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Http": () => (/* binding */ Http), -/* harmony export */ "Resource": () => (/* binding */ Resource), -/* harmony export */ "Url": () => (/* binding */ Url), +/* harmony export */ Http: () => (/* binding */ Http), +/* harmony export */ Resource: () => (/* binding */ Resource), +/* harmony export */ Url: () => (/* binding */ Url), /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /*! diff --git a/public/js/build/vendor.js b/public/js/build/vendor.js index 68a04d9a7a..ca6456d063 100644 --- a/public/js/build/vendor.js +++ b/public/js/build/vendor.js @@ -35001,9 +35001,9 @@ var effectsEffectTransfer = effect; })); /*! - * 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) */ (function(factory){ @@ -35063,7 +35063,7 @@ var effectsEffectTransfer = effect; replace: function(new_array){ if (!new_array) return; - if (!$.isArray(new_array)) + if (!Array.isArray(new_array)) new_array = [new_array]; this.clear(); this.push.apply(this, new_array); @@ -35105,9 +35105,15 @@ var effectsEffectTransfer = effect; this.isInput = this.element.is('input'); this.inputField = this.isInput ? this.element : this.element.find('input'); this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .input-group-append, .input-group-prepend, .btn') : false; - if (this.component && this.component.length === 0) + if (this.component && this.component.length === 0){ this.component = false; - this.isInline = !this.component && this.element.is('div'); + } + + if (this.o.isInline === null){ + this.isInline = !this.component && !this.isInput; + } else { + this.isInline = this.o.isInline; + } this.picker = $(DPGlobal.template); @@ -35178,7 +35184,7 @@ var effectsEffectTransfer = effect; }, _resolveDaysOfWeek: function(daysOfWeek){ - if (!$.isArray(daysOfWeek)) + if (!Array.isArray(daysOfWeek)) daysOfWeek = daysOfWeek.split(/[,\s]*/); return $.map(daysOfWeek, Number); }, @@ -35265,7 +35271,7 @@ var effectsEffectTransfer = effect; o.daysOfWeekHighlighted = this._resolveDaysOfWeek(o.daysOfWeekHighlighted||[]); o.datesDisabled = o.datesDisabled||[]; - if (!$.isArray(o.datesDisabled)) { + if (!Array.isArray(o.datesDisabled)) { o.datesDisabled = o.datesDisabled.split(','); } o.datesDisabled = $.map(o.datesDisabled, function(d){ @@ -35572,16 +35578,15 @@ var effectsEffectTransfer = effect; clearDates: function(){ this.inputField.val(''); - this.update(); this._trigger('changeDate'); - + this.update(); if (this.o.autoclose) { this.hide(); } }, setDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; + var args = Array.isArray(arguments[0]) ? arguments[0] : arguments; this.update.apply(this, args); this._trigger('changeDate'); this.setValue(); @@ -35589,7 +35594,7 @@ var effectsEffectTransfer = effect; }, setUTCDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; + var args = Array.isArray(arguments[0]) ? arguments[0] : arguments; this.setDates.apply(this, $.map(args, this._utc_to_local)); return this; }, @@ -36041,7 +36046,7 @@ var effectsEffectTransfer = effect; //Check if uniqueSort exists (supported by jquery >=1.12 and >=2.2) //Fallback to unique function for older jquery versions - if ($.isFunction($.uniqueSort)) { + if (typeof $.uniqueSort === "function") { clsName = $.uniqueSort(clsName); } else { clsName = $.unique(clsName); @@ -36573,12 +36578,12 @@ var effectsEffectTransfer = effect; if (new_date < this.dates[j]){ // Date being moved earlier/left - while (j >= 0 && new_date < this.dates[j]){ + while (j >= 0 && new_date < this.dates[j] && (this.pickers[j].element.val() || "").length > 0) { this.pickers[j--].setUTCDate(new_date); } } else if (new_date > this.dates[k]){ // Date being moved later/right - while (k < l && new_date > this.dates[k]){ + while (k < l && new_date > this.dates[k] && (this.pickers[k].element.val() || "").length > 0) { this.pickers[k++].setUTCDate(new_date); } } @@ -36692,6 +36697,7 @@ var effectsEffectTransfer = effect; endDate: Infinity, forceParse: true, format: 'mm/dd/yyyy', + isInline: null, keepEmptyValues: false, keyboardNavigation: true, language: 'en', @@ -37009,7 +37015,7 @@ var effectsEffectTransfer = effect; /* DATEPICKER VERSION * =================== */ - $.fn.datepicker.version = '1.9.0'; + $.fn.datepicker.version = '1.10.0'; $.fn.datepicker.deprecated = function(msg){ var console = window.console; @@ -58616,4 +58622,894 @@ module.exports = function(text, pattern, options) { /***/ }) -/******/ ]); \ No newline at end of file +/******/ ]); +/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["ClipboardJS"] = factory(); + else + root["ClipboardJS"] = factory(); +})(this, function() { +return /******/ (function() { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 686: +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + "default": function() { return /* binding */ clipboard; } +}); + +// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js +var tiny_emitter = __webpack_require__(279); +var tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter); +// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js +var listen = __webpack_require__(370); +var listen_default = /*#__PURE__*/__webpack_require__.n(listen); +// EXTERNAL MODULE: ./node_modules/select/src/select.js +var src_select = __webpack_require__(817); +var select_default = /*#__PURE__*/__webpack_require__.n(src_select); +;// CONCATENATED MODULE: ./src/common/command.js +/** + * Executes a given operation type. + * @param {String} type + * @return {Boolean} + */ +function command(type) { + try { + return document.execCommand(type); + } catch (err) { + return false; + } +} +;// CONCATENATED MODULE: ./src/actions/cut.js + + +/** + * Cut action wrapper. + * @param {String|HTMLElement} target + * @return {String} + */ + +var ClipboardActionCut = function ClipboardActionCut(target) { + var selectedText = select_default()(target); + command('cut'); + return selectedText; +}; + +/* harmony default export */ var actions_cut = (ClipboardActionCut); +;// CONCATENATED MODULE: ./src/common/create-fake-element.js +/** + * Creates a fake textarea element with a value. + * @param {String} value + * @return {HTMLElement} + */ +function createFakeElement(value) { + var isRTL = document.documentElement.getAttribute('dir') === 'rtl'; + var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS + + fakeElement.style.fontSize = '12pt'; // Reset box model + + fakeElement.style.border = '0'; + fakeElement.style.padding = '0'; + fakeElement.style.margin = '0'; // Move element out of screen horizontally + + fakeElement.style.position = 'absolute'; + fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically + + var yPosition = window.pageYOffset || document.documentElement.scrollTop; + fakeElement.style.top = "".concat(yPosition, "px"); + fakeElement.setAttribute('readonly', ''); + fakeElement.value = value; + return fakeElement; +} +;// CONCATENATED MODULE: ./src/actions/copy.js + + + +/** + * Create fake copy action wrapper using a fake element. + * @param {String} target + * @param {Object} options + * @return {String} + */ + +var fakeCopyAction = function fakeCopyAction(value, options) { + var fakeElement = createFakeElement(value); + options.container.appendChild(fakeElement); + var selectedText = select_default()(fakeElement); + command('copy'); + fakeElement.remove(); + return selectedText; +}; +/** + * Copy action wrapper. + * @param {String|HTMLElement} target + * @param {Object} options + * @return {String} + */ + + +var ClipboardActionCopy = function ClipboardActionCopy(target) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { + container: document.body + }; + var selectedText = ''; + + if (typeof target === 'string') { + selectedText = fakeCopyAction(target, options); + } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) { + // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange + selectedText = fakeCopyAction(target.value, options); + } else { + selectedText = select_default()(target); + command('copy'); + } + + return selectedText; +}; + +/* harmony default export */ var actions_copy = (ClipboardActionCopy); +;// CONCATENATED MODULE: ./src/actions/default.js +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + + + +/** + * Inner function which performs selection from either `text` or `target` + * properties and then executes copy or cut operations. + * @param {Object} options + */ + +var ClipboardActionDefault = function ClipboardActionDefault() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + // Defines base properties passed from constructor. + var _options$action = options.action, + action = _options$action === void 0 ? 'copy' : _options$action, + container = options.container, + target = options.target, + text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'. + + if (action !== 'copy' && action !== 'cut') { + throw new Error('Invalid "action" value, use either "copy" or "cut"'); + } // Sets the `target` property using an element that will be have its content copied. + + + if (target !== undefined) { + if (target && _typeof(target) === 'object' && target.nodeType === 1) { + if (action === 'copy' && target.hasAttribute('disabled')) { + throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); + } + + if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { + throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); + } + } else { + throw new Error('Invalid "target" value, use a valid Element'); + } + } // Define selection strategy based on `text` property. + + + if (text) { + return actions_copy(text, { + container: container + }); + } // Defines which selection strategy based on `target` property. + + + if (target) { + return action === 'cut' ? actions_cut(target) : actions_copy(target, { + container: container + }); + } +}; + +/* harmony default export */ var actions_default = (ClipboardActionDefault); +;// CONCATENATED MODULE: ./src/clipboard.js +function clipboard_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return clipboard_typeof(obj); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + + + + + + +/** + * Helper function to retrieve attribute value. + * @param {String} suffix + * @param {Element} element + */ + +function getAttributeValue(suffix, element) { + var attribute = "data-clipboard-".concat(suffix); + + if (!element.hasAttribute(attribute)) { + return; + } + + return element.getAttribute(attribute); +} +/** + * Base class which takes one or more elements, adds event listeners to them, + * and instantiates a new `ClipboardAction` on each click. + */ + + +var Clipboard = /*#__PURE__*/function (_Emitter) { + _inherits(Clipboard, _Emitter); + + var _super = _createSuper(Clipboard); + + /** + * @param {String|HTMLElement|HTMLCollection|NodeList} trigger + * @param {Object} options + */ + function Clipboard(trigger, options) { + var _this; + + _classCallCheck(this, Clipboard); + + _this = _super.call(this); + + _this.resolveOptions(options); + + _this.listenClick(trigger); + + return _this; + } + /** + * Defines if attributes would be resolved using internal setter functions + * or custom functions that were passed in the constructor. + * @param {Object} options + */ + + + _createClass(Clipboard, [{ + key: "resolveOptions", + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + this.action = typeof options.action === 'function' ? options.action : this.defaultAction; + this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; + this.text = typeof options.text === 'function' ? options.text : this.defaultText; + this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body; + } + /** + * Adds a click event listener to the passed trigger. + * @param {String|HTMLElement|HTMLCollection|NodeList} trigger + */ + + }, { + key: "listenClick", + value: function listenClick(trigger) { + var _this2 = this; + + this.listener = listen_default()(trigger, 'click', function (e) { + return _this2.onClick(e); + }); + } + /** + * Defines a new `ClipboardAction` on each click event. + * @param {Event} e + */ + + }, { + key: "onClick", + value: function onClick(e) { + var trigger = e.delegateTarget || e.currentTarget; + var action = this.action(trigger) || 'copy'; + var text = actions_default({ + action: action, + container: this.container, + target: this.target(trigger), + text: this.text(trigger) + }); // Fires an event based on the copy operation result. + + this.emit(text ? 'success' : 'error', { + action: action, + text: text, + trigger: trigger, + clearSelection: function clearSelection() { + if (trigger) { + trigger.focus(); + } + + window.getSelection().removeAllRanges(); + } + }); + } + /** + * Default `action` lookup function. + * @param {Element} trigger + */ + + }, { + key: "defaultAction", + value: function defaultAction(trigger) { + return getAttributeValue('action', trigger); + } + /** + * Default `target` lookup function. + * @param {Element} trigger + */ + + }, { + key: "defaultTarget", + value: function defaultTarget(trigger) { + var selector = getAttributeValue('target', trigger); + + if (selector) { + return document.querySelector(selector); + } + } + /** + * Allow fire programmatically a copy action + * @param {String|HTMLElement} target + * @param {Object} options + * @returns Text copied. + */ + + }, { + key: "defaultText", + + /** + * Default `text` lookup function. + * @param {Element} trigger + */ + value: function defaultText(trigger) { + return getAttributeValue('text', trigger); + } + /** + * Destroy lifecycle. + */ + + }, { + key: "destroy", + value: function destroy() { + this.listener.destroy(); + } + }], [{ + key: "copy", + value: function copy(target) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { + container: document.body + }; + return actions_copy(target, options); + } + /** + * Allow fire programmatically a cut action + * @param {String|HTMLElement} target + * @returns Text cutted. + */ + + }, { + key: "cut", + value: function cut(target) { + return actions_cut(target); + } + /** + * Returns the support of the given action, or all actions if no action is + * given. + * @param {String} [action] + */ + + }, { + key: "isSupported", + value: function isSupported() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut']; + var actions = typeof action === 'string' ? [action] : action; + var support = !!document.queryCommandSupported; + actions.forEach(function (action) { + support = support && !!document.queryCommandSupported(action); + }); + return support; + } + }]); + + return Clipboard; +}((tiny_emitter_default())); + +/* harmony default export */ var clipboard = (Clipboard); + +/***/ }), + +/***/ 828: +/***/ (function(module) { + +var DOCUMENT_NODE_TYPE = 9; + +/** + * A polyfill for Element.matches() + */ +if (typeof Element !== 'undefined' && !Element.prototype.matches) { + var proto = Element.prototype; + + proto.matches = proto.matchesSelector || + proto.mozMatchesSelector || + proto.msMatchesSelector || + proto.oMatchesSelector || + proto.webkitMatchesSelector; +} + +/** + * Finds the closest parent that matches a selector. + * + * @param {Element} element + * @param {String} selector + * @return {Function} + */ +function closest (element, selector) { + while (element && element.nodeType !== DOCUMENT_NODE_TYPE) { + if (typeof element.matches === 'function' && + element.matches(selector)) { + return element; + } + element = element.parentNode; + } +} + +module.exports = closest; + + +/***/ }), + +/***/ 438: +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +var closest = __webpack_require__(828); + +/** + * Delegates event to a selector. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ +function _delegate(element, selector, type, callback, useCapture) { + var listenerFn = listener.apply(this, arguments); + + element.addEventListener(type, listenerFn, useCapture); + + return { + destroy: function() { + element.removeEventListener(type, listenerFn, useCapture); + } + } +} + +/** + * Delegates event to a selector. + * + * @param {Element|String|Array} [elements] + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ +function delegate(elements, selector, type, callback, useCapture) { + // Handle the regular Element usage + if (typeof elements.addEventListener === 'function') { + return _delegate.apply(null, arguments); + } + + // Handle Element-less usage, it defaults to global delegation + if (typeof type === 'function') { + // Use `document` as the first parameter, then apply arguments + // This is a short way to .unshift `arguments` without running into deoptimizations + return _delegate.bind(null, document).apply(null, arguments); + } + + // Handle Selector-based usage + if (typeof elements === 'string') { + elements = document.querySelectorAll(elements); + } + + // Handle Array-like based usage + return Array.prototype.map.call(elements, function (element) { + return _delegate(element, selector, type, callback, useCapture); + }); +} + +/** + * Finds closest match and invokes callback. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Function} + */ +function listener(element, selector, type, callback) { + return function(e) { + e.delegateTarget = closest(e.target, selector); + + if (e.delegateTarget) { + callback.call(element, e); + } + } +} + +module.exports = delegate; + + +/***/ }), + +/***/ 879: +/***/ (function(__unused_webpack_module, exports) { + +/** + * Check if argument is a HTML element. + * + * @param {Object} value + * @return {Boolean} + */ +exports.node = function(value) { + return value !== undefined + && value instanceof HTMLElement + && value.nodeType === 1; +}; + +/** + * Check if argument is a list of HTML elements. + * + * @param {Object} value + * @return {Boolean} + */ +exports.nodeList = function(value) { + var type = Object.prototype.toString.call(value); + + return value !== undefined + && (type === '[object NodeList]' || type === '[object HTMLCollection]') + && ('length' in value) + && (value.length === 0 || exports.node(value[0])); +}; + +/** + * Check if argument is a string. + * + * @param {Object} value + * @return {Boolean} + */ +exports.string = function(value) { + return typeof value === 'string' + || value instanceof String; +}; + +/** + * Check if argument is a function. + * + * @param {Object} value + * @return {Boolean} + */ +exports.fn = function(value) { + var type = Object.prototype.toString.call(value); + + return type === '[object Function]'; +}; + + +/***/ }), + +/***/ 370: +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +var is = __webpack_require__(879); +var delegate = __webpack_require__(438); + +/** + * Validates all params and calls the right + * listener function based on its target type. + * + * @param {String|HTMLElement|HTMLCollection|NodeList} target + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listen(target, type, callback) { + if (!target && !type && !callback) { + throw new Error('Missing required arguments'); + } + + if (!is.string(type)) { + throw new TypeError('Second argument must be a String'); + } + + if (!is.fn(callback)) { + throw new TypeError('Third argument must be a Function'); + } + + if (is.node(target)) { + return listenNode(target, type, callback); + } + else if (is.nodeList(target)) { + return listenNodeList(target, type, callback); + } + else if (is.string(target)) { + return listenSelector(target, type, callback); + } + else { + throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); + } +} + +/** + * Adds an event listener to a HTML element + * and returns a remove listener function. + * + * @param {HTMLElement} node + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenNode(node, type, callback) { + node.addEventListener(type, callback); + + return { + destroy: function() { + node.removeEventListener(type, callback); + } + } +} + +/** + * Add an event listener to a list of HTML elements + * and returns a remove listener function. + * + * @param {NodeList|HTMLCollection} nodeList + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenNodeList(nodeList, type, callback) { + Array.prototype.forEach.call(nodeList, function(node) { + node.addEventListener(type, callback); + }); + + return { + destroy: function() { + Array.prototype.forEach.call(nodeList, function(node) { + node.removeEventListener(type, callback); + }); + } + } +} + +/** + * Add an event listener to a selector + * and returns a remove listener function. + * + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenSelector(selector, type, callback) { + return delegate(document.body, selector, type, callback); +} + +module.exports = listen; + + +/***/ }), + +/***/ 817: +/***/ (function(module) { + +function select(element) { + var selectedText; + + if (element.nodeName === 'SELECT') { + element.focus(); + + selectedText = element.value; + } + else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { + var isReadOnly = element.hasAttribute('readonly'); + + if (!isReadOnly) { + element.setAttribute('readonly', ''); + } + + element.select(); + element.setSelectionRange(0, element.value.length); + + if (!isReadOnly) { + element.removeAttribute('readonly'); + } + + selectedText = element.value; + } + else { + if (element.hasAttribute('contenteditable')) { + element.focus(); + } + + var selection = window.getSelection(); + var range = document.createRange(); + + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + + selectedText = selection.toString(); + } + + return selectedText; +} + +module.exports = select; + + +/***/ }), + +/***/ 279: +/***/ (function(module) { + +function E () { + // Keep this empty so it's easier to inherit from + // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) +} + +E.prototype = { + on: function (name, callback, ctx) { + var e = this.e || (this.e = {}); + + (e[name] || (e[name] = [])).push({ + fn: callback, + ctx: ctx + }); + + return this; + }, + + once: function (name, callback, ctx) { + var self = this; + function listener () { + self.off(name, listener); + callback.apply(ctx, arguments); + }; + + listener._ = callback + return this.on(name, listener, ctx); + }, + + emit: function (name) { + var data = [].slice.call(arguments, 1); + var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); + var i = 0; + var len = evtArr.length; + + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data); + } + + return this; + }, + + off: function (name, callback) { + var e = this.e || (this.e = {}); + var evts = e[name]; + var liveEvents = []; + + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]); + } + } + + // Remove event from queue to prevent memory leak + // Suggested by https://github.com/lazd + // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 + + (liveEvents.length) + ? e[name] = liveEvents + : delete e[name]; + + return this; + } +}; + +module.exports = E; +module.exports.TinyEmitter = E; + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ if(__webpack_module_cache__[moduleId]) { +/******/ return __webpack_module_cache__[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ !function() { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function() { return module['default']; } : +/******/ function() { return module; }; +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ !function() { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = function(exports, definition) { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ !function() { +/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } +/******/ }(); +/******/ +/************************************************************************/ +/******/ // module exports must be returned from runtime so entry inlining is disabled +/******/ // startup +/******/ // Load entry module and return exports +/******/ return __webpack_require__(686); +/******/ })() +.default; +}); \ No newline at end of file diff --git a/public/js/dist/all-defer.js b/public/js/dist/all-defer.js new file mode 100644 index 0000000000..e81b6e8766 --- /dev/null +++ b/public/js/dist/all-defer.js @@ -0,0 +1,3235 @@ +(() => { + // packages/alpinejs/src/scheduler.js + var flushPending = false; + var flushing = false; + var queue = []; + var lastFlushedIndex = -1; + function scheduler(callback) { + queueJob(callback); + } + function queueJob(job) { + if (!queue.includes(job)) + queue.push(job); + queueFlush(); + } + function dequeueJob(job) { + let index = queue.indexOf(job); + if (index !== -1 && index > lastFlushedIndex) + queue.splice(index, 1); + } + function queueFlush() { + if (!flushing && !flushPending) { + flushPending = true; + queueMicrotask(flushJobs); + } + } + function flushJobs() { + flushPending = false; + flushing = true; + for (let i = 0; i < queue.length; i++) { + queue[i](); + lastFlushedIndex = i; + } + queue.length = 0; + lastFlushedIndex = -1; + flushing = false; + } + + // packages/alpinejs/src/reactivity.js + var reactive; + var effect; + var release; + var raw; + var shouldSchedule = true; + function disableEffectScheduling(callback) { + shouldSchedule = false; + callback(); + shouldSchedule = true; + } + function setReactivityEngine(engine) { + reactive = engine.reactive; + release = engine.release; + effect = (callback) => engine.effect(callback, { scheduler: (task) => { + if (shouldSchedule) { + scheduler(task); + } else { + task(); + } + } }); + raw = engine.raw; + } + function overrideEffect(override) { + effect = override; + } + function elementBoundEffect(el) { + let cleanup2 = () => { + }; + let wrappedEffect = (callback) => { + let effectReference = effect(callback); + if (!el._x_effects) { + el._x_effects = /* @__PURE__ */ new Set(); + el._x_runEffects = () => { + el._x_effects.forEach((i) => i()); + }; + } + el._x_effects.add(effectReference); + cleanup2 = () => { + if (effectReference === void 0) + return; + el._x_effects.delete(effectReference); + release(effectReference); + }; + return effectReference; + }; + return [wrappedEffect, () => { + cleanup2(); + }]; + } + + // packages/alpinejs/src/utils/dispatch.js + function dispatch(el, name, detail = {}) { + el.dispatchEvent( + new CustomEvent(name, { + detail, + bubbles: true, + // Allows events to pass the shadow DOM barrier. + composed: true, + cancelable: true + }) + ); + } + + // packages/alpinejs/src/utils/walk.js + function walk(el, callback) { + if (typeof ShadowRoot === "function" && el instanceof ShadowRoot) { + Array.from(el.children).forEach((el2) => walk(el2, callback)); + return; + } + let skip = false; + callback(el, () => skip = true); + if (skip) + return; + let node = el.firstElementChild; + while (node) { + walk(node, callback, false); + node = node.nextElementSibling; + } + } + + // packages/alpinejs/src/utils/warn.js + function warn(message, ...args) { + console.warn(`Alpine Warning: ${message}`, ...args); + } + + // packages/alpinejs/src/lifecycle.js + var started = false; + function start() { + if (started) + warn("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."); + started = true; + if (!document.body) + warn("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` @include ('partials.bootstrap-table') @stop diff --git a/resources/views/account/accept/index.blade.php b/resources/views/account/accept/index.blade.php index f2a9bc56f2..77cf21664c 100755 --- a/resources/views/account/accept/index.blade.php +++ b/resources/views/account/accept/index.blade.php @@ -24,7 +24,7 @@ data-side-pagination="client" data-show-columns="true" data-show-export="true" - data-show-refresh="true" + data-show-refresh="false" data-sort-order="asc" id="pendingAcceptances" class="table table-striped snipe-table" @@ -41,8 +41,13 @@ @foreach ($acceptances as $acceptance) + @if ($acceptance->checkoutable) {{ ($acceptance->checkoutable) ? $acceptance->checkoutable->present()->name : '' }} {{ trans('general.accept_decline') }} + @else + ----- + {{ trans('general.error_user_company_accept_view') }} + @endif @endforeach diff --git a/resources/views/account/profile.blade.php b/resources/views/account/profile.blade.php index 2c3158c88f..a42c91e7a1 100755 --- a/resources/views/account/profile.blade.php +++ b/resources/views/account/profile.blade.php @@ -46,7 +46,7 @@
-
+
@if (!config('app.lock_passwords')) {!! Form::locales('locale', old('locale', $user->locale), 'select2') !!} diff --git a/resources/views/account/requestable-assets.blade.php b/resources/views/account/requestable-assets.blade.php index ceadd2e374..8e3c08fd57 100644 --- a/resources/views/account/requestable-assets.blade.php +++ b/resources/views/account/requestable-assets.blade.php @@ -57,6 +57,7 @@ {{ trans('general.image') }} + {{ trans('general.asset_tag') }} {{ trans('admin/hardware/table.asset_model') }} {{ trans('admin/models/table.modelnumber') }} {{ trans('admin/hardware/form.name') }} @@ -64,6 +65,12 @@ {{ trans('admin/hardware/table.location') }} {{ trans('admin/hardware/table.status') }} {{ trans('admin/hardware/form.expected_checkin') }} + + @foreach(\App\Models\CustomField::get() as $field) + @if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) + {{ $field->name }} + @endif + @endforeach {{ trans('table.actions') }} @@ -78,7 +85,7 @@
@if ($models->count() > 0) -

{{ trans('general.requestable_models') }}

+

{{ trans('general.requestable_models') }}

- - - - - - - + + + + + + + + + @foreach(\App\Models\CustomField::get() as $field) + @if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) + + @endif + @endforeach
{{ trans('general.image') }}{{ trans('general.item_name') }}{{ trans('general.type') }}{{ trans('general.qty') }}{{ trans('admin/hardware/table.location') }} {{ trans('admin/hardware/form.expected_checkin') }} {{ trans('general.requested_date') }}{{ trans('general.image') }}{{ trans('general.item_name') }}{{ trans('general.type') }}{{ trans('general.qty') }}{{ trans('admin/hardware/table.location') }} {{ trans('admin/hardware/form.expected_checkin') }} {{ trans('general.requested_date') }}{{ $field->name }}
diff --git a/resources/views/account/view-assets.blade.php b/resources/views/account/view-assets.blade.php index b77bddcf0d..81011175a6 100755 --- a/resources/views/account/view-assets.blade.php +++ b/resources/views/account/view-assets.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') -{{ trans('general.hello_name', array('name' => $user->present()->fullName())) }} +{{ trans('general.hello_name', array('name' => $user->present()->getFullNameAttribute())) }} @parent @stop @@ -388,7 +388,7 @@ data-show-columns="true" data-show-export="true" data-show-footer="true" - data-show-refresh="true" + data-show-refresh="false" data-sort-order="asc" id="userAssets" class="table table-striped snipe-table" @@ -478,7 +478,7 @@ data-side-pagination="client" data-show-columns="true" data-show-export="true" - data-show-refresh="true" + data-show-refresh="false" data-sort-order="asc" id="userLicenses" class="table table-striped snipe-table" @@ -525,7 +525,7 @@ data-show-fullscreen="true" data-show-export="true" data-show-footer="true" - data-show-refresh="true" + data-show-refresh="false" data-sort-order="asc" data-sort-name="name" class="table table-striped snipe-table table-hover" @@ -576,7 +576,7 @@ data-show-fullscreen="true" data-show-export="true" data-show-footer="true" - data-show-refresh="true" + data-show-refresh="false" data-sort-order="asc" data-sort-name="name" class="table table-striped snipe-table table-hover" diff --git a/resources/views/asset_maintenances/view.blade.php b/resources/views/asset_maintenances/view.blade.php index cea5eb2c04..58abdbc7f5 100644 --- a/resources/views/asset_maintenances/view.blade.php +++ b/resources/views/asset_maintenances/view.blade.php @@ -93,7 +93,7 @@ use Carbon\Carbon;
{{ trans('admin/asset_maintenances/form.notes') }}: - {{ $assetMaintenance->notes }} + {!! nl2br(Helper::parseEscapedMarkedownInline($assetMaintenance->notes)) !!}
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 330d7bf503..bd91170b7d 100755 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -4,9 +4,10 @@ {{-- Page content --}} @section('content') -
+ + @@ -45,12 +46,12 @@
- + {!! $errors->first('username', '') !!}
- + {!! $errors->first('password', '') !!}
@@ -64,7 +65,7 @@
@if (!config('app.require_saml') && $snipeSettings->saml_enabled) -
+ -
+ @if ($snipeSettings->custom_forgot_pass_url) - {{ trans('auth/general.forgot_password') }} + @elseif (!config('app.require_saml')) - {{ trans('auth/general.forgot_password') }} + @endif -
+
+ @if (($snipeSettings->google_login=='1') && ($snipeSettings->google_client_id!='') && ($snipeSettings->google_client_secret!='')) + + + {{ trans('auth/general.google_login') }} + + @endif +
diff --git a/resources/views/categories/edit.blade.php b/resources/views/categories/edit.blade.php index 1705fe2332..1ef3b7aa6a 100755 --- a/resources/views/categories/edit.blade.php +++ b/resources/views/categories/edit.blade.php @@ -23,59 +23,13 @@
- - - -
- -
- {{ Form::textarea('eula_text', old('eula_text', $item->eula_text), array('class' => 'form-control', 'aria-label'=>'eula_text')) }} -

{!! trans('admin/categories/general.eula_text_help') !!}

-

{!! trans('admin/settings/general.eula_markdown') !!}

- - {!! $errors->first('eula_text', '') !!} -
-
- - -
-
- @if ($snipeSettings->default_eula_text!='') - - @else - - @endif -
-
- - - -
-
- -
-
- - - -
-
- -
-
- + @include ('partials.forms.edit.image-upload', ['image_path' => app('categories_upload_path')]) diff --git a/resources/views/companies/edit.blade.php b/resources/views/companies/edit.blade.php index 6896cee972..60d5f74ccb 100644 --- a/resources/views/companies/edit.blade.php +++ b/resources/views/companies/edit.blade.php @@ -9,6 +9,9 @@ {{-- Page content --}} @section('inputFields') @include ('partials.forms.edit.name', ['translated_name' => trans('admin/companies/table.name')]) +@include ('partials.forms.edit.phone') +@include ('partials.forms.edit.fax') +@include ('partials.forms.edit.email') @include ('partials.forms.edit.image-upload', ['image_path' => app('companies_upload_path')]) @stop diff --git a/resources/views/components/checkin.blade.php b/resources/views/components/checkin.blade.php index 6a7055f727..e195685d2a 100644 --- a/resources/views/components/checkin.blade.php +++ b/resources/views/components/checkin.blade.php @@ -17,7 +17,7 @@
- + {{csrf_field()}}
diff --git a/resources/views/components/view.blade.php b/resources/views/components/view.blade.php index 70bd87948b..6c3c04511a 100644 --- a/resources/views/components/view.blade.php +++ b/resources/views/components/view.blade.php @@ -203,10 +203,15 @@ @if ($file->filename) - + {{ trans('general.download') }} + + + + + @endif {{ $file->created_at }} diff --git a/resources/views/consumables/view.blade.php b/resources/views/consumables/view.blade.php index 6bb3dc26be..80987335ff 100644 --- a/resources/views/consumables/view.blade.php +++ b/resources/views/consumables/view.blade.php @@ -74,7 +74,7 @@ data-sort-name="name" id="consumablesCheckedoutTable" class="table table-striped snipe-table" - data-url="{{route('api.consumables.showUsers', $consumable->id)}}" + data-url="{{route('api.consumables.show.users', $consumable->id)}}" data-export-options='{ "fileName": "export-consumables-{{ str_slug($consumable->name) }}-checkedout-{{ date('Y-m-d') }}", "ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"] @@ -155,15 +155,19 @@ @if ($file->note) - {{ $file->note }} + {!! nl2br(Helper::parseEscapedMarkedownInline($file->note)) !!} @endif @if ($file->filename) - + {{ trans('general.download') }} + + + + @endif {{ $file->created_at }} @@ -275,7 +279,7 @@
- {!! nl2br(e($consumable->notes)) !!} + {!! nl2br(Helper::parseEscapedMarkedownInline($consumable->notes)) !!}
@endif diff --git a/resources/views/custom_fields/fields/edit.blade.php b/resources/views/custom_fields/fields/edit.blade.php index 9d51138442..504b556faa 100644 --- a/resources/views/custom_fields/fields/edit.blade.php +++ b/resources/views/custom_fields/fields/edit.blade.php @@ -88,7 +88,7 @@ } @endphp
- {{ Form::select("format",Helper::predefined_formats(), ($field_format == '') ? $field->format : $field_format, array('class'=>'format select2 form-control', 'aria-label'=>'format')) }} + {{ Form::select("format",Helper::predefined_formats(), ($field_format == '') ? $field->format : $field_format, array('class'=>'format select2 form-control', 'aria-label'=>'format', 'style' => 'width:100%;')) }} {!! $errors->first('format', '') !!}
@@ -118,8 +118,40 @@ - -
+ +
+ + + @if (($field->id) && ($field->field_encrypted=='1')) +
+
+ + {{ trans('general.notification_warning') }}: + {{ trans('admin/custom_fields/general.encrypted_options') }} +
+ +
+ @endif + + @if (!$field->id) + +
+ +
+ + + @endif + + + +
- @if (!$field->id) - -
- -
+ +
+ +
- - @endif + + @if ((!$field->id) || ($field->field_encrypted=='0')) + + +
+ +
-
+
+ +
+ +
+ @endif + -
+
- -
- -
+
@@ -278,11 +316,13 @@ $("#show_in_email").hide(); $("#display_in_user_view").hide(); $("#is_unique").hide(); + $("#show_in_requestable_list").hide(); } else { $("#encrypt_warning").hide(); $("#show_in_email").show(); $("#display_in_user_view").show(); $("#is_unique").show(); + $("#show_in_requestable_list").show(); } }); diff --git a/resources/views/custom_fields/fieldsets/view.blade.php b/resources/views/custom_fields/fieldsets/view.blade.php index c330522cc1..003ee0cd84 100644 --- a/resources/views/custom_fields/fieldsets/view.blade.php +++ b/resources/views/custom_fields/fieldsets/view.blade.php @@ -51,7 +51,7 @@ @endcan - {{$field->pivot->order}} + {{$field->pivot->order + 1}} {{$field->name}} {{$field->format}} {{$field->element}} diff --git a/resources/views/custom_fields/index.blade.php b/resources/views/custom_fields/index.blade.php index ba95199ead..08f2fb3845 100644 --- a/resources/views/custom_fields/index.blade.php +++ b/resources/views/custom_fields/index.blade.php @@ -145,9 +145,14 @@ - - - {{ trans('admin/custom_fields/general.field_element_short') }} + + + + + + + {{ trans('admin/custom_fields/general.unique') }} + {{ trans('admin/custom_fields/general.field_element_short') }} {{ trans('admin/custom_fields/general.fieldsets') }} {{ trans('button.actions') }} @@ -158,7 +163,7 @@ {{ $field->name }} {{ $field->help_text }} - {!! ($field->is_unique=='1') ? '' : '' !!} + {!! ($field->is_unique=='1') ? '' : '' !!} {{ $field->convertUnicodeDbSlug() }} @if ($field->convertUnicodeDbSlug()!=$field->db_column) @@ -167,9 +172,12 @@ @endif {{ $field->format }} - {!! ($field->field_encrypted=='1' ? '' : '') !!} - {!! ($field->display_in_user_view=='1' ? '' : '') !!} - {!! ($field->show_in_email=='1') ? '' : '' !!} + {!! ($field->field_encrypted=='1' ? '' : '') !!} + {!! ($field->show_in_listview=='1' ? '' : '') !!} + {!! ($field->display_in_user_view=='1' ? '' : '') !!} + {!! ($field->show_in_email=='1') ? '' : '' !!} + {!! ($field->show_in_requestable_list=='1') ? '' : '' !!} + {!! ($field->is_unique=='1') ? '' : '' !!} {{ $field->element }} @foreach($field->fieldset as $fieldset) diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 296a940b3f..2744946871 100755 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -274,63 +274,136 @@
- -
-
-

{{ trans('general.asset') }} {{ trans('general.locations') }}

-
- -
-
- -
-
-
-
- - - - - - - - - - - -
{{ trans('general.name') }} - - {{ trans('general.asset_count') }} - - - {{ trans('general.assigned') }} - - - {{ trans('general.people') }} - -
-
-
- -
+ @if ($snipeSettings->full_multiple_companies_support=='1') + +
+
+

{{ trans('general.companies') }}

+
+ +
+
+ +
+
+
+
+ - - + + + + + + + + + + + +
{{ trans('general.name') }} + + {{ trans('general.people') }} + + + {{ trans('general.asset_count') }} + + + {{ trans('general.accessories_count') }} + + + {{ trans('general.consumables_count') }} + + + {{ trans('general.components_count') }} + + + {{ trans('general.licenses_count') }} +
+
+
+ +
+ +
+
+ + @else + +
+
+

{{ trans('general.locations') }}

+
+ +
+
+ +
+
+
+
+ + + + + + + + + + + + +
{{ trans('general.name') }} + + {{ trans('general.asset_count') }} + + + {{ trans('general.assigned') }} + + + {{ trans('general.people') }} + +
+
+
+ +
+ +
+
+ + @endif +
diff --git a/resources/views/departments/edit.blade.php b/resources/views/departments/edit.blade.php index 9e6fd40e80..c1a2b38d56 100644 --- a/resources/views/departments/edit.blade.php +++ b/resources/views/departments/edit.blade.php @@ -16,6 +16,9 @@ @endif + @include ('partials.forms.edit.phone') + @include ('partials.forms.edit.fax') + @include ('partials.forms.edit.user-select', ['translated_name' => trans('admin/users/table.manager'), 'fieldname' => 'manager_id']) diff --git a/resources/views/hardware/bulk.blade.php b/resources/views/hardware/bulk.blade.php index c2fe105a2d..a7e52dfa56 100755 --- a/resources/views/hardware/bulk.blade.php +++ b/resources/views/hardware/bulk.blade.php @@ -21,6 +21,9 @@
{{ trans_choice('admin/hardware/form.bulk_update_warn', count($assets), ['asset_count' => count($assets)]) }} + @if (count($models) > 0) + {{ trans_choice('admin/hardware/form.bulk_update_with_custom_field', count($models), ['asset_model_count' => count($models)]) }} + @endif
@@ -59,7 +62,7 @@
@@ -89,9 +92,13 @@ {{ Form::radio('update_real_loc', '1', old('update_real_loc'), ['checked'=> 'checked', 'aria-label'=>'update_real_loc']) }} {{ trans('admin/hardware/form.asset_location_update_default_current') }} +
@@ -182,6 +189,8 @@
+ @include("models/custom_fields_form_bulk_edit",["models" => $models]) + @foreach ($assets as $key => $value) @endforeach diff --git a/resources/views/hardware/checkin.blade.php b/resources/views/hardware/checkin.blade.php index f7fe067c8a..dd6237b1bd 100755 --- a/resources/views/hardware/checkin.blade.php +++ b/resources/views/hardware/checkin.blade.php @@ -73,7 +73,7 @@
- @include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id', 'help_text' => ($asset->defaultLoc) ? 'You can choose to check this asset in to a location other than the default location of '.$asset->defaultLoc->name.' if one is set.' : null]) + @include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id', 'help_text' => ($asset->defaultLoc) ? 'You can choose to check this asset in to a location other than the default location of '.$asset->defaultLoc->name.' if one is set.' : null, 'hide_location_radio' => true])
diff --git a/resources/views/hardware/index.blade.php b/resources/views/hardware/index.blade.php index a9a0dba580..d30a1c012a 100755 --- a/resources/views/hardware/index.blade.php +++ b/resources/views/hardware/index.blade.php @@ -34,7 +34,7 @@ {{ trans('general.assets') }} @if (Request::has('order_number')) - : Order #{{ Request::get('order_number') }} + : Order #{{ strval(Request::get('order_number')) }} @endif @stop @@ -88,7 +88,7 @@ class="table table-striped snipe-table" data-url="{{ route('api.assets.index', array('status' => e(Request::get('status')), - 'order_number'=>e(Request::get('order_number')), + 'order_number'=>e(strval(Request::get('order_number'))), 'company_id'=>e(Request::get('company_id')), 'status_id'=>e(Request::get('status_id')))) }}" data-export-options='{ diff --git a/resources/views/hardware/labels.blade.php b/resources/views/hardware/labels.blade.php index b25b08a0a9..ab4cb5e339 100644 --- a/resources/views/hardware/labels.blade.php +++ b/resources/views/hardware/labels.blade.php @@ -52,7 +52,7 @@ $qr_size = ($settings->alt_barcode_enabled=='1') && ($settings->alt_barcode!='') } img.barcode { display:block; - margin-top:-7px; + margin-top:{{$settings->qr_code=='1' ? '-15px' : '-7px;'}}; width: 100%; } div.label-logo { diff --git a/resources/views/hardware/requested.blade.php b/resources/views/hardware/requested.blade.php index 25b2a51e28..dcc8a01936 100644 --- a/resources/views/hardware/requested.blade.php +++ b/resources/views/hardware/requested.blade.php @@ -17,11 +17,6 @@
- {{ Form::open([ - 'method' => 'POST', - 'route' => ['hardware/bulkedit'], - 'class' => 'form-inline', - 'id' => 'bulkForm']) }}
@@ -51,7 +46,7 @@ {{ trans('admin/hardware/form.expected_checkin') }} {{ trans('admin/hardware/table.requesting_user') }} {{ trans('admin/hardware/table.requested_date') }} - {{ trans('general.checkin').'/'.trans('general.checkout') }} + {{ trans('button.actions') }} @@ -94,7 +89,7 @@ @endif - @if ($request->requestingUser()) + @if ($request->requestingUser() && !$request->requestingUser()->trashed()) {{ $request->requestingUser()->present()->fullName() }} @@ -103,6 +98,14 @@ @endif {{ App\Helpers\Helper::getFormattedDateObject($request->created_at, 'datetime', false) }} + + {{ Form::open([ + 'method' => 'POST', + 'route' => ['account/request-item', $request->itemType(), $request->requestable->id, true, $request->requestingUser()->id], + ]) }} + + {{ Form::close() }} + @if ($request->itemType() == "asset") @if ($request->requestable->assigned_to=='') diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 50b80de0f2..5a043ffc40 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -6,67 +6,6 @@ @parent @stop -{{-- Right header --}} -@section('header_right') - - - @can('manage', \App\Models\Asset::class) - @if ($asset->deleted_at=='') - - @endif - @endcan -@stop - {{-- Page content --}} @section('content') @@ -75,8 +14,7 @@ @if (!$asset->model)
-

{{ trans('admin/models/message.no_association') }}

-

{{ trans('admin/models/message.no_association_fix') }}

+

{{ trans('admin/models/message.no_association') }} {{ trans('admin/models/message.no_association_fix') }}

@endif @@ -183,7 +121,7 @@ @@ -283,7 +221,11 @@ {{ trans('admin/hardware/form.serial') }}
- {{ $asset->serial }} + {{ $asset->serial }} + +
@endif @@ -342,8 +284,9 @@ @if (($asset->model) && ($asset->model->manufacturer->url))
  • - + {{ $asset->model->manufacturer->url }} +
  • @endif @@ -351,8 +294,19 @@ @if (($asset->model) && ($asset->model->manufacturer->support_url))
  • - + {{ $asset->model->manufacturer->support_url }} + + +
  • + @endif + + @if (($asset->model->manufacturer) && ($asset->model->manufacturer->warranty_lookup_url!='')) +
  • + + + {{ $asset->present()->dynamicWarrantyUrl() }} +
  • @endif @@ -460,7 +414,7 @@ @endif @if ($field->isFieldDecryptable($asset->{$field->db_column_name()} )) - @can('superuser') + @can('assets.view.encrypted_custom_fields') @if (($field->format=='URL') && ($asset->{$field->db_column_name()}!='')) {{ Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()}) }} @elseif (($field->format=='DATE') && ($asset->{$field->db_column_name()}!='')) @@ -614,9 +568,9 @@ {{ $asset->warranty_months }} {{ trans('admin/hardware/form.months') }} - @if (($asset->model->manufacturer) && ($asset->model->manufacturer->warranty_lookup_url!='')) + @if (($asset->model) && ($asset->model->manufacturer) && ($asset->model->manufacturer->warranty_lookup_url!='')) - {{ trans('admin/hardware/general.mfg_warranty_lookup', ['manufacturer' => $asset->model->manufacturer->name]) }} + @endif
    @@ -677,7 +631,7 @@
    @endif - @if (($asset->model) && ($asset->model->eol)) + @if (($asset->asset_eol_date) && ($asset->purchase_date))
    @@ -685,7 +639,7 @@
    - {{ $asset->model->eol }} + {{ Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date) }} {{ trans('admin/hardware/form.months') }}
    @@ -696,6 +650,9 @@
    {{ trans('admin/hardware/form.eol_date') }} + @if ($asset->purchase_date) + {!! $asset->asset_eol_date < date("Y-m-d") ? '' : '' !!} + @endif
    @@ -706,6 +663,15 @@ @else {{ trans('general.na_no_purchase_date') }} @endif + @if ($asset->eol_explicit) + + @endif
    @endif @@ -730,7 +696,7 @@
    - {!! nl2br(e($asset->notes)) !!} + {!! nl2br(Helper::parseEscapedMarkedownInline($asset->notes)) !!}
    @@ -873,17 +839,73 @@
    @if (($asset->image) || (($asset->model) && ($asset->model->image!=''))) - - + @else + @endif + + + @if (($asset->assetstatus) && ($asset->assetstatus->deployable=='1')) + @if (($asset->assigned_to != '') && ($asset->deleted_at=='')) + @can('checkin', \App\Models\Asset::class) + + @endcan + @elseif (($asset->assigned_to == '') && ($asset->deleted_at=='')) + @can('checkout', \App\Models\Asset::class) + + @endcan + @endif + @endif + + + @can('update', $asset) + + @endcan + + @can('create', $asset) + + @endcan + + @can('audit', \App\Models\Asset::class) + + @endcan + + @can('delete', $asset) + @if ($asset->deleted_at=='') +
    + + {{ trans('general.delete') }} +
    + @endif + @endcan + @if ($asset->deleted_at!='') -
    +
    @csrf @@ -896,11 +918,13 @@ @endif @if (($asset->assignedTo) && ($asset->deleted_at=='')) -

    {{ trans('admin/hardware/form.checkedout_to') }}

    +
    +

    {{ trans('admin/hardware/form.checkedout_to') }}

    @if($asset->checkedOutToUser()) {{ $asset->assignedTo->present()->fullName() }} @endif +

    {!! $asset->assignedTo->present()->glyph() . ' ' .$asset->assignedTo->present()->nameUrl() !!}

    @@ -919,6 +943,10 @@ @endif + @if((isset($asset->assignedTo)) && ($asset->assignedTo->department)) +
  • {{ trans('admin/hardware/general.user_department') }}: {{ $asset->assignedTo->department->name}}
  • + @endif + @if (isset($asset->location))
  • {{ $asset->location->name }}
  • {{ $asset->location->address }} @@ -934,7 +962,16 @@ {{ $asset->location->state }} {{ $asset->location->zip }}
  • @endif +
  • + {{ trans('admin/hardware/form.checkout_date') }}: {{ Helper::getFormattedDateObject($asset->last_checkout, 'date', false) }} +
  • + @if (isset($asset->expected_checkin)) +
  • + {{ trans('admin/hardware/form.expected_checkin') }}: {{ Helper::getFormattedDateObject($asset->expected_checkin, 'date', false) }} +
  • + @endif +
    @endif
    @@ -996,6 +1033,8 @@ {{ trans('general.qty') }} {{ trans('general.purchase_cost') }} {{trans('admin/hardware/form.serial')}} + {{trans('general.checkin')}} + @@ -1010,6 +1049,9 @@ {{ $component->pivot->assigned_qty }} {{ Helper::formatCurrencyOutput($component->purchase_cost) }} each {{ $component->serial }} + + {{ trans('general.checkin') }} + purchase_cost *$component->pivot->assigned_qty) ?> @@ -1163,7 +1205,7 @@ data-cookie="true"> - {{ trans('admin/hardware/table.icon') }} + {{ trans('admin/hardware/table.icon') }} {{ trans('general.date') }} {{ trans('general.admin') }} {{ trans('general.action') }} @@ -1245,9 +1287,13 @@ @if (($file->filename) && (Storage::exists('private_uploads/assets/'.$file->filename))) - + + + + + @endif @@ -1281,7 +1327,7 @@
    - @if ($asset->model->uploads->count() > 0) + @if (($asset->model) && ($asset->model->uploads->count() > 0)) + diff --git a/resources/views/reports/audit.blade.php b/resources/views/reports/audit.blade.php index 15ad80964d..3d16b0714b 100644 --- a/resources/views/reports/audit.blade.php +++ b/resources/views/reports/audit.blade.php @@ -34,7 +34,7 @@ - + diff --git a/resources/views/reports/custom.blade.php b/resources/views/reports/custom.blade.php index 2220b2f163..3eb5e01866 100644 --- a/resources/views/reports/custom.blade.php +++ b/resources/views/reports/custom.blade.php @@ -141,6 +141,11 @@ {{ trans('admin/hardware/table.checkout_date') }} + + + + + + + + + + + + + + + + @if ($customfields->count() > 0) @@ -235,72 +272,116 @@
    - {!! trans('general.report_fields_info') !!} +

    + {!! trans('general.report_fields_info') !!} +

    - @include ('partials.forms.edit.company-select', ['translated_name' => trans('general.company'), 'fieldname' => 'by_company_id', 'hide_new' => 'true']) - @include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'by_location_id', 'hide_new' => 'true']) - @include ('partials.forms.edit.location-select', ['translated_name' => trans('admin/hardware/form.default_location'), 'fieldname' => 'by_rtd_location_id', 'hide_new' => 'true']) - @include ('partials.forms.edit.department-select', ['translated_name' => trans('general.department'), 'fieldname' => 'by_dept_id', 'hide_new' => 'true']) - @include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'by_supplier_id', 'hide_new' => 'true']) - @include ('partials.forms.edit.model-select', ['translated_name' => trans('general.asset_model'), 'fieldname' => 'by_model_id', 'hide_new' => 'true']) +
    + + @include ('partials.forms.edit.company-select', ['translated_name' => trans('general.company'),'multiple' => 'true', 'fieldname' => 'by_company_id[]', 'hide_new' => 'true']) + @include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'multiple' => 'true', 'fieldname' => 'by_location_id[]', 'hide_new' => 'true']) + @include ('partials.forms.edit.location-select', ['translated_name' => trans('admin/hardware/form.default_location'), 'multiple' => 'true', 'fieldname' => 'by_rtd_location_id[]', 'hide_new' => 'true']) + @include ('partials.forms.edit.department-select', ['translated_name' => trans('general.department'), 'fieldname' => 'by_dept_id', 'hide_new' => 'true']) + @include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'by_supplier_id[]', 'multiple' => 'true', 'hide_new' => 'true']) + @include ('partials.forms.edit.model-select', ['translated_name' => trans('general.asset_model'), 'fieldname' => 'by_model_id[]', 'multiple' => 'true', 'hide_new' => 'true']) @include ('partials.forms.edit.manufacturer-select', ['translated_name' => trans('general.manufacturer'), 'fieldname' => 'by_manufacturer_id', 'hide_new' => 'true']) @include ('partials.forms.edit.category-select', ['translated_name' => trans('general.category'), 'fieldname' => 'by_category_id', 'hide_new' => 'true', 'category_type' => 'asset']) - - -
    - -
    - {{ Form::select('by_status_id', Helper::statusLabelList() , old('by_status_id'), array('class'=>'select2', 'style'=>'width:100%', 'aria-label'=>'by_status_id')) }} -
    -
    - + @include ('partials.forms.edit.status-select', ['translated_name' => trans('admin/hardware/form.status'), 'fieldname' => 'by_status_id[]', 'multiple' => 'true', 'hide_new' => 'true'])
    -
    +
    -
    +
    -
    - +
    + to - +
    + + @if ($errors->has('purchase_start') || $errors->has('purchase_end')) +
    + {!! $errors->first('purchase_start', '') !!} + {!! $errors->first('purchase_end', '') !!} +
    + @endif +
    -
    +
    -
    - +
    + to - +
    + + @if ($errors->has('created_start') || $errors->has('created_end')) +
    + {!! $errors->first('created_start', '') !!} + {!! $errors->first('created_end', '') !!} +
    + @endif
    -
    +
    -
    - +
    + to - +
    + + @if ($errors->has('checkout_date_start') || $errors->has('checkout_date_end')) +
    + {!! $errors->first('checkout_date_start', '') !!} + {!! $errors->first('checkout_date_end', '') !!} +
    + @endif + +
    + + +
    + +
    + + {{ strtolower(trans('general.to')) }} + +
    + + @if ($errors->has('checkin_date_start') || $errors->has('checkin_date_end')) +
    + {!! $errors->first('checkin_date_start', '') !!} + {!! $errors->first('checkin_date_end', '') !!} +
    + @endif
    -
    +
    -
    - +
    + to - +
    + + @if ($errors->has('expected_checkin_start') || $errors->has('expected_checkin_end')) +
    + {!! $errors->first('expected_checkin_start', '') !!} + {!! $errors->first('expected_checkin_end', '') !!} +
    + @endif +
    @@ -314,23 +395,37 @@
    -
    +
    -
    - +
    + to - +
    + + @if ($errors->has('last_audit_start') || $errors->has('last_audit_end')) +
    + {!! $errors->first('last_audit_start', '') !!} + {!! $errors->first('last_audit_end', '') !!} +
    + @endif
    -
    +
    -
    - +
    + to - +
    + + @if ($errors->has('next_audit_start') || $errors->has('next_audit_end')) +
    + {!! $errors->first('next_audit_start', '') !!} + {!! $errors->first('next_audit_end', '') !!} +
    + @endif
    @@ -395,6 +490,13 @@ format: 'yyyy-mm-dd' }); + $('.checkin-range .input-daterange').datepicker({ + clearBtn: true, + todayHighlight: true, + endDate: '0d', + format: 'yyyy-mm-dd' + }); + $('.expected_checkin-range .input-daterange').datepicker({ clearBtn: true, todayHighlight: true, diff --git a/resources/views/reports/unaccepted_assets.blade.php b/resources/views/reports/unaccepted_assets.blade.php index 37ae66ce30..80d151fd42 100644 --- a/resources/views/reports/unaccepted_assets.blade.php +++ b/resources/views/reports/unaccepted_assets.blade.php @@ -77,11 +77,23 @@
    - @endif diff --git a/resources/views/settings/backups.blade.php b/resources/views/settings/backups.blade.php index 94733141c0..67c1976541 100644 --- a/resources/views/settings/backups.blade.php +++ b/resources/views/settings/backups.blade.php @@ -49,7 +49,7 @@ - + @@ -71,7 +71,7 @@ class="btn delete-asset btn-danger btn-sm {{ (config('app.lock_passwords')) ? ' disabled': '' }}" data-toggle="modal" href="{{ route('settings.backups.destroy', $file['filename']) }}" data-content="{{ trans('admin/settings/message.backup.delete_confirm') }}" - data-title="{{ trans('general.delete') }} {{ e($file['filename']) }} ?" + data-title="{{ trans('general.delete') }} {{ e($file['filename']) }}?" onClick="return false;"> {{ trans('general.delete') }} @@ -137,7 +137,7 @@ @csrf -
    +
    @@ -145,26 +145,18 @@ - +
    - -

    -

    {{ trans_choice('general.filetypes_accepted_help', 1, ['size' => Helper::file_upload_max_size_readable(), 'types' => '.zip']) }}

    - {!! $errors->first('image', '') !!} - - + {!! $errors->first('file', '') !!} +
    diff --git a/resources/views/settings/general.blade.php b/resources/views/settings/general.blade.php index 433f6c8123..49620d2e0b 100644 --- a/resources/views/settings/general.blade.php +++ b/resources/views/settings/general.blade.php @@ -14,11 +14,7 @@ {{-- Page content --}} @section('content') - + @@ -260,11 +256,13 @@ trans('admin/settings/general.show_archived_in_list')) }}
    - {{ Form::checkbox('show_archived_in_list', '1', Request::old('show_archived_in_list', $setting->show_archived_in_list),array('class' => 'minimal')) }} - {{ trans('admin/settings/general.show_archived_in_list_text') }} - {!! $errors->first('show_archived_in_list', '') !!} -
    + + {!! $errors->first('show_archived_in_list', '') !!} + @@ -274,8 +272,10 @@ trans('admin/settings/general.show_assigned_assets')) }}
    +

    {{ trans('admin/settings/general.show_assigned_assets_help') }}

    {!! $errors->first('show_assigned_assets', ':message') !!}
    diff --git a/resources/views/settings/google.blade.php b/resources/views/settings/google.blade.php new file mode 100644 index 0000000000..d9a4c4c622 --- /dev/null +++ b/resources/views/settings/google.blade.php @@ -0,0 +1,117 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') + {{ trans('admin/settings/general.google_login') }} + @parent +@stop + +@section('header_right') + {{ trans('general.back') }} +@stop + + +{{-- Page content --}} +@section('content') + + + + {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }} + + {{csrf_field()}} + +
    +
    + + +
    +
    +

    + {{ trans('admin/settings/general.google_login') }} +

    +
    +
    + + +
    + + +
    +
    + Redirect URL +
    +
    +

    {{ config('app.url') }}/google/callback

    +

    {!! trans('admin/settings/general.google_callback_help') !!}

    +
    +
    + + + +
    + +
    + +

    {{ trans('admin/settings/general.enable_google_login_help') }}

    +
    +
    + + + +
    +
    + {{ Form::label('google_client_id', 'Client ID') }} +
    +
    + {{ Form::text('google_client_id', old('google_client_id', $setting->google_client_id), ['class' => 'form-control','placeholder' => trans('general.example') .'000000000000-XXXXXXXXXXX.apps.googleusercontent.com', (config('app.lock_passwords')===true) ? 'disabled': '']) }} + {!! $errors->first('google_client_id', '') !!} + @if (config('app.lock_passwords')===true) +

    {{ trans('general.feature_disabled') }}

    + @endif +
    +
    + + +
    +
    + {{ Form::label('google_client_secret', 'Client Secret') }} +
    +
    + + @if (config('app.lock_passwords')===true) + {{ Form::text('google_client_secret', 'XXXXXXXXXXXXXXXXXXXXXXX', ['class' => 'form-control', 'disabled']) }} + @else + {{ Form::text('google_client_secret', old('google_client_secret', $setting->google_client_secret), ['class' => 'form-control','placeholder' => trans('general.example') .'XXXXXXXXXXXX']) }} + @endif + + {!! $errors->first('google_client_secret', '') !!} + @if (config('app.lock_passwords')===true) +

    {{ trans('general.feature_disabled') }}

    + @endif +
    +
    + +
    + + +
    + +
    +
    +
    + + {{Form::close()}} + +@stop diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php index 678233c933..9ff9c94f71 100755 --- a/resources/views/settings/index.blade.php +++ b/resources/views/settings/index.blade.php @@ -54,7 +54,7 @@ } - +
    @@ -235,6 +235,21 @@
    +
    +
    +
    +
    + + +

    + Google +
    +
    +

    {{ trans('admin/settings/general.google_login') }}

    +
    +
    +
    +
    @@ -331,7 +346,7 @@
    - + @@ -343,19 +358,19 @@

    {{ trans('admin/settings/general.system') }}

    -
    +
    - {{ trans('admin/settings/general.snipe_version') }} + {{ trans('admin/settings/general.snipe_version') }}:
    {{ config('version.app_version') }} build {{ config('version.build_version') }} ({{ config('version.hash_version') }})
    - {{ trans('admin/settings/general.license') }} + {{ trans('admin/settings/general.license') }}:
    AGPL3 @@ -366,19 +381,68 @@
    - {{ trans('admin/settings/general.php') }} + {{ trans('admin/settings/general.php') }}:
    {{ phpversion() }}
    - {{ trans('admin/settings/general.laravel') }} + {{ trans('admin/settings/general.laravel') }}:
    {{ $snipeSettings->lar_ver() }}
    +
    + +
    +
    + {{ trans('admin/settings/general.timezone') }}: +
    +
    + {{ config('app.timezone') }} +
    + +
    + {{ trans('admin/settings/general.database_driver') }}: +
    +
    + {{ config('database.default') }} +
    +
    + + +
    +
    + {{ trans('admin/settings/general.mail_from') }}: +
    +
    + {{ config('mail.from.name') }} + <{{ config('mail.from.address') }}> +
    + +
    + {{ trans('admin/settings/general.mail_reply_to') }}: +
    +
    + {{ config('mail.reply_to.name') }} + <{{ config('mail.reply_to.address') }}> +
    +
    + + +
    +
    + {{ trans('admin/settings/general.bs_table_storage') }}: +
    +
    + {{ config('session.bs_table_storage') }} +
    + +
    + +
    @@ -387,6 +451,9 @@
    + + + @section('moar_scripts') -
    -
    - {{ Form::text('labels_display_bgutter', old('labels_display_bgutter', $setting->labels_display_bgutter), ['class' => 'form-control', 'aria-label'=>'labels_display_bgutter']) }} -
    {{ trans('admin/settings/general.vertical') }}
    -
    -
    -
    - {!! $errors->first('labels_display_sgutter', '') !!} - {!! $errors->first('labels_display_bgutter', '') !!} -
    - -
    -
    - {{ Form::label('labels_pmargin_top', trans('admin/settings/general.page_padding')) }} -
    -
    -
    - {{ Form::text('labels_pmargin_top', old('labels_pmargin_top', $setting->labels_pmargin_top), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_top']) }} -
    {{ trans('admin/settings/general.top') }}
    + +
    +
    + {{ Form::label('label2_title', trans('admin/settings/general.label2_title'), ['class'=>'control-label']) }}
    -
    - {{ Form::text('labels_pmargin_right', old('labels_pmargin_right', $setting->labels_pmargin_right), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_right']) }} -
    {{ trans('admin/settings/general.right') }}
    +
    + {{ Form::text('label2_title', old('label2_title', $setting->label2_title), [ 'class'=>'form-control', 'placeholder'=>$setting->qr_text, 'aria-label'=>'label2_title' ]) }} + {!! $errors->first('label2_title', '') !!} +

    {!! trans('admin/settings/general.label2_title_help') !!}

    +

    + {!! trans('admin/settings/general.label2_title_help_phold') !!}.
    + {!! trans('admin/settings/general.help_asterisk_bold') !!}.
    + {!! + trans('admin/settings/general.help_blank_to_use', [ + 'setting_name' => trans('admin/settings/general.barcodes').' > '.trans('admin/settings/general.qr_text') + ]) + !!} +

    -
    -
    - {{ Form::text('labels_pmargin_bottom', old('labels_pmargin_bottom', $setting->labels_pmargin_bottom), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_bottom']) }} -
    {{ trans('admin/settings/general.bottom') }}
    -
    -
    - {{ Form::text('labels_pmargin_left', old('labels_pmargin_left', $setting->labels_pmargin_left), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_left']) }} -
    {{ trans('admin/settings/general.left') }}
    -
    -
    -
    - {!! $errors->first('labels_width', '') !!} - {!! $errors->first('labels_height', '') !!} -
    -
    + +
    has('label2_asset_logo') ? 'error' : '' }}"> +
    -
    -
    - {{ Form::label('labels_pagewidth', trans('admin/settings/general.page_dimensions')) }} -
    -
    -
    - {{ Form::text('labels_pagewidth', old('labels_pagewidth', $setting->labels_pagewidth), ['class' => 'form-control', 'aria-label'=>'labels_pagewidth']) }} -
    {{ trans('admin/settings/general.width_w') }}
    -
    -
    -
    -
    - {{ Form::text('labels_pageheight', old('labels_pageheight', $setting->labels_pageheight), ['class' => 'form-control', 'aria-label'=>'labels_pageheight']) }} -
    {{ trans('admin/settings/general.height_h') }}
    -
    -
    -
    - {!! $errors->first('labels_pagewidth', '') !!} - {!! $errors->first('labels_pageheight', '') !!} -
    -
    - -
    -
    - {{ Form::label('labels_display', trans('admin/settings/general.label_fields')) }} -
    -
    - - - - +

    + {!! trans('admin/settings/general.label2_asset_logo_help', ['setting_name' => trans('admin/settings/general.brand').' > '.trans('admin/settings/general.label_logo')]) !!} +

    -
    -
    +
    +
    + +
    +
    + {{ Form::label('label2_1d_type', trans('admin/settings/general.label2_1d_type'), ['class'=>'control-label']) }} +
    +
    + @php + $select1DValues = [ + 'default' => trans('admin/settings/general.default').' [ '.$setting->alt_barcode.' ]', + 'none' => trans('admin/settings/general.none'), + 'C128' => 'C128', + 'C39' => 'C39', + 'EAN5' => 'EAN5', + 'EAN13' => 'EAN13', + 'UPCA' => 'UPCA', + 'UPCE' => 'UPCE' + ]; + @endphp + {{ Form::select('label2_1d_type', $select1DValues, old('label2_1d_type', $setting->label2_1d_type), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_1d_type' ]) }} + {!! $errors->first('label2_1d_type', '') !!} +

    + {{ trans('admin/settings/general.label2_1d_type_help') }}. + {!! + trans('admin/settings/general.help_default_will_use', [ + 'default' => trans('admin/settings/general.default'), + 'setting_name' => trans('admin/settings/general.barcodes').' > '.trans('admin/settings/general.alt_barcode_type'), + ]) + !!} +

    +
    +
    + +
    +
    + {{ Form::label('label2_2d_type', trans('admin/settings/general.label2_2d_type'), ['class'=>'control-label']) }} +
    +
    + @php + $select2DValues = [ + 'default' => trans('admin/settings/general.default').' [ '.$setting->barcode_type.' ]', + 'none' => trans('admin/settings/general.none'), + 'QRCODE' => 'QRCODE', + 'DATAMATRIX' => 'DATAMATRIX', + 'PDF417' => 'PDF417', + ]; + @endphp + {{ Form::select('label2_2d_type', $select2DValues, old('label2_2d_type', $setting->label2_2d_type), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_2d_type' ]) }} + {!! $errors->first('label2_2d_type', '') !!} +

    + {{ trans('admin/settings/general.label2_2d_type_help', ['current' => $setting->barcode_type]) }}. + {!! + trans('admin/settings/general.help_default_will_use', [ + 'default' => trans('admin/settings/general.default'), + 'setting_name' => trans('admin/settings/general.barcodes').' > '.trans('admin/settings/general.barcode_type'), + ]) + !!} +

    +
    +
    + + +
    +
    + {{ Form::label('label2_2d_target', trans('admin/settings/general.label2_2d_target'), ['class'=>'control-label']) }} +
    +
    + {{ Form::select('label2_2d_target', ['hardware_id'=>'/hardware/{id} ('.trans('admin/settings/general.default').')', 'ht_tag'=>'/ht/{asset_tag}'], old('label2_2d_target', $setting->label2_2d_target), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_2d_target' ]) }} + {!! $errors->first('label2_2d_target', '') !!} +

    {{ trans('admin/settings/general.label2_2d_target_help') }}

    +
    +
    + + +
    +
    + {{ Form::label('label2_fields', trans('admin/settings/general.label2_fields')) }} +
    +
    + @include('partials.label2-field-definitions', [ 'name' => 'label2_fields', 'value' => old('label2_fields', $setting->label2_fields), 'customFields' => $customFields ]) + {!! $errors->first('label2_fields', '') !!} +

    {{ trans('admin/settings/general.label2_fields_help') }}

    +
    +
    + + @include('partials.bootstrap-table') + + @else + + {{ Form::hidden('label2_template', old('label2_template', $setting->label2_template)) }} + {{ Form::hidden('label2_title', old('label2_title', $setting->label2_title)) }} + {{ Form::hidden('label2_asset_logo', old('label2_asset_logo', $setting->label2_asset_logo)) }} + {{ Form::hidden('label2_1d_type', old('label2_1d_type', $setting->label2_1d_type)) }} + {{ Form::hidden('label2_2d_type', old('label2_2d_type', $setting->label2_2d_type)) }} + {{ Form::hidden('label2_2d_target', old('label2_2d_target', $setting->label2_2d_target)) }} + {{ Form::hidden('label2_fields', old('label2_fields', $setting->label2_fields)) }} + @endif + + @if ($setting->label2_enable && ($setting->label2_template != 'DefaultLabel')) + + {{ Form::hidden('labels_per_page', old('labels_per_page', $setting->labels_per_page)) }} + {{ Form::hidden('labels_fontsize', old('labels_fontsize', $setting->labels_fontsize)) }} + {{ Form::hidden('labels_width', old('labels_width', $setting->labels_width)) }} + {{ Form::hidden('labels_height', old('labels_height', $setting->labels_height)) }} + {{ Form::hidden('labels_display_sgutter', old('labels_display_sgutter', $setting->labels_display_sgutter)) }} + {{ Form::hidden('labels_display_bgutter', old('labels_display_bgutter', $setting->labels_display_bgutter)) }} + {{ Form::hidden('labels_pmargin_top', old('labels_pmargin_top', $setting->labels_pmargin_top)) }} + {{ Form::hidden('labels_pmargin_bottom', old('labels_pmargin_bottom', $setting->labels_pmargin_bottom)) }} + {{ Form::hidden('labels_pmargin_left', old('labels_pmargin_left', $setting->labels_pmargin_left)) }} + {{ Form::hidden('labels_pmargin_right', old('labels_pmargin_right', $setting->labels_pmargin_right)) }} + {{ Form::hidden('labels_pagewidth', old('labels_pagewidth', $setting->labels_pagewidth)) }} + {{ Form::hidden('labels_pageheight', old('labels_pageheight', $setting->labels_pageheight)) }} + {{ Form::hidden('labels_display_name', old('labels_display_name', $setting->labels_display_name)) }} + {{ Form::hidden('labels_display_serial', old('labels_display_serial', $setting->labels_display_serial)) }} + {{ Form::hidden('labels_display_tag', old('labels_display_tag', $setting->labels_display_tag)) }} + {{ Form::hidden('labels_display_model', old('labels_display_model', $setting->labels_display_model)) }} + {{ Form::hidden('labels_display_company_name', old('labels_display_company_name', $setting->labels_display_company_name)) }} + @else + + +
    +
    + {{ Form::label('labels_per_page', trans('admin/settings/general.labels_per_page'), ['class'=>'control-label']) }} +
    +
    + {{ Form::text('labels_per_page', old('labels_per_page', $setting->labels_per_page), ['class' => 'form-control','style' => 'width: 100px;', 'aria-label'=>'labels_per_page']) }} + {!! $errors->first('labels_per_page', '') !!} +
    +
    + +
    +
    + {{ Form::label('labels_fontsize', trans('admin/settings/general.labels_fontsize'), ['class'=>'control-label']) }} +
    +
    +
    + {{ Form::text('labels_fontsize', old('labels_fontsize', $setting->labels_fontsize), ['class' => 'form-control', 'aria-label'=>'labels_fontsize']) }} +
    {{ trans('admin/settings/general.text_pt') }}
    +
    +
    +
    + {!! $errors->first('labels_fontsize', '') !!} +
    +
    + +
    +
    + {{ Form::label('labels_width', trans('admin/settings/general.label_dimensions'), ['class'=>'control-label']) }} +
    +
    +
    + {{ Form::text('labels_width', old('labels_width', $setting->labels_width), ['class' => 'form-control', 'aria-label'=>'labels_width']) }} +
    {{ trans('admin/settings/general.width_w') }}
    +
    +
    +
    +
    + {{ Form::text('labels_height', old('labels_height', $setting->labels_height), ['class' => 'form-control', 'aria-label'=>'labels_height']) }} +
    {{ trans('admin/settings/general.height_h') }}
    +
    +
    +
    + {!! $errors->first('labels_width', '') !!} + {!! $errors->first('labels_height', '') !!} +
    +
    + +
    +
    + {{ Form::label('labels_display_sgutter', trans('admin/settings/general.label_gutters')) }} +
    +
    +
    + {{ Form::text('labels_display_sgutter', old('labels_display_sgutter', $setting->labels_display_sgutter), ['class' => 'form-control', 'aria-label'=>'labels_display_sgutter']) }} +
    {{ trans('admin/settings/general.horizontal') }}
    +
    +
    +
    +
    + {{ Form::text('labels_display_bgutter', old('labels_display_bgutter', $setting->labels_display_bgutter), ['class' => 'form-control', 'aria-label'=>'labels_display_bgutter']) }} +
    {{ trans('admin/settings/general.vertical') }}
    +
    +
    +
    + {!! $errors->first('labels_display_sgutter', '') !!} + {!! $errors->first('labels_display_bgutter', '') !!} +
    +
    + +
    +
    + {{ Form::label('labels_pmargin_top', trans('admin/settings/general.page_padding')) }} +
    +
    +
    + {{ Form::text('labels_pmargin_top', old('labels_pmargin_top', $setting->labels_pmargin_top), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_top']) }} +
    {{ trans('admin/settings/general.top') }}
    +
    +
    + {{ Form::text('labels_pmargin_right', old('labels_pmargin_right', $setting->labels_pmargin_right), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_right']) }} +
    {{ trans('admin/settings/general.right') }}
    +
    +
    +
    +
    + {{ Form::text('labels_pmargin_bottom', old('labels_pmargin_bottom', $setting->labels_pmargin_bottom), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_bottom']) }} +
    {{ trans('admin/settings/general.bottom') }}
    +
    +
    + {{ Form::text('labels_pmargin_left', old('labels_pmargin_left', $setting->labels_pmargin_left), ['class' => 'form-control', 'aria-label'=>'labels_pmargin_left']) }} +
    {{ trans('admin/settings/general.left') }}
    +
    + +
    +
    + {!! $errors->first('labels_width', '') !!} + {!! $errors->first('labels_height', '') !!} +
    +
    + +
    +
    + {{ Form::label('labels_pagewidth', trans('admin/settings/general.page_dimensions'), ['class'=>'control-label']) }} +
    +
    +
    + {{ Form::text('labels_pagewidth', old('labels_pagewidth', $setting->labels_pagewidth), ['class' => 'form-control', 'aria-label'=>'labels_pagewidth']) }} +
    {{ trans('admin/settings/general.width_w') }}
    +
    +
    +
    +
    + {{ Form::text('labels_pageheight', old('labels_pageheight', $setting->labels_pageheight), ['class' => 'form-control', 'aria-label'=>'labels_pageheight']) }} +
    {{ trans('admin/settings/general.height_h') }}
    +
    +
    +
    + {!! $errors->first('labels_pagewidth', '') !!} + {!! $errors->first('labels_pageheight', '') !!} +
    +
    + +
    +
    + {{ Form::label('labels_display', trans('admin/settings/general.label_fields'), ['class' => 'control-label']) }} +
    +
    + + + + + + +
    +
    + + @endif
    diff --git a/resources/views/settings/ldap.blade.php b/resources/views/settings/ldap.blade.php index 078b09cad5..016d54f48f 100644 --- a/resources/views/settings/ldap.blade.php +++ b/resources/views/settings/ldap.blade.php @@ -18,6 +18,15 @@ .checkbox label { padding-right: 40px; } + + /* + Don't make the password field *look* readonly - this is for usability, so admins don't think they can't edit this field. + */ + .form-control[readonly] { + background-color: white; + color: #555555; + cursor:text; + } @if ((!function_exists('ldap_connect')) || (!function_exists('ldap_set_option')) || (!function_exists('ldap_bind'))) @@ -34,10 +43,12 @@ @endif - {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'false', 'class' => 'form-horizontal', 'role' => 'form']) }} + {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form']) }} {{csrf_field()}} + + @@ -54,18 +65,38 @@
    -
    - {{ Form::label('ldap_integration', trans('admin/settings/general.ldap_integration')) }} + {{ Form::label('ldap_enabled', trans('admin/settings/general.ldap_integration')) }}
    -
    +
    - {{ Form::checkbox('ldap_enabled', '1', Request::old('ldap_enabled', $setting->ldap_enabled), [((config('app.lock_passwords')===true)) ? 'disabled ': '', 'class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }} + + @if (config('app.lock_passwords')===true) +

    {{ trans('general.feature_disabled') }}

    + @endif +
    +
    + + + +
    +
    + {{ Form::label('is_ad', trans('admin/settings/general.ad')) }} +
    +
    + + {!! $errors->first('is_ad', '') !!} + @if (config('app.lock_passwords')===true)

    {{ trans('general.feature_disabled') }}

    @endif @@ -77,10 +108,11 @@
    {{ Form::label('ldap_pw_sync', trans('admin/settings/general.ldap_pw_sync')) }}
    -
    - +
    +

    {{ trans('admin/settings/general.ldap_pw_sync_help') }}

    {!! $errors->first('ldap_pw_sync_help', '') !!} @@ -91,75 +123,12 @@
    - - -
    -
    - {{ Form::label('ldap_default_group', trans('admin/settings/general.ldap_default_group')) }} -
    - -
    - - @if ($groups->count()) - @if ((Config::get('app.lock_passwords') || (!Auth::user()->isSuperUser()))) -
      - @foreach ($groups as $id => $group) - {!! '
    • '.e($group).'
    • ' !!} - @endforeach -
    - - - {{ trans('admin/users/general.group_memberships_helpblock') }} - @else -
    - - - - {{ trans('admin/settings/general.ldap_default_group_info') }} - -
    - @endif - @else -

    No groups have been created yet. Visit Admin Settings > Permission Groups to add one.

    - @endif - -
    -
    - - -
    -
    - {{ Form::label('is_ad', trans('admin/settings/general.ad')) }} -
    -
    - {{ Form::checkbox('is_ad', '1', Request::old('is_ad', $setting->is_ad), [((config('app.lock_passwords')===true)) ? 'disabled ': '', 'class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }} - {{ trans('admin/settings/general.is_ad') }} - {!! $errors->first('is_ad', '') !!} - - @if (config('app.lock_passwords')===true) -

    {{ trans('general.feature_disabled') }}

    - @endif -
    -
    -
    {{ Form::label('ad_domain', trans('admin/settings/general.ad_domain')) }}
    -
    +
    {{ Form::text('ad_domain', Request::old('ad_domain', $setting->ad_domain), ['class' => 'form-control','placeholder' => trans('general.example') .'example.com', $setting->demoMode]) }}

    {{ trans('admin/settings/general.ad_domain_help') }}

    {!! $errors->first('ad_domain', '') !!} @@ -174,7 +143,7 @@
    {{ Form::label('ad_append_domain', trans('admin/settings/general.ad_append_domain_label')) }}
    -
    +
    {{ Form::checkbox('ad_append_domain', '1', Request::old('ad_append_domain', $setting->ad_append_domain),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }} {{ trans('admin/settings/general.ad_append_domain') }}

    {{ trans('admin/settings/general.ad_append_domain_help') }}

    @@ -190,7 +159,7 @@
    {{ Form::label('ldap_client_tls_key', trans('admin/settings/general.ldap_client_tls_key')) }}
    -
    +
    {{ Form::textarea('ldap_client_tls_key', Request::old('ldap_client_tls_key', $setting->ldap_client_tls_key), ['class' => 'form-control','placeholder' => trans('general.example') .'-----BEGIN RSA PRIVATE KEY-----'."\r\n1234567890\r\n-----END RSA PRIVATE KEY----- ", $setting->demoMode]) }} {!! $errors->first('ldap_client_tls_key', '') !!} @@ -205,7 +174,7 @@
    {{ Form::label('ldap_client_tls_cert', trans('admin/settings/general.ldap_client_tls_cert')) }}
    -
    +
    {{ Form::textarea('ldap_client_tls_cert', Request::old('ldap_client_tls_cert', $setting->ldap_client_tls_cert), ['class' => 'form-control','placeholder' => trans('general.example') .'-----BEGIN CERTIFICATE-----'."\r\n1234567890\r\n-----END CERTIFICATE-----", $setting->demoMode]) }}

    {{ trans('admin/settings/general.ldap_client_tls_cert_help') }}

    {!! $errors->first('ldap_client_tls_cert', '') !!} @@ -220,7 +189,7 @@
    {{ Form::label('ldap_server', trans('admin/settings/general.ldap_server')) }}
    -
    +
    {{ Form::text('ldap_server', Request::old('ldap_server', $setting->ldap_server), ['class' => 'form-control','placeholder' => trans('general.example') .'ldap://ldap.example.com', $setting->demoMode]) }}

    {{ trans('admin/settings/general.ldap_server_help') }}

    {!! $errors->first('ldap_server', '') !!} @@ -235,9 +204,11 @@
    {{ Form::label('ldap_tls', trans('admin/settings/general.ldap_tls')) }}
    -
    - {{ Form::checkbox('ldap_tls', '1', Request::old('ldap_tls', $setting->ldap_tls),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }} - {{ trans('admin/settings/general.ldap_tls_help') }} +
    + {!! $errors->first('ldap_tls', '') !!} @if (config('app.lock_passwords')===true)

    {{ trans('general.feature_disabled') }}

    @@ -250,9 +221,11 @@
    {{ Form::label('ldap_server_cert_ignore', trans('admin/settings/general.ldap_server_cert')) }}
    -
    - {{ Form::checkbox('ldap_server_cert_ignore', '1', Request::old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }} - {{ trans('admin/settings/general.ldap_server_cert_ignore') }} +
    + {!! $errors->first('ldap_server_cert_ignore', '') !!}

    {{ trans('admin/settings/general.ldap_server_cert_help') }}

    @if (config('app.lock_passwords')===true) @@ -266,8 +239,8 @@
    {{ Form::label('ldap_uname', trans('admin/settings/general.ldap_uname')) }}
    -
    - {{ Form::text('ldap_uname', Request::old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','placeholder' => trans('general.example') .'binduser@example.com', $setting->demoMode]) }} +
    + {{ Form::text('ldap_uname', Request::old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','autocomplete' => 'off', 'placeholder' => trans('general.example') .'binduser@example.com', $setting->demoMode]) }} {!! $errors->first('ldap_uname', '') !!} @if (config('app.lock_passwords')===true)

    {{ trans('general.feature_disabled') }}

    @@ -280,8 +253,8 @@
    {{ Form::label('ldap_pword', trans('admin/settings/general.ldap_pword')) }}
    -
    - {{ Form::password('ldap_pword', ['class' => 'form-control','placeholder' => trans('general.example') .' binduserpassword', $setting->demoMode]) }} +
    + {{ Form::password('ldap_pword', ['class' => 'form-control', 'autocomplete' => 'off', 'onfocus' => "this.removeAttribute('readonly');", $setting->demoMode, ' readonly']) }} {!! $errors->first('ldap_pword', '') !!} @if (config('app.lock_passwords')===true)

    {{ trans('general.feature_disabled') }}

    @@ -294,7 +267,7 @@
    {{ Form::label('ldap_basedn', trans('admin/settings/general.ldap_basedn')) }}
    -
    +
    {{ Form::text('ldap_basedn', Request::old('ldap_basedn', $setting->ldap_basedn), ['class' => 'form-control', 'placeholder' => trans('general.example') .'cn=users/authorized,dc=example,dc=com', $setting->demoMode]) }} {!! $errors->first('ldap_basedn', '') !!} @if (config('app.lock_passwords')===true) @@ -308,7 +281,7 @@
    {{ Form::label('ldap_filter', trans('admin/settings/general.ldap_filter')) }}
    -
    +
    {{ Form::text('ldap_filter', Request::old('ldap_filter', $setting->ldap_filter), ['class' => 'form-control','placeholder' => trans('general.example') .'&(cn=*)', $setting->demoMode]) }} {!! $errors->first('ldap_filter', '') !!} @if (config('app.lock_passwords')===true) @@ -322,7 +295,7 @@
    {{ Form::label('ldap_username_field', trans('admin/settings/general.ldap_username_field')) }}
    -
    +
    {{ Form::text('ldap_username_field', Request::old('ldap_username_field', $setting->ldap_username_field), ['class' => 'form-control','placeholder' => trans('general.example') .'samaccountname', $setting->demoMode]) }} {!! $errors->first('ldap_username_field', '') !!} @if (config('app.lock_passwords')===true) @@ -336,7 +309,7 @@
    {{ Form::label('ldap_lname_field', trans('admin/settings/general.ldap_lname_field')) }}
    -
    +
    {{ Form::text('ldap_lname_field', Request::old('ldap_lname_field', $setting->ldap_lname_field), ['class' => 'form-control','placeholder' => trans('general.example') .'sn', $setting->demoMode]) }} {!! $errors->first('ldap_lname_field', '') !!} @if (config('app.lock_passwords')===true) @@ -350,7 +323,7 @@
    {{ Form::label('ldap_fname_field', trans('admin/settings/general.ldap_fname_field')) }}
    -
    +
    {{ Form::text('ldap_fname_field', Request::old('ldap_fname_field', $setting->ldap_fname_field), ['class' => 'form-control', 'placeholder' => trans('general.example') .'givenname', $setting->demoMode]) }} {!! $errors->first('ldap_fname_field', '') !!} @if (config('app.lock_passwords')===true) @@ -364,7 +337,7 @@
    {{ Form::label('ldap_auth_filter_query', trans('admin/settings/general.ldap_auth_filter_query')) }}
    -
    +
    {{ Form::text('ldap_auth_filter_query', Request::old('ldap_auth_filter_query', $setting->ldap_auth_filter_query), ['class' => 'form-control','placeholder' => trans('general.example') .'uid=', $setting->demoMode]) }} {!! $errors->first('ldap_auth_filter_query', '') !!} @if (config('app.lock_passwords')===true) @@ -373,17 +346,45 @@
    - -
    + + +
    - {{ Form::label('ldap_version', trans('admin/settings/general.ldap_version')) }} + {{ Form::label('ldap_default_group', trans('admin/settings/general.ldap_default_group')) }}
    -
    - {{ Form::text('ldap_version', Request::old('ldap_version', $setting->ldap_version), ['class' => 'form-control','placeholder' => '3', $setting->demoMode]) }} - {!! $errors->first('ldap_version', '') !!} - @if (config('app.lock_passwords')===true) -

    {{ trans('general.feature_disabled') }}

    + +
    + + @if ($groups->count()) + @if ((Config::get('app.lock_passwords') || (!Auth::user()->isSuperUser()))) +
      + @foreach ($groups as $id => $group) + {!! '
    • '.e($group).'
    • ' !!} + @endforeach +
    + + + {{ trans('admin/users/general.group_memberships_helpblock') }} + @else +
    + + + + {{ trans('admin/settings/general.ldap_default_group_info') }} + +
    + @endif + @else +

    No groups have been created yet. Visit Admin Settings > Permission Groups to add one.

    @endif +
    @@ -392,7 +393,7 @@
    {{ Form::label('ldap_active_flag', trans('admin/settings/general.ldap_active_flag')) }}
    -
    +
    {{ Form::text('ldap_active_flag', Request::old('ldap_active_flag', $setting->ldap_active_flag), ['class' => 'form-control', $setting->demoMode]) }}

    {!! trans('admin/settings/general.ldap_activated_flag_help') !!}

    @@ -409,7 +410,7 @@
    {{ Form::label('ldap_emp_num', trans('admin/settings/general.ldap_emp_num')) }}
    -
    +
    {{ Form::text('ldap_emp_num', Request::old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => trans('general.example') .'employeenumber/employeeid', $setting->demoMode]) }} {!! $errors->first('ldap_emp_num', '') !!} @if (config('app.lock_passwords')===true) @@ -422,7 +423,7 @@
    {{ Form::label('ldap_dept', trans('admin/settings/general.ldap_dept')) }}
    -
    +
    {{ Form::text('ldap_dept', Request::old('ldap_dept', $setting->ldap_dept), ['class' => 'form-control','placeholder' => trans('general.example') .'department', $setting->demoMode]) }} {!! $errors->first('ldap_dept', '') !!} @if (config('app.lock_passwords')===true) @@ -435,7 +436,7 @@
    {{ Form::label('ldap_dept', trans('admin/settings/general.ldap_manager')) }}
    -
    +
    {{ Form::text('ldap_manager', Request::old('ldap_manager', $setting->ldap_manager), ['class' => 'form-control','placeholder' => trans('general.example') .'manager', $setting->demoMode]) }} {!! $errors->first('ldap_manager', '') !!} @if (config('app.lock_passwords')===true) @@ -449,7 +450,7 @@
    {{ Form::label('ldap_email', trans('admin/settings/general.ldap_email')) }}
    -
    +
    {{ Form::text('ldap_email', Request::old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => trans('general.example') .'mail', $setting->demoMode]) }} {!! $errors->first('ldap_email', '') !!} @if (config('app.lock_passwords')===true) @@ -463,7 +464,7 @@
    {{ Form::label('ldap_phone', trans('admin/settings/general.ldap_phone')) }}
    -
    +
    {{ Form::text('ldap_phone', Request::old('ldap_phone', $setting->ldap_phone_field), ['class' => 'form-control','placeholder' => trans('general.example') .'telephonenumber', $setting->demoMode]) }} {!! $errors->first('ldap_phone', '') !!} @if (config('app.lock_passwords')===true) @@ -477,7 +478,7 @@
    {{ Form::label('ldap_jobtitle', trans('admin/settings/general.ldap_jobtitle')) }}
    -
    +
    {{ Form::text('ldap_jobtitle', Request::old('ldap_jobtitle', $setting->ldap_jobtitle), ['class' => 'form-control','placeholder' => trans('general.example') .'title', $setting->demoMode]) }} {!! $errors->first('ldap_jobtitle', '') !!} @if (config('app.lock_passwords')===true) @@ -491,7 +492,7 @@
    {{ Form::label('ldap_country', trans('admin/settings/general.ldap_country')) }}
    -
    +
    {{ Form::text('ldap_country', Request::old('ldap_country', $setting->ldap_country), ['class' => 'form-control','placeholder' => trans('general.example') .'c', $setting->demoMode]) }} {!! $errors->first('ldap_country', '') !!} @if (config('app.lock_passwords')===true) @@ -504,7 +505,7 @@
    {{ Form::label('ldap_location', trans('admin/settings/general.ldap_location')) }}
    -
    +
    {{ Form::text('ldap_location', Request::old('ldap_location', $setting->ldap_location), ['class' => 'form-control','placeholder' => trans('general.example') .'physicaldeliveryofficename', $setting->demoMode]) }}

    {!! trans('admin/settings/general.ldap_location_help') !!}

    {!! $errors->first('ldap_location', '') !!} @@ -520,14 +521,14 @@
    {{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
    -
    + -
    +

    -
    +

    {{ trans('admin/settings/general.ldap_login_sync_help') }}

    @if (config('app.lock_passwords')===true)

    {{ trans('general.feature_disabled') }}

    @@ -541,13 +542,13 @@
    {{ Form::label('test_ldap_login', 'Test LDAP Login') }}
    -
    +
    -
    +
    -
    +

    {{ trans('admin/settings/general.ldap_login_test_help') }}

    @@ -575,7 +576,7 @@
    {{ Form::label('custom_forgot_pass_url', trans('admin/settings/general.custom_forgot_pass_url')) }}
    -
    +
    {{ Form::text('custom_forgot_pass_url', Request::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), ['class' => 'form-control','placeholder' => trans('general.example') .'https://my.ldapserver-forgotpass.com', $setting->demoMode]) }}

    {{ trans('admin/settings/general.custom_forgot_pass_url_help') }}

    {!! $errors->first('custom_forgot_pass_url', '') !!} @@ -610,6 +611,8 @@ @push('js') diff --git a/routes/api.php b/routes/api.php index 5c139837ec..2ae1bfe559 100644 --- a/routes/api.php +++ b/routes/api.php @@ -280,17 +280,9 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi Api\ConsumablesController::class, 'getDataView' ] - )->name('api.consumables.showUsers'); + )->name('api.consumables.show.users'); - // This is LEGACY endpoint URL and should be removed in the next major release - Route::get('view/{id}/users', - [ - Api\ConsumablesController::class, - 'getDataView' - ] - )->name('api.consumables.showUsers'); - Route::post('{consumable}/checkout', [ Api\ConsumablesController::class, @@ -606,6 +598,16 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ); // end imports API routes + /** + * Labels API routes + */ + Route::group(['prefix' => 'labels'], function() { + Route::get('{name}', [ Api\LabelsController::class, 'show']) + ->where('name', '.*') + ->name('api.labels.show'); + Route::get('', [ Api\LabelsController::class, 'index']) + ->name('api.labels.index'); + }); /** * Licenses API routes @@ -876,6 +878,13 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.statuslabels.deployable'); + Route::get('selectlist', + [ + Api\StatuslabelsController::class, + 'selectlist' + ] + )->name('api.statuslabels.selectlist'); + }); Route::resource('statuslabels', diff --git a/routes/web.php b/routes/web.php index 4644f41ffb..635cdbcb94 100644 --- a/routes/web.php +++ b/routes/web.php @@ -10,6 +10,7 @@ use App\Http\Controllers\DepreciationsController; use App\Http\Controllers\GroupsController; use App\Http\Controllers\HealthController; use App\Http\Controllers\ImportsController; +use App\Http\Controllers\LabelsController; use App\Http\Controllers\LocationsController; use App\Http\Controllers\ManufacturersController; use App\Http\Controllers\ModalController; @@ -39,8 +40,14 @@ Route::group(['middleware' => 'auth'], function () { Route::resource('categories', CategoriesController::class, [ 'parameters' => ['category' => 'category_id'], ]); - - + + /* + * Labels + */ + Route::get( + 'labels/{labelName}', + [LabelsController::class, 'show'] + )->where('labelName', '.*')->name('labels.show'); /* * Locations @@ -68,9 +75,6 @@ Route::group(['middleware' => 'auth'], function () { ]); - - - /* * Manufacturers */ @@ -192,6 +196,9 @@ Route::group(['prefix' => 'admin', 'middleware' => ['auth', 'authorize:superuser Route::get('oauth', [SettingsController::class, 'api'])->name('settings.oauth.index'); + Route::get('google', [SettingsController::class, 'getGoogleLoginSettings'])->name('settings.google.index'); + Route::post('google', [SettingsController::class, 'postGoogleLoginSettings'])->name('settings.google.save'); + Route::get('purge', [SettingsController::class, 'getPurge'])->name('settings.purge.index'); Route::post('purge', [SettingsController::class, 'postPurge'])->name('settings.purge.save'); @@ -217,6 +224,11 @@ Route::group(['prefix' => 'admin', 'middleware' => ['auth', 'authorize:superuser [SettingsController::class, 'postUploadBackup'] )->name('settings.backups.upload'); + // Handle redirect from after POST request from backup restore + Route::get('/restore/{filename?}', function () { + return redirect(route('settings.backups.index')); + }); + Route::get('/', [SettingsController::class, 'getBackups'])->name('settings.backups.index'); }); @@ -278,7 +290,7 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { )->name('account/request-asset'); Route::post( - 'request/{itemType}/{itemId}', + 'request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}', [ViewAssetsController::class, 'getRequestItem'] )->name('account/request-item'); @@ -291,7 +303,8 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () { Route::get('accept/{id}', [Account\AcceptanceController::class, 'create']) ->name('account.accept.item'); - Route::post('accept/{id}', [Account\AcceptanceController::class, 'store']); + Route::post('accept/{id}', [Account\AcceptanceController::class, 'store']) + ->name('account.store-acceptance'); Route::get( 'print', @@ -360,8 +373,8 @@ Route::group(['middleware' => ['auth']], function () { 'reports/unaccepted_assets/{deleted?}', [ReportsController::class, 'getAssetAcceptanceReport'] )->name('reports/unaccepted_assets'); - Route::get( - 'reports/unaccepted_assets/{acceptanceId}/sent_reminder', + Route::post( + 'reports/unaccepted_assets/sent_reminder', [ReportsController::class, 'sentAssetAcceptanceReminder'] )->name('reports/unaccepted_assets_sent_reminder'); Route::delete( @@ -452,8 +465,6 @@ Route::group(['middleware' => 'web'], function () { [LoginController::class, 'postTwoFactorAuth'] ); - - Route::post( 'password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'] @@ -482,7 +493,9 @@ Route::group(['middleware' => 'web'], function () { )->name('password.email')->middleware('throttle:forgotten_password'); - + // Socialite Google login + Route::get('google', 'App\Http\Controllers\GoogleAuthController@redirectToGoogle')->name('google.redirect'); + Route::get('google/callback', 'App\Http\Controllers\GoogleAuthController@handleGoogleCallback')->name('google.callback'); Route::get( diff --git a/routes/web/hardware.php b/routes/web/hardware.php index 690d8e0d1c..d4f2892281 100644 --- a/routes/web/hardware.php +++ b/routes/web/hardware.php @@ -122,9 +122,10 @@ Route::group( [AssetCheckinController::class, 'store'] )->name('hardware.checkin.store'); - Route::get('{assetId}/view', - [AssetsController::class, 'show'] - )->name('hardware.view'); + // Redirect old legacy /asset_id/view urls to the resource route version + Route::get('{assetId}/view', function ($assetId) { + return redirect()->route('hardware.show', ['hardware' => $assetId]); + }); Route::get('{assetId}/qr_code', [AssetsController::class, 'getQrCode'] @@ -178,12 +179,20 @@ Route::group( Route::post('bulkcheckout', [BulkAssetsController::class, 'storeCheckout'] )->name('hardware.bulkcheckout.store'); + }); Route::resource('hardware', AssetsController::class, [ 'middleware' => ['auth'], - 'parameters' => ['asset' => 'asset_id' + 'parameters' => ['asset' => 'asset_id', + 'names' => [ + 'show' => 'view', + ], ], ]); + +Route::get('ht/{any?}', + [AssetsController::class, 'getAssetByTag'] +)->where('any', '.*')->name('ht/assetTag'); diff --git a/sample_csvs/MOCK_ACCESSORIES.csv b/sample_csvs/MOCK_ACCESSORIES.csv deleted file mode 100644 index f7e490a104..0000000000 --- a/sample_csvs/MOCK_ACCESSORIES.csv +++ /dev/null @@ -1,2 +0,0 @@ -Item Name,Purchase Date,Purchase Cost,Location,Company,Order Number,Category,Requestable,Quantity -Walter Carter,09/01/2006,,metus. Vivamus,Macromedia,J935H60W,Customers,False,278 diff --git a/sample_csvs/MOCK_ASSETS.csv b/sample_csvs/MOCK_ASSETS.csv deleted file mode 100644 index 282747dd9c..0000000000 --- a/sample_csvs/MOCK_ASSETS.csv +++ /dev/null @@ -1,201 +0,0 @@ -Full Name,Email,Username,item Name,Category,Model name,Manufacturer,Model Number,Serial Number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty Months,Supplier,BYOD -Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6.38E+15,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",4/5/16,133289.59,Alpha,Undeployable,14,Blogspan,yes -Judith Ferguson,jferguson1@state.tx.us,jferguson1,mi in porttitor,justo,congue diam id,Flipstorm,5.02E+18,4bc7fc90-5a97-412f-8eed-77ecacc643fc,544574073-0,Cirangga Kidul,,3/8/16,763.46,,Undeployable,12,Oyope,no -Mildred Gibson,mgibson2@wiley.com,mgibson2,morbi quis tortor id,nunc nisl duis,convallis tortor risus,Lajo,3.75E+14,2837ab20-8f0d-4935-8a52-226392f2b1b0,710141467-2,Shekou,In congue. Etiam justo. Etiam pretium iaculis justo.,8/9/15,233.57,Konklab,Lost,,,no -Brandon Lee,blee3@quantcast.com,blee3,amet cursus id turpis,sed,in faucibus orci,Zoomlounge,3.55E+15,18d6e6a4-d362-4de9-beb4-7f62fb93de6f,103538064-1,Leksand,Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.,10/11/15,,,Pending Diagnostics,,,no -Betty Powell,bpowell4@tuttocitta.it,bpowell4,ipsum praesent,condimentum curabitur,et ultrices,Kazu,3.57E+15,f9b473c6-c810-42f2-8335-27ce468889a8,118753405-6,Dresi Wetan,,6/16/15,324.8,,Ready to Deploy,,,no -Anthony Wheeler,awheeler5@cocolog-nifty.com,awheeler5,dictumst maecenas ut,sem praesent,accumsan felis,Layo,3.01E+13,4751495c-cee0-4961-b788-94a545b5643e,998233705-X,Dante Delgado,,4/16/16,261.79,,Archived,15,Ntag,no -Dennis Reynolds,dreynolds6@ustream.tv,dreynolds6,libero nam,risus,interdum mauris,Twiyo,3.59E+15,17b3cf8d-fead-46f5-a8b0-49906bb90a00,177687256-8,Pingle,"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",5/24/15,,,Pending Diagnostics,,,no -Andrea Arnold,aarnold7@cbc.ca,aarnold7,mauris morbi non,ante vel,sapien dignissim,Cogibox,3.55E+15,7a6a2fdb-160c-4d91-8e05-a0337a90d9db,129556040-2,Zhuli,Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.,9/15/15,434.86,,Archived,14,Kwilith,no -Anna Butler,abutler8@wikia.com,abutler8,eleifend pede libero,sapien a libero,et ultrices posuere cubilia,Flipbug,6.76E+17,c1a57909-3b2e-47fe-ab2f-843401b2a7de,117517007-0,Niopanda,"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.",4/13/16,89.53,,Archived,15,Linkbridge,no -Mark Bennett,mbennett9@diigo.com,mbennett9,convallis nulla neque,eu sapien,duis mattis egestas metus aenean,Centimia,3.78E+14,07540238-fb3c-4c8a-8e11-d43883ee4268,007968217-0,Zoumaling,,7/4/15,,,Lost,,,no -Emily Wheeler,ewheelera@google.de,ewheelera,in felis,leo odio,quam sapien varius,Roombo,2.02E+14,527b2445-2c67-4f76-912f-6ec42400a584,441402118-9,Luts‰Ûªk,Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.,5/18/16,,Bitwolf,Lost,36,Wikizz,no -Wanda Fox,wfoxb@virginia.edu,wfoxb,vel ipsum praesent,potenti nullam porttitor,augue vestibulum rutrum rutrum neque,Yakijo,3.57E+15,,863829558-8,Pravdinsk,"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",11/10/15,,,Lost,14,Linkbridge,no -,,,odio elementum,posuere cubilia curae,ante vel ipsum praesent blandit,Oyope,3.53E+15,9a863968-180e-451d-a723-dc85e2d5d8ff,742114860-4,Panay,Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.,3/20/16,881.4,,,30,Tagcat,no -Janet Grant,jgrantd@cpanel.net,jgrantd,viverra diam vitae,semper sapien,dapibus dolor vel,Flashset,3.56E+15,e287bb64-ff4f-434c-88ab-210ad433c77b,927820758-6,Achiaman,,3/5/16,675.3,,Archived,22,Meevee,no -Antonio Larson,alarsone@tripod.com,alarsone,felis sed interdum venenatis,id lobortis,dui proin,Chatterbridge,4.07E+12,90bcab28-ffd4-48c9-ba5d-c2eeb1400698,789757925-5,Oemanu,Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.,7/25/15,,,Ready to Deploy,30,Shuffledrive,no -Lois Powell,lpowellf@com.com,lpowellf,id consequat,justo nec,odio porttitor id consequat in,Skipstorm,3.63E+13,08e440f7-bd0b-47a7-a577-4a3ce3c7dfe7,202652281-2,Qiaolin,"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",8/13/15,446.22,,Lost,,,no -Mildred Allen,malleng@com.com,malleng,porta volutpat quam pede,in hac habitasse,donec vitae nisi nam,Devpulse,3.54E+15,5f900903-0ffe-4405-b5ad-aa4aa59d911c,210119288-8,Accha,"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.",12/24/15,923.9,,Lost,20,Quamba,no -Clarence Austin,caustinh@bigcartel.com,caustinh,libero nam dui proin,aliquet at feugiat,eget tincidunt eget tempus,Photobug,2.02E+14,bde85740-f103-4b49-a691-a60c7f6859a8,022102715-7,Kerkrade,In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.,3/29/16,,,Undeployable,22,Browsedrive,no -Walter Chavez,wchavezi@blogs.com,wchavezi,sociis natoque penatibus,vel est,at diam nam,Photofeed,3.53E+15,bf4a2a92-6f29-4d24-be90-8126d4dcbd64,610672722-8,Villa Regina,,5/13/16,442.51,,Archived,28,Tekfly,no -Marie Elliott,melliottj@constantcontact.com,melliottj,sed tristique in,rutrum,luctus et ultrices,Riffpath,4.55E+12,9a02817e-de79-4850-a212-84c9ae3dd1a2,898880851-7,Tibro,Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.,9/10/15,906.39,,Undeployable,,,no -,,,dui luctus rutrum,sapien ut nunc,dictumst morbi vestibulum,Aivee,4.41E+15,514aca2a-9080-4c49-8695-7ba4c78edc65,466008300-4,Menglie,Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.,12/21/15,151.27,,,,,no -,,,ut massa volutpat,sed,quis odio consequat varius integer,Abatz,3.54E+15,b99208e2-b8d8-4f7a-8a06-366a27733b97,313295582-5,Solidaridad,"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",2/15/16,211.07,,,,,no -Benjamin Ford,bfordm@woothemes.com,bfordm,habitasse platea dictumst,primis in faucibus,quam a odio in,Blogtag,5.02E+15,c8680b36-a13c-427f-a6a1-1b9b351eb129,552327520-4,Sorinomo,In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.,11/25/15,112.11,,Ready to Deploy,18,Quatz,no -Timothy Warren,twarrenn@printfriendly.com,twarrenn,leo pellentesque ultrices,vestibulum,in sapien iaculis congue vivamus,Brightdog,3.04E+13,6c1e7556-063f-4c71-87ce-e46b06e8c238,446504693-6,Tamel,"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.",8/19/15,221.03,,Undeployable,26,Mydeo,no -Carlos Kelley,ckelleyo@1und1.de,ckelleyo,suspendisse potenti nullam,magnis dis parturient,at nibh in hac,Voolia,5.02E+17,95605fc3-f82e-4472-a2b0-93acd5e255ed,860990227-7,Tu€epi,,6/8/15,443.05,,Archived,35,Aimbu,no -Marilyn Bryant,mbryantp@uiuc.edu,mbryantp,nam nulla integer,praesent,turpis adipiscing lorem vitae,Divape,5.02E+16,53d9eef2-e0f8-4c81-9a31-8c12e4f52ab0,536251475-X,Peddie,Phasellus in felis. Donec semper sapien a libero. Nam dui.,6/8/15,79.2,Zaam-Dox,Ready to Deploy,,,no -Douglas Marshall,dmarshallq@cdc.gov,dmarshallq,cras mi pede malesuada,odio,aliquam augue quam sollicitudin,Thoughtsphere,5.61E+15,50d3ba91-06d6-4453-84ff-5ed801d3faa5,148599723-2,Betafo,,3/6/16,321.96,,Archived,,,no -Ruth Cunningham,rcunninghamr@biglobe.ne.jp,rcunninghamr,duis faucibus accumsan,ipsum aliquam,metus vitae ipsum,Jabbertype,4.02E+15,d4f93ed0-5010-4026-9d3f-0e59e0efee15,416706302-6,Margate,"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.",8/6/15,211.17,,Ready to Deploy,,,no -,,,non ligula pellentesque ultrices,arcu adipiscing molestie,vestibulum ante ipsum,Shuffletag,3.58E+15,657a313a-361c-4733-b444-ec26dc02fdfe,065976674-4,Igurubi,"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.",4/15/16,,,,26,Wikido,no -Jose Freeman,jfreemant@free.fr,jfreemant,duis aliquam convallis,potenti cras in,enim blandit,Twitterlist,6.05E+14,98080972-ad72-4581-8aee-ec78a6b3f528,712283421-2,Kuala Lumpur,Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.,9/5/15,744.98,,Lost,,,no -Wayne Woods,wwoodsu@tuttocitta.it,wwoodsu,massa id nisl,est,sit amet erat nulla,Browsecat,3.55E+15,fb146f1d-b0ed-4ffc-8653-3ad0afe99dd0,666189117-3,Saint-Ìätienne,,3/14/16,,,Undeployable,14,Jabbersphere,no -Frank Butler,fbutlerv@reuters.com,fbutlerv,arcu adipiscing,duis faucibus accumsan,nisi venenatis,Shuffledrive,3.75E+14,7820e851-362b-4187-b64a-c1ef8800fbce,323903931-1,Jinxiang,"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.",8/5/15,697.01,,Pending Diagnostics,17,Vipe,no -,,,eget eros,sapien varius,morbi non quam nec,Eamia,3.74E+14,9c67b528-2389-4733-92e7-a09415649c72,172486278-2,Neuzina,"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.",10/7/15,541.17,,,,,no -Christina Hunt,chuntx@businessinsider.com,chuntx,ut suscipit a feugiat,in faucibus,neque vestibulum eget,Quamba,5.55E+15,,620738847-X,Agpangi,"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.",5/31/15,199.6,,Pending Diagnostics,,,no -Cynthia Gordon,cgordony@livejournal.com,cgordony,morbi non quam nec,faucibus,eu orci mauris lacinia,Meevee,3.74E+14,1a67f546-8de4-4d2d-b32f-6c1605cac858,786105964-2,Dallas,,4/1/16,19.94,,Archived,33,Meembee,no -Jose Hicks,jhicksz@behance.net,jhicksz,habitasse platea dictumst etiam,fermentum donec ut,diam vitae quam suspendisse,Jaxspan,3.72E+14,71a5bd14-ba88-4cda-860d-9ca62347ba8d,539439867-4,JoÁeva,Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.,4/9/16,,,Ready to Deploy,22,Zoombeat,no -Anna Turner,aturner10@opensource.org,aturner10,sed tristique in tempus,sodales scelerisque mauris,nullam porttitor,Oozz,4.51E+15,2b9c723e-e530-44c5-ab95-72f3ad50a631,526130314-1,Tawau,Sed ante. Vivamus tortor. Duis mattis egestas metus.,3/10/16,815,Flexidy,Pending Diagnostics,36,Bluejam,no -Patricia Lawson,plawson11@businessinsider.com,plawson11,proin at,justo in,sed magna at nunc commodo,Nlounge,3.57E+15,f2c0248d-b6f6-42bf-a9a2-49c54372c01c,558108071-2,Valejas,,7/27/15,,,Lost,35,Quire,no -Marilyn Gilbert,mgilbert12@comcast.net,mgilbert12,in hac habitasse platea,semper porta volutpat,proin eu,Rhyloo,6.30E+17,6e9c56b8-5266-47f1-86bc-f73a0d42b05e,562820613-X,Bulacnin,,7/5/15,665.67,,Lost,,,no -Anne Lynch,alynch13@biglobe.ne.jp,alynch13,tincidunt nulla,turpis elementum,lobortis convallis tortor,Divavu,5.10E+15,db9913ae-d9ba-4653-aae5-590c2a3b5a69,943970793-3,Mayenne,Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.,1/9/16,468.49,,Pending Diagnostics,33,Dynava,no -Lois Morgan,lmorgan14@wired.com,lmorgan14,imperdiet et commodo,vel,quis odio consequat varius,Camido,3.05E+13,a6f3372c-73f1-4857-8ae6-432a2bdfd1c2,372636920-1,Shuiying,,8/15/15,,,Archived,,,no -Lois Johnson,ljohnson15@japanpost.jp,ljohnson15,nulla suscipit ligula,eros elementum,felis fusce posuere,Zoomcast,3.55E+15,1c0928ec-2ef7-430b-a556-18b33451612b,903861200-1,Kahuripan,Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.,3/29/16,721.77,,Pending Diagnostics,,,no -Jennifer Jones,jjones16@fda.gov,jjones16,primis in faucibus orci,porttitor pede justo,lorem quisque ut erat curabitur,Myworks,5.13E+15,3efa8d3c-01b2-408e-b592-f8cf18a510f4,413846137-X,Charneca,,11/9/15,293.99,Trippledex,Pending Diagnostics,29,Devify,no -,,,aliquet pulvinar,tincidunt,rutrum at lorem,Meembee,5.43E+15,ef71ff15-fae8-4f6b-a19c-9277d5a29df9,762391548-7,Szydâowo,Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.,12/23/15,15,,,,,no -David Jackson,djackson18@gravatar.com,djackson18,pede venenatis,magna,id pretium,Shuffledrive,2.01E+14,eab62ffe-2e0b-4c77-8aa2-133d27bb90ab,309187247-5,Jinglongqiao,,8/12/15,572.92,,Undeployable,21,Twimbo,no -Janice Ford,jford19@dmoz.org,jford19,nibh ligula,nibh in,lobortis vel dapibus,Brightbean,3.57E+15,42ec7b7d-5a37-4587-a3a7-e038ed650e94,741760231-2,Waenenda,Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.,11/1/15,,,Pending Diagnostics,,,no -Todd Boyd,tboyd1a@nsw.gov.au,tboyd1a,metus vitae ipsum aliquam,in felis,sapien non mi,Jabbertype,3.58E+15,70e99274-5df6-4f1f-b2cc-3f9cffd3a828,387700497-0,Siedlce,,6/7/15,983.85,,Ready to Deploy,,,no -,,,semper est,primis in faucibus,justo in hac habitasse platea,Dabvine,4.94E+18,e8aa6b0f-8f69-45cd-972c-b2b1e0415061,286742654-5,Minggang,"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",8/10/15,,,,,,no -Andrea Brooks,abrooks1c@shareasale.com,abrooks1c,non ligula,fusce posuere,ac enim in tempor,Youtags,5.61E+15,,226358965-1,Santa Gertrudes,Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.,3/5/16,387.62,,Lost,29,Zava,no -,,,nisl venenatis,sodales,varius nulla facilisi cras non,Realbuzz,2.01E+14,2952e308-122a-45ea-ac49-a836ca71a597,403999355-1,Huanxi,"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.",4/10/16,925.87,,,,,no -Anne Butler,abutler1e@elpais.com,abutler1e,sapien ut nunc,ultrices posuere cubilia,rhoncus mauris enim,Skibox,6.77E+18,3c1b4617-2219-419e-87fb-d654a0dab45e,756532468-X,Vyshniy VolochÌÇk,In congue. Etiam justo. Etiam pretium iaculis justo.,9/21/15,271.56,,Pending Diagnostics,,,no -,,,congue etiam justo,potenti nullam,congue risus semper porta,Oba,3.57E+15,9637ee81-b7ae-4465-8d19-61eec98ddf7f,577368722-4,Mostovskoy,"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.",7/27/15,,,,24,Edgeblab,no -Cynthia Stevens,cstevens1g@dyndns.org,cstevens1g,cursus vestibulum proin,ut volutpat,vel est donec odio justo,Skinte,6.33E+17,0bd330d0-3bde-4c47-a156-06a34435d8b4,058534741-7,Santa Rosa del Sara,"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",4/26/16,,,Archived,36,Wikizz,no -,,,sapien urna,habitasse,eget semper rutrum nulla nunc,Browsecat,5.01E+15,bf03e70c-fb5d-4164-ab40-9b96d5465e6c,629074247-7,Bonneuil-sur-Marne,Phasellus in felis. Donec semper sapien a libero. Nam dui.,3/21/16,810.91,,,12,Brainlounge,no -Donald Richardson,drichardson1i@over-blog.com,drichardson1i,eros vestibulum ac,nulla justo,natoque penatibus et magnis dis,Feedmix,3.59E+15,f0d7f3c9-dfea-4bb6-a401-571fffbf3090,984987793-6,Palue,,10/2/15,718.94,,Ready to Deploy,34,Midel,no -,,,erat vestibulum sed magna,ipsum,eget eleifend luctus ultricies,Skipfire,3.53E+15,343daa1a-d1f8-4ffb-8368-a26670381a40,604121509-0,Medicine Hat,"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.",1/4/16,,,,,,no -Chris Ellis,cellis1k@squidoo.com,cellis1k,eros viverra,velit nec,mauris laoreet ut rhoncus aliquet,Jaxnation,5.00E+15,f66bce43-fccf-49d9-94d1-cb0b8fe56472,546719356-2,Qigzhi,,11/21/15,585.87,,Pending Diagnostics,,,no -,,,non velit nec nisi,orci luctus et,sed vestibulum,Katz,3.05E+13,e3d0a6ac-024b-4c21-ac30-536e6c3d5959,404764262-2,Mojokerto,"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.",2/3/16,605.49,,,32,Shufflester,no -Timothy Greene,tgreene1m@npr.org,tgreene1m,massa id lobortis,mattis pulvinar nulla,suscipit a feugiat et,Ainyx,3.54E+15,3c11f36f-e17b-4392-890b-35e5bc0bdffa,552778185-6,Chongqing,"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.",7/22/15,115.77,,Lost,34,Vinte,no -Doris Taylor,dtaylor1n@mapy.cz,dtaylor1n,magnis dis,neque aenean auctor,primis in faucibus orci luctus,Vipe,5.05E+15,bc134863-84be-4421-9938-d8ce0525ef1e,360343994-5,Xinbao,Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.,11/18/15,,,Pending Diagnostics,33,Flashdog,no -Melissa Day,mday1o@archive.org,mday1o,justo sollicitudin,vestibulum velit id,tellus semper,Fivechat,3.54E+15,996499bc-968c-4ecc-a7d6-07e9348d552b,077200127-8,Nancheng,"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.",8/1/15,166.87,,Ready to Deploy,35,Trudeo,no -Laura Watkins,lwatkins1p@quantcast.com,lwatkins1p,quam pharetra,imperdiet,ut massa,Oyondu,4.91E+18,a7db733b-732c-4b54-901e-1ad28e145cb0,177033838-1,Wuhu,"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.",12/4/15,406.96,,Lost,14,Devpoint,no -Donna Kelley,dkelley1q@dailymotion.com,dkelley1q,sapien placerat,nulla elit ac,tincidunt eu felis,Aimbo,5.61E+15,fed4c9f9-8b0d-4d6a-ac43-c7929a31bb72,952276162-1,Arnhem,"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.",1/24/16,,,Pending Diagnostics,17,Kayveo,no -Adam Marshall,amarshall1r@marketwatch.com,amarshall1r,etiam faucibus cursus,cubilia curae mauris,massa id nisl venenatis,Skajo,3.55E+15,e7c5e464-9bbd-4d86-b549-3d3883b3139e,224555307-1,Dachang,Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.,2/13/16,,,Lost,27,Skidoo,no -Peter Mills,pmills1s@instagram.com,pmills1s,diam id,dolor sit,ligula vehicula consequat,Gabtune,3.57E+15,efdf785c-a484-4454-8be0-d3b598c77a8d,269508722-5,Voi,"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.",5/23/16,312.07,,Archived,,,no -Christina George,cgeorge1t@163.com,cgeorge1t,vivamus vestibulum,quisque,id mauris vulputate elementum nullam,Yabox,6.71E+15,1a4d5946-1539-4ff1-921b-c442eef8c28b,596138325-3,Campamento,,4/3/16,221.14,,Archived,,,no -Theresa Cunningham,tcunningham1u@infoseek.co.jp,tcunningham1u,nisl aenean lectus pellentesque,fermentum,nisl ut,Skimia,3.58E+15,70d0b5d4-8569-421c-912c-9ee11c565b62,769779519-1,Plei KÌ¢íÛn,"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",3/22/16,,Konklab,Ready to Deploy,13,Wordify,yes -Stephanie Burke,sburke1v@unicef.org,sburke1v,ac neque duis bibendum,duis consequat,rhoncus aliquam lacus morbi,Centizu,3.58E+15,,709282846-5,Donghui,,6/23/15,29.7,,Pending Diagnostics,,,yes -Lillian Ferguson,lferguson1w@go.com,lferguson1w,elementum eu interdum,arcu libero rutrum,viverra pede,Muxo,5.60E+16,29de084a-9376-48dc-a310-cd01b1baf715,603152718-9,Camp Diable,,2/9/16,,,Ready to Deploy,,,yes -Bonnie Graham,bgraham1x@hexun.com,bgraham1x,mauris morbi non,tristique in tempus,ipsum aliquam non,Devpoint,6.30E+18,d8a596a6-f59a-4423-b424-b3d132c06791,297281098-8,Vlachovice,"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.",2/10/16,553.28,,Archived,,,yes -Michael Lawrence,mlawrence1y@addtoany.com,mlawrence1y,nonummy maecenas,nec euismod scelerisque,a suscipit nulla elit,Katz,3.59E+15,e95115b8-b45e-4a89-b271-c46732934394,740776342-9,Babica,"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",3/18/16,552.55,,Lost,,,yes -Martin Scott,mscott1z@list-manage.com,mscott1z,sodales scelerisque mauris sit,ut erat,sem fusce consequat nulla,Linklinks,3.74E+14,9a108aa1-568c-4141-a8d8-92a451f68e22,915137664-4,Reshetikha,Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.,8/26/15,,,Archived,16,Kanoodle,yes -Kelly Mitchell,kmitchell20@google.it,kmitchell20,vestibulum ante,quam sapien varius,morbi odio odio,Edgeclub,5.60E+18,319bf374-753a-49ef-885a-03a27dca4275,225439463-0,Lere,"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.",7/25/15,,,Ready to Deploy,34,Kwilith,yes -Carol Hunter,chunter21@prnewswire.com,chunter21,phasellus in,est congue,tortor eu,Ozu,5.60E+15,a8be5948-b765-4f5d-9f1f-0f26952842d0,001757218-5,Tanabi,Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.,3/11/16,81.15,,Lost,20,Skynoodle,yes -Michelle Simpson,msimpson22@discuz.net,msimpson22,amet sapien dignissim,luctus nec molestie,mauris non ligula pellentesque,Mudo,3.58E+15,b8098077-560e-4a12-8bc1-220d83291032,815922090-1,Aegela,In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.,11/21/15,664.35,,Lost,28,Edgeblab,yes -Pamela Martin,pmartin23@ca.gov,pmartin23,mollis molestie lorem quisque,ut,donec semper sapien a libero,Zoombeat,5.05E+15,cb7572bc-d7ca-4b5b-903e-fc567c337e32,057863524-0,Empangeni,"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.",4/6/16,711.1,Transcof,Ready to Deploy,,,yes -Johnny Burke,jburke24@va.gov,jburke24,eros suspendisse,arcu,justo eu massa donec dapibus,Skipfire,5.60E+17,,155395513-7,Half Way Tree,In congue. Etiam justo. Etiam pretium iaculis justo.,5/25/15,538.84,,Pending Diagnostics,,,yes -Adam Stevens,astevens25@feedburner.com,astevens25,justo maecenas rhoncus,sed,in consequat ut nulla,Jetwire,3.56E+15,,296612694-9,Xinjian,Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.,11/1/15,,,Undeployable,19,Minyx,yes -Gloria Black,gblack26@samsung.com,gblack26,pharetra magna,et ultrices posuere,habitasse platea dictumst morbi,Bubbletube,3.58E+15,271065e6-f1fe-4d2b-aa47-76234656e29b,515439385-6,Santiago del Estero,,5/12/16,948.04,,Lost,,,yes -Christine Jenkins,cjenkins27@pbs.org,cjenkins27,justo morbi ut odio,sagittis,auctor gravida sem praesent id,Mynte,6.76E+18,30f840cb-acfd-4797-82ad-14c6ccad4d03,790003898-1,Ban Phan Don,,1/19/16,89.14,,Archived,,,yes -Michelle Riley,mriley28@networkadvertising.org,mriley28,justo maecenas,nulla tellus in,ligula in lacus,Izio,4.94E+18,b695a1c5-c4af-493e-8851-63aa41032c01,524481245-9,Munggang,,11/21/15,895.08,,Undeployable,29,Feednation,yes -Diana Torres,dtorres29@sciencedaily.com,dtorres29,nulla pede ullamcorper augue,rutrum rutrum neque,enim in tempor turpis,Jabberbean,3.57E+15,,743088297-8,Bromma,"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",2/29/16,833.54,,Archived,,,no -Dorothy Simmons,dsimmons2a@adobe.com,dsimmons2a,amet sem fusce,id,hendrerit at vulputate vitae,Gabtype,5.41E+15,c6144e5d-0b64-44d1-bba5-0a7b0e3e78dc,998959453-8,Tawau,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",8/25/15,,,Lost,,,no -Michelle Hunt,mhunt2b@t-online.de,mhunt2b,orci vehicula,odio,eleifend luctus ultricies eu,Dazzlesphere,3.54E+15,9e1a7fdc-e410-4369-8203-f170e5e749b0,118824344-6,North Vancouver,Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.,2/29/16,663.19,,Undeployable,30,Twiyo,no -,,,eu mi nulla ac,bibendum,ridiculus mus vivamus vestibulum,Skiptube,5.60E+15,659b0fc6-7107-4be4-8478-836366c4e23c,867984064-5,Hekou,Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.,5/6/16,,,,16,Ntags,no -Andrew White,awhite2d@a8.net,awhite2d,a nibh,amet cursus id,curabitur in,Ooba,3.54E+15,f2cfaa82-b40f-4119-81c0-f5a8eeb06a38,222270511-8,Angeghakot‰Ûª,"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",6/8/15,651.3,,Pending Diagnostics,,,no -Brian Franklin,bfranklin2e@dot.gov,bfranklin2e,nisi eu orci mauris,hendrerit at vulputate,tortor duis mattis egestas metus,Jazzy,3.75E+14,638497c0-4690-4872-b241-c15545806c8a,255225792-5,Durayk€Çsh,"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.",10/8/15,141.33,,Ready to Deploy,,,no -Martha Carter,mcarter2f@soundcloud.com,mcarter2f,posuere cubilia curae mauris,quis turpis,in magna bibendum imperdiet nullam,Demizz,6.76E+18,b3c36765-d42a-4c83-bc62-300bb07fde98,361952926-4,Opatov,,7/8/15,599.89,,Ready to Deploy,,,no -Joe Stone,jstone2g@shinystat.com,jstone2g,ut at,dapibus,ultrices aliquet,Wikibox,3.55E+15,,100773046-3,Atbasar,"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.",1/31/16,16.78,,Pending Diagnostics,20,Gigazoom,no -Todd Brooks,tbrooks2h@nbcnews.com,tbrooks2h,sed sagittis nam,lacinia eget tincidunt,porta volutpat erat,Wikibox,3.70E+13,,703472241-2,Oslo,Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.,6/28/15,573.06,Solarbreeze,Ready to Deploy,31,Buzzshare,no -Janet Cox,jcox2i@virginia.edu,jcox2i,tristique est et tempus,sociis natoque penatibus,auctor sed,Dynazzy,6.39E+15,54316132-26aa-4bf0-9e1b-6dcd2afda6ce,715414385-1,Saidpur,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",9/15/15,,,Lost,,,no -Henry Richardson,hrichardson2j@shinystat.com,hrichardson2j,enim leo rhoncus,eros,dolor sit,Blogpad,3.55E+15,7d1c8d21-7a56-4c23-8649-061ddd708893,764086552-2,Momanalu,"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.",8/29/15,,,Pending Diagnostics,,,no -Rose Thompson,rthompson2k@goo.gl,rthompson2k,in faucibus,vitae ipsum aliquam,quam sollicitudin vitae consectetuer eget,Yakitri,5.60E+15,7bd1251a-ea8c-46dc-93d2-93f62d90a969,509026038-9,Rennes,In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.,4/18/16,551.23,,Pending Diagnostics,,,no -Julie Hunter,jhunter2l@delicious.com,jhunter2l,morbi odio odio,sociis natoque,in sapien,Jatri,6.71E+15,ea523cc9-6428-4a4b-aed9-c470703891f1,900328219-6,Quevedo,"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",9/7/15,291.69,Otcom,Undeployable,25,Jazzy,no -Dorothy Reed,dreed2m@dedecms.com,dreed2m,donec posuere metus,ut,tellus nisi,Kazio,3.55E+15,3772945a-1148-4051-8443-0ee8a92c5b17,328780394-8,Grujugan,,9/19/15,488.83,,Pending Diagnostics,,,no -Phyllis Foster,pfoster2n@indiegogo.com,pfoster2n,amet erat nulla tempus,sit amet,aenean auctor,Shufflebeat,3.53E+15,db9552e3-13f9-40c4-9c2c-8bff3c4c0a89,611870818-5,Oepula,,6/13/15,,Vagram,Ready to Deploy,,,no -Marie Henry,mhenry2o@accuweather.com,mhenry2o,odio consequat varius,pede justo lacinia,posuere cubilia curae nulla,Rhybox,3.57E+15,d6289b7b-4ed2-4ec8-a486-4cdd3591da21,277787832-3,Kotabaru,Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.,7/3/15,157.31,,Undeployable,25,Bluejam,no -Linda Crawford,lcrawford2p@ocn.ne.jp,lcrawford2p,adipiscing elit proin,lobortis sapien sapien,blandit non,Jetpulse,6.77E+15,79bfd454-34f7-4eb5-8873-aaf43cc63856,982547121-2,Efeng,,6/27/15,287.02,,Ready to Deploy,21,Babbleset,no -Thomas Dean,tdean2q@prnewswire.com,tdean2q,pede posuere nonummy,at nulla suspendisse,interdum in ante vestibulum,Demimbu,3.57E+15,11515f0c-7d36-4938-9656-ae35be5aad59,847767021-8,‰Û÷Ayn al Bay‡üÔ€,"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.",3/15/16,,,Ready to Deploy,20,Devcast,no -George Sanchez,gsanchez2r@photobucket.com,gsanchez2r,cras in purus,sed tristique in,consequat varius integer ac,Feedfire,6.33E+15,354f339d-547d-4b10-8753-a8e5cb18e1d9,408291765-9,Lakatnik,Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.,1/1/16,,,Lost,25,Edgewire,no -Kathleen Alvarez,kalvarez2s@amazonaws.com,kalvarez2s,duis faucibus,mauris ullamcorper,orci luctus,Wikizz,6.30E+18,edb67bfb-ef81-44e7-a9fe-ee496816f7bc,134054338-9,Longsha,Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.,7/6/15,,,Archived,,,no -Joan Rodriguez,jrodriguez2t@sohu.com,jrodriguez2t,morbi porttitor lorem id,leo,sem duis aliquam convallis,Janyx,6.30E+15,30a3b488-c5db-4569-ac68-46cb86424ff1,145564427-7,Arlington,"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.",7/17/15,,,Archived,28,Trunyx,no -Jean Russell,jrussell2u@shop-pro.jp,jrussell2u,nulla tempus vivamus,eu felis fusce,cubilia curae nulla dapibus,Yata,3.58E+15,f285b067-6992-4a07-b34d-ed174dc73611,761164290-1,Gamut,Sed ante. Vivamus tortor. Duis mattis egestas metus.,1/11/16,27.46,,Ready to Deploy,,,no -Henry Chapman,hchapman2v@usa.gov,hchapman2v,erat nulla,morbi non,varius ut,Avamba,6.76E+15,d1f90868-5e42-46c3-aba7-7de3e3d74091,415061883-6,Ondoy,Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.,7/7/15,,,Archived,,,no -Rebecca Nichols,rnichols2w@who.int,rnichols2w,praesent blandit lacinia,eu sapien cursus,lacinia sapien quis libero,Camimbo,3.01E+13,cb932a82-1853-40cc-b6c0-28bf3b4ea677,194809534-3,Foundiougne,In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.,12/3/15,,,Lost,29,Edgeblab,no -Norma Harper,nharper2x@histats.com,nharper2x,nisi vulputate nonummy,justo in,ornare consequat,Twinder,3.75E+14,,875593621-0,Pruchnik,,6/19/15,,,Lost,,,no -Henry Jacobs,hjacobs2y@tamu.edu,hjacobs2y,fermentum donec ut mauris,augue luctus,suspendisse ornare consequat lectus,Realcube,3.53E+15,88a71d08-c42b-43c7-bdb8-973a2e0efe47,112308770-9,Cabugao,"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.",3/5/16,787.63,,Undeployable,15,Tagpad,no -Matthew Walker,mwalker2z@shareasale.com,mwalker2z,vel augue,ipsum dolor sit,ante ipsum primis in faucibus,Tazz,5.02E+16,84198bfa-740b-4ff9-907e-05a9ef1bece4,160413103-9,Gwanda,In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.,11/13/15,899.41,,Lost,18,Photobug,no -Diana Wells,dwells30@upenn.edu,dwells30,lobortis convallis,maecenas tincidunt,morbi non,Meevee,3.57E+15,5e030a75-f9b4-42fc-9958-f3dba0b7fc0a,171773317-4,SokodÌ©,Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.,5/25/15,243.93,,Undeployable,,,no -Keith Barnes,kbarnes31@prnewswire.com,kbarnes31,dui vel sem,iaculis,justo maecenas rhoncus aliquam,Topicstorm,4.04E+12,60e9678e-026b-47c7-b7cb-dc00677aaed7,501758906-6,Goz BÌ©Ìøda,,7/18/15,706.29,,Ready to Deploy,29,Jaxspan,no -Julia Romero,jromero32@youku.com,jromero32,lacinia sapien quis libero,ante vestibulum,at feugiat non pretium quis,Dabtype,3.56E+15,147b19d6-1712-4435-aa35-07de51b13306,647505330-9,Ishurdi,"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.",3/6/16,993.95,,Archived,,,no -Catherine Gonzales,cgonzales33@apache.org,cgonzales33,suscipit nulla,aliquet massa,viverra dapibus nulla suscipit ligula,Mudo,3.01E+13,aa2e2be0-9f07-4311-a402-a11b2a6ca0d8,650783961-2,Xianxi,Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.,1/25/16,669.89,,Undeployable,20,Yabox,no -,,,ultrices enim lorem,leo rhoncus sed,cras non velit,Npath,5.05E+15,7fa57f76-d4fe-4871-8296-20ecd1627acb,192573987-2,Zhongzhai,Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.,11/9/15,352.64,,,,,no -Arthur Carter,acarter35@time.com,acarter35,pede lobortis,suspendisse accumsan tortor,lacus curabitur at,Dynabox,2.02E+14,7ae2a979-52f8-4ce4-ab76-3c0f336589ae,613690706-2,Mangunjaya,Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.,9/24/15,,Alphazap,Archived,35,Tekfly,no -Jack Henderson,jhenderson36@skype.com,jhenderson36,ut erat id,sapien,etiam pretium,Zoombox,4.91E+18,80744318-59a6-4be4-8876-428bb2eb6182,122507763-X,Lieqiao,"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",1/3/16,91.35,,Pending Diagnostics,,,no -Janice Mills,jmills37@sphinn.com,jmills37,cubilia curae donec pharetra,non,pede libero quis,Oyondu,3.56E+15,51df7750-55b9-43ee-ba42-4baf70209c96,113885169-8,El Crucero,"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",1/4/16,475.07,,Lost,,,no -Janet Burke,jburke38@home.pl,jburke38,tincidunt nulla mollis,vulputate ut,nibh in quis,Zooveo,6.39E+15,,244101401-3,Cuijiamatou,"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.",7/25/15,900.01,,Archived,12,Npath,no -Rose Smith,rsmith39@nymag.com,rsmith39,pede venenatis non,pellentesque quisque porta,cursus id turpis integer,Buzzshare,3.57E+15,c8de4be3-fdc8-4d57-b1d8-26414ad341bf,691575472-5,Berdyans‰Ûªk,,10/9/15,708.94,,Undeployable,14,Topiclounge,no -Rachel Rice,rrice3a@blogs.com,rrice3a,in hac habitasse platea,nulla,odio odio,Bubbletube,3.03E+13,6388f77f-cc36-41ca-8482-2cfc5c0e6bdf,007983155-9,Cibeusi,"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.",6/25/15,532.83,,Pending Diagnostics,16,Shuffletag,no -Judith Morrison,jmorrison3b@nhs.uk,jmorrison3b,ut rhoncus,quam fringilla rhoncus,neque libero convallis,Jamia,6.76E+18,b8186328-c3b6-479b-bec9-3613ce9698d1,977093557-3,Ramada,,12/7/15,,,Pending Diagnostics,,,no -Kathy Peterson,kpeterson3c@weebly.com,kpeterson3c,nulla ultrices,ac est lacinia,est quam pharetra magna ac,Flashpoint,5.10E+15,02bead54-767b-4f61-a085-5439aeaf1712,349567419-5,Chonghe,,9/8/15,,,Pending Diagnostics,,,no -,,,tortor duis,dictumst morbi,quam a odio in,Avavee,5.52E+15,2fb047a6-ff89-4d9d-bfe6-304acad6ef17,145992551-3,Strelitsa,Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.,12/22/15,741.63,,,,,no -Richard Howell,rhowell3e@msu.edu,rhowell3e,venenatis non,et,vehicula condimentum,Tagcat,6.33E+17,8bbd2e80-6a66-4210-926d-6f45c896143a,957728455-8,R€wandÇz,Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.,7/17/15,205.46,,Pending Diagnostics,26,Youtags,no -Mark Evans,mevans3f@businessweek.com,mevans3f,dolor quis odio,imperdiet,viverra diam vitae quam,Plambee,3.56E+15,,871649171-8,Klukeng,Sed ante. Vivamus tortor. Duis mattis egestas metus.,5/9/16,413.07,,Archived,22,Izio,no -Joan Wagner,jwagner3g@earthlink.net,jwagner3g,adipiscing elit,fusce,suspendisse ornare consequat,Kamba,2.01E+14,4571c470-5dce-40b5-8b2f-1262071ec940,747966524-5,Moll€síã€n€Ç,,3/8/16,,,Pending Diagnostics,26,Wikibox,no -Lillian Simmons,lsimmons3h@icio.us,lsimmons3h,ac enim in tempor,in tempor turpis,lacinia erat,Chatterbridge,5.10E+15,9a43a613-f009-4dd8-94ac-8af4a67b4dd0,178043767-6,€oan HÌ_ng,"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",5/2/16,181.91,,Archived,18,Voomm,no -,,,amet diam,consectetuer eget,et magnis dis,Trupe,2.02E+14,2947a0fc-1cae-4838-b315-df505ae4a9d6,808569614-2,Mweka,Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.,11/9/15,966.58,,,,,no -Arthur Morgan,amorgan3j@google.ca,amorgan3j,vestibulum eget,ut odio cras,vel nisl,Tanoodle,3.53E+15,fa077cd0-ba8d-4225-8eb4-84033ed235fc,863693363-3,Zbøch,,9/23/15,,,Archived,31,Blognation,no -,,,tempus vivamus,cras,nonummy maecenas tincidunt lacus,Zoomzone,3.53E+15,a37407ca-e6af-408c-9b9a-1aa9f90f7681,009595242-X,Sanzhang,Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.,3/27/16,,,,18,Layo,no -Joan Matthews,jmatthews3l@ca.gov,jmatthews3l,ultrices aliquet maecenas,pretium iaculis,mus vivamus vestibulum,Bubblemix,3.53E+15,1e99a09a-06ab-4ba2-a98d-785f7c64ebe2,827817985-9,Jalasenga,"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.",3/19/16,784.52,It,Undeployable,,,no -Jose Larson,jlarson3m@blogspot.com,jlarson3m,dolor morbi vel lectus,cum sociis,fringilla rhoncus mauris enim,Thoughtmix,3.58E+15,4c75547e-5c7a-45be-b532-ec13873b5ddd,179689579-2,JaboatÌ£o,,7/15/15,638.29,,Archived,24,Meejo,no -Eric James,ejames3n@nbcnews.com,ejames3n,nulla integer,aliquam augue,sem duis aliquam,Trudeo,3.58E+15,,059264762-5,Suwa,"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.",2/14/16,,,Ready to Deploy,34,Zoonder,no -Sara George,sgeorge3o@thetimes.co.uk,sgeorge3o,leo odio,pretium quis,varius nulla facilisi,Babbleblab,3.38E+14,ccf578a6-e39c-45cf-a656-3a948922aee1,133207649-1,Krajan Puru,"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.",4/13/16,604.35,,Lost,14,Tagfeed,no -,,,dapibus at diam,morbi,ante ipsum primis in faucibus,Lazz,2.02E+14,b15d20dc-eed3-47a4-a83f-67229d154e95,041974769-9,Kengkou,Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.,12/29/15,662.67,,,,,no -Ashley Torres,atorres3q@google.ru,atorres3q,eu interdum eu,eleifend,orci nullam molestie nibh in,Yacero,6.30E+18,bfdad967-af1e-4bbf-b456-eca3259120de,184953647-3,Tosontsengel,"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.",4/10/16,330.47,,Ready to Deploy,18,Trilith,no -Andrea Owens,aowens3r@digg.com,aowens3r,eget congue,congue,purus eu,Realmix,3.56E+15,d3b995cd-cc81-4761-b7bd-5b160bb199b8,795122803-6,Middelburg,"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.",7/11/15,925.85,,Archived,15,Mydeo,no -,,,pede justo eu,pellentesque volutpat dui,consectetuer adipiscing elit proin,Riffpedia,3.74E+14,b16407a3-9d08-421e-b386-ab93a838a67d,892497373-8,Zaragoza,Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.,4/3/16,187.77,,,,,no -Evelyn Wright,ewright3t@gmpg.org,ewright3t,ut suscipit,sit,tortor id nulla ultrices aliquet,Browseblab,3.57E+15,c698ba68-bc82-4aff-acb3-5884c9f80296,162198086-3,Fuchun,,10/29/15,718.76,,Pending Diagnostics,21,Edgeblab,no -,,,velit donec diam neque,augue,non velit,Jabberbean,3.58E+15,89a8527c-f296-4146-b4c0-81bf896f8af6,653699580-5,Crumlin,Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.,10/18/15,648.77,,,,,no -Melissa Rogers,mrogers3v@army.mil,mrogers3v,id ornare imperdiet sapien,a ipsum,libero nam dui proin,Rhynoodle,4.03E+15,fb87796a-6f51-4dd8-af0c-c079423a0364,356963504-X,Verkhnyaya Belka,"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.",2/23/16,646.41,,Pending Diagnostics,35,Yambee,no -Susan Palmer,spalmer3w@tiny.cc,spalmer3w,montes nascetur ridiculus,ut,sed justo,Jabbersphere,3.58E+15,216ce7c7-3bc1-446a-b240-9ee34dbbf605,333143943-6,Raoshi,,1/5/16,469.31,,Undeployable,,,no -Joseph Mason,jmason3x@scribd.com,jmason3x,volutpat dui maecenas,sed vestibulum sit,eu est congue,Photobean,3.53E+15,1dd924ac-243a-42f7-af03-18e7074d2d8a,657518473-1,Lyon,Fusce consequat. Nulla nisl. Nunc nisl.,3/4/16,908.76,,Lost,30,Yamia,no -Christina Wood,cwood3y@digg.com,cwood3y,eget nunc donec,potenti cras in,lorem id ligula,Topicware,6.76E+15,d2170b43-c046-4189-88a0-0f3b3b4ab636,499571423-5,Pengfang,,11/24/15,562.82,,Pending Diagnostics,15,Camido,no -Dennis Chavez,dchavez3z@senate.gov,dchavez3z,nisi vulputate nonummy maecenas,dictumst aliquam augue,duis ac,Blogspan,5.05E+15,1bba6cc4-b7fd-4614-82a1-058a6dcf5cff,340219850-9,Carmen de Viboral,,8/31/15,764.65,,Undeployable,,,no -Emily Roberts,eroberts40@privacy.gov.au,eroberts40,sed lacus morbi,est,nibh fusce lacus purus,Skyba,6.77E+17,79a0ca4b-0cfc-450b-8b99-91995e017d06,393164024-8,Syamzha,"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",9/26/15,129.34,,Lost,,,no -Christine Cooper,ccooper41@buzzfeed.com,ccooper41,rutrum nulla nunc,vulputate,commodo vulputate justo,Skyba,2.02E+14,0dcd299f-f6ad-448e-8ee2-9366b2c6fab1,634821306-4,Vyerkhnyadzvinsk,"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.",7/10/15,970.05,,Undeployable,13,Kare,no -Lois Sanders,lsanders42@nps.gov,lsanders42,elementum eu,donec,purus aliquet at feugiat,Ntags,2.02E+14,da4f2d20-d589-4d67-99a5-ba1a91f89c67,696680010-4,Kwali,,1/23/16,592.32,,Lost,,,no -Jonathan Carroll,jcarroll43@microsoft.com,jcarroll43,ut nunc vestibulum ante,nec,maecenas tincidunt lacus at velit,Zoomzone,5.02E+18,f2721951-674f-42a6-a318-ebc4c2ad01d6,767022282-4,Hukou,Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.,7/25/15,100.04,,Lost,,,no -,,,sagittis nam congue,neque vestibulum eget,lorem vitae mattis nibh,Rhynoodle,3.54E+15,fd38d1eb-46bc-438c-a1f1-e3d46c7ab7ff,446825369-X,Pogag,"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",9/14/15,282.91,,,,,no -Heather Welch,hwelch45@cyberchimps.com,hwelch45,lorem id,quis odio,ullamcorper augue a suscipit nulla,Dynabox,3.58E+15,2b8a3a33-23e4-4f7c-bfa2-cda5a4f161a7,453899036-1,BulqizÌÇ,"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",5/22/16,937.05,,Undeployable,,,no -Paul Allen,pallen46@gravatar.com,pallen46,mus etiam,consequat varius,non ligula,Youopia,3.58E+15,8c118547-498c-4c9a-bb7c-38bf34c948f6,059415701-3,Niederanven,,4/10/16,528.2,,Archived,,,no -Sarah Cox,scox47@nih.gov,scox47,venenatis tristique fusce congue,ultrices aliquet maecenas,quam turpis adipiscing lorem,Tagchat,5.40E+15,,907635086-8,_wi€ªtajno,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",5/28/15,136.71,,Undeployable,29,Rooxo,no -Sara Hayes,shayes48@boston.com,shayes48,lobortis est phasellus sit,at,at velit eu,Ozu,3.53E+15,f6dfecd8-6e31-4f7a-ac69-7d78288b3c50,924210851-0,Las Trojes,,6/10/15,239.46,,Lost,,,no -Marilyn Hunt,mhunt49@dagondesign.com,mhunt49,pulvinar nulla pede ullamcorper,vel nulla eget,justo aliquam quis turpis eget,Mycat,3.58E+15,,052859686-1,La Mesa,,11/21/15,665.35,,Lost,,,no -Antonio Jordan,ajordan4a@joomla.org,ajordan4a,tempus semper est,massa volutpat convallis,id ligula suspendisse ornare,Babblestorm,3.56E+15,bad2fb72-05bf-4bd1-928e-4474f41bf61e,937286949-X,San Mariano,,3/8/16,738.25,,Lost,,,no -Doris Russell,drussell4b@utexas.edu,drussell4b,nunc donec,varius,ut dolor morbi vel lectus,Skyble,5.10E+15,d028c3fb-74ea-4e2f-a4ff-86b869f88f7a,523965895-1,Fenghuanglu,"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",9/19/15,433.76,,Pending Diagnostics,,,no -Gary Gutierrez,ggutierrez4c@washington.edu,ggutierrez4c,quam pharetra magna ac,ullamcorper augue a,platea dictumst,Realfire,4.91E+18,6c1b81d0-0b44-4cfd-9a29-1abb81d6dd8b,624477386-1,Wuyanquan,,12/27/15,783.9,,Ready to Deploy,,,no -Mary Long,mlong4d@springer.com,mlong4d,dolor morbi vel,sit amet cursus,ante ipsum primis in faucibus,Shuffledrive,3.56E+15,6d8835b2-8241-4b4d-afff-95f6aa252ef4,737509813-1,Freiria,"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.",2/11/16,139.88,Solarbreeze,Undeployable,16,Chatterpoint,no -Martha Olson,molson4e@myspace.com,molson4e,nulla dapibus,orci luctus,felis sed,Vinder,5.52E+15,10ce22fe-38b7-4329-92a7-bd6dd7a35779,140866058-X,Rangah,"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.",3/21/16,493.73,,Archived,,,no -Willie Crawford,wcrawford4f@chronoengine.com,wcrawford4f,ac nibh,pharetra magna,non interdum,Jamia,3.53E+15,bb5962ec-37e3-4e87-93ae-e748aad36f0d,564625708-5,H€Çrna,"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",9/7/15,,,Undeployable,,,no -Shirley Butler,sbutler4g@google.fr,sbutler4g,augue luctus tincidunt,nibh in,nulla justo aliquam quis,Skyble,3.54E+15,61ea2737-22f0-40c9-b1d0-8c8bcb49024c,510924762-5,Bradag,Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.,6/4/15,232.69,,Lost,,,no -Amanda Perkins,aperkins4h@facebook.com,aperkins4h,magna ac,leo,eu orci mauris,Gigashots,3.53E+15,723cb029-3cc3-4fa1-ae81-46897e1b0cd5,992799439-7,Charneca da Cotovia,,6/2/15,185.11,,Undeployable,17,Yoveo,no -Jason Mendoza,jmendoza4i@studiopress.com,jmendoza4i,est quam pharetra magna,vestibulum sagittis,vehicula condimentum curabitur in libero,Browseblab,3.53E+15,fa051420-1c81-4f0d-a5d4-0d966801ac92,943747227-0,Yunzhong,"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",3/3/16,,,Pending Diagnostics,,,no -Ernest Spencer,espencer4j@skyrock.com,espencer4j,id pretium,fringilla,porttitor id consequat in consequat,Devshare,6.76E+16,b04f9655-5453-4003-b4ff-a2c04fb7cc14,187554544-1,Sukasenang,"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.",5/15/16,,,Undeployable,,,no -Roy Burton,rburton4k@uiuc.edu,rburton4k,sed tristique in,sit amet cursus,id massa id nisl venenatis,Tagtune,3.55E+15,,492976895-0,Pakuranga,Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.,7/14/15,44.95,,Archived,14,Meembee,no -Edward Palmer,epalmer4l@lulu.com,epalmer4l,enim blandit mi,ipsum praesent,lectus pellentesque at,Roomm,3.38E+14,5039582c-b5cf-4b9e-8f07-6341560cae04,007320130-8,Vanves,"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.",1/23/16,610.68,,Archived,,,no -Christine Gonzales,cgonzales4m@gmpg.org,cgonzales4m,pede ac diam cras,nec nisi,tincidunt lacus at velit vivamus,Dynava,3.57E+15,22bac401-b902-4fa6-987c-27238e4982f1,169662058-9,Xianglong,,7/2/15,742.54,,Lost,,,no -Samuel Snyder,ssnyder4n@spiegel.de,ssnyder4n,rhoncus aliquet pulvinar,suscipit a feugiat,sed tristique in tempus,Centizu,3.55E+15,a53ca64f-9589-4c14-9f1f-9fbd7c130bf1,134899168-2,Kopang Satu,,8/24/15,,,Undeployable,,,no -Craig Powell,cpowell4o@fda.gov,cpowell4o,adipiscing lorem vitae,at diam nam,vivamus metus arcu adipiscing,Wikivu,3.53E+15,74d45443-31f9-4149-bdbd-b81a49a8fc26,841640422-4,Valkeakoski,Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.,5/8/16,617.19,,Archived,16,InnoZ,no -Shawn Vasquez,svasquez4p@a8.net,svasquez4p,erat vestibulum,ullamcorper augue,adipiscing molestie hendrerit at vulputate,Blogspan,3.54E+15,b05201f2-5fca-4c51-804c-c5690272809b,981888153-2,Kazinka,,4/4/16,814.56,,Undeployable,27,Ooba,no -,,,fringilla rhoncus mauris,pretium,orci mauris lacinia,Brightbean,3.74E+14,f3317f7a-8b58-4e6e-9c98-bb0990b48de2,364170641-6,Abashiri,"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.",1/23/16,977.48,,,,,no -,,,in imperdiet,lectus,in congue etiam,Topdrive,5.60E+18,6264ae5b-7977-4a7c-80e0-6fb69ee37bb6,356456589-2,Songdong,"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",4/7/16,782.48,,,19,Eayo,no -Jane Mason,jmason4s@ask.com,jmason4s,nisl ut volutpat sapien,amet consectetuer,nec sem duis aliquam,InnoZ,3.58E+15,bbf6c2d3-f538-411d-b3ca-07f87eb514c9,215895129-X,Lincheng,"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.",8/18/15,,,Archived,14,Voolia,no -Jeremy Ross,jross4t@state.gov,jross4t,neque libero convallis,volutpat quam,sagittis nam,Agivu,5.60E+18,85cebfbc-d8ef-4f12-a56c-52d920daff55,533951910-4,Shangtianba,In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.,2/22/16,814.64,,Archived,,,no -Anthony Cruz,acruz4u@ycombinator.com,acruz4u,sapien placerat ante,justo sit,lectus pellentesque,Fivechat,6.77E+18,a61d9de9-aee4-41e5-b923-c9f0b6af353f,516270880-1,Bangolo,,6/15/15,748.03,,Archived,,,no -Janice Kim,jkim4v@tmall.com,jkim4v,feugiat et eros vestibulum,id ligula,dapibus dolor vel,Vitz,3.53E+15,6f58dc1c-1eec-4a6a-89ce-54c0ca6d7ace,906786221-5,Stepove,Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.,2/5/16,245.24,,Ready to Deploy,,,no -Cheryl Schmidt,cschmidt4w@mac.com,cschmidt4w,amet nulla quisque,eget massa tempor,in hac habitasse platea dictumst,Topiczoom,3.56E+15,242df59e-540c-4ae4-9574-b2a1cf0c328f,946770951-1,Bara Datu,,10/14/15,101.41,,Archived,,,no -Betty Long,blong4x@is.gd,blong4x,congue diam id,hac habitasse,quis turpis sed,Linktype,3.53E+15,806c60b9-0518-48b4-88e7-e0f7d37db7f4,714515414-5,Richmond,"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",7/14/15,48.61,,Pending Diagnostics,28,Buzzster,no -Joe Foster,jfoster4y@hao123.com,jfoster4y,elementum ligula,varius nulla facilisi,ante ipsum primis in faucibus,Twitterwire,3.55E+15,004beb8c-6350-4a5a-bc0a-63ae792afe54,109876345-9,Union,"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.",3/18/16,,,Archived,,,no -Lisa Black,lblack4z@elpais.com,lblack4z,aenean fermentum,eget nunc donec,faucibus orci luctus,Ailane,6.77E+16,5f17fc64-ef57-4884-a55f-eb573d697df3,441234671-4,Nurota Shahri,"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.",7/13/15,179.08,,Archived,22,Yodoo,no -Katherine Frazier,kfrazier50@jimdo.com,kfrazier50,nulla nisl nunc nisl,nascetur,justo eu,Edgetag,3.55E+15,fc437480-f186-49fa-87d3-bbea40c6f142,446611490-0,Reguengos de Monsaraz,Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.,5/18/16,960.59,,Pending Diagnostics,,,no -Lois Sanchez,lsanchez51@yellowbook.com,lsanchez51,consectetuer adipiscing elit proin,in felis,purus sit amet nulla quisque,Gabtune,5.00E+15,9c0affc9-3c66-4516-aea7-b1d847590c64,242378972-6,Bang Lamung,,6/10/15,,,Lost,,,no -,,,consectetuer adipiscing elit,eros,ligula vehicula consequat morbi,Oodoo,4.41E+15,b321fffc-88c6-4223-a4c7-e7b513ca12ea,869734139-6,Hongjiang,"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.",10/12/15,136.67,,,,,no -Carl Vasquez,cvasquez53@github.io,cvasquez53,sapien urna,congue etiam,nulla facilisi cras,Katz,5.10E+15,54ca7672-f4a9-46d1-8842-a4f5b3ef8a61,172650221-X,Hechun,"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.",8/4/15,29.63,,Archived,14,Thoughtworks,no -Gary Myers,gmyers54@omniture.com,gmyers54,fusce consequat nulla nisl,amet consectetuer adipiscing,rutrum neque aenean auctor gravida,Eamia,5.60E+16,59f8f748-c09d-4511-a5b2-85fd469ded76,288508590-8,Azun Atme,Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.,11/19/15,872.99,,Pending Diagnostics,,,no -Julie Watkins,jwatkins55@hatena.ne.jp,jwatkins55,sed sagittis nam congue,vehicula,vel dapibus at diam nam,Jabbercube,6.76E+18,d48acb86-3a62-42cc-a88f-75a3a458a2b3,602874973-7,Tonghu,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",12/22/15,517.54,,Ready to Deploy,27,Livetube,no -,,,ante ipsum,in quis justo,quam fringilla rhoncus,Topiczoom,6.71E+18,57e10cfb-8811-4b3b-ac41-5af69cab6d31,851596451-1,Aoluguya Ewenke Minzu,In congue. Etiam justo. Etiam pretium iaculis justo.,8/2/15,992.72,,,17,Skiptube,no -Harry Carter,hcarter57@icio.us,hcarter57,pulvinar nulla pede,potenti in eleifend,convallis nulla neque libero convallis,Linktype,3.05E+13,bca3bec1-cd84-4e44-adf9-2d266bfe0507,943283235-X,Cachoeiro de Itapemirim,,8/30/15,633.31,,Lost,31,Dabjam,no -,,,dolor quis odio consequat,diam erat fermentum,proin leo odio porttitor,Edgewire,3.56E+15,1685ce6c-7936-4563-925f-7f3b3b9c0a3d,040550980-4,Nusajaya,"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.",2/4/16,,,,,,no -Harold Rice,hrice59@si.edu,hrice59,congue etiam justo,justo sit,id massa id,Innojam,4.91E+15,291a2ce5-c22e-4782-b4aa-ef147fccef40,103809062-8,Chornyanka,Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.,6/10/15,289.46,Konklux,Undeployable,,,no -Cheryl Owens,cowens5a@friendfeed.com,cowens5a,tincidunt nulla mollis,platea dictumst,ut massa quis,Kwimbee,6.33E+18,abe4c9e3-aeb2-407b-8545-3c2e5c29b942,698953325-0,Bula,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",12/25/15,,,Undeployable,,,no -Patricia Nelson,pnelson5b@unicef.org,pnelson5b,vel augue,iaculis justo in,primis in faucibus orci luctus,Camimbo,3.74E+14,8873831c-2370-4fcc-bc23-a66a2a379151,147321108-5,Novoarkhanhel‰Ûªs‰Ûªk,Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.,8/9/15,,,Pending Diagnostics,27,Tazz,no -,,,justo sit amet,convallis,nunc viverra dapibus,Meevee,3.55E+15,d6ff99f7-3f70-4cd1-8835-12fb324783bb,404043143-X,Santiaoshi,,5/31/15,613.59,,,24,Flipstorm,no -Jonathan Dixon,jdixon5d@dailymotion.com,jdixon5d,consequat varius integer ac,amet,pellentesque at nulla suspendisse,Realcube,3.55E+15,52e75c85-f2dc-4346-8615-c4bef2675fb5,759833466-8,Verin Artashat,"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.",12/8/15,854.21,,Pending Diagnostics,12,Skiptube,no -Linda Fernandez,lfernandez5e@zimbio.com,lfernandez5e,pede lobortis ligula,fusce,pede ac,Kwideo,3.54E+15,a385ede0-c8e6-45cc-b125-be149bbc0271,697050795-5,Porsgrunn,Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.,7/4/15,412.28,,Ready to Deploy,,,no -Louis Ortiz,lortiz5f@w3.org,lortiz5f,aliquam erat volutpat in,nibh,vel sem,Jabbertype,3.58E+15,0b44ec74-81de-4421-baec-1fd1f497bedd,492892856-3,Sionogan,,11/23/15,462.04,,Ready to Deploy,34,Thoughtworks,no -Donald Lynch,dlynch5g@lycos.com,dlynch5g,vestibulum ante ipsum primis,lectus vestibulum,ut dolor morbi vel lectus,Mycat,3.53E+15,86b60c4e-9a98-47b4-92db-466162306529,234535580-8,Tokonou,Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.,2/3/16,,,Undeployable,13,Skynoodle,no -Ernest Dixon,edixon5h@rakuten.co.jp,edixon5h,nulla neque,molestie lorem,mi in porttitor,Bluejam,6.33E+18,45e9100c-17a9-4806-a7ee-b731162718f4,321912329-5,Bayasgalant,"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",6/26/15,302.69,,Lost,,,no -Carlos Hart,chart5i@imdb.com,chart5i,lectus pellentesque,lorem ipsum dolor,leo odio porttitor,Plambee,3.54E+15,44a1e395-adfa-4097-a356-2caf29f31458,679447867-6,W€d€Ç as S€Çr,"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",8/5/15,590.47,,Undeployable,36,Jayo,no -,,,dui maecenas,pulvinar sed,pede ullamcorper augue,Lazz,5.10E+15,,524105876-1,Nab€Çnagar,Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.,2/4/16,,,,,,no \ No newline at end of file diff --git a/sample_csvs/MOCK_CONSUMABLES.csv b/sample_csvs/MOCK_CONSUMABLES.csv deleted file mode 100644 index b975e31999..0000000000 --- a/sample_csvs/MOCK_CONSUMABLES.csv +++ /dev/null @@ -1,101 +0,0 @@ -Item Name,Purchase Date,Purchase Cost,Location,Company,Order Number,Category,Requestable,Quantity -eget,01/03/2011,85.91,mauris blandit mattis.,Lycos,T295T06V,Triamterene/Hydrochlorothiazide,No,322 -Morbi,10/24/2016,87.42,iaculis,Lavasoft,W787T62Q,Ranitidine HCl,1,374 -arcu.,09/22/2007,48.34,ornare,Google,N961E50A,Amoxicillin,False,252 -nec,08/16/2009,8.71,lectus,Apple Systems,X624N14C,Lantus Solostar,1,30 -Nam,03/30/2017,24.07,"a,",Macromedia,N618A20S,Hydrocodone/APAP,True,551 -Nullam,12/16/2003,73.23,"Donec est mauris,",Yahoo,B386I67L,Fluticasone Propionate,No,395 -erat,08/03/2010,17.49,Proin,Borland,G606H92I,Amlodipine Besylate,1,297 -purus,10/12/2004,63.52,tellus justo sit,Chami,R660Z45O,Omeprazole (Rx),Yes,557 -dignissim,11/10/2010,77.94,nibh vulputate mauris,Lavasoft,G230Z67X,Risperidone,1,47 -Nam,01/25/2015,64.33,taciti sociosqu ad,Microsoft,B613L84C,Suboxone,No,310 -Nunc,04/13/2017,81.02,nec orci.,Borland,O367N55N,Fluoxetine HCl,No,404 -Phasellus,12/23/2005,70.67,"quis, tristique ac,",Borland,K941C02T,Alendronate Sodium,0,590 -Nulla,07/21/2017,99.04,augue malesuada malesuada.,Lycos,D663L90H,Allopurinol,No,48 -at,10/31/2007,58.42,"dolor sit amet,",Lavasoft,Y229E62I,Simvastatin,No,181 -Sed,04/14/2011,48.86,"lectus convallis est,",Cakewalk,T666E70K,Fluconazole,True,169 -quis,01/08/2014,55.64,"varius orci,",Lycos,T767G07U,Advair Diskus,False,264 -viverra.,01/07/2013,93.48,"cursus et, magna.",Sibelius,T276L44H,Loestrin 24 Fe,No,293 -Sed,03/20/2008,64.75,arcu. Sed,Cakewalk,A933E55V,Pantoprazole Sodium,No,407 -iaculis,07/17/2015,56.74,nec,Borland,N568F73C,Venlafaxine HCl ER,No,115 -leo.,12/09/2012,96.88,Aenean,Altavista,H283Z42U,Cephalexin,True,208 -leo.,04/24/2007,40.87,tincidunt adipiscing. Mauris,Lycos,T054Q83U,Lyrica,0,486 -pede.,09/29/2010,19.64,nec enim. Nunc,Chami,L842O70A,Simvastatin,Yes,214 -massa,05/18/2015,18.43,nisi magna sed,Adobe,V029Q52K,Meloxicam,0,131 -urna,10/22/2014,7.41,ac,Sibelius,Z708U15X,Flovent HFA,Yes,15 -sapien,03/23/2017,50.94,penatibus et,Google,D258T89Z,Zolpidem Tartrate,True,48 -non,05/14/2005,71.71,dui. Fusce,Borland,C021V01R,Amphetamine Salts,True,593 -"et,",10/18/2014,94.56,natoque penatibus et,Lavasoft,L351F80J,Gianvi,1,305 -pede,02/18/2004,10.04,felis,Yahoo,D516U60J,Pravastatin Sodium,1,302 -lobortis,06/08/2011,31.17,"luctus,",Yahoo,D088E82H,Azithromycin,Yes,380 -placerat,06/19/2005,5.38,eget,Adobe,V472U75G,Nexium,0,-2 -leo,10/03/2014,17.00,justo,Lycos,F944A58V,Ibuprofen (Rx),True,294 -dictum,11/24/2015,92.11,tellus faucibus,Apple Systems,U938K61D,Oxycontin,Yes,368 -bibendum.,05/01/2010,22.45,elit erat vitae,Lavasoft,Z326Q13R,Tramadol HCl,Yes,12 -"facilisis,",01/02/2011,85.42,purus,Finale,Z475L99M,Alprazolam,True,359 -bibendum,08/20/2009,44.68,et pede. Nunc,Google,N039W21L,Fluticasone Propionate,True,569 -eleifend,03/17/2006,22.92,velit. Cras,Lavasoft,H339Z61T,Doxycycline Hyclate,Yes,219 -rhoncus.,03/17/2009,82.52,"feugiat non, lobortis",Altavista,R524N72A,Atenolol,0,176 -a,04/03/2016,4.35,nunc,Sibelius,V055S56Y,Simvastatin,Yes,326 -enim.,05/15/2014,20.18,lectus justo,Google,I747M47J,Atenolol,1,101 -molestie,06/19/2012,11.30,sem ut cursus,Apple Systems,F888J26K,Amoxicillin,Yes,405 -dui.,05/16/2010,72.42,consectetuer euismod,Cakewalk,U999A42H,Atenolol,False,253 -tortor.,08/02/2017,38.51,ultrices iaculis odio.,Lavasoft,J465G08H,Tri-Sprintec,False,577 -natoque,04/15/2010,28.88,magnis,Sibelius,N813P74X,Lipitor,0,562 -mollis,05/07/2011,89.36,Donec,Yahoo,O113X38K,Lexapro,Yes,179 -ultrices.,10/20/2013,73.45,"orci,",Lycos,N587Y98N,Benicar HCT,No,185 -dui,12/10/2011,0.57,Mauris eu turpis.,Yahoo,S354F65P,Levothyroxine Sodium,No,78 -sagittis.,04/06/2014,38.55,"elementum sem, vitae",Google,R931X11B,Loestrin 24 Fe,Yes,386 -"dui,",10/08/2010,52.52,natoque penatibus,Adobe,J615B52I,Clonazepam,0,563 -eros,06/25/2015,45.90,"aliquam, enim",Microsoft,S216N79D,Amoxicillin,True,371 -"et,",05/16/2008,4.42,nulla. In tincidunt,Adobe,Z906A47A,Sulfamethoxazole/Trimethoprim,No,507 -est,12/14/2004,16.79,amet,Yahoo,P540P32Z,Doxycycline Hyclate,True,188 -"montes,",02/23/2008,52.98,ante,Lavasoft,C140R27G,Lisinopril,True,401 -mollis,07/09/2004,83.26,Curabitur dictum. Phasellus,Cakewalk,U620A61Z,Glipizide,No,278 -dolor.,07/01/2004,50.94,"non, vestibulum",Lavasoft,F625L82E,Singulair,No,475 -aliquet,03/18/2014,56.23,erat,Altavista,L193Q72S,Clonazepam,No,3 -nonummy,09/13/2004,89.86,enim.,Apple Systems,U466F15U,Atenolol,No,218 -natoque,11/09/2015,72.14,ridiculus,Altavista,U295O53M,Cyclobenzaprin HCl,No,50 -tincidunt,01/05/2011,84.90,Cras,Cakewalk,C373S25Y,Paroxetine HCl,0,104 -tristique,03/04/2010,64.59,ipsum leo elementum,Macromedia,U140F94P,Atenolol,False,486 -eleifend.,07/11/2010,45.06,"a, arcu. Sed",Macromedia,G544Z82L,Zyprexa,1,100 -sem,04/18/2008,44.81,"magna, malesuada vel,",Chami,G143Q64L,Lorazepam,No,440 -facilisi.,06/13/2005,65.36,"ut, molestie in,",Google,N391E28J,Januvia,False,536 -arcu.,09/24/2012,21.27,"parturient montes, nascetur",Finale,Z720K67H,Furosemide,False,308 -hendrerit,01/26/2013,27.11,"eget varius ultrices,",Borland,P865K75O,Metformin HCl,False,590 -Aliquam,03/18/2010,64.99,ridiculus,Borland,K003G26G,Ibuprofen (Rx),0,412 -eu,08/27/2016,99.62,Sed eu eros.,Microsoft,H049R07D,Atenolol,False,498 -Morbi,02/13/2008,30.86,"Sed nulla ante,",Altavista,U852E06G,Fluticasone Propionate,Yes,243 -malesuada,08/17/2010,58.50,tellus. Phasellus,Sibelius,T695G73P,Ibuprofen (Rx),Yes,591 -"ut,",11/16/2006,46.64,Sed molestie.,Microsoft,T589M93D,Gianvi,True,-5 -odio.,12/09/2011,51.19,"montes, nascetur ridiculus",Chami,J311Q19L,Gabapentin,True,517 -tortor.,03/21/2017,7.11,Morbi neque,Finale,W293Z75X,Loestrin 24 Fe,Yes,242 -penatibus,12/02/2015,81.51,ipsum ac mi,Borland,U565B49H,Loestrin 24 Fe,False,220 -"et,",10/11/2013,56.41,ut nisi a,Google,G623M74Q,Triamterene/Hydrochlorothiazide,True,98 -"ut,",11/17/2007,74.85,consequat purus.,Sibelius,Q247W03U,Tramadol HCl,Yes,484 -luctus,09/30/2016,23.87,euismod,Lavasoft,T838L77W,Lisinopril,Yes,295 -gravida,12/04/2010,80.12,a neque.,Sibelius,U542S63O,Vytorin,0,188 -hymenaeos.,01/16/2015,13.19,In scelerisque scelerisque,Sibelius,V375P80B,Zyprexa,False,255 -mi,06/07/2010,17.75,libero. Proin,Google,I728A71W,APAP/Codeine,False,435 -"a,",12/19/2006,12.35,"montes,",Lavasoft,X885A67N,Metformin HCl,True,333 -ornare,09/09/2015,34.13,taciti,Lavasoft,Q602X92G,Lyrica,Yes,447 -"tellus,",09/05/2007,92.17,"lorem,",Microsoft,D008R07S,Vyvanse,0,256 -eget,07/10/2008,5.71,a odio semper,Altavista,L996D22J,Triamcinolone Acetonide,1,89 -sodales,01/26/2017,67.43,"at,",Microsoft,B922B33B,Glipizide,False,406 -"amet,",09/19/2011,5.34,Cum sociis natoque,Yahoo,C735B01G,Amoxicillin,No,85 -tempus,02/14/2017,19.18,felis,Macromedia,P379N42S,Simvastatin,False,427 -laoreet,06/25/2017,73.61,Sed,Apple Systems,N838F62B,Alendronate Sodium,No,405 -laoreet,03/28/2015,40.48,fames ac,Cakewalk,A503X28X,Ibuprofen (Rx),0,430 -sit,09/08/2013,41.23,"aliquet vel,",Sibelius,F381B10B,Nexium,No,359 -Aenean,06/10/2010,97.61,massa,Altavista,X179G33K,Doxycycline Hyclate,1,136 -non,10/13/2013,46.02,Duis,Microsoft,F567P51A,Metformin HCl,1,104 -Curabitur,02/10/2016,49.59,et magnis,Borland,A902B81X,Diovan,No,270 -risus,06/23/2014,6.22,"eu,",Apple Systems,J458L03X,Crestor,1,577 -sem,04/29/2006,36.75,orci luctus,Apple Systems,I390X10D,Lisinopril,False,131 -"eu,",08/06/2013,96.39,diam dictum sapien.,Microsoft,P937M90J,Lisinopril/Hydrochlorothiazide,Yes,229 -ultricies,01/13/2013,18.64,auctor,Microsoft,Q793S72Y,Amphetamine Salts,0,153 -et,09/04/2005,30.87,sagittis placerat. Cras,Microsoft,S427Q43E,Alprazolam,False,571 -sit,02/26/2006,80.53,non justo.,Finale,Y646A59W,Omeprazole (Rx),True,193 -eget,01/10/2014,71.57,dolor,Borland,V101B17W,Sertraline HCl,No,103 -"a,",06/02/2016,44.56,nascetur ridiculus,Lycos,O796A98J,Alprazolam,1,505 -"ante,",12/20/2013,46.04,eleifend,Lycos,K375B97M,Plavix,0,296 \ No newline at end of file diff --git a/sample_csvs/MOCK_USERS.csv b/sample_csvs/MOCK_USERS.csv deleted file mode 100644 index ad15538f99..0000000000 --- a/sample_csvs/MOCK_USERS.csv +++ /dev/null @@ -1,1001 +0,0 @@ -First Name,Last Name,email,User name,Location,Phone Num,Job Title For User,Employee Number,Company,VIP -Blanche,O'Collopy,bocollopy0@livejournal.com,bocollopy0,Hinapalanan,63-(199)661-2186,Clinical Specialist,7080919053,Morar-Ward,yes -Jessie,Primo,jprimo1@newsvine.com,jprimo1,Korenovsk,7-(885)578-0266,Paralegal,6284292031,Jast-Stiedemann,yes -Nelia,Coughtrey,ncoughtrey2@geocities.com,ncoughtrey2,Zhexiao,86-(366)635-5884,Nurse Practicioner,7242692202,Goldner-Cremin,yes -Pascale,Wimpeney,pwimpeney3@mtv.com,pwimpeney3,KuÄe,385-(688)644-8322,Senior Sales Associate,9173736066,"Paucek, Schmitt and Hagenes",yes -Alec,Twidle,atwidle4@ovh.net,atwidle4,Shi’ao,86-(163)912-1915,Human Resources Manager,4692183691,Schimmel and Sons,yes -Karalee,Carroll,kcarroll5@devhub.com,kcarroll5,Huaidian,86-(303)287-0739,Civil Engineer,1530903416,Schuster LLC,yes -Danny,Yeiles,dyeiles6@wikipedia.org,dyeiles6,Koundara,224-(953)650-5363,Software Engineer II,7026212001,"Ebert, Windler and Hessel",no -Taffy,Benson,tbenson7@mit.edu,tbenson7,Nanying,86-(152)931-1194,Dental Hygienist,4576525239,Jerde Inc,no -Hall,Tonepohl,htonepohl8@bandcamp.com,htonepohl8,Primorsko-Akhtarsk,7-(743)929-2565,Computer Systems Analyst III,6187297644,Kohler and Sons,no -Gus,Tomczykowski,gtomczykowski9@hostgator.com,gtomczykowski9,Arrah,225-(926)325-4040,Executive Secretary,9237527535,"Luettgen, Kshlerin and Carroll",no -Casandra,Retchford,cretchforda@gizmodo.com,cretchforda,Lasi Dua,62-(813)604-3660,Teacher,68486472,Rempel Inc,no -Judie,Fowler,jfowlerb@virginia.edu,jfowlerb,ÅŒgaki,81-(521)313-4757,Occupational Therapist,1692241737,Bins Inc,no -Willy,Walters,wwaltersc@cloudflare.com,wwaltersc,Mungkin,62-(114)187-7377,Electrical Engineer,1185027149,Toy-Hyatt,no -Trace,Tingly,ttinglyd@issuu.com,ttinglyd,Esigodini,263-(795)699-2623,Office Assistant II,9588821797,Satterfield LLC,no -Rustin,Withrington,rwithringtone@purevolume.com,rwithringtone,Xinjie,86-(703)213-0549,Marketing Manager,8707099886,O'Conner-Hoeger,no -Loria,McKendry,lmckendryf@imgur.com,lmckendryf,Eiriz,351-(660)417-9204,Administrative Assistant II,2622958684,Oberbrunner and Sons,no -Blancha,Carlesso,bcarlessog@bravesites.com,bcarlessog,Helsingborg,46-(218)729-2556,Account Representative I,1864046597,Stroman-Marks,no -Whitney,Olman,wolmanh@slashdot.org,wolmanh,Lisia Góra,48-(907)614-5768,Speech Pathologist,7143970867,Keebler-Wintheiser,no -Bond,McBain,bmcbaini@hc360.com,bmcbaini,Lesnoye,7-(515)172-7238,Social Worker,6816673581,Cassin LLC,no -Raeann,Kubach,rkubachj@a8.net,rkubachj,Saray,994-(690)524-2937,Electrical Engineer,1829082000,"Kiehn, Dicki and O'Reilly",no -Feodor,Kersey,fkerseyk@hibu.com,fkerseyk,Salam,62-(530)992-3129,Financial Advisor,27333248,Kassulke-Wuckert,no -Boyd,Delamere,bdelamerel@europa.eu,bdelamerel,Starigrad,385-(464)217-6673,Quality Control Specialist,7362162391,Marquardt Group,no -Terrijo,Spaducci,tspaduccim@yale.edu,tspaduccim,Pereiras,351-(169)619-5395,Health Coach II,9383160780,"Reynolds, Bins and Wunsch",no -Brandtr,Rollings,brollingsn@java.com,brollingsn,Palencia,502-(601)904-4191,Assistant Professor,9604788698,Farrell LLC,no -Diane,Bernaciak,dbernaciako@senate.gov,dbernaciako,Bitin,63-(465)596-5888,Human Resources Assistant II,1297195655,Orn Inc,no -Bertie,Sreenan,bsreenanp@japanpost.jp,bsreenanp,Muzambinho,55-(991)980-2128,VP Accounting,9614791969,Wiza-Quitzon,no -Marylinda,Hanlon,mhanlonq@sourceforge.net,mhanlonq,San José de Río Tinto,504-(312)801-5239,Associate Professor,4247222201,Heller and Sons,no -Darrin,MacPhaden,dmacphadenr@berkeley.edu,dmacphadenr,Radlje ob Dravi,386-(696)423-3227,Community Outreach Specialist,8724422533,Bradtke and Sons,no -Kizzee,Pilmer,kpilmers@mlb.com,kpilmers,Soio,244-(244)226-8720,Software Engineer I,7351560250,"Hauck, Gottlieb and Roberts",no -Murdoch,Heijne,mheijnet@unblog.fr,mheijnet,Pukë,355-(994)349-7229,Mechanical Systems Engineer,9123797894,"Bailey, Collier and Labadie",no -Maryann,Labat,mlabatu@mozilla.com,mlabatu,Skhira,216-(174)936-3617,Human Resources Assistant III,7255325211,"Feil, Kozey and Goldner",no -Carolus,Dombrell,cdombrellv@forbes.com,cdombrellv,PÄvilosta,371-(908)875-7818,Account Executive,2616418460,Hammes LLC,no -Sheena,Goede,sgoedew@samsung.com,sgoedew,Bitkine,235-(312)893-5822,Professor,9784543257,Wolf Inc,no -Dottie,Leare,dlearex@surveymonkey.com,dlearex,Pugeran,62-(693)693-0486,Analog Circuit Design manager,5256401955,"Cole, Fisher and Spinka",no -Jess,Stammer,jstammery@bloglines.com,jstammery,Margherita,256-(648)534-6980,Account Representative I,3022569831,Keeling Group,no -Izabel,Wix,iwixz@latimes.com,iwixz,Shatian,86-(812)279-4898,Physical Therapy Assistant,5047185605,Feil-Gutkowski,no -Corrinne,Lowrie,clowrie10@noaa.gov,clowrie10,Dongyuan,86-(567)116-4092,Programmer I,4016980801,Dickens and Sons,no -Ralph,Brabbs,rbrabbs11@desdev.cn,rbrabbs11,Lokea,62-(733)726-2227,Staff Scientist,3126986722,Pouros LLC,no -Wally,Boanas,wboanas12@deviantart.com,wboanas12,Garhi Khairo,92-(983)138-6735,Senior Developer,9121792674,Bauch-Hodkiewicz,no -Willyt,Poxton,wpoxton13@eepurl.com,wpoxton13,Ciénaga,57-(530)166-8558,Account Coordinator,5226358938,Orn-Grady,no -Sanderson,Colledge,scolledge14@mozilla.com,scolledge14,Capalonga,63-(228)896-9731,Dental Hygienist,2367310122,"Kessler, Bechtelar and Fisher",no -Marshal,Dillet,mdillet15@sakura.ne.jp,mdillet15,Camperdown,27-(887)538-9433,Social Worker,9119456573,Heller-Baumbach,no -Dario,Osler,dosler16@slideshare.net,dosler16,Fulong,86-(686)394-2367,Human Resources Assistant I,834912236,Strosin Inc,no -Porter,Whitehall,pwhitehall17@economist.com,pwhitehall17,AlÄ«pur,92-(345)545-1627,Programmer Analyst I,9907315249,Thiel-Schinner,no -Cyrille,Lough,clough18@foxnews.com,clough18,Jiukeng,86-(720)297-5199,Marketing Assistant,4745558287,Bailey-Hauck,no -Brina,Ashwell,bashwell19@cmu.edu,bashwell19,Três Pontas,55-(437)327-8074,VP Marketing,8645272598,Blick Group,no -Ki,Freeborn,kfreeborn1a@hhs.gov,kfreeborn1a,Pisz,48-(490)682-6629,Office Assistant I,6953353384,"Larson, Becker and Reinger",no -Gaye,Hillam,ghillam1b@creativecommons.org,ghillam1b,Lazaro Cardenas,52-(744)187-7616,Senior Cost Accountant,5489719737,"Abernathy, Zulauf and Davis",no -Herculie,Seage,hseage1c@photobucket.com,hseage1c,Careva Ćuprija,387-(372)396-4601,Environmental Tech,4880024112,"Wintheiser, Luettgen and Jerde",no -Inessa,Baldack,ibaldack1d@marriott.com,ibaldack1d,Tirah,972-(148)288-3311,Environmental Tech,4012420682,"Carter, Hoppe and Mertz",no -Sherline,Alliston,salliston1e@cnet.com,salliston1e,Prado Siongco,63-(641)703-7801,VP Sales,1648555802,Glover LLC,no -Kristin,Pitson,kpitson1f@sogou.com,kpitson1f,Las Flores,52-(755)660-4401,Financial Advisor,3629602851,Denesik-Schuster,no -Portia,Winspur,pwinspur1g@imdb.com,pwinspur1g,Cibolang,62-(194)231-7712,Engineer II,8827050418,"Osinski, Emard and Jast",no -Benedict,Churchard,bchurchard1h@dell.com,bchurchard1h,Xinglong,86-(452)414-0407,Human Resources Assistant IV,1155598784,Hane and Sons,no -Mariya,Bahlmann,mbahlmann1i@npr.org,mbahlmann1i,Wuyanquan,86-(898)637-0482,GIS Technical Architect,1627870318,Abernathy Group,no -Harri,McAleese,hmcaleese1j@theglobeandmail.com,hmcaleese1j,Nularan,62-(717)480-2343,Accounting Assistant I,181278391,Senger and Sons,no -Roda,McGriffin,rmcgriffin1k@symantec.com,rmcgriffin1k,Raduzhnyy,7-(513)767-6738,Sales Associate,7142006153,Mosciski Group,no -Carolyn,Jurges,cjurges1l@berkeley.edu,cjurges1l,Rotterdam,31-(827)912-2109,Senior Developer,8573938994,Terry and Sons,no -Darnell,Lettuce,dlettuce1m@google.com.au,dlettuce1m,Solnechnogorsk,7-(165)514-6628,Sales Associate,3973069883,Volkman-Abbott,no -Johan,Herche,jherche1n@w3.org,jherche1n,Krzeszów,48-(981)281-5260,Librarian,8066763190,Larkin-Emard,no -Ginelle,Digle,gdigle1o@4shared.com,gdigle1o,Alukama,62-(819)194-6623,Account Executive,227839102,Sipes-Bauch,no -Bunny,Macconaghy,bmacconaghy1p@huffingtonpost.com,bmacconaghy1p,Huangqiao,86-(206)588-4549,Quality Engineer,6517079629,"Dach, Marquardt and Lubowitz",no -Olivette,Tomczykiewicz,otomczykiewicz1q@eventbrite.com,otomczykiewicz1q,Sigayevo,7-(309)891-2091,Desktop Support Technician,3998906258,Schamberger Group,no -Kerwinn,Le Brom,klebrom1r@smh.com.au,klebrom1r,Lestijärvi,358-(747)269-4952,Compensation Analyst,1348822481,Kub Inc,no -Obidiah,Radke,oradke1s@imdb.com,oradke1s,Xinhua,86-(381)923-1757,Accounting Assistant II,1145530044,Prosacco Inc,no -Jermaine,Joire,jjoire1t@sun.com,jjoire1t,Huangpi,86-(814)411-4884,Developer IV,4849811027,"Schuppe, Jaskolski and Jones",no -Bobine,Jessope,bjessope1u@sbwire.com,bjessope1u,Nàng Mau,84-(454)818-4039,Budget/Accounting Analyst I,3193701467,"Gislason, Batz and Kub",no -Brinn,Calam,bcalam1v@dedecms.com,bcalam1v,Anan,81-(795)861-8389,Administrative Officer,2724742117,Shanahan Inc,no -Ax,Briiginshaw,abriiginshaw1w@shop-pro.jp,abriiginshaw1w,Bantay,63-(158)907-3803,Senior Sales Associate,1449794491,"Beer, Hoppe and Walsh",no -Sher,Wylam,swylam1x@cisco.com,swylam1x,Dmitrov,7-(454)562-7954,Health Coach IV,1778479766,"Bogisich, Gerhold and Dooley",no -Rodger,Mecco,rmecco1y@oakley.com,rmecco1y,Bulahblangaro,62-(698)245-6039,GIS Technical Architect,6679143640,Kulas Group,no -Gilly,Coggon,gcoggon1z@merriam-webster.com,gcoggon1z,Ramana,994-(225)981-5577,Statistician II,8663020252,Bogan-Swaniawski,no -Buffy,Kemsley,bkemsley20@discuz.net,bkemsley20,Tucupido,58-(595)308-1288,Dental Hygienist,9810617755,"Jacobi, Kuhn and Hills",no -Tamra,Whether,twhether21@privacy.gov.au,twhether21,Dedenëvo,7-(568)725-8281,Librarian,938798367,Doyle-Schowalter,no -Alleyn,Hedworth,ahedworth22@nbcnews.com,ahedworth22,Bweyogerere,256-(885)716-8723,Information Systems Manager,7806845402,Herman-Kuphal,no -Baldwin,Ruggieri,bruggieri23@acquirethisname.com,bruggieri23,Famões,351-(152)434-9871,Database Administrator I,6285015228,Rohan-Spinka,no -Edi,Womersley,ewomersley24@soundcloud.com,ewomersley24,Topolovgrad,359-(281)202-4851,VP Product Management,5480937758,Heaney Group,no -Vally,Condy,vcondy25@mediafire.com,vcondy25,Kovářská,420-(698)690-1129,Staff Accountant III,6354747148,Dach-Sporer,no -Addie,Kobisch,akobisch26@instagram.com,akobisch26,Ninove,32-(630)476-7548,Web Developer II,7289347471,MacGyver-Becker,no -Levi,Florey,lflorey27@umn.edu,lflorey27,DÄ™bowa ÅÄ…ka,48-(297)281-6919,Operator,1052832121,Wolf Group,no -Jolie,Niles,jniles28@goodreads.com,jniles28,Annopol,48-(783)717-5946,Quality Control Specialist,1756247129,"Batz, Spencer and Zemlak",no -Gwynne,Blaydes,gblaydes29@seattletimes.com,gblaydes29,Chervone,380-(130)591-1017,Junior Executive,7514288414,Metz Group,no -Andee,Hazard,ahazard2a@usatoday.com,ahazard2a,Bringinanom,62-(752)996-9280,Business Systems Development Analyst,463793501,O'Hara Group,no -Cristine,Cordero,ccordero2b@shop-pro.jp,ccordero2b,Cireundang,62-(647)231-1238,Associate Professor,206916523,"Bradtke, Breitenberg and Konopelski",no -Adelice,Sparhawk,asparhawk2c@google.fr,asparhawk2c,Barbacoas,58-(288)306-1967,Business Systems Development Analyst,4263563638,Reinger Group,no -Arnuad,Yirrell,ayirrell2d@indiatimes.com,ayirrell2d,Guanagazapa,502-(996)833-1054,Research Associate,4248670135,"Zemlak, Murphy and Altenwerth",no -Etta,Manuelli,emanuelli2e@webeden.co.uk,emanuelli2e,Onzaga,57-(627)371-5859,GIS Technical Architect,5140237679,Wiza LLC,no -Anetta,Demann,ademann2f@nhs.uk,ademann2f,Songhu,86-(886)244-1201,Engineer III,7383594029,"Lind, Rodriguez and Luettgen",no -Rivi,Geck,rgeck2g@wikia.com,rgeck2g,Santa Iria de Azóia,351-(351)837-4156,GIS Technical Architect,7680553459,"Kuhic, Boyle and Kozey",no -Zsa zsa,Boynes,zboynes2h@upenn.edu,zboynes2h,Cerava,355-(375)816-3821,Occupational Therapist,5933003034,Weimann and Sons,no -Carlen,Minillo,cminillo2i@sohu.com,cminillo2i,ÅÄ…cko,48-(206)819-7182,Engineer III,9864914502,"Brown, Carroll and Sporer",no -Jae,McMinn,jmcminn2j@geocities.jp,jmcminn2j,Yandang,86-(754)609-9065,VP Marketing,9052957681,Parker Inc,no -Daune,Holberry,dholberry2k@imageshack.us,dholberry2k,Daleszyce,48-(780)132-9574,Software Engineer III,5828276875,"Bartell, Casper and Kerluke",no -Thacher,Jahnisch,tjahnisch2l@nbcnews.com,tjahnisch2l,Ãpsonas,357-(881)407-6533,Research Associate,6648113243,Metz-Schimmel,no -Egon,Dumphy,edumphy2m@tiny.cc,edumphy2m,GonÄbÄd,98-(873)558-8082,Nurse,1757406042,Von and Sons,no -Aridatha,Matessian,amatessian2n@salon.com,amatessian2n,Zhenziliang,86-(691)551-3380,Structural Engineer,8930185770,Reichel-Powlowski,no -Gabby,Margett,gmargett2o@yellowpages.com,gmargett2o,Wenfu,86-(602)463-0478,Financial Advisor,5815266353,Bosco Inc,no -Andrei,Dowle,adowle2p@slashdot.org,adowle2p,Zykovo,7-(567)311-6007,Graphic Designer,6594557408,Terry Inc,no -Zack,Balsellie,zbalsellie2q@alibaba.com,zbalsellie2q,Kiihtelysvaara,358-(789)977-9884,Data Coordiator,8470986120,Wiegand-Heathcote,no -Tanny,Liccardi,tliccardi2r@scientificamerican.com,tliccardi2r,MajÄz al BÄb,216-(900)448-3810,Occupational Therapist,8509563896,Moore and Sons,no -Eda,Petren,epetren2s@go.com,epetren2s,Mina de São Domingos,351-(647)661-1537,Registered Nurse,1326684930,Hagenes-Franecki,no -Fifine,Appleby,fappleby2t@prlog.org,fappleby2t,Sokol’skoye,7-(897)976-5440,Nurse Practicioner,6949941218,"Hilll, Wiza and Connelly",no -Suki,Quilligan,squilligan2u@virginia.edu,squilligan2u,LeneÅ¡ice,420-(484)575-9887,Internal Auditor,1102845078,Eichmann and Sons,no -Brinn,Seldon,bseldon2v@dedecms.com,bseldon2v,Valerik,7-(364)838-7251,Environmental Specialist,5496253098,Schiller Inc,no -Cristie,Treadaway,ctreadaway2w@histats.com,ctreadaway2w,Vichuga,7-(809)167-3685,Dental Hygienist,743112563,Bogisich LLC,no -Kendall,Vidgeon,kvidgeon2x@ted.com,kvidgeon2x,ÅžÄnÅ«r,970-(462)447-8191,Analog Circuit Design manager,5299583540,Brekke-Ziemann,no -Osborn,Fritschel,ofritschel2y@kickstarter.com,ofritschel2y,Bayt MaqdÅ«m,970-(366)314-6696,Quality Control Specialist,3107157244,"Schultz, Runolfsson and Hauck",no -Olenolin,Spore,ospore2z@about.me,ospore2z,Kissimmee,1-(407)958-2939,Information Systems Manager,1377154629,"Bashirian, Eichmann and Bednar",no -Sherlock,O'Carmody,socarmody30@flickr.com,socarmody30,Xuhang,86-(402)403-7851,VP Product Management,4691963855,Nikolaus-Homenick,no -Pincus,Stamps,pstamps31@dagondesign.com,pstamps31,Old City,970-(348)690-7659,Analog Circuit Design manager,9573629984,"Ullrich, Halvorson and Hammes",no -Sallee,Clover,sclover32@google.it,sclover32,Tuymazy,7-(344)530-7082,Information Systems Manager,1064925987,O'Hara-Zulauf,no -Jillane,Moyes,jmoyes33@shutterfly.com,jmoyes33,Faratsiho,261-(956)463-6161,Information Systems Manager,1015767532,Ruecker LLC,no -Sully,Baugh,sbaugh34@about.com,sbaugh34,Xidajie,86-(864)668-1887,Administrative Officer,7779016676,Cormier-Zieme,no -Woodman,Filipczynski,wfilipczynski35@reference.com,wfilipczynski35,Chinú,57-(984)445-3303,Payment Adjustment Coordinator,8833195139,Abshire-Upton,no -Donia,Tayspell,dtayspell36@abc.net.au,dtayspell36,Jacarezinho,55-(631)500-4755,Cost Accountant,515630330,"Von, Paucek and DuBuque",no -Cordula,Gelling,cgelling37@berkeley.edu,cgelling37,Ipoh,60-(392)990-3252,Geologist III,539485020,Walsh and Sons,no -Cory,Franciskiewicz,cfranciskiewicz38@indiatimes.com,cfranciskiewicz38,Grand Rapids,1-(616)238-1561,Software Test Engineer I,739184717,O'Kon-Windler,no -Merrielle,Ringe,mringe39@xinhuanet.com,mringe39,Å iroki Brijeg,387-(905)648-3989,Actuary,1561596175,Lemke-Fay,no -Janka,Mahoney,jmahoney3a@newyorker.com,jmahoney3a,Lethem,592-(205)736-1572,Environmental Tech,1359840486,Hammes Group,no -Fleur,Arno,farno3b@thetimes.co.uk,farno3b,Santo Domingo,63-(244)461-5127,Research Nurse,2070821277,Baumbach LLC,no -Feliza,Kitchingman,fkitchingman3c@dailymotion.com,fkitchingman3c,Gunungbatu,62-(305)737-3215,Payment Adjustment Coordinator,3717140175,Crooks Inc,no -Brooks,Abrey,babrey3d@loc.gov,babrey3d,Nongba,86-(631)179-1441,Database Administrator III,1495770435,Ryan Inc,no -Domenico,Leeder,dleeder3e@toplist.cz,dleeder3e,StobreÄ,385-(588)803-2783,Occupational Therapist,7517254553,Gislason-Dickens,no -Gracie,Leggs,gleggs3f@posterous.com,gleggs3f,Anchorage,1-(907)739-1009,Account Representative IV,9894304044,"Auer, Fay and Kutch",no -Burton,Girodin,bgirodin3g@flavors.me,bgirodin3g,Tambo Grande,51-(473)237-9513,Teacher,2586053230,Swaniawski-O'Connell,no -Rutger,Fortnum,rfortnum3h@ucsd.edu,rfortnum3h,Zhen’an,86-(520)440-6913,Desktop Support Technician,9664475106,Stehr-Satterfield,no -Callie,Gaymar,cgaymar3i@yandex.ru,cgaymar3i,Cempaka,62-(632)111-7548,Software Consultant,9554343590,"Collins, Marvin and Predovic",no -Roslyn,Giron,rgiron3j@mashable.com,rgiron3j,Cartagena,34-(478)145-7341,Operator,4796666060,Spencer-Altenwerth,no -Lindsey,Cabera,lcabera3k@upenn.edu,lcabera3k,Tostado,54-(284)861-1251,Paralegal,8728268571,"Schulist, Nitzsche and Hettinger",no -Alfonse,Odom,aodom3l@technorati.com,aodom3l,Hägersten,46-(947)105-7610,Graphic Designer,4698060613,Windler and Sons,no -Chalmers,Carwithim,ccarwithim3m@livejournal.com,ccarwithim3m,Eslöv,46-(442)928-3035,Internal Auditor,2949190553,Fritsch Inc,no -Rosa,Totterdill,rtotterdill3n@jiathis.com,rtotterdill3n,Guoduwan,86-(359)340-9833,Legal Assistant,3214937413,Dibbert-Jacobs,no -Lionel,Tomlin,ltomlin3o@elpais.com,ltomlin3o,Montbéliard,33-(836)419-9346,Information Systems Manager,2638534692,"Greenfelder, Boyer and Pollich",no -Mireielle,Edmands,medmands3p@washington.edu,medmands3p,Kafir Qala,93-(929)432-7754,Design Engineer,9816600269,Littel-Durgan,no -Maryellen,Cordrey,mcordrey3q@networksolutions.com,mcordrey3q,Aganan,63-(246)228-0609,Associate Professor,1918577528,Goldner Inc,no -Hilario,Wadforth,hwadforth3r@npr.org,hwadforth3r,Obninsk,7-(856)647-8958,Physical Therapy Assistant,3701886741,"Raynor, Larkin and Bergstrom",no -Vanda,Gradley,vgradley3s@psu.edu,vgradley3s,København,45-(345)469-6826,Safety Technician II,2103662962,Dietrich Group,no -Portia,Guillond,pguillond3t@youtube.com,pguillond3t,Verbivka,380-(633)231-9128,General Manager,1175524999,Goodwin-O'Keefe,no -Amabel,Jestico,ajestico3u@samsung.com,ajestico3u,Palama,62-(610)896-0940,Editor,2152070887,McClure-Oberbrunner,no -Gillie,Cafferty,gcafferty3v@netvibes.com,gcafferty3v,Zátor,420-(663)207-1814,Programmer Analyst I,1345410255,"Schamberger, Cassin and Walsh",no -Dorri,Artis,dartis3w@oakley.com,dartis3w,Sam Khok,66-(547)684-7107,Registered Nurse,8689941193,"Mayert, Huel and Kshlerin",no -Gearard,Mills,gmills3x@quantcast.com,gmills3x,Iitti,358-(148)528-5126,Nurse Practicioner,2353507719,Smith-Conn,no -Brinna,Andrasch,bandrasch3y@icq.com,bandrasch3y,Karangtalun,62-(700)442-1707,Senior Quality Engineer,3147866394,"Larson, Renner and Goldner",no -Mohammed,Garthshore,mgarthshore3z@washingtonpost.com,mgarthshore3z,Janas,351-(802)307-8630,Pharmacist,2137372886,McClure-Bruen,no -Alta,Jeacocke,ajeacocke40@reuters.com,ajeacocke40,Bcharré,961-(429)530-2191,Librarian,2747388336,"Weimann, Yundt and Jerde",no -Denys,Fonzone,dfonzone41@fotki.com,dfonzone41,Kidodi,255-(569)842-8531,Programmer II,5530705758,Prohaska Inc,no -Kin,Godthaab,kgodthaab42@usatoday.com,kgodthaab42,Dongzhou,86-(738)848-9111,Developer IV,6520160466,Mueller LLC,no -Alida,Jallin,ajallin43@imageshack.us,ajallin43,Croix,33-(392)554-0240,Graphic Designer,6970422224,Donnelly Group,no -Domeniga,Pawling,dpawling44@economist.com,dpawling44,Cergy-Pontoise,33-(808)258-6123,Information Systems Manager,2752070268,Lakin and Sons,no -Teddie,Pund,tpund45@cnet.com,tpund45,Ã…kersberga,46-(594)668-9870,Sales Representative,3679850239,Gerlach-Kohler,no -Hubert,Staniland,hstaniland46@t-online.de,hstaniland46,Huanggang,86-(605)920-8337,Nurse,3003691309,Crooks and Sons,no -Valentijn,Doughton,vdoughton47@vk.com,vdoughton47,La Montañita,57-(547)938-5819,Community Outreach Specialist,5005398791,Gleichner-Ward,no -Dunstan,McTurk,dmcturk48@t-online.de,dmcturk48,Cereté,57-(431)362-8281,VP Sales,3378279060,"Kuhic, Crooks and Bergnaum",no -Ervin,Tungay,etungay49@tripadvisor.com,etungay49,Monte,351-(118)918-9096,VP Quality Control,4816619569,"Homenick, Kihn and Sporer",no -Nevil,Kelemen,nkelemen4a@mapy.cz,nkelemen4a,Cha-am,66-(292)424-3669,Programmer Analyst I,4729507800,"Armstrong, Bauch and Doyle",no -Hildegarde,Joffe,hjoffe4b@squarespace.com,hjoffe4b,SÄjir,966-(816)671-3309,Developer IV,5371857133,"Quitzon, Lakin and Koss",no -Virgil,Latchmore,vlatchmore4c@europa.eu,vlatchmore4c,Bahorí,992-(626)965-2780,Electrical Engineer,4331295168,Hackett-Ortiz,no -Shurwood,Kennon,skennon4d@trellian.com,skennon4d,Shimen,86-(122)424-6837,Actuary,5908185647,Feil-Flatley,no -Coretta,Robe,crobe4e@huffingtonpost.com,crobe4e,Paltashaco,51-(430)483-8211,Help Desk Technician,3927212415,Ortiz Inc,no -Roddie,Frudd,rfrudd4f@dion.ne.jp,rfrudd4f,Khombole,221-(980)728-8902,Legal Assistant,6679829933,Turcotte-Aufderhar,no -Geraldine,Pershouse,gpershouse4g@4shared.com,gpershouse4g,Pinheiro,55-(810)396-6565,Account Representative II,9339033949,Bradtke and Sons,no -Rex,Winterburn,rwinterburn4h@earthlink.net,rwinterburn4h,Konibodom,992-(285)759-4482,Engineer I,4013574306,Harvey-Hirthe,no -Bride,MacCawley,bmaccawley4i@skype.com,bmaccawley4i,GÅ‚ubczyce,48-(645)682-7840,Information Systems Manager,3136570391,"Swaniawski, Gerlach and Gibson",no -Nikkie,Freund,nfreund4j@freewebs.com,nfreund4j,Gusang,86-(298)352-9050,Product Engineer,9725922840,"Purdy, Rice and Fahey",no -Henrieta,Creavin,hcreavin4k@wired.com,hcreavin4k,Maoming,86-(203)885-7331,Quality Engineer,6377468806,"Weber, Tillman and Frami",no -Freddie,Irce,firce4l@narod.ru,firce4l,Koszyce Wielkie,48-(793)133-0472,Professor,8965612748,Kunde Inc,no -Erin,Thomton,ethomton4m@com.com,ethomton4m,Punta Cana,1-(194)646-6108,Speech Pathologist,8529365445,"Pfannerstill, Berge and Keebler",no -Stu,Grabiec,sgrabiec4n@desdev.cn,sgrabiec4n,Bagé,55-(658)679-1980,Senior Financial Analyst,9818636597,"Murray, Hessel and Considine",no -Marlo,Hay,mhay4o@si.edu,mhay4o,Bojonglarang,62-(342)321-9267,Systems Administrator I,9669756170,Skiles-Mueller,no -Ruthe,Rainforth,rrainforth4p@lycos.com,rrainforth4p,Zhangcheng,86-(103)571-9123,Human Resources Manager,110467094,Treutel-Schroeder,no -Nanette,Marvell,nmarvell4q@themeforest.net,nmarvell4q,Candelaria,63-(677)283-7853,Help Desk Operator,831047453,Johnston-Hayes,no -Gwendolen,Kettleson,gkettleson4r@lycos.com,gkettleson4r,Curahkalak Tengah,62-(324)997-7422,Compensation Analyst,3942331365,Russel-Satterfield,no -Whittaker,Gregh,wgregh4s@behance.net,wgregh4s,GÅ‚owno,48-(285)750-6950,Automation Specialist II,264496159,Hamill-Kemmer,no -Shay,Ramelet,sramelet4t@techcrunch.com,sramelet4t,San Pedro,54-(301)601-8361,Staff Accountant I,8224288722,Aufderhar LLC,no -Adrienne,Villar,avillar4u@list-manage.com,avillar4u,Shuangjie,86-(951)513-0325,Automation Specialist II,7072078703,"O'Keefe, Lowe and Stroman",no -Alyda,Gillford,agillford4v@tinypic.com,agillford4v,Fengxian,86-(660)158-1836,Structural Engineer,6986236239,"Moen, Mraz and Heidenreich",no -Biron,Rajchert,brajchert4w@e-recht24.de,brajchert4w,Niort,33-(207)497-8305,Quality Control Specialist,5556782318,Conn LLC,no -Drake,Foran,dforan4x@wisc.edu,dforan4x,Fort Pierce,1-(772)401-1527,Nurse Practicioner,2174876256,"Brakus, Schimmel and Turner",no -Madella,Flew,mflew4y@amazon.de,mflew4y,Haodi,86-(156)758-1712,Structural Engineer,2630355349,Abbott Group,no -Jermain,Lintot,jlintot4z@linkedin.com,jlintot4z,Koszyce Wielkie,48-(712)513-8339,Electrical Engineer,5316574915,Bruen Inc,no -Malvina,Cherrison,mcherrison50@dell.com,mcherrison50,Belung Satu,62-(983)260-7407,Geologist III,459539647,"Nader, Davis and Spinka",no -Nesta,Darell,ndarell51@sina.com.cn,ndarell51,Vostochnyy,7-(216)191-7642,Dental Hygienist,9912630998,"Aufderhar, Krajcik and Lemke",no -Valery,Sharpus,vsharpus52@cnn.com,vsharpus52,Checca,51-(325)577-5349,Budget/Accounting Analyst II,9257058077,"Koch, Keeling and Kihn",no -Ramona,Cahill,rcahill53@networksolutions.com,rcahill53,Bailieborough,353-(653)588-5654,VP Marketing,6559784584,Welch-Gleason,no -Calv,Studdeard,cstuddeard54@dion.ne.jp,cstuddeard54,Babakan,62-(727)232-5040,Geological Engineer,3900347034,"Nitzsche, Mohr and Simonis",no -Malia,Jacobssen,mjacobssen55@e-recht24.de,mjacobssen55,Aldeia do Bispo,351-(313)100-4305,Cost Accountant,3428666267,Mayer Group,no -Laural,Izon,lizon56@icq.com,lizon56,Gaoqiao,86-(275)782-1267,Assistant Media Planner,6392728392,Wintheiser Inc,no -Morgan,Caukill,mcaukill57@storify.com,mcaukill57,Torres Novas,351-(450)639-8416,Analog Circuit Design manager,2316388078,"Schiller, Kris and Farrell",no -Marlane,Palek,mpalek58@miibeian.gov.cn,mpalek58,San Isidro de Lules,54-(873)665-9102,Design Engineer,1418632929,Lubowitz and Sons,no -Broderick,Ugo,bugo59@hud.gov,bugo59,Silodakon,62-(378)217-9779,Recruiter,4977155548,Steuber-Dietrich,no -Norry,Arzu,narzu5a@oakley.com,narzu5a,Timashëvsk,7-(943)185-9478,Paralegal,8088001102,"Hettinger, Gutkowski and Bechtelar",no -Benedikta,Meininking,bmeininking5b@macromedia.com,bmeininking5b,Jiucheng,86-(218)617-0476,Legal Assistant,611180774,Douglas Inc,no -Edan,Velasquez,evelasquez5c@sitemeter.com,evelasquez5c,Juncheng,86-(623)794-4358,Associate Professor,3556333899,"Marks, Jakubowski and Boyer",no -Shea,Ernke,sernke5d@freewebs.com,sernke5d,Korniyivka,380-(988)667-0972,Senior Quality Engineer,3318216801,Huel Inc,no -Bekki,Fesby,bfesby5e@irs.gov,bfesby5e,Lille,33-(510)850-2554,Desktop Support Technician,2228160431,Ullrich LLC,no -Sean,Oda,soda5f@tinyurl.com,soda5f,Furukawa,81-(267)487-5524,Office Assistant I,2265020354,Spinka-Auer,no -Jerry,Bakster,jbakster5g@sina.com.cn,jbakster5g,HoÅ‚oby,380-(683)792-0125,Staff Scientist,2528271107,D'Amore and Sons,no -Wendi,Imlen,wimlen5h@amazon.co.uk,wimlen5h,Takatsuki,81-(641)449-9730,Internal Auditor,8591255291,Hermann-Reinger,no -Cristina,McCarry,cmccarry5i@weibo.com,cmccarry5i,Banturkrajan,62-(200)913-3412,Help Desk Technician,8664973989,"Skiles, Russel and Wisoky",no -Viviana,Selman,vselman5j@360.cn,vselman5j,Huayan,86-(681)662-3285,Research Assistant III,6262769528,Wuckert Inc,no -Shanie,Uppett,suppett5k@webeden.co.uk,suppett5k,Lac du Bonnet,1-(543)646-5383,Operator,4572433119,Fadel Inc,no -Suzie,Bridgeman,sbridgeman5l@dmoz.org,sbridgeman5l,Kamo,81-(897)485-9748,Environmental Specialist,1977897401,"Denesik, Okuneva and Bernier",no -Eudora,Casterot,ecasterot5m@psu.edu,ecasterot5m,Zonghan,86-(764)968-7549,Help Desk Operator,2008858634,"Jaskolski, Huels and Dibbert",no -Penn,Viant,pviant5n@linkedin.com,pviant5n,Andir,62-(390)901-5564,Senior Sales Associate,2238319271,"Cummings, Beatty and Hudson",no -Berne,Hannan,bhannan5o@shareasale.com,bhannan5o,Krajan Kerjo,62-(278)571-4046,Data Coordiator,7942202852,Auer-Bartoletti,no -Oralee,Malyj,omalyj5p@admin.ch,omalyj5p,Kotayk’,374-(542)956-6172,Engineer IV,4819135880,"Pacocha, Kertzmann and Rempel",no -Fifi,Small,fsmall5q@amazon.de,fsmall5q,Voznesenskoye,7-(821)863-5914,Physical Therapy Assistant,6908304248,"Rippin, Reichel and White",no -Loise,Pilger,lpilger5r@smh.com.au,lpilger5r,Hénin-Beaumont,33-(686)647-9059,Information Systems Manager,967841968,Wiza and Sons,no -Derward,Dupoy,ddupoy5s@latimes.com,ddupoy5s,Itami,81-(903)990-0489,Quality Engineer,3564905782,Dare-Kautzer,no -Chelsea,Lergan,clergan5t@youtu.be,clergan5t,Jiagao,86-(320)644-8775,Research Assistant III,9361206621,Kirlin-Effertz,no -Rosene,Flitcroft,rflitcroft5u@free.fr,rflitcroft5u,Inírida,57-(248)911-2377,Clinical Specialist,9685341567,"Dietrich, Wyman and Shanahan",no -Chariot,Barok,cbarok5v@blogspot.com,cbarok5v,CăuÅŸeni,373-(632)605-6648,Speech Pathologist,4694900498,Legros LLC,no -Elita,von Nassau,evonnassau5w@digg.com,evonnassau5w,Kutacane,62-(213)303-2731,Executive Secretary,347148085,Green Inc,no -Gael,Marjanovic,gmarjanovic5x@marriott.com,gmarjanovic5x,Huacapampa,51-(186)661-9148,Occupational Therapist,6726485387,"Stamm, Kris and Farrell",no -Gaspard,Ummfrey,gummfrey5y@t.co,gummfrey5y,Buenavista,63-(497)452-5987,Speech Pathologist,4575944572,Bradtke-Hegmann,no -Bent,Trenam,btrenam5z@auda.org.au,btrenam5z,Cerrito,57-(251)296-9228,Payment Adjustment Coordinator,1352694905,Robel Inc,no -Allie,Cardo,acardo60@nba.com,acardo60,Sayama,81-(136)328-3578,Administrative Assistant II,1953155472,Franecki Inc,no -Kendricks,Lawes,klawes61@wikimedia.org,klawes61,Kýria,30-(629)572-9182,Automation Specialist III,3013335882,Cremin and Sons,no -Israel,Earwicker,iearwicker62@latimes.com,iearwicker62,Naga,63-(980)734-2130,Help Desk Technician,9262358857,Predovic Group,no -Olivie,Haskett,ohaskett63@biblegateway.com,ohaskett63,Sadská,420-(267)684-5843,General Manager,341903078,"Ritchie, Thiel and Crona",no -Lorette,Jupp,ljupp64@eventbrite.com,ljupp64,Kalety,48-(905)755-9085,Senior Financial Analyst,6902201358,"Larson, Mann and Denesik",no -Huberto,Fieldsend,hfieldsend65@hibu.com,hfieldsend65,Rostokino,7-(885)545-7151,Sales Representative,7583497507,Buckridge-Crist,no -Galvan,Ruslen,gruslen66@biblegateway.com,gruslen66,Tomaszów Lubelski,48-(738)856-8800,Executive Secretary,7689061370,Marquardt Group,no -Lucine,Dillway,ldillway67@netlog.com,ldillway67,Nobres,55-(588)142-5888,Data Coordiator,3143583223,Morissette Group,no -Bogey,Lloyd,blloyd68@clickbank.net,blloyd68,Maskinongé,1-(480)421-6369,Recruiter,8019916377,Schmidt-Okuneva,no -Lana,Osorio,losorio69@theguardian.com,losorio69,Shuiyuesi,86-(368)799-2866,Financial Advisor,845918354,Murazik-Spinka,no -April,Shrimpton,ashrimpton6a@yahoo.co.jp,ashrimpton6a,Jurh,86-(945)443-7757,Budget/Accounting Analyst I,6199988221,"Willms, Marquardt and Kautzer",no -Thea,Brunelleschi,tbrunelleschi6b@boston.com,tbrunelleschi6b,Belovo,7-(379)764-3239,Speech Pathologist,3924467498,Feil Inc,no -Sunshine,Pymer,spymer6c@sakura.ne.jp,spymer6c,Tsibulev,380-(446)992-3711,Technical Writer,673563766,Price-Kuhic,no -Neddie,Westoll,nwestoll6d@dailymail.co.uk,nwestoll6d,Tatebayashi,81-(379)112-1552,Software Consultant,5692786702,"Lind, Schoen and Harris",no -Godfrey,Shiliton,gshiliton6e@economist.com,gshiliton6e,Caucaia,55-(324)521-2916,Assistant Media Planner,3640782380,"White, Sipes and Macejkovic",no -Bonnibelle,Meys,bmeys6f@hud.gov,bmeys6f,Barengkok Hilir,62-(132)233-0164,VP Marketing,6576141343,Greenfelder and Sons,no -Biddie,Hazart,bhazart6g@privacy.gov.au,bhazart6g,Lyubimets,359-(539)214-9362,Research Nurse,2110251921,Beatty Inc,no -Mariette,Arnoud,marnoud6h@upenn.edu,marnoud6h,Chonglou,86-(367)745-8032,Chemical Engineer,6279421910,Bailey-Flatley,no -Ralph,Ronan,rronan6i@soup.io,rronan6i,Villa del Rosario,54-(472)473-2834,Media Manager III,898611601,Conroy-Schultz,no -Kristopher,Byrth,kbyrth6j@ucoz.com,kbyrth6j,Vyselki,7-(553)161-3317,GIS Technical Architect,8368786673,"Feest, Cremin and Goodwin",no -Juliette,Peckett,jpeckett6k@desdev.cn,jpeckett6k,Dublin,353-(547)169-7510,Registered Nurse,4289815278,Jakubowski-Lueilwitz,no -Lauretta,Edgeler,ledgeler6l@instagram.com,ledgeler6l,Tarragona,63-(650)302-6149,Biostatistician IV,6817860377,"Jakubowski, Steuber and Tillman",no -Scot,Calvert,scalvert6m@blinklist.com,scalvert6m,Condong,62-(732)380-9120,Sales Associate,7437515962,Doyle-Breitenberg,no -Elyssa,Tibbles,etibbles6n@last.fm,etibbles6n,Neftobod,992-(777)638-8129,Analyst Programmer,9445635612,"Fahey, Champlin and Dietrich",no -Brander,Harmer,bharmer6o@bloglovin.com,bharmer6o,GardÄ“z,93-(358)973-4287,Librarian,1276314027,"Hauck, Simonis and Rosenbaum",no -Brennen,Springthorpe,bspringthorpe6p@salon.com,bspringthorpe6p,Mobaye,236-(308)628-4824,Safety Technician II,3086629398,Yundt-Ziemann,no -Bren,Kleynermans,bkleynermans6q@phpbb.com,bkleynermans6q,Weifang,86-(292)267-2145,Developer III,5400207502,"Treutel, Stoltenberg and Johnston",no -Candy,Tooze,ctooze6r@friendfeed.com,ctooze6r,Vlachovo BÅ™ezí,420-(676)807-2495,Nurse Practicioner,5388144038,"Russel, Blanda and Rogahn",no -Fairleigh,Diamant,fdiamant6s@blinklist.com,fdiamant6s,Sainte-Anne-de-Bellevue,1-(568)570-3951,Quality Engineer,3663658481,Armstrong-Johns,no -Basil,Howsden,bhowsden6t@ovh.net,bhowsden6t,Xylotymbou,357-(214)824-3004,Tax Accountant,3403316513,"Langosh, Bartell and Stoltenberg",no -Dora,Dobbin,ddobbin6u@amazon.co.jp,ddobbin6u,Lamovita,387-(768)310-0952,Office Assistant I,8679431729,Leannon-Kiehn,no -Demetra,Brownill,dbrownill6v@over-blog.com,dbrownill6v,Bagou,86-(713)477-6708,Sales Representative,5659782310,Johns-Kerluke,no -Dasi,Dumini,ddumini6w@patch.com,ddumini6w,Punta del Este,598-(886)595-6322,Human Resources Assistant II,1332065767,Schowalter-Altenwerth,no -Lenette,Geaveny,lgeaveny6x@pcworld.com,lgeaveny6x,Gaoqiao,86-(467)357-1181,Programmer IV,6397889467,"Will, Rath and Jenkins",no -Colin,Treace,ctreace6y@wikimedia.org,ctreace6y,Nanjiao,86-(675)354-6909,Chief Design Engineer,7751575638,"Ferry, Runolfsdottir and Yost",no -Corabelle,Clow,cclow6z@hexun.com,cclow6z,Sovetskaya Gavan’,7-(120)976-8309,Software Consultant,7813300394,Doyle Group,no -Linda,Sebborn,lsebborn70@ucoz.ru,lsebborn70,Knoxville,1-(865)117-5458,General Manager,9692754766,Reichert-O'Reilly,no -Hector,Halbeard,hhalbeard71@about.me,hhalbeard71,Batanovtsi,359-(442)822-8483,Physical Therapy Assistant,9078172762,Conn Group,no -Abbe,Mobberley,amobberley72@ucsd.edu,amobberley72,Kinna,46-(293)901-8731,Administrative Assistant II,9620491815,Hahn-Batz,no -Jasmina,Wickliffe,jwickliffe73@google.it,jwickliffe73,Mercier,1-(345)768-6426,Web Designer IV,9241731699,Wunsch Inc,no -Cissy,Saill,csaill74@google.ru,csaill74,Cifuentes,53-(247)403-7379,Analog Circuit Design manager,1682507572,Oberbrunner Group,no -Sharlene,Ozintsev,sozintsev75@google.com.br,sozintsev75,Gornji Milanovac,381-(814)830-4698,Associate Professor,2798596351,Lockman-Abbott,no -Padraic,Alyukin,palyukin76@chicagotribune.com,palyukin76,Ouricuri,55-(251)839-4014,Recruiter,6246014852,Weimann-Fay,no -Rowney,Bamblett,rbamblett77@shop-pro.jp,rbamblett77,Saint-Étienne,33-(689)956-1538,Director of Sales,2164832523,Gulgowski Inc,no -Ertha,Carayol,ecarayol78@aol.com,ecarayol78,Kanoni,256-(965)621-0576,Assistant Professor,7485417517,"Hoppe, Windler and Stiedemann",no -Jodie,Ramsdell,jramsdell79@ezinearticles.com,jramsdell79,Xingxi,86-(209)444-2347,Geological Engineer,1645038890,Padberg LLC,no -Jessie,Febre,jfebre7a@lulu.com,jfebre7a,Glugur Krajan,62-(476)504-7372,Editor,7484180890,Dickinson Group,no -Xymenes,Rihanek,xrihanek7b@mapy.cz,xrihanek7b,Tiemen,86-(815)399-0300,Tax Accountant,7418639295,Rogahn Group,no -Ynes,Castagneri,ycastagneri7c@army.mil,ycastagneri7c,Majiang,86-(109)725-0679,Web Developer IV,9966232281,Veum-Block,no -Meade,Josovich,mjosovich7d@instagram.com,mjosovich7d,Dumalinao,63-(220)787-8879,Chief Design Engineer,9532892052,Dach LLC,no -Starla,Baglan,sbaglan7e@kickstarter.com,sbaglan7e,Tucson,1-(520)306-4486,Geological Engineer,3924286000,White LLC,no -Cecil,Ditchfield,cditchfield7f@merriam-webster.com,cditchfield7f,Bouillon,32-(153)814-1718,Safety Technician II,3049249757,Wehner-Schultz,no -Albina,Caile,acaile7g@sourceforge.net,acaile7g,Limanowa,48-(598)134-6137,Statistician IV,2887139231,Gleichner LLC,no -Myrtia,Topling,mtopling7h@timesonline.co.uk,mtopling7h,Weichanglu,86-(679)742-0020,Account Coordinator,8708521727,Skiles-Rempel,no -Jillian,Parnall,jparnall7i@plala.or.jp,jparnall7i,Lobito,244-(170)955-1350,Environmental Specialist,4004831083,McCullough-Gislason,no -Martita,Gallamore,mgallamore7j@fotki.com,mgallamore7j,Watubuku,62-(829)363-5055,Food Chemist,332452107,Cummerata Group,no -Gareth,Bourchier,gbourchier7k@biblegateway.com,gbourchier7k,Benito Juarez,52-(692)902-9439,Senior Editor,2717344314,Mueller-McDermott,no -Cristine,Lydiard,clydiard7l@hubpages.com,clydiard7l,Puzi,86-(502)679-6404,Tax Accountant,2847297839,Rau-Gerhold,no -Tamar,Grishagin,tgrishagin7m@ft.com,tgrishagin7m,Singkup,62-(368)986-3322,Pharmacist,4974827138,Waters-Volkman,no -Kassandra,McGarva,kmcgarva7n@ucsd.edu,kmcgarva7n,Tunal,51-(683)835-8456,Statistician II,8446317222,Tremblay-Jacobi,no -Lulita,Millard,lmillard7o@biglobe.ne.jp,lmillard7o,Charlemagne,1-(334)407-8112,Software Engineer IV,7906208463,Nicolas-Harber,no -Deanna,Guly,dguly7p@cbslocal.com,dguly7p,Barda,994-(610)562-3650,Business Systems Development Analyst,3995952712,Bailey Group,no -Latia,Lerer,llerer7q@acquirethisname.com,llerer7q,Dykan’ka,380-(537)386-5412,Quality Control Specialist,1410485773,Daugherty and Sons,no -Jessika,Constantine,jconstantine7r@twitter.com,jconstantine7r,Parang,62-(598)114-0602,VP Marketing,1477207635,Schowalter-Becker,no -Fianna,Garz,fgarz7s@posterous.com,fgarz7s,VästerÃ¥s,46-(169)490-0251,Paralegal,2065474785,"Osinski, Stark and Wisoky",no -Georgina,McGown,gmcgown7t@va.gov,gmcgown7t,Lianghekou,86-(580)598-5939,Database Administrator IV,8574862584,Schmidt-Stark,no -Libbey,Hatto,lhatto7u@parallels.com,lhatto7u,Saint-Pierre,262-(621)379-5079,Compensation Analyst,3663439984,Wintheiser-Tromp,no -Melosa,Varvell,mvarvell7v@msu.edu,mvarvell7v,Canta,51-(384)731-8458,Executive Secretary,1754007481,Lind-Weissnat,no -Caspar,Cutbirth,ccutbirth7w@nifty.com,ccutbirth7w,Makoko,234-(315)758-3899,Senior Sales Associate,7648950596,"Kertzmann, Muller and McClure",no -Ward,Pryor,wpryor7x@xinhuanet.com,wpryor7x,Pitangui,55-(228)519-9720,Research Assistant I,7596446612,Ondricka-Murray,no -Judon,Orhrt,jorhrt7y@friendfeed.com,jorhrt7y,Rancakuya,62-(835)959-6252,Senior Financial Analyst,862524326,Ratke-Powlowski,no -Isa,Gudgin,igudgin7z@bizjournals.com,igudgin7z,Paojan,62-(130)105-8528,Administrative Assistant IV,6567173823,Hyatt and Sons,no -Jacquetta,Heinrici,jheinrici80@latimes.com,jheinrici80,Pitanga,55-(768)218-0323,Clinical Specialist,7383258630,McCullough LLC,no -Gris,Demonge,gdemonge81@eventbrite.com,gdemonge81,Sevsk,7-(385)990-5642,Software Consultant,7319470772,Prosacco Inc,no -Anna,Cordova,acordova82@vistaprint.com,acordova82,Horodnya,380-(403)720-3982,Research Associate,8382416985,Witting Inc,no -Garek,Paver,gpaver83@reddit.com,gpaver83,Baumata,62-(857)457-3277,Systems Administrator IV,5548424025,Homenick-Cummings,no -Lyndsey,Penrose,lpenrose84@amazonaws.com,lpenrose84,Xinning,86-(478)975-1877,Account Executive,2521617024,"Beier, Wolf and Rutherford",no -Vivyanne,Garment,vgarment85@hc360.com,vgarment85,Abonnema,234-(630)147-0664,Compensation Analyst,3170876961,Gibson Inc,no -Diandra,Alfonso,dalfonso86@so-net.ne.jp,dalfonso86,São Joaquim da Barra,55-(958)915-7472,Recruiter,9244448580,"Walker, Ryan and Gutmann",no -Germana,Filippi,gfilippi87@virginia.edu,gfilippi87,BoÅ™itov,420-(677)675-6041,Quality Engineer,4733992351,Rice Group,no -Huntington,Jaffa,hjaffa88@jigsy.com,hjaffa88,Puerto Padre,53-(811)758-9909,Research Associate,3705515845,Kerluke-Durgan,no -Julius,Scrace,jscrace89@sohu.com,jscrace89,Vilnius,370-(437)863-9804,Senior Financial Analyst,8560462449,"Wunsch, Wilkinson and Bernier",no -Orin,Donnersberg,odonnersberg8a@lycos.com,odonnersberg8a,Posadas,54-(799)614-8488,Assistant Manager,6525056810,"Barrows, Prohaska and Raynor",no -Nicole,Alberts,nalberts8b@businessinsider.com,nalberts8b,Circa,51-(793)830-7780,Internal Auditor,6648971908,Kihn Inc,no -Noami,Coast,ncoast8c@blogs.com,ncoast8c,Bengga,62-(245)836-9414,VP Accounting,1384386025,"Reilly, Feest and Armstrong",no -Trev,Coil,tcoil8d@nyu.edu,tcoil8d,Ol Kalou,254-(686)258-2913,Mechanical Systems Engineer,6873910341,Goyette Inc,no -Isiahi,Bingall,ibingall8e@histats.com,ibingall8e,Raleigh,1-(919)707-3481,Legal Assistant,7848853918,"Stark, Thompson and Wintheiser",no -Dasi,Balsdon,dbalsdon8f@paginegialle.it,dbalsdon8f,Douala,237-(853)665-9676,Administrative Officer,5137205664,"Olson, Mayert and Renner",no -Allx,Towl,atowl8g@wordpress.com,atowl8g,Kosti,249-(267)322-7346,Human Resources Assistant III,4848260302,Walker LLC,no -Mead,Bunton,mbunton8h@linkedin.com,mbunton8h,Zhukovskiy,7-(479)702-5593,General Manager,9816135388,Durgan-Steuber,no -Felipe,Cremen,fcremen8i@tinyurl.com,fcremen8i,Tualangcut,62-(457)527-3731,Software Consultant,960922245,Glover-Vandervort,no -Maryanne,Bevir,mbevir8j@devhub.com,mbevir8j,Petrolina,55-(430)943-6831,Analog Circuit Design manager,8076246016,Schumm-Hoppe,no -Rice,Bangley,rbangley8k@tinyurl.com,rbangley8k,San Francisco,1-(415)451-6262,Legal Assistant,2931538566,Wisoky-Hettinger,no -Rockey,Jewell,rjewell8l@printfriendly.com,rjewell8l,Mistrató,57-(273)159-4757,Marketing Manager,2358326909,Kohler-Crooks,no -Andrei,Busk,abusk8m@trellian.com,abusk8m,Shevchenkove,380-(471)522-4941,Software Test Engineer III,5139581752,Kemmer Group,no -Boyce,Colwill,bcolwill8n@google.fr,bcolwill8n,Paço de Arcos,351-(169)576-4617,Database Administrator I,8239907938,"Carter, Connelly and Turcotte",no -Rosalia,Kilcullen,rkilcullen8o@tmall.com,rkilcullen8o,SkellefteÃ¥,46-(497)227-6314,Research Nurse,7802328411,Marvin Inc,no -Wang,Howcroft,whowcroft8p@usa.gov,whowcroft8p,Seixo de Manhoses,351-(878)241-1916,Engineer I,4169975723,"Quitzon, Jacobson and Hoeger",no -Pauletta,MacFadden,pmacfadden8q@yellowbook.com,pmacfadden8q,Guanabacoa,53-(414)148-3660,Senior Cost Accountant,4033591168,"Bernhard, Greenholt and Kovacek",no -Anetta,Kikke,akikke8r@xrea.com,akikke8r,Pines,63-(455)105-7090,Staff Scientist,3797345658,"Upton, Walter and Champlin",no -Doy,Von Oertzen,dvonoertzen8s@soundcloud.com,dvonoertzen8s,Trancas,54-(227)790-7863,Quality Control Specialist,7347709652,Rogahn-Robel,no -Evania,Ettery,eettery8t@woothemes.com,eettery8t,Batuidu,62-(958)487-1137,Environmental Tech,6712438047,"McCullough, Brown and White",no -Dorothy,Burchfield,dburchfield8u@com.com,dburchfield8u,Buenos Aires,57-(104)461-3034,Structural Engineer,1232163627,"Bergnaum, Mann and Denesik",no -Marnia,Pates,mpates8v@cdc.gov,mpates8v,Wanzu,86-(584)655-8767,Structural Analysis Engineer,9486437602,"Vandervort, Reilly and Torp",no -Sherlocke,Sundin,ssundin8w@bloglovin.com,ssundin8w,Lubumbashi,242-(894)433-3030,Database Administrator I,8350287896,"Denesik, Prohaska and Jacobson",no -Cathe,Perell,cperell8x@wordpress.com,cperell8x,Kruty,380-(115)837-1904,VP Sales,42397316,"Wilkinson, Schulist and Macejkovic",no -Pennie,Gerault,pgerault8y@geocities.jp,pgerault8y,Placencia,501-(575)329-0306,Social Worker,7431658591,"Torphy, Bailey and Reilly",no -Mitchell,Crambie,mcrambie8z@privacy.gov.au,mcrambie8z,Xiamao,86-(265)196-0470,Nuclear Power Engineer,1526344351,Ritchie-Morissette,no -Melisent,Gosneye,mgosneye90@freewebs.com,mgosneye90,YÅkaichiba,81-(118)826-5920,VP Product Management,2373235900,Homenick and Sons,no -Jordain,Gheorghe,jgheorghe91@bandcamp.com,jgheorghe91,Ljungby,46-(108)213-7069,Pharmacist,2339386128,"Lubowitz, Witting and Trantow",no -Ezekiel,Winpenny,ewinpenny92@aboutads.info,ewinpenny92,Uttaradit,66-(502)534-3772,Analyst Programmer,9863270415,"Satterfield, Yost and Cruickshank",no -Alaric,Coal,acoal93@rambler.ru,acoal93,PiÅ‚a,48-(110)978-3089,Senior Editor,1002792959,Schmeler Group,no -Sigvard,Durnill,sdurnill94@senate.gov,sdurnill94,Jiuzihe,86-(285)652-3541,Desktop Support Technician,6259022751,Hodkiewicz-Treutel,no -Pauletta,Jacob,pjacob95@blog.com,pjacob95,Lillehammer,47-(466)710-7316,Budget/Accounting Analyst IV,8012816342,Kub-Streich,no -Genevieve,Easson,geasson96@gmpg.org,geasson96,Litian,86-(690)700-3316,Chemical Engineer,9267475002,Krajcik-Berge,no -Chad,Wellings,cwellings97@geocities.com,cwellings97,Corozal,57-(599)397-8105,Technical Writer,1234875780,Lang Inc,no -Constantia,Andor,candor98@angelfire.com,candor98,Luxi,86-(981)190-7061,Tax Accountant,2171737387,Kunze-Cummerata,no -Nathanil,Kennett,nkennett99@salon.com,nkennett99,Kushnytsya,380-(964)412-7626,Operator,2201713081,Auer LLC,no -Marilyn,Lambden,mlambden9a@ca.gov,mlambden9a,SÅ«q al KhamÄ«s,967-(606)751-1510,Social Worker,9824776443,"Kuhic, Wolf and Runte",no -Sol,Trownson,strownson9b@t.co,strownson9b,Venado Tuerto,54-(387)454-8901,Paralegal,4748088125,Reichert-Nitzsche,no -Marybeth,Sturney,msturney9c@163.com,msturney9c,Agassiz,1-(357)754-2461,Data Coordiator,3593006383,Spinka-Ebert,no -Tyler,Kightly,tkightly9d@ed.gov,tkightly9d,KunÄice pod OndÅ™ejníkem,420-(222)601-1668,General Manager,5778670230,Brown Group,no -Godart,Izakoff,gizakoff9e@jimdo.com,gizakoff9e,San Agustin,63-(658)848-5052,Junior Executive,3147182215,Schaden and Sons,no -Cherie,MacAndrew,cmacandrew9f@jigsy.com,cmacandrew9f,Topeka,1-(785)270-8606,Software Engineer IV,5443654527,Schuster-Windler,no -Giorgia,Cinderey,gcinderey9g@php.net,gcinderey9g,Setúbal,351-(730)743-5865,Account Representative I,6484043175,Johns Inc,no -Kasper,Deuss,kdeuss9h@myspace.com,kdeuss9h,Ostrogozhsk,7-(336)124-5046,Nurse,6142898304,"Smith, Hamill and Beatty",no -Leroy,Foord,lfoord9i@timesonline.co.uk,lfoord9i,Kijang,82-(585)819-8722,Analog Circuit Design manager,4569642292,Leffler-Casper,no -Urson,Fludder,ufludder9j@mac.com,ufludder9j,Lumbardhi,383-(659)644-8699,Programmer II,2608105793,Wisoky-Lesch,no -Victoir,Sainer,vsainer9k@4shared.com,vsainer9k,Mocoa,57-(566)759-9576,Senior Cost Accountant,4891001224,Gerlach Inc,no -Nestor,Gray,ngray9l@paginegialle.it,ngray9l,Vallauris,33-(405)253-6375,Help Desk Operator,7391899399,Rutherford-Fisher,no -Benedetto,Sebrens,bsebrens9m@quantcast.com,bsebrens9m,Hüremt,976-(937)675-7706,Health Coach I,5492941666,Rosenbaum Inc,no -Alis,Crittal,acrittal9n@fema.gov,acrittal9n,Xia’ertai,86-(808)528-2343,Nurse,9616882783,Waters LLC,no -Tanitansy,Deighan,tdeighan9o@ovh.net,tdeighan9o,Ketitang Wetan,62-(807)439-9181,Account Coordinator,5918139109,Schultz-Quigley,no -Park,Allbon,pallbon9p@topsy.com,pallbon9p,Mértola,351-(457)676-3664,Recruiter,7432415315,Franecki-Lind,no -Bridget,Houlden,bhoulden9q@telegraph.co.uk,bhoulden9q,Busay,63-(327)378-8551,Teacher,5534661836,Kutch Inc,no -Timi,Winnett,twinnett9r@wiley.com,twinnett9r,Foros de Salvaterra,351-(750)913-7504,VP Marketing,956508278,"Pagac, Wisozk and Powlowski",no -Garey,Berends,gberends9s@nasa.gov,gberends9s,Villa Nueva,54-(466)900-2563,Community Outreach Specialist,2649689285,Cruickshank Group,no -Maurise,Sabathe,msabathe9t@umn.edu,msabathe9t,Porto Alto,351-(826)409-0224,Environmental Tech,4375179112,O'Kon and Sons,no -Waiter,Newport,wnewport9u@cdc.gov,wnewport9u,Balibago,63-(808)564-2341,Media Manager I,3383596125,Frami-Kassulke,no -Clarissa,Snuggs,csnuggs9v@moonfruit.com,csnuggs9v,Weitang,86-(376)106-1168,Account Representative IV,2195207272,Harris Inc,no -Florance,Littlejohns,flittlejohns9w@tumblr.com,flittlejohns9w,Bunigeulis,62-(587)466-3755,Legal Assistant,3318922927,Marquardt-Howe,no -Gail,Skechley,gskechley9x@prweb.com,gskechley9x,Meixi,86-(203)873-6865,Web Developer I,6076144513,Nolan-Dickinson,no -Skippy,Biss,sbiss9y@prweb.com,sbiss9y,Dongshi,86-(723)211-0742,Structural Analysis Engineer,6615870533,"Connelly, Durgan and Block",no -Haskell,Ring,hring9z@bluehost.com,hring9z,Želiv,420-(641)544-3995,Financial Advisor,4876809534,"Lynch, Sipes and Rodriguez",no -Germaine,Nugent,gnugenta0@netscape.com,gnugenta0,Å»yrardów,48-(730)293-9054,Senior Editor,3174803845,"Schimmel, Zulauf and Koepp",no -Karel,Lokier,klokiera1@ow.ly,klokiera1,Dolinsk,7-(925)530-5945,Safety Technician II,4154581473,"Hirthe, Lindgren and Aufderhar",no -Helena,Jeeves,hjeevesa2@sphinn.com,hjeevesa2,Cimanggu,62-(941)630-4312,Product Engineer,7546527988,"Gusikowski, Leannon and Jacobs",no -Alberik,Dillingstone,adillingstonea3@columbia.edu,adillingstonea3,Zelenoborskiy,7-(836)258-0010,Dental Hygienist,7362248229,Mosciski-Runte,no -Carly,Linnett,clinnetta4@exblog.jp,clinnetta4,Pringgabaya,62-(246)753-9319,Administrative Officer,8092146349,Bashirian-Lebsack,no -Mortimer,Brando,mbrandoa5@upenn.edu,mbrandoa5,Vestmannaeyjar,354-(926)712-7652,Account Executive,3018918487,Kreiger Group,no -Filbert,Ablett,fabletta6@ycombinator.com,fabletta6,Mýki,30-(705)811-5277,Tax Accountant,6539074042,"Ziemann, Welch and Farrell",no -Faith,Braven,fbravena7@reuters.com,fbravena7,Thessaloníki,30-(722)343-0087,Electrical Engineer,9811784329,Daniel Inc,no -Frannie,Davidov,fdavidova8@themeforest.net,fdavidova8,Palayan City,63-(745)265-1044,Web Designer II,6579399848,Denesik and Sons,no -Lucien,Tabner,ltabnera9@xinhuanet.com,ltabnera9,Mulyosari,62-(239)594-7444,Librarian,1502298732,"Rosenbaum, Reichert and Kunze",no -Wally,Enever,weneveraa@bloglines.com,weneveraa,Satrejan,62-(645)352-9402,Office Assistant III,3527730311,Grady-Koelpin,no -Nedda,Gorke,ngorkeab@networkadvertising.org,ngorkeab,StaniÅ¡ić,381-(108)441-3898,Research Associate,3014819530,Turner LLC,no -Bobby,Froggatt,bfroggattac@soup.io,bfroggattac,PlatiÄevo,381-(674)755-5107,Quality Control Specialist,737880740,"Goodwin, Gislason and Deckow",no -Stace,Acaster,sacasterad@discovery.com,sacasterad,Macia,258-(104)153-7107,Health Coach IV,6201062777,Jones-Pollich,no -Oswald,Moulder,omoulderae@goo.gl,omoulderae,Khvastovichi,7-(214)819-4396,Social Worker,394690478,Nikolaus-Erdman,no -Wanda,Glossop,wglossopaf@infoseek.co.jp,wglossopaf,Pinyug,7-(114)659-2688,Internal Auditor,2523327775,"Frami, Leffler and Schulist",no -Drucy,Le Count,dlecountag@1und1.de,dlecountag,Skalat,380-(388)706-1735,Accountant III,1408462974,"Zieme, Terry and Runolfsdottir",no -Jaclyn,Dohrmann,jdohrmannah@is.gd,jdohrmannah,Dongshan,86-(100)568-9635,GIS Technical Architect,3924999996,McKenzie-Hermiston,no -Yvonne,Sansun,ysansunai@omniture.com,ysansunai,El Progreso,507-(224)379-6931,Staff Accountant IV,8276625738,Sporer-Trantow,no -Vonny,Skipp,vskippaj@sbwire.com,vskippaj,Paokmotong Utara,62-(835)335-0196,Payment Adjustment Coordinator,5895390900,"Treutel, Brown and Brekke",no -Karyl,Wadman,kwadmanak@imgur.com,kwadmanak,Yanjiao,86-(360)378-3302,Staff Scientist,1910238406,"Hansen, Pagac and Deckow",no -Kippie,Bradden,kbraddenal@marriott.com,kbraddenal,Jönköping,46-(543)587-0300,Staff Scientist,9176800180,Kirlin Inc,no -Paddy,Kettlesing,pkettlesingam@cornell.edu,pkettlesingam,Oslo,47-(849)145-5733,Structural Engineer,8705223008,Jaskolski-Harber,no -Kane,Wennington,kwenningtonan@ucsd.edu,kwenningtonan,Lukashin,374-(349)354-4479,Staff Scientist,7435531871,Schneider Inc,no -Sherill,Williamson,swilliamsonao@earthlink.net,swilliamsonao,Mafang,86-(496)852-1140,Accounting Assistant III,1506840779,Dooley-Olson,no -Audre,Inglish,ainglishap@ehow.com,ainglishap,Minian,62-(250)963-0003,Business Systems Development Analyst,4065598095,"Stoltenberg, Wisozk and Weimann",no -Berri,Fedorski,bfedorskiaq@umich.edu,bfedorskiaq,Norcasia,57-(162)449-5682,Safety Technician IV,98301586,Maggio LLC,no -Ursulina,Whitehurst,uwhitehurstar@wikipedia.org,uwhitehurstar,Solna,46-(377)784-6117,Operator,650376811,Bogan Inc,no -Alidia,Torr,atorras@fastcompany.com,atorras,Sandaohezi,86-(949)545-6861,Account Executive,4811145070,Lehner LLC,no -Kaye,Beane,kbeaneat@economist.com,kbeaneat,Nglojo,62-(355)846-4503,Systems Administrator III,9744419083,Fay-Schultz,no -Clywd,Rentz,crentzau@howstuffworks.com,crentzau,Yuguan,86-(368)395-3026,Community Outreach Specialist,8482525794,"Daugherty, Hammes and Grant",no -Loella,Golledge,lgolledgeav@xing.com,lgolledgeav,Sangba,86-(997)138-0005,Computer Systems Analyst IV,3818896577,Kassulke Group,no -Berty,Shankle,bshankleaw@blog.com,bshankleaw,Bailai,86-(811)447-2940,Web Designer IV,1381483984,"Rutherford, Weissnat and Stoltenberg",no -Elysha,Wormell,ewormellax@cornell.edu,ewormellax,Heshi,86-(459)905-4935,Engineer II,6903217533,Reynolds and Sons,no -Emelda,Kienl,ekienlay@edublogs.org,ekienlay,Szelków,48-(150)608-0093,Civil Engineer,403741254,Ryan-Braun,no -Nanete,Shatliffe,nshatliffeaz@wikipedia.org,nshatliffeaz,Wongsorejo,62-(801)201-7190,Social Worker,9561076268,Corkery-Davis,no -Krisha,Luddy,kluddyb0@about.me,kluddyb0,Pulau Pinang,60-(664)235-5600,Research Assistant IV,5908588660,"Beier, Daniel and Johns",no -Aloin,Bagnal,abagnalb1@webnode.com,abagnalb1,Јегуновце,389-(396)343-8442,Software Engineer IV,1870806077,Konopelski-Graham,no -Elna,Cockburn,ecockburnb2@goodreads.com,ecockburnb2,Trần Văn Thá»i,84-(508)784-0206,Dental Hygienist,3976709638,Monahan-Greenholt,no -Cointon,Leggan,clegganb3@ox.ac.uk,clegganb3,Biryulëvo Zapadnoye,7-(592)382-0444,Electrical Engineer,2202518185,Dibbert and Sons,no -Danila,Goldsbrough,dgoldsbroughb4@marriott.com,dgoldsbroughb4,Dowsk,375-(360)424-7789,Account Executive,9726332923,Bartoletti Group,no -Gigi,Philimore,gphilimoreb5@typepad.com,gphilimoreb5,Changxingbao,86-(393)226-0359,Physical Therapy Assistant,5418292374,Mayert-Casper,no -Adolf,Vakhlov,avakhlovb6@indiegogo.com,avakhlovb6,Cárdenas,53-(723)559-5803,VP Sales,6257773652,"Hamill, Okuneva and Hettinger",no -Rozelle,Ivanchenkov,rivanchenkovb7@moonfruit.com,rivanchenkovb7,Paris 12,33-(806)491-2642,Nuclear Power Engineer,8868636387,Mante-Wolf,no -Kai,Orr,korrb8@cisco.com,korrb8,Osby,46-(498)794-8561,Sales Associate,3395375684,Lemke-Bednar,no -Collie,Yakunin,cyakuninb9@edublogs.org,cyakuninb9,Nanpu,86-(352)102-7244,Health Coach I,6101887901,Medhurst-Langosh,no -Fae,Cahill,fcahillba@reuters.com,fcahillba,Krzeszów,48-(622)530-7313,Food Chemist,6503968477,"Friesen, Sanford and Mayer",no -Cyndie,Aslet,casletbb@scientificamerican.com,casletbb,Saint-Constant,1-(771)528-2913,Cost Accountant,4843499684,"Hamill, Sauer and Torphy",no -Patrizius,Twyford,ptwyfordbc@weather.com,ptwyfordbc,Lagoa,351-(291)253-5463,Business Systems Development Analyst,994297475,Mosciski Inc,no -Mattias,Streight,mstreightbd@yahoo.co.jp,mstreightbd,Skutskär,46-(177)125-2012,Actuary,5440187510,"Farrell, Sanford and Corkery",no -Carlene,Vanni,cvannibe@themeforest.net,cvannibe,Mahates,57-(796)357-4709,Food Chemist,2337222705,Roob Inc,no -Ron,Durrance,rdurrancebf@wp.com,rdurrancebf,Mapalacsiao,63-(369)788-7340,Analyst Programmer,5457066193,Mitchell LLC,no -Carina,Edwardes,cedwardesbg@mapquest.com,cedwardesbg,Valinhos,55-(577)612-0116,Recruiting Manager,5227826919,Mosciski Inc,no -Maddalena,Salerg,msalergbh@umich.edu,msalergbh,Novopodrezkovo,7-(697)647-8581,Health Coach I,1557006229,Brekke Group,no -Philippe,Packington,ppackingtonbi@hp.com,ppackingtonbi,Wenshao,86-(707)622-9285,Accounting Assistant II,6329239517,Pacocha-Blick,no -Izzy,Nias,iniasbj@soundcloud.com,iniasbj,Miami,1-(786)311-2515,Help Desk Operator,1152831127,"Block, Altenwerth and Stanton",no -Merwin,Parsell,mparsellbk@nba.com,mparsellbk,Gayny,7-(547)203-8806,Staff Scientist,7479993129,"Reinger, Quitzon and Vandervort",no -Jessi,Dunkerly,jdunkerlybl@umn.edu,jdunkerlybl,Gaizhou,86-(909)634-5462,Recruiter,2986553141,"O'Reilly, Dicki and Hahn",no -Eldon,Farndon,efarndonbm@aboutads.info,efarndonbm,Wolbrom,48-(909)281-4054,Speech Pathologist,5184371176,"Emmerich, Lehner and Yundt",no -Oates,Elham,oelhambn@mozilla.com,oelhambn,Grand Bank,1-(194)436-8497,Desktop Support Technician,1596660937,"Wehner, Abbott and O'Connell",no -Fanechka,Whyler,fwhylerbo@adobe.com,fwhylerbo,Matelândia,55-(892)769-4322,Biostatistician IV,7668303200,"Donnelly, Gerhold and Schaefer",no -Huntington,Fernant,hfernantbp@ask.com,hfernantbp,Karabas,7-(750)276-8060,Senior Developer,1332953263,Pfeffer-Hills,no -Gregorius,St. Ledger,gstledgerbq@wp.com,gstledgerbq,Vlycháda,30-(743)197-1226,Software Test Engineer III,656158816,Schaden and Sons,no -Cheri,Sainteau,csainteaubr@blinklist.com,csainteaubr,Hengtanggang,86-(187)273-0898,Data Coordiator,5175657151,"Larkin, Weber and Heathcote",no -Christophorus,Poone,cpoonebs@amazonaws.com,cpoonebs,Were Īlu,251-(931)591-8988,Analyst Programmer,8910813008,Flatley and Sons,no -Waite,Castelletto,wcastellettobt@adobe.com,wcastellettobt,Cajamarca,57-(693)677-0164,VP Quality Control,1291883037,Rohan-Goodwin,no -Garth,Legion,glegionbu@mozilla.com,glegionbu,Heihe,86-(941)380-5673,Human Resources Manager,2515034527,"Nitzsche, Pfeffer and Braun",no -Esta,Kerwin,ekerwinbv@networkadvertising.org,ekerwinbv,Mirpur Khas,92-(117)950-2082,Project Manager,8928828589,Glover Inc,no -Chloris,Beare,cbearebw@hhs.gov,cbearebw,Willowmore,27-(985)824-0594,Desktop Support Technician,8563826506,Feil-Reichel,no -Randell,Cancellieri,rcancellieribx@prnewswire.com,rcancellieribx,Bekwai,233-(905)301-0573,Human Resources Assistant I,730372995,Kutch Inc,no -Kip,Ramsdale,kramsdaleby@arstechnica.com,kramsdaleby,Somerset East,27-(218)205-0925,Senior Quality Engineer,802215807,Stehr and Sons,no -Codi,Sutherley,csutherleybz@163.com,csutherleybz,Monaghan,353-(394)919-0981,Account Representative I,6776632693,Stroman and Sons,no -Ichabod,Softley,isoftleyc0@networksolutions.com,isoftleyc0,Gorshechnoye,7-(857)425-7927,Junior Executive,503472247,"Huel, Kunze and Grant",no -Darnall,Menicomb,dmenicombc1@dyndns.org,dmenicombc1,Pisão,351-(603)152-7936,Nuclear Power Engineer,704521695,"Jenkins, Bins and Bosco",no -Hermie,Tripony,htriponyc2@state.tx.us,htriponyc2,Dahe,86-(695)324-3438,Staff Scientist,1453231722,Sauer Group,no -Rouvin,Brahms,rbrahmsc3@flickr.com,rbrahmsc3,Hyesan-dong,850-(373)849-6582,Speech Pathologist,1399653598,"Schumm, Willms and Kovacek",no -Eva,Ausiello,eausielloc4@ted.com,eausielloc4,Las Flores,52-(522)676-7887,VP Product Management,2984517733,Doyle-Hackett,no -Homerus,Benz,hbenzc5@scribd.com,hbenzc5,Karangboyo,62-(463)880-8699,Geological Engineer,9502247396,Pacocha-Rath,no -Orelia,Cardoo,ocardooc6@theatlantic.com,ocardooc6,Huai Mek,66-(562)635-6374,Administrative Officer,2407522354,Sawayn LLC,no -Andy,Whyke,awhykec7@marketwatch.com,awhykec7,Melaka,60-(619)443-9740,Cost Accountant,7266501369,Stiedemann Group,no -Wendell,Charlot,wcharlotc8@weibo.com,wcharlotc8,Shanmu,86-(399)817-3579,Legal Assistant,1929334834,Hudson and Sons,no -Zaria,Rickert,zrickertc9@phoca.cz,zrickertc9,Sukamulya,62-(489)801-0441,Actuary,6151868455,McClure-Cormier,no -Liane,Bugbird,lbugbirdca@soup.io,lbugbirdca,Jiuchenggong,86-(742)484-7736,Director of Sales,5015471092,Dickinson Group,no -Hailee,Jackalin,hjackalincb@t.co,hjackalincb,Baziqiao,86-(348)628-0372,Engineer I,9824969918,"Schuster, Balistreri and Kiehn",no -Sarina,Gretton,sgrettoncc@youtu.be,sgrettoncc,Kafr ash Shaykh,20-(570)759-6531,Programmer III,761625356,Ruecker-Cronin,no -Nedi,Raiman,nraimancd@dailymotion.com,nraimancd,Pohang,82-(100)445-2527,Geologist II,5896756089,Ratke Inc,no -Flossie,O'Keenan,fokeenance@shutterfly.com,fokeenance,Ryjewo,48-(120)425-5167,Software Consultant,4109493390,"Macejkovic, Ziemann and Medhurst",no -Cosette,Bodiam,cbodiamcf@apple.com,cbodiamcf,Falkenberg,46-(660)179-8368,Administrative Officer,1051595991,"Jast, Johnson and Eichmann",no -Wylma,Risbridger,wrisbridgercg@infoseek.co.jp,wrisbridgercg,Villa Paula de Sarmiento,54-(727)419-0510,Nurse,7423041831,"Kuphal, Lind and Smith",no -Anabal,Shuard,ashuardch@hexun.com,ashuardch,Nglojo,62-(150)961-7237,Business Systems Development Analyst,4136441134,"Lemke, Walter and Torp",no -Bill,Bortoluzzi,bbortoluzzici@drupal.org,bbortoluzzici,São Miguel da Carreira,351-(302)874-8797,Payment Adjustment Coordinator,4539837109,Trantow Group,no -Arlena,Rosenbarg,arosenbargcj@com.com,arosenbargcj,Ingarö,46-(337)343-6677,Teacher,7371988523,McDermott-Pollich,no -Kaye,Olsson,kolssonck@sciencedaily.com,kolssonck,Shawan,86-(420)204-5708,Sales Representative,7882023620,"Willms, Maggio and Ebert",no -Brady,Esmead,besmeadcl@cbsnews.com,besmeadcl,Dhahi,967-(539)198-8938,Senior Financial Analyst,3305446706,"Senger, Rau and White",no -Beulah,De Cruz,bdecruzcm@shareasale.com,bdecruzcm,Menghe,86-(695)441-1949,Budget/Accounting Analyst I,1010118013,"Stoltenberg, Gaylord and Kuhn",no -Hart,Pignon,hpignoncn@so-net.ne.jp,hpignoncn,Xishan,86-(796)134-7259,Senior Sales Associate,3487451824,Eichmann LLC,no -Gaultiero,Yapp,gyappco@gov.uk,gyappco,Silveiros,351-(968)335-1115,Editor,8432845973,Torphy-Pfeffer,no -Leonora,Camier,lcamiercp@i2i.jp,lcamiercp,Niebylec,48-(837)941-0248,Payment Adjustment Coordinator,3362041817,Tremblay-Bogan,no -Elsy,Barhem,ebarhemcq@howstuffworks.com,ebarhemcq,Baimi,86-(478)746-4408,Geologist III,3636465074,"Kling, Rempel and Schuppe",no -Ludovika,Freemantle,lfreemantlecr@biblegateway.com,lfreemantlecr,Saint Catherine,20-(525)822-1176,Computer Systems Analyst IV,2508567590,Bartell Group,no -Niki,Bartoszewski,nbartoszewskics@geocities.jp,nbartoszewskics,Toyós,504-(285)333-3663,VP Product Management,8170495997,"Labadie, Schuster and Bailey",no -Jasper,Scardifield,jscardifieldct@wordpress.org,jscardifieldct,Botoh,62-(908)487-6165,Mechanical Systems Engineer,4706641020,"Lindgren, Miller and Reilly",no -Blondie,Korda,bkordacu@jimdo.com,bkordacu,Sayama,81-(550)261-8768,Safety Technician II,3613863448,"Beier, Hodkiewicz and Labadie",no -Mari,Wildey,mwildeycv@earthlink.net,mwildeycv,Las Vegas,1-(702)780-0013,Community Outreach Specialist,6081899675,Ritchie Group,no -Rania,Caulier,rcauliercw@google.es,rcauliercw,Hongqiao,86-(135)941-5093,Recruiting Manager,9767170790,Sipes Group,no -Carmita,Balk,cbalkcx@nydailynews.com,cbalkcx,Radenković,381-(489)896-5035,Assistant Media Planner,3192007451,Rau Group,no -Raye,Rodwell,rrodwellcy@redcross.org,rrodwellcy,Ibusuki,81-(371)210-6897,Programmer IV,7817437106,Halvorson-Considine,no -Jeanelle,Gaunt,jgauntcz@chicagotribune.com,jgauntcz,Guajará Mirim,55-(942)383-5866,Environmental Tech,5293445545,"Cummings, Rohan and Olson",no -Marten,Achromov,machromovd0@google.com.br,machromovd0,Carapicuíba,55-(150)839-0871,Professor,5673414536,Grady and Sons,no -Zebadiah,Ashton,zashtond1@aboutads.info,zashtond1,Liangwangzhuang,86-(533)605-8366,Assistant Media Planner,5660743293,O'Kon Inc,no -Nikolos,Rominov,nrominovd2@livejournal.com,nrominovd2,Wulingyuan,86-(988)701-4303,Administrative Officer,9339583396,McCullough Group,no -Abel,Brockley,abrockleyd3@usgs.gov,abrockleyd3,Cortiços,351-(655)931-9613,Computer Systems Analyst IV,4253152929,Schiller Group,no -Flss,Totaro,ftotarod4@slashdot.org,ftotarod4,Kolaka,62-(103)182-1362,Chief Design Engineer,1468159798,Bruen-Torp,no -Shaine,Tellenbach,stellenbachd5@pen.io,stellenbachd5,Gllogjan,383-(183)169-1139,VP Quality Control,5580264399,"Conroy, Bradtke and Huel",no -Vanni,Zuanazzi,vzuanazzid6@sitemeter.com,vzuanazzid6,Astypálaia,30-(580)574-2168,Account Representative I,9464258799,Lakin LLC,no -Courtney,Dimitrie,cdimitried7@google.co.uk,cdimitried7,Houmt Souk,216-(733)765-8980,Associate Professor,1685573266,Paucek LLC,no -Cymbre,Footitt,cfootittd8@tiny.cc,cfootittd8,Leuwayang,62-(318)778-1877,VP Quality Control,3946614353,Hermann and Sons,no -Kaye,Jessope,kjessoped9@flavors.me,kjessoped9,Kalety,48-(845)480-1707,Senior Quality Engineer,3355884294,"Cremin, Olson and Sawayn",no -Cornie,Limmer,climmerda@state.tx.us,climmerda,Guisser,212-(384)879-2239,Automation Specialist III,7826225280,Balistreri-Wiegand,no -Brenden,Verma,bvermadb@disqus.com,bvermadb,Sulengwaseng,62-(138)357-9869,Web Designer I,6172390155,"Waters, Streich and Abbott",no -Lem,Benedikt,lbenediktdc@wunderground.com,lbenediktdc,Katoro,255-(482)187-5657,Engineer IV,497224445,"Little, Mohr and Wilderman",no -Lowell,Sichardt,lsichardtdd@globo.com,lsichardtdd,Camachile,63-(706)673-3182,Biostatistician I,2575941288,"Lemke, Lesch and Leannon",no -Reine,Delgardillo,rdelgardillode@microsoft.com,rdelgardillode,Neob,62-(936)764-5075,Desktop Support Technician,1513506838,Spencer LLC,no -Thane,Sarchwell,tsarchwelldf@instagram.com,tsarchwelldf,Porto Seguro,55-(660)356-9567,Human Resources Manager,3527153179,Runolfsdottir-Cartwright,no -Danika,Elnor,delnordg@state.tx.us,delnordg,Niimi,81-(547)958-0387,Senior Developer,5050722578,Parisian-Towne,no -Gasparo,Stranio,gstraniodh@diigo.com,gstraniodh,Lezhu,86-(206)787-4960,Financial Analyst,4348687307,Goldner-Schmitt,no -Garnette,Calyton,gcalytondi@vimeo.com,gcalytondi,Karang,62-(950)627-5151,Human Resources Manager,641236301,Prosacco-Ankunding,no -Justinn,Feakins,jfeakinsdj@mozilla.org,jfeakinsdj,Kalampáka,30-(795)552-9647,Engineer IV,8626479360,Nolan LLC,no -Linoel,Kibard,lkibarddk@bravesites.com,lkibarddk,Langpas,63-(644)213-0258,Database Administrator III,3377644679,Langworth-Veum,no -Gan,Lowde,glowdedl@issuu.com,glowdedl,Henan’an,86-(900)591-4847,Actuary,1207495840,"Pagac, White and Stroman",no -Fleurette,Wolland,fwollanddm@dagondesign.com,fwollanddm,Vellinge,46-(300)800-9559,Structural Analysis Engineer,953978931,Kirlin-Kozey,no -Wye,Duplan,wduplandn@qq.com,wduplandn,Ponong,63-(313)355-0869,Project Manager,5336314064,Shanahan-Bradtke,no -Benn,Mayo,bmayodo@oracle.com,bmayodo,Valdivia,56-(963)663-9803,Biostatistician IV,8306385837,Gaylord LLC,no -Benedict,Mannock,bmannockdp@techcrunch.com,bmannockdp,Adorjan,381-(876)163-1457,Account Coordinator,5751702727,"Ernser, Runte and Keebler",no -Eloise,Cleverly,ecleverlydq@4shared.com,ecleverlydq,Colima,506-(165)753-5527,Technical Writer,3792802872,Muller Inc,no -Francesco,Egalton,fegaltondr@woothemes.com,fegaltondr,Taboão da Serra,55-(809)764-2655,Civil Engineer,5044892408,"Crist, Davis and Hirthe",no -Jannelle,Ruddy,jruddyds@barnesandnoble.com,jruddyds,Golden,1-(272)196-2918,Assistant Media Planner,1533733090,Tromp-Emard,no -Dill,Raubenheimers,draubenheimersdt@state.tx.us,draubenheimersdt,Koumac,687-(276)528-4767,Engineer IV,221672184,"Cole, Stamm and Lynch",no -Norine,Sex,nsexdu@angelfire.com,nsexdu,Saint-Eustache,1-(297)748-3708,Nuclear Power Engineer,8545476949,Tromp LLC,no -Fernandina,Loraine,florainedv@zdnet.com,florainedv,Pojok,62-(678)818-1034,Operator,5968941170,Stark and Sons,no -Lincoln,Headly,lheadlydw@pinterest.com,lheadlydw,Rukem,62-(721)761-5670,Desktop Support Technician,1353588335,Mohr Group,no -Sibylle,Duplan,sduplandx@cloudflare.com,sduplandx,Saronída,30-(898)200-2504,Engineer II,1318020735,Nitzsche Inc,no -Durante,Plews,dplewsdy@engadget.com,dplewsdy,Quintela,351-(400)772-1387,Editor,2706273801,Jerde-Johnson,no -Simmonds,Weadick,sweadickdz@bbb.org,sweadickdz,Clisson,33-(449)808-9903,Statistician II,1395682046,Koss and Sons,no -Jinny,Southcoat,jsouthcoate0@imgur.com,jsouthcoate0,Kontiolahti,358-(307)727-1150,Director of Sales,8612730686,Glover Group,no -Tobias,Jedrys,tjedryse1@nifty.com,tjedryse1,Coalaque,51-(352)815-4498,Assistant Media Planner,3241037983,"Daugherty, Lowe and MacGyver",no -Lyndsey,Beasant,lbeasante2@usgs.gov,lbeasante2,Quiñota,51-(893)499-0636,Civil Engineer,1638165475,"Bosco, Farrell and Kuvalis",no -Morganica,Insole,minsolee3@bloglines.com,minsolee3,Alarobia,261-(772)192-7435,Software Consultant,8608912546,"Hodkiewicz, Dare and Treutel",no -Terza,Sangster,tsangstere4@sourceforge.net,tsangstere4,Jagistay,86-(384)596-5400,Editor,8754711339,"Gleason, Luettgen and Dietrich",no -Russell,Crewther,rcrewthere5@webs.com,rcrewthere5,Khiwa,998-(651)198-4172,Tax Accountant,566254271,"Grimes, Koelpin and Koepp",no -Lizabeth,Edmeads,ledmeadse6@facebook.com,ledmeadse6,Tsinandali,995-(180)383-8027,Budget/Accounting Analyst I,9980383631,Bode LLC,no -Karee,O'Feeny,kofeenye7@microsoft.com,kofeenye7,Berezayka,7-(584)773-4095,Senior Sales Associate,2984805720,"Kirlin, Bosco and Jaskolski",no -Adelbert,Ahrenius,aahreniuse8@businessweek.com,aahreniuse8,Trollhättan,46-(560)840-1787,Accountant II,6318469885,Grimes-Rippin,no -Imogene,Ahern,iaherne9@biblegateway.com,iaherne9,Binalbagan,63-(324)343-0387,Senior Cost Accountant,5597923358,"Kemmer, Wyman and Borer",no -Bethanne,Olrenshaw,bolrenshawea@feedburner.com,bolrenshawea,Veiga,351-(737)841-1999,Marketing Manager,6637045683,Stiedemann-Nader,no -Morey,Rabjohns,mrabjohnseb@google.ru,mrabjohnseb,Buang,63-(598)261-6461,Analog Circuit Design manager,4124185693,Champlin LLC,no -Bert,Shiel,bshielec@examiner.com,bshielec,Pignon,509-(674)488-9175,Programmer Analyst II,7670176377,Frami-Senger,no -Justinian,Worters,jwortersed@hp.com,jwortersed,Shanjeev Home,94-(667)505-1821,Pharmacist,380071401,Schmitt LLC,no -Isabeau,Bumpus,ibumpusee@reverbnation.com,ibumpusee,Ã…kersberga,46-(384)641-5637,Structural Engineer,3748433670,Casper Inc,no -Saunderson,Fucher,sfucheref@wikia.com,sfucheref,Tianchi,86-(579)480-2053,VP Marketing,1518810926,Grimes-Harber,no -Bernard,Nanni,bnannieg@taobao.com,bnannieg,Smimou,212-(309)174-5946,Nurse Practicioner,2559240513,Mueller-Kuhic,no -Vivie,Deerness,vdeernesseh@bbc.co.uk,vdeernesseh,Puerta de Corral Quemado,54-(623)114-6422,Systems Administrator I,6638453319,Crona LLC,no -Lyn,Portinari,lportinariei@wix.com,lportinariei,Amqui,1-(265)894-9823,Design Engineer,6404381532,Krajcik LLC,no -Netta,Tubble,ntubbleej@wikispaces.com,ntubbleej,Lentisqueira,351-(888)721-3359,Sales Associate,7584772129,"Fay, Smith and Boyle",no -Kerrill,Coultous,kcoultousek@gizmodo.com,kcoultousek,Ḩarf al Musaytirah,963-(663)978-3503,Statistician III,2943721670,Haley-Eichmann,no -Linzy,Yglesias,lyglesiasel@bizjournals.com,lyglesiasel,Anse Boileau,248-(902)643-6897,Operator,2871357994,"Abernathy, Schuppe and Nienow",no -Allison,Harden,ahardenem@jiathis.com,ahardenem,Cishangang,86-(457)230-2552,Research Associate,614061776,Barrows Group,no -Carlos,Freemantle,cfreemantleen@techcrunch.com,cfreemantleen,Ylämaa,358-(714)766-2393,Professor,6268473329,Leuschke LLC,no -Annice,Roubottom,aroubottomeo@google.ca,aroubottomeo,Herceg-Novi,382-(172)374-0117,Help Desk Operator,3869023244,Herman and Sons,no -Madeleine,Joincey,mjoinceyep@businesswire.com,mjoinceyep,MÄ›lník,420-(175)226-2829,Programmer II,3947098820,Osinski Inc,no -Berke,Rojas,brojaseq@webeden.co.uk,brojaseq,Oemollo,62-(656)492-8444,Analyst Programmer,3830440014,"Goyette, Farrell and Gleason",no -Samuele,Tassaker,stassakerer@github.io,stassakerer,Vyksa,7-(544)516-7710,Media Manager III,5627912308,Howe Inc,no -Edy,Makeswell,emakeswelles@mapquest.com,emakeswelles,København,45-(567)897-5052,VP Sales,1349041246,Schultz and Sons,no -Zebulen,Lukovic,zlukovicet@independent.co.uk,zlukovicet,Garawati,62-(251)559-0462,General Manager,684692104,Emard Group,no -Robby,Walles,rwalleseu@deliciousdays.com,rwalleseu,Dongfeng,86-(344)516-7422,Account Executive,1069939455,Klein and Sons,no -Priscilla,Rowthorn,prowthornev@yellowpages.com,prowthornev,Chodów,48-(766)379-8879,Developer I,2612438270,Cassin Group,no -Rutger,Heynen,rheynenew@ed.gov,rheynenew,Barbaza,63-(998)385-4724,Geological Engineer,716980800,Conn Group,no -Silvano,Oloman,solomanex@who.int,solomanex,Wenping,86-(729)929-2019,Executive Secretary,6206962660,"Becker, Waelchi and Rowe",no -Costanza,Flaune,cflauneey@ameblo.jp,cflauneey,Lianyun,86-(703)407-6420,Project Manager,8356088984,Runolfsdottir-Glover,no -Bronson,Espine,bespineez@ucla.edu,bespineez,Longonjo,244-(866)340-1198,Programmer IV,9275318654,"Langworth, Stracke and Littel",no -Tessy,Warman,twarmanf0@un.org,twarmanf0,Kuala Terengganu,60-(979)185-5701,Structural Analysis Engineer,7047075690,O'Kon Group,no -Dewain,Gravestone,dgravestonef1@noaa.gov,dgravestonef1,Ludvika,46-(235)721-0297,Analog Circuit Design manager,691001715,Nolan Inc,no -Rourke,Denty,rdentyf2@naver.com,rdentyf2,Molinos,51-(784)178-5510,Community Outreach Specialist,7734182216,Bartoletti-Durgan,no -Carmela,Say,csayf3@salon.com,csayf3,Zubtsov,7-(534)684-8554,Junior Executive,9517395299,"Rosenbaum, Jenkins and Zboncak",no -Hurleigh,Strongitharm,hstrongitharmf4@google.com,hstrongitharmf4,Tver,7-(506)278-6475,Analyst Programmer,6615367807,Simonis-Swift,no -Bondon,Lambregts,blambregtsf5@g.co,blambregtsf5,Xingzhen,86-(440)798-6899,Staff Accountant II,4751010697,Okuneva-Kuhn,no -Isiahi,Beard,ibeardf6@wired.com,ibeardf6,Zhavoronki,7-(974)299-4320,Tax Accountant,2230870122,"Wiza, Pfannerstill and Hyatt",no -Remy,Rzehorz,rrzehorzf7@pcworld.com,rrzehorzf7,Ketanggi,62-(750)848-8321,Research Assistant IV,1606199781,"Hegmann, Yundt and Schoen",no -Charles,Vivash,cvivashf8@livejournal.com,cvivashf8,Hantai,86-(114)123-5243,VP Sales,5008348752,Langworth and Sons,no -Angel,Coda,acodaf9@webs.com,acodaf9,Hexi,86-(364)879-7511,Programmer III,1157097758,"O'Conner, Bartell and O'Reilly",no -Althea,Threader,athreaderfa@blogger.com,athreaderfa,Sviblovo,7-(521)978-2163,Financial Analyst,343346095,"Barton, Kling and Ullrich",no -Auberta,MacKeever,amackeeverfb@com.com,amackeeverfb,Taldyqorghan,7-(663)142-7914,Administrative Officer,191723436,Kertzmann LLC,no -Jerrie,Reucastle,jreucastlefc@eepurl.com,jreucastlefc,Ondoy,63-(160)870-2513,Biostatistician IV,2763802451,Koelpin Group,no -Maible,Youll,myoullfd@soup.io,myoullfd,Alivéri,30-(400)482-8650,Computer Systems Analyst IV,2572163950,Abshire Inc,no -Donica,Hamments,dhammentsfe@xrea.com,dhammentsfe,Nida,370-(354)730-7273,Biostatistician II,5210866432,Windler Group,no -Miquela,Worman,mwormanff@shareasale.com,mwormanff,Foso,233-(566)694-7353,Dental Hygienist,3549652224,Reichel-Zieme,no -Uriah,Surcomb,usurcombfg@blogger.com,usurcombfg,La Soledad,52-(603)246-8965,Accounting Assistant III,1599078163,"Sipes, Nader and Keeling",no -Roddie,Esilmon,resilmonfh@ehow.com,resilmonfh,Palestina,57-(708)985-3162,Electrical Engineer,363630694,O'Hara LLC,no -Corinna,Geffen,cgeffenfi@usnews.com,cgeffenfi,Jastrowie,48-(332)296-1446,Budget/Accounting Analyst II,5462938233,Dickens and Sons,no -Gonzales,Muckloe,gmuckloefj@cornell.edu,gmuckloefj,Fatumuti,62-(643)699-2477,Geologist II,7192127329,Marquardt Group,no -Sharon,Chidwick,schidwickfk@unc.edu,schidwickfk,Foxton,64-(588)279-0414,Biostatistician IV,3192402563,Reynolds Inc,no -Vern,LeEstut,vleestutfl@abc.net.au,vleestutfl,Las Palmas,52-(921)609-1168,Senior Financial Analyst,5113676810,Marvin and Sons,no -Bob,Hof,bhoffm@theglobeandmail.com,bhoffm,Rama,505-(907)179-2015,Geological Engineer,120147319,"Willms, Kling and Hand",no -Nobie,Gerbl,ngerblfn@cbc.ca,ngerblfn,DaugavgrÄ«va,371-(698)955-4899,Desktop Support Technician,9420600106,Bahringer and Sons,no -Antons,Exrol,aexrolfo@china.com.cn,aexrolfo,Shizuishan,86-(784)172-4409,Help Desk Operator,8368132096,Ferry-Conn,no -Wye,Hayward,whaywardfp@phoca.cz,whaywardfp,Espinal,57-(245)501-5469,Accountant IV,2488307750,Hammes-Lubowitz,no -Son,Falconer-Taylor,sfalconertaylorfq@github.io,sfalconertaylorfq,‘AmrÄn,967-(212)195-4767,Product Engineer,6404706520,McDermott-Ledner,no -Pavlov,Reek,preekfr@typepad.com,preekfr,Thị Trấn Thất Khê,84-(333)510-3759,Desktop Support Technician,9682249406,"Ruecker, Lowe and Maggio",no -Amabel,Iannazzi,aiannazzifs@shop-pro.jp,aiannazzifs,Karangpari,62-(156)856-7713,Environmental Tech,594104572,Hodkiewicz Inc,no -Anselm,Pickworth,apickworthft@earthlink.net,apickworthft,Kukës,355-(384)728-4155,Professor,6571348071,Rowe Group,no -Hildegarde,Gleder,hglederfu@vistaprint.com,hglederfu,Pancheng,86-(840)213-5262,Geological Engineer,195510852,Carroll Inc,no -Benita,Marvell,bmarvellfv@tripadvisor.com,bmarvellfv,MalakwÄl,92-(801)819-9067,Account Coordinator,3394329107,Bernier and Sons,no -Willdon,Stanlack,wstanlackfw@ucoz.ru,wstanlackfw,Trảng Bàng,84-(463)410-2718,Project Manager,6933652521,"Labadie, Lehner and Gusikowski",no -Almira,Bartens,abartensfx@who.int,abartensfx,Lac-Brome,1-(112)440-7909,VP Accounting,1810818982,"Hilll, Hane and Langworth",no -Berenice,Broschke,bbroschkefy@about.me,bbroschkefy,Na Di,66-(281)869-0575,Senior Cost Accountant,2630118150,Schaden-Schaden,no -Melony,Rosenfelt,mrosenfeltfz@nih.gov,mrosenfeltfz,Huitang,86-(373)267-4793,Food Chemist,1356491731,"Langosh, Altenwerth and Johnson",no -Matias,Casaccia,mcasacciag0@t.co,mcasacciag0,Astghadzor,374-(558)741-2748,Librarian,6078806653,Gulgowski and Sons,no -Godfrey,Dorran,gdorrang1@gnu.org,gdorrang1,Longde Chengguanzhen,86-(621)856-4764,Programmer III,5779767149,"Bosco, Moen and Bosco",no -Ynes,Gonin,ygoning2@huffingtonpost.com,ygoning2,Jacksonville,1-(904)987-7870,Human Resources Assistant III,8997890921,Purdy Inc,no -Erasmus,Catterick,ecatterickg3@mozilla.com,ecatterickg3,Shanghu,86-(554)629-1624,Senior Financial Analyst,1930579853,Rohan-Romaguera,no -Alfred,Pickton,apicktong4@mapquest.com,apicktong4,SÅ­edinenie,359-(979)995-4685,Professor,814432573,Hayes-Windler,no -Stacia,Mordy,smordyg5@apple.com,smordyg5,Springfield,1-(217)733-3909,VP Sales,4240850924,Beier-Wisozk,no -Kimberli,Keymer,kkeymerg6@tuttocitta.it,kkeymerg6,BorÃ¥s,46-(203)476-9733,Paralegal,1853533335,"Fahey, Hudson and Jones",no -Vivienne,McGeoch,vmcgeochg7@chron.com,vmcgeochg7,Campraksanta,62-(302)790-3621,VP Product Management,2746698242,"Kassulke, Wilderman and Kassulke",no -Ezmeralda,Gutteridge,egutteridgeg8@moonfruit.com,egutteridgeg8,Motema,232-(245)252-3308,Chief Design Engineer,4899196601,Graham-Emard,no -Beitris,Andino,bandinog9@disqus.com,bandinog9,Brailiv,380-(777)219-2795,Senior Cost Accountant,9458256909,Mosciski Group,no -Maury,Bonick,mbonickga@comcast.net,mbonickga,Ubrub,62-(119)279-1772,Recruiting Manager,5647159451,"D'Amore, Nitzsche and Leffler",no -Verene,Leatherland,vleatherlandgb@miitbeian.gov.cn,vleatherlandgb,Jaroměřice nad Rokytnou,420-(156)711-3140,Internal Auditor,3971930999,"Pollich, Turner and Bergstrom",no -Hewett,Dent,hdentgc@tripod.com,hdentgc,Gaoyi,86-(921)190-0405,Administrative Assistant III,4114980911,Marks and Sons,no -Rozina,Seeds,rseedsgd@ox.ac.uk,rseedsgd,Nàng Mau,84-(207)987-9555,Administrative Officer,5612798245,"Nicolas, Schamberger and Stoltenberg",no -Emmalee,Billany,ebillanyge@elegantthemes.com,ebillanyge,Santo André,351-(578)791-9024,Account Executive,3066103560,Howe-Kuphal,no -Jeniffer,Salan,jsalangf@devhub.com,jsalangf,Sete Lagoas,55-(474)631-1789,Marketing Manager,1699081859,Schaden-Fadel,no -Welbie,Palmby,wpalmbygg@nymag.com,wpalmbygg,Payaman,62-(397)816-2725,Programmer Analyst II,9993174750,Price Group,no -Ossie,Machans,omachansgh@bravesites.com,omachansgh,Libas,63-(840)761-5683,Nuclear Power Engineer,1326272306,Sanford-Wolf,no -Cristobal,Binne,cbinnegi@jiathis.com,cbinnegi,Arcoverde,55-(521)304-2880,Desktop Support Technician,7531238543,McLaughlin-Stracke,no -Annissa,McLice,amclicegj@tinypic.com,amclicegj,Selce,389-(892)609-3070,Recruiting Manager,4860531817,"Swift, Koelpin and Mohr",no -Kalle,Betty,kbettygk@earthlink.net,kbettygk,Yasenskaya,7-(832)933-4153,Executive Secretary,7077128954,Hartmann-Rempel,no -Sinclair,Cattrall,scattrallgl@aol.com,scattrallgl,Ipuh,62-(543)627-2201,Quality Engineer,8290193610,Dicki-Prohaska,no -Laure,Meatcher,lmeatchergm@bizjournals.com,lmeatchergm,Tarsouat,212-(795)252-4345,Chief Design Engineer,5305354692,"Farrell, Morissette and Armstrong",no -Jacquelyn,Kidman,jkidmangn@youku.com,jkidmangn,Bwizibwera,256-(558)763-0287,Design Engineer,3633113959,Mann-Goyette,no -Marcos,Boulger,mboulgergo@nbcnews.com,mboulgergo,Kalinkavichy,375-(595)296-8756,Environmental Specialist,2167764766,"Parisian, Abernathy and Jakubowski",no -Conchita,Brunnstein,cbrunnsteingp@nsw.gov.au,cbrunnsteingp,Iturama,55-(102)637-6336,Help Desk Operator,1423412257,"Kovacek, Pfeffer and Carter",no -Moyra,Metcalfe,mmetcalfegq@facebook.com,mmetcalfegq,Huangmao,86-(656)348-7372,Professor,7600769646,"Sipes, Pollich and Maggio",no -Nikolia,Eastope,neastopegr@1688.com,neastopegr,Viçosa,55-(184)114-8152,Nuclear Power Engineer,4776960494,"Lemke, Mayer and Fisher",no -Gabey,Charteris,gcharterisgs@hugedomains.com,gcharterisgs,Sidi Yahya Ou Saad,212-(198)542-6078,VP Accounting,3560342406,Renner and Sons,no -Kirbie,Claxton,kclaxtongt@php.net,kclaxtongt,Gualán,502-(814)325-5193,Nurse,2826332090,Kiehn Group,no -Hinze,Snoddin,hsnoddingu@google.pl,hsnoddingu,Kastélli,30-(288)788-8171,Web Developer I,6651023586,"Padberg, Runolfsson and Torphy",no -Costa,Rix,crixgv@taobao.com,crixgv,Pueblo Nuevo,504-(962)252-2773,Assistant Professor,8691814632,Cartwright Group,no -Abdul,Doodney,adoodneygw@icq.com,adoodneygw,San Esteban,63-(872)929-4757,Senior Financial Analyst,5370482837,"Strosin, Runolfsson and Steuber",no -Martyn,Stanyland,mstanylandgx@moonfruit.com,mstanylandgx,Folques,351-(942)195-7146,Geological Engineer,9248462006,"Rice, Connelly and Murray",no -Erda,Shewring,eshewringgy@php.net,eshewringgy,Gostivar,389-(117)756-5444,Paralegal,1798073269,Schamberger-Luettgen,no -Kerry,Yeaman,kyeamangz@elpais.com,kyeamangz,Gongju,82-(786)382-2951,Marketing Assistant,4905126673,"Schulist, Fadel and Emard",no -Shirley,Wholesworth,swholesworthh0@opensource.org,swholesworthh0,Abuko,220-(724)675-0904,Software Consultant,321418409,Heller and Sons,no -Ennis,Abrahmovici,eabrahmovicih1@wiley.com,eabrahmovicih1,Bambas,51-(472)197-9673,Developer IV,8962080176,Considine-Gibson,no -Sharla,Sutherley,ssutherleyh2@behance.net,ssutherleyh2,Tabuadelo,351-(811)635-6156,VP Quality Control,6808874077,Runolfsdottir LLC,no -Gunther,Brandsen,gbrandsenh3@i2i.jp,gbrandsenh3,Buayan,63-(375)213-4457,Paralegal,5717407505,"Hane, Gutmann and Franecki",no -Kaylee,Enocksson,kenockssonh4@t-online.de,kenockssonh4,Qingshi,86-(192)784-2084,Environmental Specialist,2870295510,Hayes and Sons,no -Peyton,Dearnaly,pdearnalyh5@storify.com,pdearnalyh5,Mulyosari,62-(688)290-2297,Speech Pathologist,4872281845,"Champlin, Miller and Mueller",no -Angelle,Kindread,akindreadh6@sciencedaily.com,akindreadh6,GÄ«dolÄ“,251-(603)654-7849,Assistant Professor,831351950,Gorczany-Hayes,no -Leonerd,Lardez,llardezh7@deliciousdays.com,llardezh7,HerÄt,93-(513)328-2863,Tax Accountant,7061036621,"Renner, Schamberger and Ryan",no -Louise,Veltman,lveltmanh8@elegantthemes.com,lveltmanh8,Kluki,48-(852)795-6465,General Manager,2227283882,Nolan-Labadie,no -Daryle,Peacey,dpeaceyh9@answers.com,dpeaceyh9,Lenart v Slov. Goricah,386-(848)241-2826,Nuclear Power Engineer,6994450376,Weissnat Inc,no -Petey,Atyeo,patyeoha@yahoo.com,patyeoha,Osa,7-(360)966-8784,Systems Administrator I,1033908479,Bartoletti Group,no -Ely,Jessope,ejessopehb@issuu.com,ejessopehb,Sidoaji,62-(919)917-3443,Internal Auditor,7107889222,"Padberg, Kshlerin and Skiles",no -Susie,Wyleman,swylemanhc@feedburner.com,swylemanhc,ZajeÄí,420-(235)631-9169,Technical Writer,1491248297,Wisoky Inc,no -Shari,Byne,sbynehd@hao123.com,sbynehd,Portela,351-(260)826-4437,Quality Control Specialist,7458232994,"Hyatt, Ward and Collier",no -Rosanna,Chasles,rchasleshe@elegantthemes.com,rchasleshe,Bombon,63-(602)353-2130,Health Coach III,1370246951,Dibbert-Wuckert,no -Trista,Twydell,ttwydellhf@sfgate.com,ttwydellhf,Magdug,63-(267)213-3595,Technical Writer,3196286061,Goodwin-Walsh,no -Alisun,Harvatt,aharvatthg@miibeian.gov.cn,aharvatthg,Hongcao,86-(527)384-1497,Editor,1503083594,Herzog-Koch,no -Corenda,Keijser,ckeijserhh@163.com,ckeijserhh,Laguna,55-(197)776-6176,Professor,9644133080,Crooks-Bergstrom,no -Osbourn,Kilmurry,okilmurryhi@icq.com,okilmurryhi,Horní Suchá,420-(955)543-1671,Analog Circuit Design manager,179532030,Leffler-Towne,no -Laurel,Standell,lstandellhj@wired.com,lstandellhj,Lysyanka,380-(164)654-2407,Research Nurse,63563037,"Lehner, Carroll and Miller",no -Patrizius,Hayworth,phayworthhk@usgs.gov,phayworthhk,Khao Yoi,66-(417)147-7061,Research Associate,8831310038,"Lynch, Bergnaum and Breitenberg",no -Mellie,Begent,mbegenthl@time.com,mbegenthl,Longtan,86-(227)445-3953,Senior Developer,4637997372,Bernhard and Sons,no -Genny,Belderfield,gbelderfieldhm@imgur.com,gbelderfieldhm,Kotaagung,62-(726)439-6434,Geological Engineer,1472967216,Lueilwitz and Sons,no -Kurt,Laminman,klaminmanhn@blogspot.com,klaminmanhn,Vellinge,46-(774)244-0032,Physical Therapy Assistant,8906345453,Mertz and Sons,no -Sherye,Hoff,shoffho@dyndns.org,shoffho,Jiang’an,86-(860)511-6274,Technical Writer,7175131356,"Stroman, Hermann and Boyle",no -Lauren,Burroughes,lburrougheshp@tinyurl.com,lburrougheshp,Molchanovo,7-(213)371-4675,Senior Editor,4118331314,Lockman LLC,no -Arlin,Tatteshall,atatteshallhq@imdb.com,atatteshallhq,Nashville,1-(615)299-5360,Senior Quality Engineer,5243521639,"Stracke, Fadel and Emard",no -Simon,McGruar,smcgruarhr@soup.io,smcgruarhr,Koroyo,62-(952)895-9075,Analyst Programmer,4293044027,Pagac and Sons,no -Butch,Spieght,bspieghths@51.la,bspieghths,Alupay,63-(195)738-7403,Senior Cost Accountant,3544033011,"Jones, Hudson and Strosin",no -Ernestus,Wayte,ewayteht@pagesperso-orange.fr,ewayteht,Tmourghout,212-(185)849-2999,Community Outreach Specialist,4620300438,"Macejkovic, Luettgen and Hilll",no -Melania,Danis,mdanishu@shinystat.com,mdanishu,Tubigan,63-(155)678-5743,Cost Accountant,700955313,Smitham-Walter,no -Fanechka,Snookes,fsnookeshv@biglobe.ne.jp,fsnookeshv,Periyiáli,30-(948)845-5272,Engineer I,8597224908,"Zieme, Mante and Stokes",no -Tasha,Perche,tperchehw@zimbio.com,tperchehw,"Coruña, A",34-(281)688-2390,Senior Developer,2704074135,"Volkman, Conn and Orn",no -Devi,Jadczak,djadczakhx@statcounter.com,djadczakhx,Carrigtwohill,353-(169)296-6030,Data Coordiator,8972860239,Ferry Group,no -Giraldo,Klouz,gklouzhy@irs.gov,gklouzhy,GareÅ¡nica,385-(653)814-6119,Computer Systems Analyst IV,6329487731,Leannon-Becker,no -Kania,Camacke,kcamackehz@weebly.com,kcamackehz,Dingbao,86-(241)334-0377,Structural Engineer,2689561948,Witting-Vandervort,no -Arv,Neads,aneadsi0@so-net.ne.jp,aneadsi0,Ueno,81-(627)675-4358,Chemical Engineer,8213531027,Nitzsche Inc,no -Buck,Dumbar,bdumbari1@techcrunch.com,bdumbari1,Tawun,62-(229)591-2099,Internal Auditor,884547655,Mraz and Sons,no -Britte,O'Downe,bodownei2@amazon.com,bodownei2,HÄjÄ«ganj,880-(992)543-4663,Staff Accountant I,305047736,Hickle-Hand,no -Vinson,Guislin,vguislini3@cbc.ca,vguislini3,Cernik,385-(362)625-9868,Software Consultant,2429844117,Bosco LLC,no -Clem,Foulcher,cfoulcheri4@who.int,cfoulcheri4,Kuala Lumpur,60-(583)708-9044,Design Engineer,5142570715,"Marvin, Hirthe and Buckridge",no -Marijn,Doe,mdoei5@cmu.edu,mdoei5,Stetseva,380-(786)296-7914,Business Systems Development Analyst,2327586224,Borer Group,no -Franky,Harmar,fharmari6@eventbrite.com,fharmari6,Imperatriz,55-(402)985-2787,Research Nurse,2751212336,Hessel-Borer,no -Melanie,Lanston,mlanstoni7@lycos.com,mlanstoni7,Kebloran,62-(547)549-5683,Environmental Tech,6385884392,Weissnat-Torphy,no -Myrta,Clyburn,mclyburni8@networksolutions.com,mclyburni8,Rukunlima Bawah,62-(779)426-2396,Staff Scientist,5509025476,"McLaughlin, Osinski and Collier",no -Morley,Daunter,mdaunteri9@wordpress.com,mdaunteri9,Luebo,242-(881)386-7556,Community Outreach Specialist,5450221975,Erdman-Lang,no -Sergei,McLachlan,smclachlania@mit.edu,smclachlania,Lipka,48-(694)410-6816,Dental Hygienist,4778154002,Stracke Group,no -Cris,Madner,cmadnerib@t.co,cmadnerib,Cibitungmasjid,62-(256)723-0358,Account Representative IV,864545916,Altenwerth Group,no -Kare,Kusick,kkusickic@booking.com,kkusickic,Shaogongzhuang,86-(248)651-0611,Account Coordinator,9219229250,Koelpin Group,no -Pepi,Gibbie,pgibbieid@tripod.com,pgibbieid,Simeykyne,380-(307)793-5872,Operator,7579392461,"Kuvalis, Fadel and Grady",no -Edgar,Howieson,ehowiesonie@princeton.edu,ehowiesonie,San Rafael,52-(867)635-2442,Mechanical Systems Engineer,4239028027,Kiehn and Sons,no -Athena,Rackstraw,arackstrawif@sina.com.cn,arackstrawif,Baoquan,86-(211)673-0672,Engineer IV,9897657584,Vandervort Inc,no -Caryn,McLaughlin,cmclaughlinig@example.com,cmclaughlinig,Menara,62-(517)611-9675,Media Manager II,5101719706,Baumbach and Sons,no -Erminie,MacVaugh,emacvaughih@about.me,emacvaughih,Wuying,86-(982)324-4880,Sales Representative,7268375182,Jaskolski-Hodkiewicz,no -Christian,O'Corhane,cocorhaneii@uiuc.edu,cocorhaneii,Lianghe,86-(280)250-8522,Professor,4233331798,Doyle-Rogahn,no -Adeline,McPeice,amcpeiceij@vinaora.com,amcpeiceij,Kilifarevo,359-(467)886-3465,Assistant Professor,9912656946,Abernathy-Ritchie,no -Gibby,Cable,gcableik@nydailynews.com,gcableik,Belsh,355-(430)161-7493,Compensation Analyst,4006105606,Brown and Sons,no -Jaime,Kilpin,jkilpinil@ihg.com,jkilpinil,Agbor,234-(550)573-5938,Quality Control Specialist,9442626183,"Gutmann, Gutmann and Hahn",no -Melinda,Murkus,mmurkusim@telegraph.co.uk,mmurkusim,Pensacola,1-(850)183-4895,Media Manager II,1475423861,"Gutkowski, Bernhard and Hoppe",no -Aharon,Gierhard,agierhardin@jugem.jp,agierhardin,Donnybrook,353-(865)934-3131,Compensation Analyst,3658785446,"Boyle, Rogahn and Orn",no -Dulcia,Gilham,dgilhamio@slate.com,dgilhamio,Butere,254-(251)327-8937,Senior Editor,4146349249,"Sawayn, Paucek and Schroeder",no -Lyman,Juniper,ljuniperip@ft.com,ljuniperip,Hässleholm,46-(383)546-4101,Automation Specialist III,767169050,Yost Inc,no -Darbie,Fintoph,dfintophiq@lulu.com,dfintophiq,Hepu,86-(187)731-4385,Developer III,4370176271,"Schmeler, Cassin and Schoen",no -Rufus,Ollerhead,rollerheadir@berkeley.edu,rollerheadir,Ban Mai,66-(642)265-2874,Community Outreach Specialist,3832984623,"Dietrich, Rice and Reichel",no -Norby,Matthensen,nmatthensenis@google.com.au,nmatthensenis,Tranca,63-(890)438-0411,General Manager,5299514352,Morar Inc,no -Burch,Boys,bboysit@time.com,bboysit,Guomaying,86-(713)916-0654,Assistant Professor,3655303270,Zboncak-Padberg,no -Helge,Bexley,hbexleyiu@dedecms.com,hbexleyiu,Beiwenquan,86-(409)408-7860,Administrative Officer,8185761264,"Bergstrom, Wisozk and Runolfsson",no -Michelle,Sommerlin,msommerliniv@cdbaby.com,msommerliniv,JÄ«sh,972-(622)160-0841,Structural Engineer,7599030719,Durgan-Rodriguez,no -Isabelita,Wolstenholme,iwolstenholmeiw@stumbleupon.com,iwolstenholmeiw,Azinhal,351-(223)866-1661,Director of Sales,7664763312,"Tromp, Dibbert and Zieme",no -Krispin,O'Fergus,kofergusix@wikispaces.com,kofergusix,Banturkrajan,62-(483)542-1518,Physical Therapy Assistant,8741838637,"Strosin, Turner and Quigley",no -Meredeth,Budd,mbuddiy@biblegateway.com,mbuddiy,Svetlogorsk,7-(415)890-6602,Project Manager,9331158890,"Rippin, Bartoletti and Hermann",no -Babbette,Dunsleve,bdunsleveiz@thetimes.co.uk,bdunsleveiz,Maddela,63-(210)680-5698,Chemical Engineer,144827441,Wilderman Inc,no -Bar,Reiners,breinersj0@sun.com,breinersj0,Caldas Novas,55-(971)994-8220,Quality Control Specialist,3672681563,McClure-Moen,no -Dalt,McKellar,dmckellarj1@163.com,dmckellarj1,Thị Trấn Hà Trung,84-(749)471-2636,Senior Quality Engineer,4697508440,Kautzer and Sons,no -Ronalda,Gentric,rgentricj2@umich.edu,rgentricj2,Bahe,86-(924)710-4685,Office Assistant II,6446558430,Pouros and Sons,no -Gill,Matthessen,gmatthessenj3@shop-pro.jp,gmatthessenj3,Nangka,63-(915)902-5478,Web Developer I,3664671848,Blick-Terry,no -Hailey,Danet,hdanetj4@bbb.org,hdanetj4,Xuebu,86-(655)642-0475,Dental Hygienist,7854730350,Moore Group,no -Donalt,Lynnett,dlynnettj5@ucla.edu,dlynnettj5,Pingshan,86-(712)618-8254,Community Outreach Specialist,3037055472,"Gleichner, Heathcote and McKenzie",no -Oralla,Clausius,oclausiusj6@state.gov,oclausiusj6,Villa del Rosario,57-(671)722-4375,Director of Sales,5376445232,Kemmer Inc,no -Nance,Deware,ndewarej7@twitter.com,ndewarej7,Ibusuki,81-(617)941-4064,Software Engineer III,2028791357,"Yundt, Corwin and Quitzon",no -Aloysia,Friman,afrimanj8@edublogs.org,afrimanj8,Gambalidio,63-(313)382-0374,General Manager,8838662975,"Rau, Wolff and Kovacek",no -Elga,Starkey,estarkeyj9@salon.com,estarkeyj9,Gävle,46-(645)607-0229,Marketing Manager,7385675786,Hirthe-Thiel,no -Jeannine,Annett,jannettja@so-net.ne.jp,jannettja,Burayevo,7-(461)292-8366,Account Coordinator,7989157179,Rippin Group,no -Dulci,Sparkwell,dsparkwelljb@lulu.com,dsparkwelljb,Abóboda,351-(425)960-3179,Sales Associate,8042244673,"Will, Williamson and Bogisich",no -Gisela,Skoughman,gskoughmanjc@hao123.com,gskoughmanjc,Huashixia,86-(933)889-2829,Actuary,3828915523,Langosh LLC,no -Cosme,Pryer,cpryerjd@amazon.com,cpryerjd,Belle-Anse,509-(582)842-9287,Tax Accountant,6487901008,Upton and Sons,no -Thurstan,Pringle,tpringleje@indiegogo.com,tpringleje,Dongchen,86-(943)729-3038,Account Coordinator,5562817837,"Satterfield, Mraz and Reichel",no -Ebeneser,Longworthy,elongworthyjf@furl.net,elongworthyjf,Patquía,54-(789)253-7217,Registered Nurse,1170122795,Schulist-Braun,no -Johnette,Wildes,jwildesjg@answers.com,jwildesjg,Frederiksberg,45-(869)821-2650,Automation Specialist I,3643331975,"Effertz, Parisian and Bergnaum",no -Allan,Raecroft,araecroftjh@hud.gov,araecroftjh,Sapele,234-(501)388-9974,Senior Developer,7938194387,"Dicki, White and Harber",no -Appolonia,Aizikov,aaizikovji@marriott.com,aaizikovji,Toukh,20-(857)889-1761,Technical Writer,4754105559,Green LLC,no -Yevette,Songist,ysongistjj@google.it,ysongistjj,Dallas,1-(214)546-6784,Staff Accountant IV,401661873,Erdman Group,no -Tandy,Abeau,tabeaujk@aboutads.info,tabeaujk,Denver,1-(720)737-3543,Librarian,8505630270,Bashirian-Koepp,no -Marsh,Pencost,mpencostjl@constantcontact.com,mpencostjl,Buenaventura,57-(263)213-3776,Analyst Programmer,310343887,Hoeger LLC,no -Dan,Reignard,dreignardjm@squidoo.com,dreignardjm,Tanahedang,62-(883)790-2315,Financial Analyst,3648884352,"Parisian, Barrows and Osinski",no -Nerta,Ruffler,nrufflerjn@wikispaces.com,nrufflerjn,Jincheng,886-(342)705-8378,Internal Auditor,8567561698,"Wintheiser, VonRueden and Lowe",no -Zed,Bolsover,zbolsoverjo@omniture.com,zbolsoverjo,Matão,55-(339)205-0401,Recruiting Manager,4399340495,Crist-Will,no -Gonzalo,Paddington,gpaddingtonjp@phpbb.com,gpaddingtonjp,Montelíbano,57-(405)589-5804,Product Engineer,8554752384,"Lubowitz, Sanford and Lehner",no -Lucas,Elington,lelingtonjq@utexas.edu,lelingtonjq,Anding,86-(960)550-4751,Web Designer III,2740694938,Davis Group,no -Leoine,Lovett,llovettjr@multiply.com,llovettjr,Pittsburgh,1-(412)586-0854,Electrical Engineer,8541351939,Hoppe-Romaguera,no -Kara,McGeagh,kmcgeaghjs@cyberchimps.com,kmcgeaghjs,Kubang,62-(768)313-0005,Software Engineer IV,452302706,Huel-Zemlak,no -Jabez,Saylor,jsaylorjt@blogger.com,jsaylorjt,Luhans’ke,380-(559)819-5482,Food Chemist,7450835533,"Bergstrom, Stoltenberg and Satterfield",no -Debor,Vautre,dvautreju@lycos.com,dvautreju,Zhuangbu,86-(936)413-2474,Teacher,3855224137,"Emmerich, Muller and Yundt",no -Odille,Simonou,osimonoujv@blogtalkradio.com,osimonoujv,ÅÄ™ki Szlacheckie,48-(989)791-3951,Senior Cost Accountant,849963257,"Doyle, Abbott and Lind",no -Abraham,Chalfont,achalfontjw@stumbleupon.com,achalfontjw,Bang Kaeo,66-(342)372-0269,Pharmacist,7010972532,Powlowski Group,no -Willy,Ives,wivesjx@squarespace.com,wivesjx,Cimenga,62-(444)941-8150,Analyst Programmer,1782184376,"Spencer, Oberbrunner and Daniel",no -Reinald,Worley,rworleyjy@cnn.com,rworleyjy,Sumberpandan,62-(148)705-1796,Junior Executive,2076403906,Feeney-Cruickshank,no -Leone,Borris,lborrisjz@desdev.cn,lborrisjz,Tisco,51-(892)888-9517,Technical Writer,3611286087,Schumm-Christiansen,no -Jethro,Ede,jedek0@cbc.ca,jedek0,Auch,33-(947)670-3513,Senior Developer,5503574176,Senger Group,no -Netti,Hallford,nhallfordk1@bloomberg.com,nhallfordk1,FalÄvarjÄn,98-(501)345-1506,Engineer IV,8529835794,"Bruen, Funk and Thompson",no -Tiffi,Potapczuk,tpotapczukk2@xinhuanet.com,tpotapczukk2,Castanheira de Pêra,351-(462)484-6335,Tax Accountant,8392227476,Mertz-Runolfsdottir,no -Garald,Doddrell,gdoddrellk3@comcast.net,gdoddrellk3,Ketawang,62-(195)332-5729,Senior Financial Analyst,3328544089,Mohr-Jast,no -Haze,MacGillacolm,hmacgillacolmk4@123-reg.co.uk,hmacgillacolmk4,Langgen,62-(754)132-9274,Administrative Assistant IV,9044438050,"Brekke, Strosin and Berge",no -Emili,Coon,ecoonk5@youtube.com,ecoonk5,Nzérékoré,224-(495)776-5371,Information Systems Manager,1695217985,O'Conner Inc,no -Padriac,Alvy,palvyk6@ted.com,palvyk6,Biryulëvo Zapadnoye,7-(421)373-0762,Graphic Designer,3669986006,"Schiller, Willms and Feeney",no -Hamnet,Layman,hlaymank7@desdev.cn,hlaymank7,Ifon,234-(343)694-2565,Programmer Analyst II,6185480751,Hahn-Weber,no -Redd,Calvie,rcalviek8@google.cn,rcalviek8,Ambar,51-(774)884-7397,Senior Sales Associate,185044085,Bashirian Inc,no -Kerrie,Kenrick,kkenrickk9@samsung.com,kkenrickk9,Al ManÄqil,249-(655)472-4763,Software Consultant,9046610284,Wuckert-Farrell,no -Nara,Innwood,ninnwoodka@sbwire.com,ninnwoodka,Chadi,86-(547)693-2410,Budget/Accounting Analyst III,3718838753,"Kertzmann, Funk and Tremblay",no -Bernice,Deftie,bdeftiekb@rediff.com,bdeftiekb,Charleston,1-(304)936-1410,Senior Developer,9632166434,Ward-Senger,no -Cletus,Minor,cminorkc@mapy.cz,cminorkc,Suraż,48-(241)331-0004,Junior Executive,4560795215,Wilderman Inc,no -Clerkclaude,Knighton,cknightonkd@bigcartel.com,cknightonkd,Haoyi,86-(660)290-9882,Account Executive,2632094100,Barton-Bechtelar,no -Burtie,Bowen,bbowenke@booking.com,bbowenke,Avallon,33-(509)681-5577,Structural Engineer,588974048,"Hudson, Kautzer and Senger",no -Cris,Cody,ccodykf@miibeian.gov.cn,ccodykf,Saskatoon,1-(865)113-1166,Analyst Programmer,2698386916,MacGyver-Kessler,no -Wainwright,Aylen,waylenkg@microsoft.com,waylenkg,San Martín de los Andes,54-(334)143-7746,Electrical Engineer,9984965074,Nicolas-Kassulke,no -Clare,Calbreath,ccalbreathkh@independent.co.uk,ccalbreathkh,Pará de Minas,55-(147)835-8826,Research Nurse,6199708253,"Runte, Stanton and Nitzsche",no -Fenelia,Brewood,fbrewoodki@uiuc.edu,fbrewoodki,Agra,351-(684)979-6665,Data Coordiator,51836793,"Schmidt, O'Connell and Treutel",no -Hans,Chiese,hchiesekj@reference.com,hchiesekj,Kamieniec WrocÅ‚awski,48-(988)578-1699,Data Coordiator,4667259698,Gibson-Barrows,no -Tabina,Janauschek,tjanauschekkk@berkeley.edu,tjanauschekkk,Río Grande,1-(470)119-5478,Human Resources Assistant II,6083625874,Keebler Group,no -Bridgette,Nisuis,bnisuiskl@theatlantic.com,bnisuiskl,Baie-Saint-Paul,1-(702)724-5290,Geological Engineer,9705430446,Kihn Group,no -Syd,Leahair,sleahairkm@wiley.com,sleahairkm,Guarabira,55-(457)962-6675,Community Outreach Specialist,4844140248,Bernier and Sons,no -Gannie,Vennard,gvennardkn@macromedia.com,gvennardkn,Kayapa,63-(909)747-4808,Web Designer II,3698363402,"Schimmel, Friesen and Witting",no -Avictor,Ligertwood,aligertwoodko@bigcartel.com,aligertwoodko,Ustrzyki Dolne,48-(416)290-1849,Nurse Practicioner,4174955700,Ernser-Kihn,no -Heidi,Castelijn,hcastelijnkp@apache.org,hcastelijnkp,Ilihan,63-(500)806-5161,Administrative Officer,1259802647,"Osinski, Sanford and Ritchie",no -Ida,Barday,ibardaykq@spotify.com,ibardaykq,Tourcoing,33-(795)533-7343,Clinical Specialist,5206989193,"Jerde, Douglas and Rodriguez",no -Melodee,Valek,mvalekkr@samsung.com,mvalekkr,Shichuan,86-(456)232-9420,Financial Analyst,4251707303,Lakin-Dooley,no -Mellie,Blois,mbloisks@vinaora.com,mbloisks,Cagliari,39-(810)963-1749,Recruiting Manager,3769720873,"Strosin, Wuckert and Mann",no -Marabel,Chester,mchesterkt@delicious.com,mchesterkt,Si Bun Rueang,66-(161)732-1476,Geologist III,6339789307,Williamson-Witting,no -Gertrud,Keats,gkeatsku@tripadvisor.com,gkeatsku,Lam Plai Mat,66-(582)827-0005,Recruiter,297472623,Farrell and Sons,no -Orsola,Adriaens,oadriaenskv@hibu.com,oadriaenskv,Shaguotun,86-(820)144-0827,Paralegal,5854900106,"Ledner, Murazik and McGlynn",no -Kendricks,Livesley,klivesleykw@yelp.com,klivesleykw,Palilula,381-(321)472-2578,Junior Executive,7051295772,Robel and Sons,no -Randall,Denkel,rdenkelkx@about.me,rdenkelkx,SpÃ¥nga,46-(892)234-6007,Recruiter,8622374491,"McDermott, Cartwright and Beatty",no -Edythe,Boundey,eboundeyky@simplemachines.org,eboundeyky,Al Qaţīf,966-(760)484-2393,Editor,8633506122,Medhurst-Miller,no -Dillon,Piesing,dpiesingkz@bluehost.com,dpiesingkz,Qiucun,86-(543)608-4097,Account Coordinator,7448756443,"Kunze, Medhurst and Grant",no -Constancia,Eagland,ceaglandl0@photobucket.com,ceaglandl0,Staryy Dobrotvir,380-(868)809-9720,Desktop Support Technician,6850975951,Zulauf Group,no -Tressa,Bourne,tbournel1@odnoklassniki.ru,tbournel1,Aulnay-sous-Bois,33-(523)199-1041,Programmer IV,7844943165,Hamill Group,no -Iolande,Tunnicliffe,itunnicliffel2@artisteer.com,itunnicliffel2,Síkinos,30-(238)211-8025,Associate Professor,4285351382,"Mills, Romaguera and Anderson",no -Nalani,Bignall,nbignalll3@ftc.gov,nbignalll3,Itapipoca,55-(365)774-4231,GIS Technical Architect,7259801018,Dicki-Schroeder,no -Horatio,Vanner,hvannerl4@apple.com,hvannerl4,Laohugang,86-(312)980-2632,Marketing Assistant,784848610,VonRueden-Feil,no -Felice,Jenken,fjenkenl5@fda.gov,fjenkenl5,San Francisco de Coray,504-(252)407-9161,Software Test Engineer III,7301006063,Stanton Inc,no -Fax,Critchard,fcritchardl6@posterous.com,fcritchardl6,Feilaixia,86-(530)993-0259,Research Assistant I,1837505667,"Halvorson, Rodriguez and Murray",no -Eberto,Matzeitis,ematzeitisl7@tinypic.com,ematzeitisl7,Paris 08,33-(813)124-1255,Physical Therapy Assistant,3214003879,"Schimmel, Pouros and Yost",no -Gamaliel,Yakebowitch,gyakebowitchl8@dyndns.org,gyakebowitchl8,Zhendeqiao,86-(902)945-5865,Health Coach IV,3858926728,Morissette-Hand,no -Flor,Boarer,fboarerl9@i2i.jp,fboarerl9,Laventure,230-(655)375-4043,Quality Control Specialist,3619658951,Casper Group,no -Deeanne,Burghill,dburghillla@smugmug.com,dburghillla,Safotu,685-(601)390-5584,Software Engineer I,3672399876,"Conroy, Kerluke and Steuber",no -Brant,Greenstead,bgreensteadlb@toplist.cz,bgreensteadlb,Adelaide,61-(892)926-0595,Systems Administrator IV,6507194689,Stanton Group,no -Edita,Stanes,estaneslc@ning.com,estaneslc,Asikkala,358-(104)994-3852,Compensation Analyst,6256493079,Schaefer-Mayer,no -Audie,Suche,asucheld@ed.gov,asucheld,Xinjian,86-(928)968-5707,Community Outreach Specialist,7207127251,Heaney Group,no -Phelia,de Amaya,pdeamayale@ovh.net,pdeamayale,Comagascas,63-(548)192-2681,Tax Accountant,6804167333,Moen-Medhurst,no -Brodie,Capelow,bcapelowlf@google.es,bcapelowlf,San Miguel,52-(505)227-1532,Help Desk Operator,321116399,"Block, Hirthe and Lemke",no -Darelle,Hickin,dhickinlg@addtoany.com,dhickinlg,Tonghu,86-(429)741-1635,Recruiting Manager,9703631940,"Boyer, Ullrich and Hirthe",no -Gabriell,Priestley,gpriestleylh@topsy.com,gpriestleylh,Szeged,36-(890)962-0345,Compensation Analyst,9074958451,Weber-Bins,no -Ainsley,Batchelar,abatchelarli@adobe.com,abatchelarli,Plast,7-(909)894-8943,Staff Accountant III,3774125082,"Parker, Bechtelar and Mosciski",no -Carole,Hammett,chammettlj@deliciousdays.com,chammettlj,Shchukino,7-(234)811-0491,VP Marketing,7633646497,"Beer, Veum and Lowe",no -Alfie,Duffett,aduffettlk@cam.ac.uk,aduffettlk,Krajan Dua Putukrejo,62-(959)919-5850,Project Manager,2080467263,Durgan-Fritsch,no -Arley,Gillman,agillmanll@cdbaby.com,agillmanll,Karangori,62-(707)970-1378,Financial Advisor,893767778,Stamm Group,no -Khalil,Goodboddy,kgoodboddylm@zimbio.com,kgoodboddylm,Dijon,33-(369)847-3482,Geological Engineer,6558979381,Goyette and Sons,no -Halimeda,Gowman,hgowmanln@cam.ac.uk,hgowmanln,Krasni Okny,380-(937)249-4520,Senior Financial Analyst,1911763253,Howe Inc,no -Ardis,Benardet,abenardetlo@nytimes.com,abenardetlo,Concepción del Uruguay,54-(567)429-0565,Account Representative IV,4637416302,"Feil, Bartoletti and Adams",no -Ivory,Lawlie,ilawlielp@thetimes.co.uk,ilawlielp,Schroeder,55-(878)697-6010,Senior Cost Accountant,9431774984,"King, Crona and Von",no -Barb,Derisley,bderisleylq@mashable.com,bderisleylq,Tangyu,86-(708)194-0125,Marketing Assistant,6826292726,Deckow-Lemke,no -Malissia,Sharer,msharerlr@java.com,msharerlr,Jinghai,86-(252)422-3886,Human Resources Manager,8382713882,Grimes-Hansen,no -Jessamine,Lichfield,jlichfieldls@businesswire.com,jlichfieldls,Micoud,1-(934)806-6515,VP Product Management,6093340430,Kozey Inc,no -Rogerio,Tindall,rtindalllt@biblegateway.com,rtindalllt,Vistino,7-(527)696-9343,Actuary,2542803676,Ritchie-Marquardt,no -Averyl,Garratt,agarrattlu@instagram.com,agarrattlu,Lamovita,387-(200)844-4864,Senior Quality Engineer,1885568886,Ernser-Hartmann,no -Becki,Edelmann,bedelmannlv@gmpg.org,bedelmannlv,Mawlamyine,95-(718)995-2413,Actuary,2746351579,Schamberger LLC,no -Micky,Smitherman,msmithermanlw@chicagotribune.com,msmithermanlw,Belén,598-(243)420-8445,Civil Engineer,2781994006,"Ward, Harris and Nader",no -Barbabra,Cork,bcorklx@nytimes.com,bcorklx,Tanzhesi,86-(229)632-7950,Research Associate,8772167009,Hane Group,no -Erastus,Cauldfield,ecauldfieldly@friendfeed.com,ecauldfieldly,Nglojo,62-(962)330-7466,Senior Developer,5790337376,"Leuschke, Jaskolski and Jaskolski",no -Hube,Rowaszkiewicz,hrowaszkiewiczlz@tinypic.com,hrowaszkiewiczlz,Ventanas,593-(568)247-0181,Software Engineer IV,6793591614,Littel-Deckow,no -Justis,Ickov,jickovm0@tripod.com,jickovm0,Issy-les-Moulineaux,33-(574)130-0058,Software Test Engineer II,2573983161,Hintz Inc,no -Wilie,Mawditt,wmawdittm1@wordpress.org,wmawdittm1,Zlatar,385-(148)463-0786,Mechanical Systems Engineer,1825804036,Kerluke-O'Connell,no -Henriette,Early,hearlym2@cisco.com,hearlym2,Salcedo,63-(859)802-4167,Quality Engineer,9157866023,Rowe and Sons,no -Kaycee,Nattriss,knattrissm3@ning.com,knattrissm3,Pindangan Centro,63-(749)241-7828,Chemical Engineer,1628687517,Rutherford-Kihn,no -Hart,Petran,hpetranm4@cnbc.com,hpetranm4,Pampas,51-(195)539-5625,Information Systems Manager,1793147906,"McKenzie, Weimann and Crona",no -Holt,Kernock,hkernockm5@hhs.gov,hkernockm5,Dasiping,86-(729)481-1150,Recruiting Manager,3841216129,Lindgren Inc,no -Friedrick,Yurchenko,fyurchenkom6@cyberchimps.com,fyurchenkom6,Uppsala,46-(881)710-7992,Quality Control Specialist,8282139564,"Durgan, Simonis and Hilll",no -Maybelle,Tapenden,mtapendenm7@cafepress.com,mtapendenm7,Guadalupe,52-(698)116-6571,Professor,4954283094,Goodwin Inc,no -Francesco,Hyatt,fhyattm8@blog.com,fhyattm8,Vänersborg,46-(590)336-4962,Systems Administrator IV,752649760,Dach LLC,no -Marlene,Shevlan,mshevlanm9@independent.co.uk,mshevlanm9,København,45-(461)205-7636,Tax Accountant,5683419905,Gleichner-Schoen,no -Ignatius,Seals,isealsma@4shared.com,isealsma,Chimboy Shahri,998-(740)447-9648,Analog Circuit Design manager,331255189,Kovacek-Bednar,no -Jasmina,Huggins,jhugginsmb@harvard.edu,jhugginsmb,Kulotino,7-(439)872-4789,Programmer Analyst IV,5646721199,"Koss, Bode and Halvorson",no -Arther,Bisseker,abissekermc@redcross.org,abissekermc,Ad Dimnah,967-(145)760-6372,Web Designer IV,238046222,"Fay, Reynolds and Schultz",no -Brita,Cleynman,bcleynmanmd@scribd.com,bcleynmanmd,Joliet,1-(815)856-8278,Mechanical Systems Engineer,2016930195,"Hickle, Hagenes and Bode",no -Danita,Aubry,daubryme@fema.gov,daubryme,Somanda,255-(761)470-0112,Food Chemist,2405625261,Kuphal-Littel,no -Hilton,Kyrkeman,hkyrkemanmf@sphinn.com,hkyrkemanmf,Linxia Chengguanzhen,86-(933)120-9575,Chief Design Engineer,9953149593,Sanford-Kiehn,no -Blondie,Heinrich,bheinrichmg@harvard.edu,bheinrichmg,Ban Lam Luk Ka,66-(919)563-5992,Nurse Practicioner,8391355985,Farrell-Stroman,no -Nathalia,Bettleson,nbettlesonmh@lulu.com,nbettlesonmh,Weston,44-(284)766-3674,Assistant Media Planner,7451462927,Schumm Inc,no -Nap,Croxford,ncroxfordmi@mapy.cz,ncroxfordmi,Liugou,86-(814)399-2809,Mechanical Systems Engineer,1028596766,"Vandervort, Howe and Schulist",no -Matthieu,Pitney,mpitneymj@sourceforge.net,mpitneymj,Lulindi,255-(965)744-8651,Senior Sales Associate,9954867694,Stehr-Stehr,no -Bernadina,Batiste,bbatistemk@theglobeandmail.com,bbatistemk,Jinping,86-(805)406-0826,Marketing Assistant,681175303,"Kemmer, Bednar and Schmidt",no -Jo,Vasin,jvasinml@yandex.ru,jvasinml,SuchaÅ„,48-(719)533-4494,Social Worker,672565900,McKenzie Group,no -Isabeau,Josovich,ijosovichmm@shutterfly.com,ijosovichmm,Detroit,1-(313)728-3071,Quality Engineer,9522586072,Stark Group,no -Hyacinth,Streatley,hstreatleymn@va.gov,hstreatleymn,Hanam,82-(962)234-8098,Assistant Professor,4446717154,Littel-Wisozk,no -Margarita,Roller,mrollermo@php.net,mrollermo,Itapevi,55-(849)570-9451,Teacher,4015506529,Kuhic Inc,no -Gottfried,Mawdsley,gmawdsleymp@hatena.ne.jp,gmawdsleymp,Duraznopampa,51-(850)770-5042,Teacher,4615844786,Sauer Inc,no -Colline,Tanner,ctannermq@squarespace.com,ctannermq,Osilnica,386-(375)123-4522,Accounting Assistant IV,4307546075,"Heaney, Prohaska and Treutel",no -Toiboid,Slyman,tslymanmr@geocities.jp,tslymanmr,Niort,33-(246)445-0387,Safety Technician II,876852541,Marquardt Group,no -Franky,Willden,fwilldenms@mlb.com,fwilldenms,Nanyaojie,86-(772)392-4262,Web Developer III,4615626932,"Botsford, Mante and Lind",no -Cory,Sexti,csextimt@ft.com,csextimt,Smiths Falls,1-(856)759-7718,Senior Developer,8931416504,"Herman, Buckridge and Wehner",no -Nevsa,Paoloni,npaolonimu@ox.ac.uk,npaolonimu,Songjiang,86-(861)892-4735,Registered Nurse,9011403533,Mosciski-Dietrich,no -Rosabelle,Harkess,rharkessmv@hexun.com,rharkessmv,Tambo,51-(320)850-5323,Geological Engineer,3284443002,Howe LLC,no -Bibbye,Maleham,bmalehammw@ifeng.com,bmalehammw,Dakoro,227-(563)968-9242,Programmer Analyst IV,1459020774,"Kuhlman, Bogan and Kshlerin",no -Marcela,Houlston,mhoulstonmx@ebay.co.uk,mhoulstonmx,Ngedhusuba,62-(645)344-7228,Social Worker,8322158963,"Trantow, Kassulke and Lebsack",no -Allen,Trembley,atrembleymy@goodreads.com,atrembleymy,MÄnÄnwÄla,92-(715)605-9507,Design Engineer,5813315675,"Miller, Yost and Stamm",no -Wallas,Stansfield,wstansfieldmz@networkadvertising.org,wstansfieldmz,Xiongguan,86-(361)169-6974,Structural Engineer,8350979879,Hills-Waters,no -Alasdair,Konerding,akonerdingn0@msu.edu,akonerdingn0,Rennes,33-(354)767-4981,Web Designer III,6590238693,Nitzsche LLC,no -Kendell,Garvan,kgarvann1@quantcast.com,kgarvann1,Yong’an,86-(763)605-1170,Senior Developer,8673840058,Harris-Smith,no -Sayre,Orthmann,sorthmannn2@linkedin.com,sorthmannn2,Yabuli,86-(956)184-5074,Sales Associate,3238931334,Kozey-Kulas,no -Holt,Francillo,hfrancillon3@sciencedirect.com,hfrancillon3,Jiangcheng,86-(517)345-2568,Software Consultant,8031543178,Runolfsdottir Group,no -Gusella,Kinig,gkinign4@xing.com,gkinign4,Fastiv,380-(645)966-9040,Account Representative II,511641788,MacGyver-Treutel,no -Otis,Reinhard,oreinhardn5@answers.com,oreinhardn5,Qiancang,86-(285)544-2263,Environmental Specialist,3357295903,Jerde Group,no -Carlin,MacAne,cmacanen6@samsung.com,cmacanen6,Yahotyn,380-(892)306-8669,Desktop Support Technician,5175081594,Hackett LLC,no -Berny,Ferri,bferrin7@prnewswire.com,bferrin7,Jimenez,63-(103)580-5845,Junior Executive,4749556203,Kuhn-Ullrich,no -Warren,Fulks,wfulksn8@wufoo.com,wfulksn8,Jose Maria Morelos,52-(886)281-8268,Statistician IV,434789003,"West, Kuphal and Hills",no -Barnett,Bane,bbanen9@arizona.edu,bbanen9,Namerikawa,81-(412)158-7515,Administrative Assistant III,1843810425,Wiegand LLC,no -Arin,Trimming,atrimmingna@constantcontact.com,atrimmingna,NÄrÄyanganj,880-(506)140-6037,Mechanical Systems Engineer,6579691301,"Trantow, Franecki and Huel",no -Vinnie,Bruce,vbrucenb@economist.com,vbrucenb,Los Frentones,54-(143)653-9690,Occupational Therapist,7266214238,"Collins, Parker and Volkman",no -Leandra,Jerdon,ljerdonnc@barnesandnoble.com,ljerdonnc,Mqabba,356-(828)530-5167,Cost Accountant,2421177081,Schimmel and Sons,no -Maurise,McRuvie,mmcruviend@cisco.com,mmcruviend,Takeo,855-(719)155-4461,VP Marketing,1603639632,Cassin-Gislason,no -Galen,Clutterham,gclutterhamne@reddit.com,gclutterhamne,Tantu,86-(856)743-6023,Developer IV,6406520327,"Pagac, Abbott and Renner",no -Orsa,Faulkener,ofaulkenernf@51.la,ofaulkenernf,Guoyuan,86-(353)293-0614,Nurse,2168428441,Schamberger-Moore,no -Chas,Gaylard,cgaylardng@msn.com,cgaylardng,Ambat,62-(316)304-0070,Speech Pathologist,230204961,Rodriguez Inc,no -Erek,Kilius,ekiliusnh@mapy.cz,ekiliusnh,Karakol,996-(757)549-4010,Account Representative III,8914898016,Champlin-Parker,no -Torrence,Dallow,tdallowni@livejournal.com,tdallowni,Rumat Heib,972-(551)251-4133,Payment Adjustment Coordinator,7839003737,Macejkovic LLC,no -Son,Nowakowski,snowakowskinj@home.pl,snowakowskinj,Pasirpengarayan,62-(403)683-3142,Media Manager IV,9394032258,"Weissnat, Bergnaum and Paucek",no -Osbert,Nuscha,onuschank@bluehost.com,onuschank,San Antonio de Los Altos,58-(278)228-1044,Statistician II,9263802645,Wolff-Hudson,no -Kaspar,Smullin,ksmullinnl@ebay.co.uk,ksmullinnl,HihyÄ,20-(858)200-1173,Operator,1352488523,Boyer and Sons,no -Temp,Palk,tpalknm@irs.gov,tpalknm,Llorente,63-(559)417-0912,Safety Technician II,5716467989,Rutherford-Dare,no -Julie,Poad,jpoadnn@webeden.co.uk,jpoadnn,Lajeosa,351-(229)448-4862,Office Assistant IV,3869073314,Jones-Pollich,no -Blanche,Schonfeld,bschonfeldno@umn.edu,bschonfeldno,Fresno,1-(209)844-2209,Desktop Support Technician,4348050724,Hand LLC,no -Tremaine,Wicks,twicksnp@ted.com,twicksnp,Tiglauigan,63-(592)251-6057,Executive Secretary,9357962085,Hessel LLC,no -Jessamyn,Mauro,jmauronq@ucsd.edu,jmauronq,Shuangpu,86-(782)336-1634,Senior Developer,2073896022,"Tillman, Schowalter and Kertzmann",no -Morris,Pedro,mpedronr@wunderground.com,mpedronr,Fort Worth,1-(682)403-1808,Human Resources Assistant I,2418447138,Wiza-Lynch,no -Rossie,Lindblom,rlindblomns@flavors.me,rlindblomns,Espera Feliz,55-(354)448-5176,Senior Developer,9846657803,"D'Amore, Morar and Lubowitz",no -Kelcie,Woolner,kwoolnernt@histats.com,kwoolnernt,Sainyabuli,856-(703)619-9373,Senior Cost Accountant,9679866378,Farrell-Windler,no -Brittaney,O' Bee,bobeenu@linkedin.com,bobeenu,Mombaça,55-(107)230-7103,Information Systems Manager,4366497247,Stroman-Blick,no -Kassie,Questier,kquestiernv@imageshack.us,kquestiernv,Bielsk Podlaski,48-(127)626-9904,Web Developer I,7973903593,O'Keefe-Altenwerth,no -Wilhelmina,Baldacco,wbaldacconw@yahoo.co.jp,wbaldacconw,Dasha,86-(814)517-9700,Actuary,898566878,"Grant, Pollich and O'Keefe",no -Griz,Jagels,gjagelsnx@alexa.com,gjagelsnx,Druzhny,375-(873)367-3588,Cost Accountant,5688512105,Hoeger Inc,no -Nikolaos,Abrahamowitcz,nabrahamowitczny@google.ca,nabrahamowitczny,WawrzeÅ„czyce,48-(788)215-2200,Community Outreach Specialist,3376050767,Champlin and Sons,no -Cesare,Kelf,ckelfnz@friendfeed.com,ckelfnz,Lucaya,1-(362)126-9927,Business Systems Development Analyst,4434280716,Turner-Conn,no -Olympia,McCaughey,omccaugheyo0@toplist.cz,omccaugheyo0,Soio,244-(652)536-5008,Senior Editor,2665247380,Swaniawski-Kihn,no -Magdalen,Wadhams,mwadhamso1@patch.com,mwadhamso1,Köping,46-(202)987-2026,Automation Specialist II,7922502885,Fahey LLC,no -Laughton,Charrette,lcharretteo2@plala.or.jp,lcharretteo2,El Paso,1-(915)296-9008,Software Engineer II,3832140409,Ferry-Hettinger,no -Antonin,McGaughey,amcgaugheyo3@technorati.com,amcgaugheyo3,Guanyao,86-(385)294-7834,Graphic Designer,4413617177,"Stoltenberg, Anderson and Schroeder",no -Douglas,Peploe,dpeploeo4@paypal.com,dpeploeo4,Bala Murghab,93-(552)750-0409,Compensation Analyst,6584465276,Will and Sons,no -Saloma,Halcro,shalcroo5@biglobe.ne.jp,shalcroo5,Yongjiu,86-(663)473-4515,Assistant Media Planner,5412294472,Upton and Sons,no -Genia,Dockree,gdockreeo6@ed.gov,gdockreeo6,K’ulashi,995-(693)621-4145,Sales Representative,6311406926,Murazik-Gerlach,no -Ulysses,Vigne,uvigneo7@wisc.edu,uvigneo7,Bosobolo,242-(869)770-6551,Payment Adjustment Coordinator,6121045191,"Kuphal, Smith and Becker",no -Amaleta,Hellcat,ahellcato8@com.com,ahellcato8,Paamiut,299-(827)658-4181,VP Marketing,9930317341,Sanford Group,no -Marys,Stobo,mstoboo9@addthis.com,mstoboo9,Fraga,351-(646)681-5809,Programmer Analyst IV,8215542689,"Conroy, Luettgen and Haag",no -Carmelia,Cantillion,ccantillionoa@prweb.com,ccantillionoa,Sumberpucung,62-(715)732-9346,Structural Analysis Engineer,5743542775,Marquardt LLC,no -Jenifer,Ondrus,jondrusob@vinaora.com,jondrusob,Banos,63-(887)517-5901,Accounting Assistant III,1196707006,"Bruen, Paucek and Fisher",no -Gillian,Espadas,gespadasoc@microsoft.com,gespadasoc,Brejoeira,351-(160)844-3330,Research Associate,1027647391,Weissnat-Blick,no -Vonni,Corley,vcorleyod@feedburner.com,vcorleyod,Melaka,60-(193)177-1978,Civil Engineer,5905339317,Lubowitz-Morar,no -Phillip,Ivanchin,pivanchinoe@addthis.com,pivanchinoe,Sasaguri,81-(145)417-6376,Account Coordinator,3636969412,Turcotte-Wisozk,no -Klarrisa,Letessier,kletessierof@wordpress.com,kletessierof,GostyÅ„,48-(126)737-5592,VP Quality Control,1411646746,Ankunding-Pfannerstill,no -Gasparo,Agron,gagronog@etsy.com,gagronog,Nangka,63-(410)186-8167,Senior Sales Associate,5644040968,Rippin-Goyette,no -Joni,Burnard,jburnardoh@timesonline.co.uk,jburnardoh,IlÄm,977-(207)827-4512,Analyst Programmer,5890418432,Krajcik Inc,no -Kim,Kytter,kkytteroi@weibo.com,kkytteroi,Mojokerto,62-(279)874-2608,Environmental Specialist,1343560179,"Schulist, Weber and Paucek",no -Lora,Elms,lelmsoj@narod.ru,lelmsoj,Cầu Gồ,84-(800)294-5849,VP Marketing,1493946587,Schamberger Group,no -Arlena,Paullin,apaullinok@reddit.com,apaullinok,Örebro,46-(532)455-1043,Senior Editor,4085398636,Grant and Sons,no -Richard,Darcy,rdarcyol@seesaa.net,rdarcyol,Batarasa,63-(298)338-2430,Community Outreach Specialist,7860234254,Bahringer LLC,no -Felizio,Padginton,fpadgintonom@gmpg.org,fpadgintonom,Nanzhen,86-(584)560-0295,Research Assistant III,3766611003,Spencer Group,no -Antonina,Kilpin,akilpinon@alexa.com,akilpinon,Schieren,352-(152)223-8179,Chemical Engineer,8146664725,"Labadie, Goyette and Jacobson",no -Erika,Geator,egeatoroo@bloglovin.com,egeatoroo,Retorta,351-(404)826-3743,Assistant Professor,4828740651,Lakin Group,no -Ali,Straffon,astraffonop@hexun.com,astraffonop,Babiak,48-(388)809-9155,Actuary,5262099386,Reichel-Hand,no -Amabel,Board,aboardoq@shareasale.com,aboardoq,Diancun,86-(214)304-0961,Information Systems Manager,6355389466,Upton-Jaskolski,no -Gabie,Bradwell,gbradwellor@yandex.ru,gbradwellor,GobÅ,81-(496)451-0957,Analog Circuit Design manager,8680375713,Nienow-Hermiston,no -Claretta,Finlaison,cfinlaisonos@trellian.com,cfinlaisonos,Chatturat,66-(920)250-8360,Database Administrator III,4852140383,Wiza-Predovic,no -Kippie,Panting,kpantingot@geocities.jp,kpantingot,Tojeira,351-(648)874-8790,Recruiter,7921358802,"Kunze, Jaskolski and Stoltenberg",no -Linell,Croysdale,lcroysdaleou@buzzfeed.com,lcroysdaleou,Orós,55-(435)669-9483,Help Desk Technician,1212044088,Ledner-Johnston,no -Vasilis,Goublier,vgoublierov@altervista.org,vgoublierov,Caujul,51-(101)425-0809,Structural Analysis Engineer,4372624972,Bayer-Heller,no -Brocky,Gianiello,bgianielloow@flickr.com,bgianielloow,Huoxian,86-(427)267-9428,Developer IV,6023283195,Witting-Howe,no -Roland,Blowes,rblowesox@apache.org,rblowesox,Padej,381-(688)750-4717,Business Systems Development Analyst,7378441384,Bashirian-Wiegand,no -Garv,Peerman,gpeermanoy@a8.net,gpeermanoy,Yolöten,993-(132)948-7743,Cost Accountant,8711924012,Quigley Group,no -Damaris,Amberson,dambersonoz@noaa.gov,dambersonoz,Uchaly,7-(891)257-7839,Social Worker,871438763,Cummerata and Sons,no -Reba,Laudham,rlaudhamp0@uiuc.edu,rlaudhamp0,Mendefera,291-(385)541-4183,Cost Accountant,9459415148,Gleichner LLC,no -Dill,Teligin,dteliginp1@telegraph.co.uk,dteliginp1,Garango,226-(818)796-5422,Tax Accountant,2544502487,Abbott-Thiel,no -Greta,Lewsam,glewsamp2@psu.edu,glewsamp2,Rostov-na-Donu,7-(207)756-1434,Staff Accountant I,3029138410,Schmitt-Bogisich,no -Norah,Jeremaes,njeremaesp3@netvibes.com,njeremaesp3,Xiakouyi,86-(488)113-1826,Office Assistant III,6252335258,Rempel-Abshire,no -Datha,Lenahan,dlenahanp4@unicef.org,dlenahanp4,Kuzhu,86-(851)353-6927,Sales Representative,1680930990,Lubowitz and Sons,no -Sophey,Phlipon,sphliponp5@behance.net,sphliponp5,Jiaoziya,86-(315)182-8625,Account Coordinator,5389921747,Effertz Group,no -Alix,Schirach,aschirachp6@lulu.com,aschirachp6,San Marcos,503-(169)421-0329,Electrical Engineer,2023606950,"Herzog, Funk and Gottlieb",no -Mignon,Paddington,mpaddingtonp7@sogou.com,mpaddingtonp7,Panikian,63-(636)557-0276,Legal Assistant,2892389844,Cormier-Hudson,no -Darn,Elfleet,delfleetp8@blogtalkradio.com,delfleetp8,Dubravica,387-(878)576-7012,Mechanical Systems Engineer,2839634538,Olson-Bogan,no -Monique,Harmon,mharmonp9@admin.ch,mharmonp9,Roissy Charles-de-Gaulle,33-(545)532-7958,Associate Professor,9092943010,Hickle LLC,no -Tull,Infante,tinfantepa@tinyurl.com,tinfantepa,Xiaoruo,86-(387)599-5542,Marketing Assistant,9863380032,"Sanford, Bradtke and Farrell",no -Nyssa,Madoc-Jones,nmadocjonespb@epa.gov,nmadocjonespb,Puerto Quellón,56-(955)181-7227,Assistant Manager,1890626058,Littel-Toy,no -Florence,Solomon,fsolomonpc@bbc.co.uk,fsolomonpc,Blois,33-(767)315-1932,VP Quality Control,3079951077,Eichmann Inc,no -Vanya,Schermick,vschermickpd@berkeley.edu,vschermickpd,Kunvald,420-(153)357-8683,Staff Accountant I,920565719,Quigley-Bergstrom,no -Danita,Juanico,djuanicope@ca.gov,djuanicope,Cluny,230-(619)594-7589,Human Resources Manager,3146677113,"Kassulke, Ruecker and Hane",no -Shelagh,Shelley,sshelleypf@marriott.com,sshelleypf,Sabaneta,58-(196)295-4803,Paralegal,7166846496,"Koelpin, Cremin and Langosh",no -Kerk,Dufer,kduferpg@google.com.au,kduferpg,Rukem,62-(346)971-3292,Executive Secretary,2386201910,"Simonis, Johnson and Fay",no -Ab,Davidde,adaviddeph@prnewswire.com,adaviddeph,Daxing,86-(500)810-4696,Community Outreach Specialist,5805532379,Boyer and Sons,no -Billye,Betteson,bbettesonpi@redcross.org,bbettesonpi,Longos,351-(101)580-5428,Physical Therapy Assistant,1479316938,Frami-Lesch,no -Harley,Brewer,hbrewerpj@upenn.edu,hbrewerpj,Dadoupu,86-(480)441-7751,Community Outreach Specialist,39146294,"Corkery, Schaefer and Marks",no -Wynnie,Solano,wsolanopk@zdnet.com,wsolanopk,Laidian,86-(253)399-5610,Assistant Professor,2137078449,"McClure, Satterfield and Barton",no -Darn,Haffard,dhaffardpl@ask.com,dhaffardpl,Horton,44-(564)751-3439,Financial Advisor,5938631068,Pollich-Hermann,no -Howey,Stallan,hstallanpm@uol.com.br,hstallanpm,Anhai,86-(567)926-3005,VP Sales,3450645838,"Lockman, Crona and Abernathy",no -King,Cicculini,kcicculinipn@devhub.com,kcicculinipn,Bolekhiv,380-(723)375-7883,VP Sales,1728709016,"Murazik, Yundt and Hane",no -Kaela,Crispe,kcrispepo@fc2.com,kcrispepo,Sundsvall,46-(597)539-5689,Actuary,3188646194,Gleichner-Lindgren,no -Stearne,Wardingley,swardingleypp@about.me,swardingleypp,Shirgjan,355-(356)611-0499,Librarian,2611445389,Schmidt LLC,no -Devan,Swait,dswaitpq@flavors.me,dswaitpq,Yilan,86-(426)167-0401,Nuclear Power Engineer,436813416,"Prosacco, Muller and Gleason",no -Arline,Cunradi,acunradipr@samsung.com,acunradipr,San Luis,63-(588)882-9972,Speech Pathologist,6075640940,Beatty-Watsica,no -Cordi,Barkway,cbarkwayps@g.co,cbarkwayps,Gävle,46-(676)140-3109,Budget/Accounting Analyst I,6375200660,Balistreri-Collier,no -Carma,Radcliffe,cradcliffept@mit.edu,cradcliffept,Mineralni Bani,359-(527)871-2652,Programmer Analyst II,8264713580,Quigley Inc,no -Camila,Capehorn,ccapehornpu@sfgate.com,ccapehornpu,Oguma,234-(989)667-0635,Statistician I,7343647008,Gutkowski-Ullrich,no -Gerri,Molen,gmolenpv@bloglines.com,gmolenpv,Zhoukou,86-(924)447-1128,Professor,195842693,"Howe, Bogisich and Stroman",no -Maurizio,Cacacie,mcacaciepw@mit.edu,mcacaciepw,Ostrogozhsk,7-(519)339-7578,Data Coordiator,9811795614,"Johnson, Schinner and Pagac",no -Jerrine,Sancias,jsanciaspx@cpanel.net,jsanciaspx,Hayan Hudong,86-(285)373-2471,Graphic Designer,3440547981,Littel-Feeney,no -Willie,Worlock,wworlockpy@omniture.com,wworlockpy,Campina Grande,55-(796)613-7498,Staff Scientist,8176496995,"Walsh, Jakubowski and Ernser",no -Page,Salvadori,psalvadoripz@netscape.com,psalvadoripz,Kota Kinabalu,60-(507)527-2407,Account Coordinator,9922888610,"Kertzmann, Funk and Jenkins",no -Alvie,Howsan,ahowsanq0@omniture.com,ahowsanq0,Parion,63-(234)698-7388,Operator,8071753939,Thompson-Keebler,no -Emera,Stout,estoutq1@craigslist.org,estoutq1,Dahu,86-(284)198-0386,Quality Control Specialist,7804586396,"Muller, Murazik and Zieme",no -Lois,Gladdifh,lgladdifhq2@instagram.com,lgladdifhq2,Lleida,34-(715)382-4713,Database Administrator II,3633654518,Schumm LLC,no -Doug,Lamerton,dlamertonq3@si.edu,dlamertonq3,Villa Alemana,56-(149)630-1570,Research Nurse,2253131156,Hilpert Group,no -Theda,Ebbing,tebbingq4@craigslist.org,tebbingq4,Alung,62-(738)502-4534,Analyst Programmer,4591819175,"Farrell, Murazik and Mayert",no -Kassandra,Swalwel,kswalwelq5@amazon.co.jp,kswalwelq5,Sacapulas,502-(884)290-1622,Senior Editor,7185464110,Gibson-Leffler,no -Livvy,Burroughes,lburroughesq6@amazonaws.com,lburroughesq6,Bailianhe,86-(458)619-0316,Librarian,1535422378,O'Hara Inc,no -Temp,Wardington,twardingtonq7@craigslist.org,twardingtonq7,Ngozi,257-(217)180-8803,Junior Executive,848046633,"Waelchi, Schamberger and Huels",no -Rhetta,Petrishchev,rpetrishchevq8@irs.gov,rpetrishchevq8,Tsinandali,995-(430)243-4289,Statistician I,7791803836,Pacocha LLC,no -Anastassia,Edgar,aedgarq9@cmu.edu,aedgarq9,Labuhansumbawa,62-(644)719-6905,General Manager,1184092605,"Cartwright, Bergstrom and Monahan",no -Giana,Giacobazzi,ggiacobazziqa@jimdo.com,ggiacobazziqa,Tambac,63-(579)835-6229,Quality Control Specialist,6363216184,"Kuhic, Monahan and McGlynn",no -Teriann,Dafydd,tdafyddqb@vistaprint.com,tdafyddqb,Babakanloa,62-(482)920-2072,Human Resources Assistant III,5821778638,"Ritchie, Herman and Lindgren",no -Willa,Tacker,wtackerqc@bigcartel.com,wtackerqc,Ḩurayḑah,967-(806)911-9319,Health Coach II,9135157542,Kunze-Bahringer,no -Lucila,Sabater,lsabaterqd@apache.org,lsabaterqd,Humen,86-(614)109-2198,Software Engineer I,5293367323,Dach Group,no -Dael,Ondrak,dondrakqe@stumbleupon.com,dondrakqe,Marxog,86-(260)249-8251,Health Coach II,4372560729,Daugherty-Weber,no -Wait,St Pierre,wstpierreqf@wordpress.com,wstpierreqf,Biru,86-(860)639-5320,Programmer Analyst III,8047175459,Gaylord-Ebert,no -Rasla,Godlonton,rgodlontonqg@altervista.org,rgodlontonqg,Balgatay,976-(232)485-2320,Programmer Analyst III,3673385615,Veum-Braun,no -Kristine,Heersema,kheersemaqh@ft.com,kheersemaqh,Choibalsan,976-(538)269-3674,Structural Analysis Engineer,8626952910,Hilpert LLC,no -Sly,Causnett,scausnettqi@mozilla.com,scausnettqi,Krajan Gebangan,62-(773)388-2291,Occupational Therapist,7422892277,"Tromp, Ziemann and Grant",no -Cecilius,Sully,csullyqj@mysql.com,csullyqj,Meukek,62-(638)624-8698,Quality Control Specialist,6934177193,Doyle Inc,no -Adelaida,Stickford,astickfordqk@washingtonpost.com,astickfordqk,Khoa,856-(931)334-2132,Account Coordinator,7872285312,"Dicki, Bailey and Ratke",no -Rochelle,Reynish,rreynishql@indiatimes.com,rreynishql,FÄryÄb,98-(957)306-8416,Media Manager IV,1676544003,Lang Inc,no -Odie,Ianniello,oiannielloqm@independent.co.uk,oiannielloqm,La Cumbre,54-(979)726-7040,Account Representative III,7068196918,"Bernhard, Rau and Marvin",no -Haskel,Ladloe,hladloeqn@timesonline.co.uk,hladloeqn,George Town,1-(687)711-7923,Quality Control Specialist,5936963217,"Ryan, Parisian and Swaniawski",no -Olympie,Vassall,ovassallqo@samsung.com,ovassallqo,Vannes,33-(222)237-4683,Media Manager II,8854580236,Koepp-Boehm,no -Noll,Beech,nbeechqp@plala.or.jp,nbeechqp,Bloemfontein,27-(804)795-7592,Food Chemist,4394656052,Purdy and Sons,no -Konstanze,Thon,kthonqq@google.ru,kthonqq,TÄ«rÄn,98-(846)964-8726,Payment Adjustment Coordinator,1904564429,Simonis and Sons,no -Leonid,Kerford,lkerfordqr@un.org,lkerfordqr,Dzüünbulag,976-(101)272-5436,Administrative Officer,2082237710,"Swaniawski, Steuber and Runte",no -Reeva,Dyka,rdykaqs@geocities.com,rdykaqs,Mstów,48-(190)592-9280,Environmental Specialist,9030963425,Rippin-Waters,no -Galvin,Lumbley,glumbleyqt@reuters.com,glumbleyqt,Feodosiya,380-(562)472-4251,Chemical Engineer,83583572,"Hilpert, Satterfield and Walker",no -Chet,Spinozzi,cspinozziqu@marriott.com,cspinozziqu,Det Udom,66-(457)173-1407,Data Coordiator,7957694462,Bashirian LLC,no -Ryann,Wimlett,rwimlettqv@free.fr,rwimlettqv,Paniówki,48-(203)574-3233,Actuary,6548690974,Nienow and Sons,no -Craggie,McCotter,cmccotterqw@feedburner.com,cmccotterqw,Portela,351-(482)524-3475,Pharmacist,4298812022,Abernathy-Kuhic,no -Ulberto,Heifer,uheiferqx@yandex.ru,uheiferqx,Omaha,1-(402)346-5913,Software Engineer I,187278784,Yundt LLC,no -Billie,Franchi,bfranchiqy@etsy.com,bfranchiqy,Yinping,86-(788)959-3511,Web Designer I,4035720887,Nolan LLC,no -Norbie,Goundsy,ngoundsyqz@fda.gov,ngoundsyqz,São José dos Campos,55-(914)752-8310,Senior Editor,9192798357,Feeney LLC,no -Barde,Emlen,bemlenr0@infoseek.co.jp,bemlenr0,Pétange,352-(762)627-9841,Software Consultant,2432326261,"Rice, Eichmann and Will",no -Shelagh,Zemler,szemlerr1@mayoclinic.com,szemlerr1,Filiátes,30-(866)635-4243,Geological Engineer,3958946542,"Hansen, Bernier and Goldner",no -Isidro,Ciani,icianir2@yahoo.co.jp,icianir2,Serednye,380-(288)355-3486,Tax Accountant,1506003982,Torp-Bogisich,no -Vivia,Lapworth,vlapworthr3@joomla.org,vlapworthr3,Malino,63-(291)269-3418,Environmental Specialist,157700771,Jast-Kuhlman,no -Roshelle,Churn,rchurnr4@berkeley.edu,rchurnr4,Lingqiao,86-(997)692-6158,Geological Engineer,2603479822,Marquardt and Sons,no -Bentley,Vanshin,bvanshinr5@ehow.com,bvanshinr5,Stockholm,46-(653)768-6050,Information Systems Manager,7297467227,"King, Tillman and Nicolas",no -Lois,Mynett,lmynettr6@reuters.com,lmynettr6,Nantes,33-(554)638-9803,Speech Pathologist,1603340408,Veum LLC,no -Charity,Krzyzowski,ckrzyzowskir7@archive.org,ckrzyzowskir7,Masebewa,62-(958)826-5880,Staff Scientist,4975412993,"Brakus, Gusikowski and Padberg",no -Clemente,Pottage,cpottager8@census.gov,cpottager8,Tenggun Dajah,62-(506)558-9919,Associate Professor,6455213033,"Purdy, Vandervort and Douglas",no -Kurt,Cowap,kcowapr9@jugem.jp,kcowapr9,Kutao,86-(358)991-3144,Nurse,616313527,White-Goyette,no -Chrissy,Finey,cfineyra@irs.gov,cfineyra,Ningzhong,86-(138)301-0438,Professor,9107665083,Davis and Sons,no -Clarinda,Hallgate,challgaterb@linkedin.com,challgaterb,Zinder,227-(332)911-9324,Product Engineer,7179109576,"Lesch, Balistreri and Morissette",no -Carmelle,Chatelain,cchatelainrc@woothemes.com,cchatelainrc,Monte Branco,351-(468)889-1359,Tax Accountant,6018619938,Adams-Harris,no -Erskine,Bonin,eboninrd@redcross.org,eboninrd,Sabang,63-(956)745-4358,Actuary,1793329427,"Farrell, White and Bednar",no -Linnet,Castelot,lcastelotre@1688.com,lcastelotre,Jintun,86-(448)985-9715,Senior Sales Associate,6843609481,Pagac and Sons,no -Aurthur,Thomke,athomkerf@whitehouse.gov,athomkerf,GÅ‚uchów,48-(339)416-4557,Engineer I,7088789183,"Roob, Runolfsdottir and Leffler",no -Donni,Salomon,dsalomonrg@japanpost.jp,dsalomonrg,Forninho,351-(549)953-6906,Account Executive,4908221146,"Wisozk, Greenholt and Hamill",no -Skip,Edensor,sedensorrh@alexa.com,sedensorrh,Victoria,503-(808)383-0356,Project Manager,126141614,Mayer-Mante,no -Albrecht,Ellam,aellamri@businessweek.com,aellamri,La Roche-sur-Yon,33-(857)717-9828,Actuary,4743554497,Parisian-Ankunding,no -Loutitia,Simson,lsimsonrj@plala.or.jp,lsimsonrj,Tamontaka,63-(423)893-7361,Assistant Media Planner,6240594598,Schamberger LLC,no -Ariel,Cockarill,acockarillrk@amazonaws.com,acockarillrk,Santa Quitéria,55-(584)271-7334,VP Sales,9593299637,"Lang, Bednar and Mayer",no -Nonie,Paulton,npaultonrl@cbslocal.com,npaultonrl,Kladno,420-(149)126-3944,Senior Developer,7724518181,"Bruen, Murray and Kunde",no -Yasmin,Rilston,yrilstonrm@gizmodo.com,yrilstonrm,Petoa,504-(697)889-4705,Environmental Specialist,9003612854,McKenzie and Sons,no -Izaak,Gowthrop,igowthroprn@webmd.com,igowthroprn,Ancasti,54-(334)952-0828,Account Executive,9924782984,Cartwright LLC,yes -Jelene,Badman,jbadmanro@shinystat.com,jbadmanro,Asunción,595-(802)109-9845,Electrical Engineer,3501920857,"Cremin, Heaney and Morissette",no -Staffard,Clausius,sclausiusrp@yellowbook.com,sclausiusrp,Sampao,63-(733)481-2381,Junior Executive,6139413206,Herzog-Beer,no -Lilia,Dalliwatr,ldalliwatrrq@deviantart.com,ldalliwatrrq,Hengshan,86-(225)596-7008,Operator,2989513345,Mante-Funk,no -Karlotta,Altamirano,kaltamiranorr@vistaprint.com,kaltamiranorr,Ajax,1-(705)315-7495,Legal Assistant,1030691320,Carroll Group,no \ No newline at end of file diff --git a/sample_csvs/accessories-sample.csv b/sample_csvs/accessories-sample.csv new file mode 100644 index 0000000000..6c203ec5a0 --- /dev/null +++ b/sample_csvs/accessories-sample.csv @@ -0,0 +1,51 @@ +Company,Name,Category,Supplier,Manufacturer,QTY,Location,Order Number,Min Amount,Model Number,Notes,Purchase Date,Purchase Cost +Kovacek and Sons,Dragline,Roofing (Metal),Runte-Corwin,Cremin Inc,61,Artashat,017019279-2,59,3558344370409403,sit amet diam in magna bibendum imperdiet nullam orci pede venenatis non sodales sed tincidunt eu felis,2022-08-24,73.96 +"Russel, Marquardt and Frami",Skid-Steer,Drywall & Acoustical (MOB),Bins-Gerhold,Breitenberg Inc,17,Dugcal,801930572-6,10,30104302249895,diam cras pellentesque volutpat dui maecenas tristique est et tempus semper est quam pharetra magna ac,2022-06-24,5.27 +Nicolas Group,Dragline,Structural & Misc Steel Erection,"Glover, Kovacek and Bechtelar",Kulas-Powlowski,36,ZafarwÄl,963844173-9,30,3577672351043826,vulputate ut ultrices vel augue vestibulum ante ipsum primis in,2023-03-16,87.89 +Roob-Mante,Dump Truck,Electrical and Fire Alarm,"Rath, Mueller and Halvorson","Bogisich, Schaefer and Goldner",14,Askiz,778878918-6,58,3578492711349816,donec ut mauris eget massa tempor convallis nulla neque libero convallis eget eleifend luctus,2023-03-30,57.51 +"Marquardt, Blanda and Heaney",Crawler,RF Shielding,Bode-Armstrong,Rosenbaum LLC,54,Arjona,414053159-2,82,201552275235023,augue luctus tincidunt nulla mollis molestie lorem quisque ut erat curabitur gravida nisi at nibh,2022-06-27,76.18 +Herman and Sons,Skid-Steer,"Doors, Frames & Hardware","Volkman, Pagac and Tillman",McKenzie-Hand,12,ViÅ¡njevac,064912598-3,29,3530080559523225,dolor morbi vel lectus in quam fringilla rhoncus mauris enim leo rhoncus sed vestibulum sit amet cursus id turpis,2022-08-14,97.9 +Morar Inc,Crawler,Exterior Signage,Bergstrom-Ullrich,"Mann, Ondricka and Fadel",69,Birni N Konni,770515508-7,11,3529943453252733,sapien in sapien iaculis congue vivamus metus arcu adipiscing molestie,2022-10-28,73.62 +Jerde-Bogisich,Compactor,Drilled Shafts,Fadel-Sporer,"Bode, Gutkowski and Schamberger",58,Galižana,188679996-2,95,4041376614376,gravida nisi at nibh in hac habitasse platea dictumst aliquam augue quam sollicitudin,2023-03-09,3.83 +"Dibbert, Roberts and Orn",Dragline,EIFS,Waters LLC,"Pollich, Treutel and Homenick",93,Tongyangdao,303831002-6,53,5602237646576545,donec ut mauris eget massa tempor convallis nulla neque libero convallis eget eleifend luctus,2022-12-07,67.91 +Schmidt-Jones,Compactor,Site Furnishings,"Monahan, Dooley and Rowe",Gorczany and Sons,5,Chunghwa,054966680-X,8,3548920584271979,enim sit amet nunc viverra dapibus nulla suscipit ligula in lacus curabitur at,2022-08-23,91.79 +"Lemke, Nicolas and Jaskolski",Trencher,Drilled Shafts,Nienow-Moen,Kuhn-Sawayn,40,Mkushi,921741957-5,94,3566485930049161,arcu libero rutrum ac lobortis vel dapibus at diam nam tristique,2022-09-16,37.6 +Hills-Sipes,Compactor,Roofing (Asphalt),"Kulas, Bogisich and Mante","Grimes, Ziemann and Jacobs",99,Orahovica,794640267-8,69,490344354231467621,eu nibh quisque id justo sit amet sapien dignissim vestibulum,2023-02-11,91.86 +Block and Sons,Bulldozer,Prefabricated Aluminum Metal Canopies,Jakubowski LLC,Feest-King,32,Al Jubayhah,459697074-2,27,5010129157541263,nec sem duis aliquam convallis nunc proin at turpis a pede posuere nonummy integer non velit donec diam,2022-11-17,82.9 +Denesik-Champlin,Compactor,Retaining Wall and Brick Pavers,"Barrows, Bradtke and Kertzmann","Ledner, Hodkiewicz and McLaughlin",28,Talalayivka,301762319-X,81,4575609485591,nullam orci pede venenatis non sodales sed tincidunt eu felis fusce posuere felis sed lacus morbi sem,2023-04-20,78.21 +Towne Inc,Trencher,Rebar & Wire Mesh Install,Runolfsdottir-Klein,"Hilll, Herman and Roberts",76,Cunén,739224317-9,56,3529871501469720,luctus rutrum nulla tellus in sagittis dui vel nisl duis ac nibh fusce lacus,2022-08-31,87.49 +Cummerata LLC,Dragline,Fire Sprinkler System,O'Kon-Conn,"Schmeler, Reichel and Jakubowski",11,Boju,674114839-6,61,3580499860326855,lacus curabitur at ipsum ac tellus semper interdum mauris ullamcorper purus sit amet nulla quisque arcu libero rutrum ac,2022-10-15,56.48 +Brekke LLC,Backhoe,"Temp Fencing, Decorative Fencing and Gates",Fisher LLC,Graham-Kshlerin,83,Lolak,496879420-7,51,56022279121238808,scelerisque quam turpis adipiscing lorem vitae mattis nibh ligula nec sem,2023-02-22,97.21 +"King, Homenick and Conn",Dump Truck,EIFS,"Reilly, Windler and Cremin",Langworth-Ortiz,28,Bugo,546264257-1,48,4905157085342998398,cubilia curae duis faucibus accumsan odio curabitur convallis duis consequat dui nec nisi volutpat eleifend,2023-02-02,54.04 +"Koelpin, Dooley and Kuvalis",Excavator,Structural & Misc Steel Erection,Shields-Senger,McLaughlin-Koepp,48,Livadiya,470334065-8,69,0604372523721069615,mattis pulvinar nulla pede ullamcorper augue a suscipit nulla elit ac nulla,2023-04-08,67.44 +Donnelly and Sons,Crawler,Plumbing & Medical Gas,Maggio LLC,Hahn-Flatley,17,Alibago,314425716-8,83,337941169979548,enim leo rhoncus sed vestibulum sit amet cursus id turpis integer,2023-04-07,53.56 +Boyer-Medhurst,Grader,Prefabricated Aluminum Metal Canopies,"Howell, Huel and Nicolas","Deckow, Jenkins and Bartell",54,Picungbera,323060315-X,15,6706951103338750,felis fusce posuere felis sed lacus morbi sem mauris laoreet ut rhoncus aliquet pulvinar sed nisl nunc rhoncus dui vel,2022-09-19,5.46 +"Wolff, Dietrich and Kshlerin",Dragline,Masonry & Precast,"Spencer, Dooley and Ullrich","Zieme, Kuvalis and Leannon",13,Santisimo Rosario,946650016-3,65,5018417463180740,erat nulla tempus vivamus in felis eu sapien cursus vestibulum proin eu mi nulla ac enim,2023-04-17,63.98 +"Hansen, McLaughlin and Bernhard",Excavator,Drilled Shafts,"Gislason, Heaney and O'Connell",Wiegand Group,84,San Jose,445247479-9,5,201910973168492,velit nec nisi vulputate nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget,2022-11-01,58.75 +Daniel LLC,Crawler,Rebar & Wire Mesh Install,"Mitchell, Barrows and Hamill",Weber-Kemmer,88,Sinjil,145272905-0,95,3537378655700793,gravida nisi at nibh in hac habitasse platea dictumst aliquam augue,2023-04-04,33.02 +"Casper, Muller and Macejkovic",Dragline,Marlite Panels (FED),"Spencer, Shields and McDermott",Rutherford and Sons,21,Pocsi,758023521-8,93,3563461502339028,pede justo lacinia eget tincidunt eget tempus vel pede morbi porttitor lorem id,2022-12-26,63.67 +Beahan-Beatty,Backhoe,Framing (Steel),Johns-Reinger,Stamm-Bergstrom,4,Buarcos,483986544-2,14,3572461996231391,vehicula condimentum curabitur in libero ut massa volutpat convallis morbi odio odio elementum eu interdum eu tincidunt in,2023-05-13,12.03 +"Schowalter, Green and Ankunding",Trencher,Soft Flooring and Base,Kreiger-Hoppe,Reynolds-Cummerata,18,San Pedro Pinula,131062548-4,15,3568617180162173,elit ac nulla sed vel enim sit amet nunc viverra dapibus nulla suscipit ligula in lacus curabitur,2022-09-27,77.12 +"Prohaska, Bosco and Abshire",Bulldozer,EIFS,Larson LLC,"Hammes, Fadel and Legros",57,Golopau,194463835-0,3,5002359023338541,vivamus vestibulum sagittis sapien cum sociis natoque penatibus et magnis dis,2022-06-30,67.31 +"Bauch, Rodriguez and Jenkins",Crawler,Masonry & Precast,"Rosenbaum, Stokes and Rau","Ferry, Huels and Sipes",86,Xiangyang,454529838-9,29,6767656690937643,est et tempus semper est quam pharetra magna ac consequat metus sapien ut nunc vestibulum ante ipsum,2022-08-22,17.81 +"Schmeler, Lockman and Stiedemann",Excavator,Framing (Steel),Kihn LLC,"Lubowitz, Roberts and Marquardt",13,Makanya,647395719-7,15,3556460198629428,quam suspendisse potenti nullam porttitor lacus at turpis donec posuere metus,2023-04-29,7.58 +Lubowitz Group,Scraper,Ornamental Railings,"Fisher, Haag and Schmidt",West LLC,10,Mabunga,787506589-5,64,3560289848556143,nam congue risus semper porta volutpat quam pede lobortis ligula sit amet eleifend pede libero quis,2022-08-01,71.58 +Hansen-Beatty,Excavator,Epoxy Flooring,Hauck-Rempel,West Inc,48,Payxambabazar,518250572-8,82,337941555646784,vivamus vel nulla eget eros elementum pellentesque quisque porta volutpat erat quisque,2022-08-19,1.48 +Herzog and Sons,Dragline,Elevator,Crist Inc,"Dooley, Wyman and Rempel",14,Kranuan,490046086-9,22,3556364929607085,arcu libero rutrum ac lobortis vel dapibus at diam nam tristique tortor eu,2023-02-04,45.0 +Lebsack-Romaguera,Dragline,Wall Protection,Hamill-Abbott,Haley-Labadie,6,Ruzayevka,731594232-7,63,3564668857002550,nunc purus phasellus in felis donec semper sapien a libero nam dui proin leo odio porttitor id consequat in,2023-03-08,13.62 +"Kirlin, Wyman and Wehner",Crawler,Structural & Misc Steel Erection,Beier LLC,Schuppe Inc,25,Shuanghe,343694285-5,67,3568605663689317,duis aliquam convallis nunc proin at turpis a pede posuere nonummy integer non velit donec,2023-03-10,34.31 +"Nader, Fadel and Bode",Bulldozer,RF Shielding,Williamson and Sons,Schmidt-Towne,68,Mölndal,607118553-X,3,340880868925813,donec ut dolor morbi vel lectus in quam fringilla rhoncus mauris,2022-06-09,28.76 +"Rohan, Wisoky and Gerhold",Dump Truck,Electrical and Fire Alarm,"Ward, Sauer and Harber",Shields-Zieme,17,Vsevolozhsk,100246862-0,39,5578993697112313,lacinia aenean sit amet justo morbi ut odio cras mi pede malesuada in imperdiet et commodo vulputate,2022-12-28,34.43 +"Goldner, Metz and Prohaska",Crawler,Roofing (Asphalt),Schamberger Inc,Reilly-Bernhard,32,Saraktash,815422723-1,98,3568940927447214,volutpat in congue etiam justo etiam pretium iaculis justo in hac habitasse platea dictumst etiam faucibus,2023-02-28,82.16 +Thompson and Sons,Dump Truck,Plumbing & Medical Gas,Russel and Sons,"Nolan, Cartwright and Ebert",31,Lívingston,617917766-X,19,3564430382302611,habitasse platea dictumst morbi vestibulum velit id pretium iaculis diam erat fermentum justo nec condimentum neque sapien placerat,2023-04-23,41.52 +"Nitzsche, Schaden and Strosin",Crawler,Electrical and Fire Alarm,Hahn and Sons,"Lehner, Hayes and Grant",22,Gennevilliers,860027339-0,13,5641827092783026,libero rutrum ac lobortis vel dapibus at diam nam tristique tortor eu,2023-05-08,19.4 +Larkin LLC,Dragline,Roofing (Metal),Fay-Erdman,Nolan and Sons,20,San Agustin,906705311-2,82,3531330383605949,felis ut at dolor quis odio consequat varius integer ac leo pellentesque ultrices mattis odio donec vitae,2022-11-30,58.55 +Feest Group,Compactor,Ornamental Railings,"Rowe, Bins and Torp",Romaguera-Greenholt,27,Ribeira Seca,842784499-9,88,3580033745274830,nisi nam ultrices libero non mattis pulvinar nulla pede ullamcorper augue a suscipit nulla elit,2023-01-22,54.88 +Osinski Inc,Bulldozer,Epoxy Flooring,"Abernathy, Huels and Greenfelder","Jaskolski, Sporer and Corwin",54,Mae Sai,627022315-6,71,5594694799398897,ipsum dolor sit amet consectetuer adipiscing elit proin interdum mauris non ligula pellentesque ultrices phasellus id sapien in sapien iaculis,2022-09-30,44.05 +Bauch Inc,Backhoe,Prefabricated Aluminum Metal Canopies,Howell-Rogahn,Hessel-Nienow,50,Mayisad,619216443-6,90,3557488995743232,in purus eu magna vulputate luctus cum sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus mus vivamus vestibulum,2023-02-23,37.19 +Welch Inc,Skid-Steer,Casework,"Grant, Fadel and Macejkovic","Towne, Johnston and Rice",1,Huarong,533477095-X,93,345254385886483,placerat ante nulla justo aliquam quis turpis eget elit sodales scelerisque,2022-12-27,18.46 +"Boehm, Windler and Konopelski",Compactor,"Doors, Frames & Hardware",Hansen-Turcotte,Beier-Hermiston,94,Buyunshan,149301071-9,50,5380142384134598,eleifend pede libero quis orci nullam molestie nibh in lectus pellentesque at nulla suspendisse,2023-04-14,62.31 +"McGlynn, Zulauf and Hauck",Trencher,Retaining Wall and Brick Pavers,Beahan-Bernhard,Gutmann LLC,44,Nanjie,647091867-0,71,5641826426694164,dapibus augue vel accumsan tellus nisi eu orci mauris lacinia sapien quis libero nullam sit amet turpis,2022-07-25,31.94 +Howe-Powlowski,Skid-Steer,Drilled Shafts,Toy Group,Medhurst LLC,81,Saramech,156653384-8,64,3538154733411268,ut dolor morbi vel lectus in quam fringilla rhoncus mauris enim leo rhoncus sed vestibulum sit amet,2022-07-31,16.01 +"Dach, Trantow and Larson",Excavator,Structural and Misc Steel (Fabrication),"Hagenes, Stamm and Wisoky",Bosco-Anderson,9,Patos,471131697-3,92,4017957124324,curabitur convallis duis consequat dui nec nisi volutpat eleifend donec ut dolor,2023-02-16,46.16 +"Weber, McCullough and Champlin",Scraper,Casework,"D'Amore, Lebsack and Stoltenberg","Herman, Beahan and Willms",65,Chaloem Phra Kiat,703706719-9,82,56022380102896588,turpis enim blandit mi in porttitor pede justo eu massa donec dapibus duis at velit,2022-06-12,48.58 diff --git a/sample_csvs/MOCK_ASSETS_BAD.csv b/sample_csvs/assets-sample-BAD.csv similarity index 100% rename from sample_csvs/MOCK_ASSETS_BAD.csv rename to sample_csvs/assets-sample-BAD.csv diff --git a/sample_csvs/MOCK_ASSETS_BLANKS.csv b/sample_csvs/assets-sample-BLANKS.csv similarity index 100% rename from sample_csvs/MOCK_ASSETS_BLANKS.csv rename to sample_csvs/assets-sample-BLANKS.csv diff --git a/sample_csvs/assets-sample.csv b/sample_csvs/assets-sample.csv new file mode 100644 index 0000000000..c17cac3bc8 --- /dev/null +++ b/sample_csvs/assets-sample.csv @@ -0,0 +1,151 @@ +Company,Name,Asset Tag,Category,Supplier,Manufacturer,Location,Order Number,Model,Model Notes,Model Number,Asset Notes,Purchase Date,Purchase Cost,Checkout Type,Checked Out To: Username,Checked Out To: First Name,Checked Out To: Last Name,Checked Out To: Email,Checked Out To: Location,Asset EOL Date +Abshire and Sons,Backhoe,ICC-2065556,Ornamental Railings,"Kunde, Doyle and Kozey",Berge Inc,"Wilkinson, Waters and Kerluke",3271901481,"Macbook Pro 13""",,1786VM80X07,at nulla suspendisse potenti cras in purus eu magna vulputate luctus cum sociis natoque penatibus et magnis dis,2023-01-23,2266.13,,,,,,,2028-10-27 +"Quitzon, Oberbrunner and Dibbert",Dragline,WBH-2841795,Structural and Misc Steel (Fabrication),Krajcik LLC,"Botsford, Boyle and Herzog",Lindgren-Marquardt,5504512275,"Macbook Pro 13""",ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae mauris viverra diam vitae quam suspendisse potenti nullam,9351IS25A51,aliquam convallis nunc proin at turpis a pede posuere nonummy integer,2022-11-14,1292.94,User,gmccrackem2a,Gage,McCrackem,gmccrackem2a@bing.com,,2028-10-27 +Boyer and Sons,Excavator,NNH-3656031,Soft Flooring and Base,"Heaney, Altenwerth and Emmerich",Pollich LLC,Pacocha-Kiehn,4861125177,"Macbook Pro 13""",,9929FR08W85,,2023-03-01,2300.71,Location,,,,,Pacocha-Kiehn,2028-10-27 +Hayes-Rippin,Trencher,BOL-0305383,Prefabricated Aluminum Metal Canopies,"Botsford, Boyle and Herzog",Walker-Towne,Fritsch-Abernathy,2416994639,"Macbook Pro 13""",neque vestibulum eget vulputate ut ultrices vel augue vestibulum ante ipsum primis in faucibus orci luctus,9139KQ78G81,,2022-10-26,1777.56,User,ksennett6,Katerina,Sennett,ksennett6@ibm.com,,2028-10-27 +Romaguera-Flatley,Compactor,YVN-3440973,"Temp Fencing, Decorative Fencing and Gates",Ankunding-Ledner,Berge Inc,Roberts-Anderson,6080904229,"Macbook Pro 13""",turpis adipiscing lorem vitae mattis nibh ligula nec sem duis aliquam convallis nunc proin at turpis a pede,0910VB28Q61,,2022-07-24,2967.97,,,,,,,2028-10-27 +Auer LLC,Bulldozer,YOO-5936907,Electrical,Berge Inc,"Heaney, Altenwerth and Emmerich",Roberts-Anderson,8204459090,"Macbook Pro 13""",,7375EM02N97,proin eu mi nulla ac enim in tempor turpis nec euismod scelerisque quam turpis adipiscing lorem,2022-10-01,3819.73,,,,,,,2028-10-27 +Olson Group,Skid-Steer,EJS-7488052,Roofing (Metal),"Fritsch, Sauer and Conn","Romaguera, Goldner and Crooks",Lindgren-Marquardt,1252634576,"Macbook Pro 13""",blandit non interdum in ante vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae,,rhoncus sed vestibulum sit amet cursus id turpis integer aliquet massa id lobortis convallis,2022-06-25,2711.89,Location,,,,,"McGlynn, Hagenes and Bruen",2028-10-27 +"Powlowski, Monahan and Reichel",Bulldozer,HNY-7340937,Ornamental Railings,"Botsford, Boyle and Herzog",Watsica LLC,Roberts-Anderson,7294510907,"Macbook Pro 13""",sapien urna pretium nisl ut volutpat sapien arcu sed augue aliquam erat volutpat in,"",erat fermentum justo nec condimentum neque sapien placerat ante nulla justo aliquam quis turpis eget elit sodales,2022-07-24,1833.42,User,mhasley21,Marion,Hasley,mhasley21@clickbank.net,,2028-10-27 +Harris LLC,Grader,JHN-0598394,Curb & Gutter,"Heaney, Altenwerth and Emmerich","Fritsch, Sauer and Conn",Waters LLC,0709494209,"Macbook Pro 13""",orci luctus et ultrices posuere cubilia curae duis faucibus accumsan odio curabitur,"",vestibulum vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere,2023-04-11,3263.79,User,mbaudy2b,Mickie,Baudy,mbaudy2b@intel.com,,2028-10-27 +Boyer-Okuneva,Backhoe,GEX-7216431,Prefabricated Aluminum Metal Canopies,Pollich LLC,"Upton, Feil and Jast","Jenkins, Goldner and Cruickshank",4030738107,"Macbook Pro 13""",velit vivamus vel nulla eget eros elementum pellentesque quisque porta volutpat,"",,2022-08-25,2448.17,Location,,,,,Lynch and Sons,2028-10-27 +Ebert-Reilly,Scraper,DWM-3752227,Construction Clean and Final Clean,Watsica LLC,Berge Inc,"Mills, Gleichner and Schamberger",9381884673,"Macbook Pro 13""",sed nisl nunc rhoncus dui vel sem sed sagittis nam congue risus semper porta volutpat quam pede lobortis ligula sit,"",rhoncus aliquet pulvinar sed nisl nunc rhoncus dui vel sem sed sagittis nam congue risus semper,2022-12-23,2721.12,User,sbucknall28,Saidee,Bucknall,sbucknall28@cbsnews.com,,"" +"Mitchell, Ward and Hettinger",Dragline,OLM-0226994,Structural and Misc Steel (Fabrication),Walker-Towne,"Kutch, Johnson and Olson",Frami and Sons,7504201033,"Macbook Pro 13""",curae mauris viverra diam vitae quam suspendisse potenti nullam porttitor lacus at turpis donec posuere metus vitae ipsum aliquam non,"",amet nulla quisque arcu libero rutrum ac lobortis vel dapibus at diam nam tristique,2023-05-14,3178.59,Location,,,,,"O'Conner, Nitzsche and Aufderhar","" +Prosacco-Ledner,Scraper,LYO-8134459,Roofing (Asphalt),Mosciski Inc,Mosciski Inc,Waters LLC,9994825740,"Macbook Pro 13""",molestie hendrerit at vulputate vitae nisl aenean lectus pellentesque eget nunc donec quis orci,"",,2023-05-07,3322.34,,,,,,,"" +Harvey and Sons,Compactor,HLK-1645158,Structural and Misc Steel (Fabrication),"Romaguera, Goldner and Crooks",Mosciski Inc,Treutel Inc,8322817966,"Macbook Pro 13""",egestas metus aenean fermentum donec ut mauris eget massa tempor convallis nulla neque libero convallis eget eleifend,"",,2022-09-03,1823.95,User,flewins10,Filip,Lewins,flewins10@amazonaws.com,,"" +"Lebsack, Roob and Streich",Scraper,AZT-4280937,Marlite Panels (FED),Okuneva Group,"Upton, Feil and Jast",Lynch and Sons,8146943004,"Macbook Pro 13""",,"",,2022-12-17,1614.68,Location,,,,,Roberts-Anderson,"" +Abbott-Nikolaus,Dump Truck,IAB-5332824,Structural & Misc Steel Erection,"Upton, Feil and Jast","Romaguera, Goldner and Crooks",Lynch and Sons,3906729636,"Macbook Pro 13""",dui luctus rutrum nulla tellus in sagittis dui vel nisl duis ac nibh fusce,2315CN41G71,,2022-11-02,1444.69,Location,,,,,Fritsch-Abernathy,"" +Nolan-Wisoky,Trencher,OID-4455781,HVAC,Ankunding-Ledner,Abernathy-Stamm,Schinner Group,8981616853,"Macbook Pro 13""",maecenas pulvinar lobortis est phasellus sit amet erat nulla tempus,6080UE59E09,,2022-09-07,3637.94,User,bmctrustrie1c,Balduin,McTrustrie,bmctrustrie1c@free.fr,,"" +"Miller, Morissette and Kihn",Trencher,UQY-2172679,Curb & Gutter,"Legros, Paucek and Collier",Mosciski Inc,"O'Conner, Nitzsche and Aufderhar",3983411009,"Macbook Pro 13""",,5505YF23M46,,2023-02-10,4253.88,User,kkubanek1t,Kalinda,Kubanek,kkubanek1t@umich.edu,,"" +Erdman and Sons,Grader,HRI-2262410,Granite Surfaces,Berge Inc,Walker-Towne,Ledner-Barrows,9311141848,"Macbook Pro 13""",,8673QP30R80,,2022-06-20,1784.66,Location,,,,,Lynch and Sons,"" +"Rogahn, Cormier and Ruecker",Bulldozer,BBK-5960598,Drywall & Acoustical (MOB),Abernathy-Stamm,"Heaney, Altenwerth and Emmerich",Frami and Sons,5157837617,"Macbook Pro 13""",nisl ut volutpat sapien arcu sed augue aliquam erat volutpat in congue etiam justo etiam pretium,9088XV67Q94,,2022-12-15,668.43,Location,,,,,Russel Group,"" +Pagac-Feeney,Compactor,RRO-8557470,Structural and Misc Steel (Fabrication),Mosciski Inc,Okuneva Group,Russel Group,2776102414,"Macbook Pro 13""",quis libero nullam sit amet turpis elementum ligula vehicula consequat morbi a ipsum integer a nibh in quis,,,2022-12-27,3391.42,,,,,,,"" +Toy-Daniel,Compactor,SUQ-5159067,Fire Protection,"Botsford, Boyle and Herzog",Berge Inc,"Jenkins, Goldner and Cruickshank",0313117719,Robby,,2830MI42B80,,2022-06-24,4402.74,User,hbucknall29,Hillie,Bucknall,hbucknall29@webnode.com,,2028-10-27 +"Rau, O'Kon and Predovic",Grader,KUW-8075173,Hard Tile & Stone,"Kunde, Doyle and Kozey",Abernathy-Stamm,"Torp, Kautzer and Rodriguez",3235657209,Tommy,,8752PQ41C20,,2023-04-16,2428.95,User,wtomblin2r,Waiter,Tomblin,wtomblin2r@cisco.com,,2028-10-27 +"Rodriguez, Rippin and Maggio",Backhoe,KGD-2478881,Epoxy Flooring,"Upton, Feil and Jast",Krajcik LLC,Roberts-Anderson,9968615213,Sumner,,,interdum mauris ullamcorper purus sit amet nulla quisque arcu libero rutrum ac lobortis vel dapibus at diam nam tristique tortor,2022-11-14,2353.49,,,,,,,2028-10-27 +Collins-Langworth,Backhoe,NLM-1912680,Glass & Glazing,Walker-Towne,"Kunde, Doyle and Kozey","Nitzsche, Gislason and Douglas",2439578863,Paloma,quam pharetra magna ac consequat metus sapien ut nunc vestibulum ante ipsum,4241FY30X65,aenean sit amet justo morbi ut odio cras mi pede malesuada in imperdiet et commodo vulputate justo,2022-05-29,413.99,User,esargersonc,El,Sargerson,esargersonc@moonfruit.com,,2028-10-27 +"Tillman, Rippin and Robel",Backhoe,HKF-2889015,Framing (Steel),Rippin-Schiller,"Upton, Feil and Jast","Nitzsche, Gislason and Douglas",1249482779,Mabelle,,7737HG97C81,nibh in hac habitasse platea dictumst aliquam augue quam sollicitudin vitae consectetuer eget rutrum at lorem integer tincidunt ante,2022-09-27,174.79,User,lloosely2i,Leia,Loosely,lloosely2i@ebay.co.uk,,2028-10-27 +Bogisich-Gerhold,Excavator,HIN-5577764,HVAC,Ankunding-Ledner,"Kutch, Johnson and Olson",Treutel Inc,5300366684,Robby,,,vestibulum rutrum rutrum neque aenean auctor gravida sem praesent id massa id nisl venenatis lacinia,2023-04-09,4662.35,User,bogg1s,Blakeley,Ogg,bogg1s@examiner.com,,2028-10-27 +DuBuque-Jones,Excavator,RSF-1042461,Roofing (Asphalt),Abernathy-Stamm,"Stokes, Daniel and Johnson",Frami and Sons,8526994711,Jere,vestibulum rutrum rutrum neque aenean auctor gravida sem praesent id massa id nisl venenatis lacinia aenean,,,2022-11-25,2356.93,,,,,,,2028-10-27 +Runolfsson-McCullough,Scraper,DAK-0644656,Framing (Wood),Mosciski Inc,"Legros, Paucek and Collier",Lynch and Sons,5644034134,Mabelle,,3664GP06B10,molestie nibh in lectus pellentesque at nulla suspendisse potenti cras in purus eu,2022-10-05,3912.18,User,mnollet1,Maury,Nollet,mnollet1@ow.ly,,2028-10-27 +Hilpert-Vandervort,Crawler,XWJ-0341580,Roofing (Asphalt),Ankunding-Ledner,Abernathy-Stamm,"Mills, Gleichner and Schamberger",0635168064,Chase,,9438FZ85N89,dolor sit amet consectetuer adipiscing elit proin interdum mauris non ligula pellentesque ultrices phasellus id sapien,2022-08-28,4426.46,User,nhumburton1m,Noelle,Humburton,nhumburton1m@ow.ly,,2028-10-27 +"Schmitt, Kuhlman and Gusikowski",Excavator,UQF-6263661,Electrical,"Upton, Feil and Jast","Kunde, Doyle and Kozey",Fritsch-Abernathy,6394370767,Ward,,0642OF31I56,adipiscing elit proin risus praesent lectus vestibulum quam sapien varius ut blandit non interdum in ante vestibulum ante ipsum primis,2022-12-04,3182.1,Location,,,,,Powlowski LLC,2028-10-27 +Klein Inc,Excavator,IOC-4424021,Elevator,Watsica LLC,Pacocha-Goodwin,Treutel Inc,9408196701,Beverly,,4124VH09F17,,2022-09-12,3321.66,,,,,,,2028-10-27 +"Runte, Feeney and Lueilwitz",Compactor,TBU-9704770,Drilled Shafts,"Kunde, Doyle and Kozey",Rippin-Schiller,"Nitzsche, Gislason and Douglas",5300662174,Jere,consectetuer adipiscing elit proin interdum mauris non ligula pellentesque ultrices phasellus id sapien in,,,2022-07-19,1573.26,User,tdoumerquef,Tobe,Doumerque,tdoumerquef@twitpic.com,,2028-10-27 +Reinger LLC,Dump Truck,TTW-1274810,Masonry,"Heaney, Altenwerth and Emmerich",Abernathy-Stamm,"Torp, Kautzer and Rodriguez",6161734867,Karry,,,,2023-03-05,2677.79,User,rtitterrell0,Rora,Titterrell,rtitterrell0@prnewswire.com,,2028-10-27 +"Breitenberg, Crooks and Goldner",Grader,RIU-2781610,Drilled Shafts,Pollich LLC,"Kunde, Doyle and Kozey","Nitzsche, Gislason and Douglas",1424455064,Nial,,6982GT82L84,,2023-03-22,4085.21,User,otaverner7,Odey,Taverner,otaverner7@discuz.net,,2028-10-27 +"Rolfson, Pollich and Kertzmann",Compactor,BFO-5970934,"Doors, Frames & Hardware","Romaguera, Goldner and Crooks",Pacocha-Goodwin,Pacocha-Kiehn,9193131620,Robby,donec dapibus duis at velit eu est congue elementum in hac habitasse platea dictumst morbi vestibulum velit id pretium,3185PP20K43,gravida sem praesent id massa id nisl venenatis lacinia aenean sit amet justo morbi ut odio cras,2022-05-31,1819.48,,,,,,,2028-10-27 +Luettgen Inc,Grader,QEY-4583797,Painting & Vinyl Wall Covering,"Upton, Feil and Jast","Kunde, Doyle and Kozey","O'Conner, Nitzsche and Aufderhar",6610625137,Debbi,,4971UO73K02,,2022-07-07,4635.63,User,glippitt22,Gregorius,Lippitt,glippitt22@yandex.ru,,2028-10-27 +"Hermann, Cremin and Crona",Dragline,OPB-2390560,Ornamental Railings,"Fritsch, Sauer and Conn",Ankunding-Ledner,Treutel Inc,7358467570,Beverly,,7467PI87D35,nisi vulputate nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget eros,2023-04-09,2089.47,User,dhailwood18,Duffy,Hailwood,dhailwood18@ask.com,,2028-10-27 +Cruickshank-Blanda,Grader,CUD-4868409,Electrical and Fire Alarm,Watsica LLC,Ankunding-Ledner,Treutel Inc,7859007380,Kizzee,,0709PI47U54,,2023-01-19,484.12,,,,,,,2028-10-27 +"Reilly, Yundt and Keeling",Bulldozer,ORL-2640580,RF Shielding,"Legros, Paucek and Collier",Berge Inc,Schinner Group,8731356307,Bondon,,6643DS43O77,,2023-03-29,254.99,,,,,,,2028-10-27 +"Franecki, Nolan and Swift",Trencher,GFV-9868944,RF Shielding,"Kunde, Doyle and Kozey","Kunde, Doyle and Kozey",Lynch and Sons,8841110804,Karry,,1465WJ98O96,,2022-08-27,1191.44,User,acharplinge,Andreana,Charpling,acharplinge@dell.com,,2028-10-27 +"Lind, Reinger and Grant",Dump Truck,KFR-9464842,Epoxy Flooring,"Botsford, Boyle and Herzog","Upton, Feil and Jast",Erdman-West,5382312507,Nial,,7440CN27Q04,amet turpis elementum ligula vehicula consequat morbi a ipsum integer a nibh in,2022-08-19,430.07,,,,,,,2028-10-27 +Quigley Inc,Dragline,XQS-0788077,Casework,Ankunding-Ledner,"Fritsch, Sauer and Conn",Lynch and Sons,5163086523,Rolland,ac tellus semper interdum mauris ullamcorper purus sit amet nulla quisque arcu libero rutrum ac lobortis vel,1663OK46D26,,2022-12-25,1308.3,User,ahugonnet1f,Alvis,Hugonnet,ahugonnet1f@vinaora.com,,2028-10-27 +Hudson-Graham,Dragline,APW-0602098,Waterproofing & Caulking,"Romaguera, Goldner and Crooks",Berge Inc,Waters LLC,1473905199,Mabelle,urna pretium nisl ut volutpat sapien arcu sed augue aliquam erat,,,2022-08-18,2037.76,Location,,,,,"Mills, Gleichner and Schamberger",2028-10-27 +"Schmeler, Dietrich and Buckridge",Crawler,RFX-9706298,Drilled Shafts,Krajcik LLC,Krajcik LLC,Roberts-Anderson,2238031407,Robby,ornare consequat lectus in est risus auctor sed tristique in,7387DP63Q90,tempor turpis nec euismod scelerisque quam turpis adipiscing lorem vitae mattis nibh,2023-02-13,3395.33,Location,,,,,Erdman-West,2028-10-27 +"Jast, Cassin and Hane",Grader,EQQ-2912281,Wall Protection,"Fritsch, Sauer and Conn",Krajcik LLC,Lynch and Sons,6313951394,Mabelle,sagittis sapien cum sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus mus etiam vel augue vestibulum,4541TF76I78,tempus semper est quam pharetra magna ac consequat metus sapien ut nunc vestibulum ante ipsum primis,2022-05-25,4787.17,Location,,,,,Schinner Group,2028-10-27 +Bergstrom-Block,Excavator,IEG-8406586,Masonry,Mosciski Inc,Mosciski Inc,Roberts-Anderson,6040029438,Bondon,cubilia curae donec pharetra magna vestibulum aliquet ultrices erat tortor sollicitudin mi sit,4324BJ82Z56,,2022-09-09,2371.3,Location,,,,,Powlowski LLC,2028-10-27 +"Powlowski, Lowe and Streich",Dragline,ZCB-5287486,Roofing (Asphalt),Watsica LLC,Watsica LLC,"McGlynn, Hagenes and Bruen",6324836270,Debbi,,7369JZ83X28,pede ullamcorper augue a suscipit nulla elit ac nulla sed vel enim,2022-05-30,3261.46,User,ohynson1y,Olivia,Hynson,ohynson1y@ask.com,,2028-10-27 +"Purdy, Leannon and Boyer",Crawler,VVZ-9417930,Retaining Wall and Brick Pavers,"Botsford, Boyle and Herzog","Legros, Paucek and Collier",Schinner Group,1391767652,Bale,,,nulla eget eros elementum pellentesque quisque porta volutpat erat quisque erat eros viverra eget congue eget semper rutrum nulla,2022-06-22,807.74,User,vgunning1z,Vince,Gunning,vgunning1z@arstechnica.com,,2028-10-27 +"Schowalter, Grady and Stracke",Backhoe,LHC-9749327,Fire Protection,Walker-Towne,Ankunding-Ledner,Schinner Group,3460146131,Flo,,4031MX62O09,aenean lectus pellentesque eget nunc donec quis orci eget orci vehicula condimentum curabitur in,2023-02-14,2051.25,User,akneath13,Arron,Kneath,akneath13@sphinn.com,,2028-10-27 +Schulist-Marks,Dragline,HUM-7311338,Sitework & Site Utilities,Berge Inc,Okuneva Group,Roberts-Anderson,4146242985,Bondon,,0468AG53C12,,2022-05-20,2788.78,Location,,,,,"O'Conner, Nitzsche and Aufderhar",2028-10-27 +Cole-Feeney,Trencher,LHK-1308041,"Temp Fencing, Decorative Fencing and Gates","Upton, Feil and Jast","Stokes, Daniel and Johnson",Lindgren-Marquardt,7648736489,Robby,,,massa donec dapibus duis at velit eu est congue elementum in hac habitasse,2022-11-02,2755.2,User,dkent23,Debbi,Kent,dkent23@mtv.com,,2028-10-27 +O'Hara Inc,Backhoe,CIC-1996687,"Temp Fencing, Decorative Fencing and Gates","Romaguera, Goldner and Crooks","Romaguera, Goldner and Crooks",Frami and Sons,5059462108,Bale,,5053ZY27R14,placerat praesent blandit nam nulla integer pede justo lacinia eget tincidunt eget tempus vel pede morbi,2023-01-17,2100.84,User,kephson2f,Kimberly,Ephson,kephson2f@sina.com.cn,,2028-10-27 +Rolfson-Kulas,Dump Truck,YBW-8150273,EIFS,Krajcik LLC,"Upton, Feil and Jast","Wilkinson, Waters and Kerluke",5490351215,Mabelle,,9162YL93W88,ullamcorper augue a suscipit nulla elit ac nulla sed vel enim,2023-01-14,877.57,Location,,,,,Fritsch-Abernathy,2028-10-27 +Lehner-Effertz,Excavator,OFB-4987804,Casework,"Stokes, Daniel and Johnson","Kutch, Johnson and Olson",Waters LLC,2712763720,Robby,,0270YA01J87,sit amet sapien dignissim vestibulum vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae nulla dapibus,2022-09-03,3428.56,User,acansdell14,Adela,Cansdell,acansdell14@cbc.ca,,2028-10-27 +"Dietrich, Von and Mante",Excavator,RXG-3730482,Elevator,Rippin-Schiller,"Botsford, Boyle and Herzog",Lindgren-Marquardt,0868073143,Jere,,8281VU07P52,,2023-01-25,713.89,User,pwippers,Peirce,Wipper,pwippers@liveinternet.ru,,2028-10-27 +Predovic-Roob,Bulldozer,ATA-2489950,EIFS,"Botsford, Boyle and Herzog","Romaguera, Goldner and Crooks",Frami and Sons,8905034852,Chase,platea dictumst maecenas ut massa quis augue luctus tincidunt nulla mollis molestie lorem quisque ut,,,2023-02-07,1138.85,User,odella1g,Osgood,Della,odella1g@hc360.com,,2028-10-27 +Adams-Larkin,Grader,SFI-7496689,"Doors, Frames & Hardware","Kutch, Johnson and Olson",Abernathy-Stamm,Lynch and Sons,3092970929,Flo,id ornare imperdiet sapien urna pretium nisl ut volutpat sapien arcu sed augue aliquam erat volutpat in congue etiam justo,0649BJ66S09,at feugiat non pretium quis lectus suspendisse potenti in eleifend quam a odio in hac,2022-09-10,278.59,,,,,,,2028-10-27 +"Beier, Grant and Thiel",Grader,GSX-1706311,Masonry,"Heaney, Altenwerth and Emmerich",Mosciski Inc,Ledner-Barrows,5141273631,Mabelle,convallis nunc proin at turpis a pede posuere nonummy integer non velit donec diam neque vestibulum eget,6967FJ73D06,sem mauris laoreet ut rhoncus aliquet pulvinar sed nisl nunc rhoncus dui vel sem sed sagittis nam,2022-07-18,1732.28,Location,,,,,Fritsch-Abernathy,2028-10-27 +Armstrong Group,Dump Truck,LXV-5626274,Plumbing & Medical Gas,Rippin-Schiller,Ankunding-Ledner,"Wilkinson, Waters and Kerluke",3202695143,Jeana,,5970GY37L93,purus eu magna vulputate luctus cum sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus mus vivamus,2022-10-11,1981.53,User,cwinfreyl,Casper,Winfrey,cwinfreyl@adobe.com,,2028-10-27 +Rodriguez-Schultz,Excavator,BRF-0336967,Structural and Misc Steel (Fabrication),Abernathy-Stamm,"Botsford, Boyle and Herzog","Torp, Kautzer and Rodriguez",8211664431,Nial,nec nisi volutpat eleifend donec ut dolor morbi vel lectus in quam fringilla rhoncus mauris enim leo rhoncus sed vestibulum,,ultrices erat tortor sollicitudin mi sit amet lobortis sapien sapien non mi integer ac neque duis bibendum morbi,2022-12-30,4924.25,User,mgooday1q,Mariellen,Gooday,mgooday1q@alexa.com,,2028-10-27 +"Huels, Satterfield and Kautzer",Backhoe,CVH-2946076,Painting & Vinyl Wall Covering,Ankunding-Ledner,"Upton, Feil and Jast",Frami and Sons,0835287084,Paloma,justo pellentesque viverra pede ac diam cras pellentesque volutpat dui maecenas tristique est et tempus,,,2022-07-15,2406.57,,,,,,,2028-10-27 +"Lueilwitz, Herzog and Runte",Skid-Steer,OTH-2531332,HVAC,Mosciski Inc,Ankunding-Ledner,Ledner-Barrows,7517530461,Paloma,,1256WA18C94,,2023-01-14,531.46,,,,,,,2028-10-27 +Funk LLC,Grader,ZMB-4808007,Fire Sprinkler System,"Upton, Feil and Jast",Ankunding-Ledner,"Wilkinson, Waters and Kerluke",4467089765,Jeana,,0663SC93W29,quis orci eget orci vehicula condimentum curabitur in libero ut massa volutpat convallis morbi odio odio elementum eu interdum eu,2022-07-19,4741.85,,,,,,,2028-10-27 +"Doyle, Kulas and Mosciski",Dump Truck,XES-9665196,Masonry & Precast,"Botsford, Boyle and Herzog",Pacocha-Goodwin,Ledner-Barrows,4220776022,Sumner,,0099EC66L98,in hac habitasse platea dictumst morbi vestibulum velit id pretium iaculis,2022-08-06,276.15,,,,,,,2028-10-27 +"Robel, Johnston and Crist",Excavator,KUJ-7232314,Construction Clean and Final Clean,Ankunding-Ledner,"Romaguera, Goldner and Crooks",Fritsch-Abernathy,5757955088,Paloma,,4270AX61I99,,2022-09-21,3749.65,Location,,,,,"Wilkinson, Waters and Kerluke",2028-10-27 +Wiza-Mante,Grader,UNN-0016298,Roofing (Metal),Watsica LLC,Ankunding-Ledner,Frami and Sons,6746036070,Tommy,interdum mauris non ligula pellentesque ultrices phasellus id sapien in sapien iaculis congue vivamus metus arcu adipiscing molestie,9540RE21E79,est congue elementum in hac habitasse platea dictumst morbi vestibulum velit id pretium iaculis diam erat,2022-08-08,4600.22,Location,,,,,Roberts-Anderson,2028-10-27 +Jacobi Group,Bulldozer,JRP-9799959,Roofing (Metal),Rippin-Schiller,"Stokes, Daniel and Johnson",Russel Group,1090875903,Nobe,,5159PT28N59,,2023-05-02,3255.63,Location,,,,,Pacocha-Kiehn,2028-10-27 +Murazik-Cormier,Crawler,GTI-0627775,Marlite Panels (FED),"Kutch, Johnson and Olson","Fritsch, Sauer and Conn",Powlowski LLC,7650476155,Jeana,,9055CM63Z89,,2022-06-24,1295.04,Location,,,,,"Jenkins, Goldner and Cruickshank",2028-10-27 +"Simonis, Schulist and Volkman",Compactor,WEL-4481760,RF Shielding,Mosciski Inc,Rippin-Schiller,Powlowski LLC,6908782578,Bale,curae nulla dapibus dolor vel est donec odio justo sollicitudin ut suscipit a feugiat,3712NE31P89,,2022-08-02,2038.79,User,ksennett6,Katerina,Sennett,ksennett6@ibm.com,,2028-10-27 +Conroy-Abshire,Grader,VKB-3777629,Masonry & Precast,Krajcik LLC,Pollich LLC,Erdman-West,6651233484,Illa,duis aliquam convallis nunc proin at turpis a pede posuere nonummy integer non velit donec,,in hac habitasse platea dictumst etiam faucibus cursus urna ut tellus nulla ut erat id mauris,2023-04-28,4988.38,,,,,,,2028-10-27 +Mueller and Sons,Bulldozer,YFK-3812221,Waterproofing & Caulking,Rippin-Schiller,Berge Inc,"Mills, Gleichner and Schamberger",3679743009,Illa,sed vel enim sit amet nunc viverra dapibus nulla suscipit ligula in lacus curabitur at ipsum ac tellus semper interdum,2077OM84Z52,,2022-09-18,3085.39,User,moscullya,Maurice,O'Scully,moscullya@engadget.com,,2028-10-27 +"Turner, Schulist and Hodkiewicz",Compactor,ENN-1432777,Ornamental Railings,"Romaguera, Goldner and Crooks","Kunde, Doyle and Kozey","Mills, Gleichner and Schamberger",7184133416,Robby,,6321OY95W96,,2022-09-25,4517.54,User,lloosely2i,Leia,Loosely,lloosely2i@ebay.co.uk,,2028-10-27 +Dicki-Hartmann,Skid-Steer,JNS-3537485,Epoxy Flooring,Pacocha-Goodwin,Mosciski Inc,Pacocha-Kiehn,2137927703,Mabelle,erat fermentum justo nec condimentum neque sapien placerat ante nulla justo aliquam,2809IW22A70,consectetuer adipiscing elit proin interdum mauris non ligula pellentesque ultrices phasellus id sapien in,2022-09-12,4293.51,,,,,,,2028-10-27 +Schoen and Sons,Dump Truck,RAA-2200532,Soft Flooring and Base,Walker-Towne,"Upton, Feil and Jast",Waters LLC,5479047972,Beverly,maecenas leo odio condimentum id luctus nec molestie sed justo pellentesque viverra,,,2023-01-19,1760.88,User,rsapeyb,Raquela,Sapey,rsapeyb@furl.net,,2028-10-27 +"Rolfson, Champlin and Cole",Bulldozer,GDB-9523081,EIFS,"Heaney, Altenwerth and Emmerich","Upton, Feil and Jast",Frami and Sons,6544577007,Rolland,,,,2022-11-29,2769.18,,,,,,,2028-10-27 +"Maggio, Wolff and Friesen",Excavator,IOH-2003935,Structural and Misc Steel (Fabrication),Okuneva Group,Abernathy-Stamm,Lindgren-Marquardt,1508089431,Flo,vehicula condimentum curabitur in libero ut massa volutpat convallis morbi odio odio elementum eu interdum eu,2753UJ02X37,porttitor lorem id ligula suspendisse ornare consequat lectus in est risus auctor sed tristique in tempus,2022-08-05,3709.55,,,,,,,2028-10-27 +Brekke-Hoeger,Backhoe,UPX-0286727,Plumbing & Medical Gas,"Upton, Feil and Jast",Rippin-Schiller,Lynch and Sons,6217027186,Bondon,,6387KP57B15,vivamus metus arcu adipiscing molestie hendrerit at vulputate vitae nisl aenean lectus pellentesque eget nunc donec,2023-03-21,4138.24,,,,,,,2028-10-27 +Konopelski Inc,Dump Truck,NZE-5809675,Casework,"Stokes, Daniel and Johnson","Kutch, Johnson and Olson",Erdman-West,8486277027,Mabelle,dui luctus rutrum nulla tellus in sagittis dui vel nisl duis ac nibh fusce lacus purus,4732FS58G78,,2023-05-11,114.69,,,,,,,2028-10-27 +"Nolan, Kertzmann and Rippin",Bulldozer,YXY-3875893,RF Shielding,"Romaguera, Goldner and Crooks",Abernathy-Stamm,Frami and Sons,7979699903,Bondon,habitasse platea dictumst maecenas ut massa quis augue luctus tincidunt,4378XF72E06,,2023-04-29,1781.53,,,,,,,2028-10-27 +Stroman and Sons,Skid-Steer,BDD-0117730,"Temp Fencing, Decorative Fencing and Gates",Okuneva Group,Mosciski Inc,Frami and Sons,1053653041,Karry,,0484GG78J91,odio elementum eu interdum eu tincidunt in leo maecenas pulvinar lobortis est phasellus sit amet erat,2022-09-12,4854.6,User,mbaudy2b,Mickie,Baudy,mbaudy2b@intel.com,,2028-10-27 +Howe-Volkman,Crawler,YMB-2902862,Masonry & Precast,"Kutch, Johnson and Olson",Krajcik LLC,Roberts-Anderson,0269899359,Nial,,2685ZN23G49,,2022-08-27,133.33,User,jdriffill2,Jeanne,Driffill,jdriffill2@google.fr,,2028-10-27 +Heathcote and Sons,Skid-Steer,LWA-2897309,Exterior Signage,"Heaney, Altenwerth and Emmerich",Okuneva Group,Schinner Group,1268560442,Paloma,,8414GM02P21,,2022-12-27,4524.09,,,,,,,2028-10-27 +Borer-Aufderhar,Scraper,OOY-9898294,Electrical,Pacocha-Goodwin,Berge Inc,"Nitzsche, Gislason and Douglas",4556315148,Karry,,0187NT56E91,,2022-08-21,390.85,,,,,,,2028-10-27 +"Hauck, Bogisich and King",Dragline,XHB-6046299,Sitework & Site Utilities,"Fritsch, Sauer and Conn","Romaguera, Goldner and Crooks","Jenkins, Goldner and Cruickshank",9343736607,Debbi,aliquam erat volutpat in congue etiam justo etiam pretium iaculis,8559BT54S22,in lectus pellentesque at nulla suspendisse potenti cras in purus eu magna vulputate,2022-05-25,1439.23,Location,,,,,Ledner-Barrows, +"Upton, Luettgen and Mayer",Grader,JVB-8844609,Structural & Misc Steel Erection,Pollich LLC,"Kunde, Doyle and Kozey",Treutel Inc,6715000024,Karry,nulla eget eros elementum pellentesque quisque porta volutpat erat quisque erat eros viverra eget congue eget semper,0088HF56H55,,2022-12-17,4849.95,Location,,,,,"McGlynn, Hagenes and Bruen", +Fadel Inc,Skid-Steer,VAA-1096481,Hard Tile & Stone,Mosciski Inc,Krajcik LLC,Schinner Group,2384395201,Nobe,parturient montes nascetur ridiculus mus etiam vel augue vestibulum rutrum rutrum neque aenean auctor gravida sem praesent id,,tortor duis mattis egestas metus aenean fermentum donec ut mauris,2022-07-15,1190.44,User,tphysick3,Tiphanie,Physick,tphysick3@umn.edu,, +Dickens and Sons,Skid-Steer,OAC-1765208,Ornamental Railings,Ankunding-Ledner,Rippin-Schiller,Lindgren-Marquardt,2119388227,Ward,non velit nec nisi vulputate nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget eros elementum pellentesque quisque porta,9895LS02S32,,2022-09-03,1868.26,Location,,,,,"Jenkins, Goldner and Cruickshank", +Cassin Group,Bulldozer,CPK-3286546,Structural & Misc Steel Erection,Berge Inc,"Botsford, Boyle and Herzog",Frami and Sons,6044719518,Chase,,5420GG86P87,,2022-07-22,1463.24,,,,,,, +"Wolf, Wiza and Pacocha",Excavator,ZHU-6798344,Electrical,"Kutch, Johnson and Olson",Abernathy-Stamm,Frami and Sons,6111751517,Tommy,odio in hac habitasse platea dictumst maecenas ut massa quis augue luctus tincidunt nulla mollis molestie lorem quisque ut erat,5462TF70C36,amet erat nulla tempus vivamus in felis eu sapien cursus vestibulum proin eu mi nulla ac enim,2023-02-26,1268.92,User,pshirleyi,Pen,Shirley,pshirleyi@theguardian.com,, +"Davis, Klein and Kiehn",Backhoe,WHR-8533403,Plumbing & Medical Gas,"Botsford, Boyle and Herzog",Mosciski Inc,Treutel Inc,7632050235,Tommy,,5478JY13D60,,2022-09-11,4908.48,Location,,,,,Pacocha-Kiehn, +"Gorczany, Lindgren and Hand",Compactor,FAC-2072179,Framing (Steel),Berge Inc,"Legros, Paucek and Collier",Ledner-Barrows,8755015421,Rolland,,2138TK23O47,massa id nisl venenatis lacinia aenean sit amet justo morbi ut odio cras mi pede,2022-09-19,2249.9,User,kgrealish16,Krystalle,Grealish,kgrealish16@sciencedirect.com,, +Romaguera-Ondricka,Grader,KON-1821691,Curb & Gutter,"Kutch, Johnson and Olson",Okuneva Group,"O'Conner, Nitzsche and Aufderhar",4908345668,Chase,,5414US21B26,pede libero quis orci nullam molestie nibh in lectus pellentesque at nulla suspendisse potenti,2022-11-14,1545.41,User,ewarriner2o,Ellene,Warriner,ewarriner2o@wikimedia.org,, +"Anderson, Goldner and Bailey",Compactor,NDF-4324420,Roofing (Metal),Mosciski Inc,"Kunde, Doyle and Kozey","McGlynn, Hagenes and Bruen",3212882737,Nial,,0754DG10B38,dui nec nisi volutpat eleifend donec ut dolor morbi vel lectus in quam fringilla rhoncus mauris enim leo,2023-05-15,4423.19,,,,,,, +Mraz-Windler,Trencher,DLM-4710838,Plumbing & Medical Gas,"Kunde, Doyle and Kozey",Walker-Towne,"Jenkins, Goldner and Cruickshank",1315337182,Debbi,,2430PB13D95,,2022-07-11,2708.18,User,fmcguiness1k,Fredrika,McGuiness,fmcguiness1k@woothemes.com,, +Volkman Group,Compactor,MLJ-9313486,Electrical and Fire Alarm,"Romaguera, Goldner and Crooks","Botsford, Boyle and Herzog",Fritsch-Abernathy,2738670737,Illa,,,,2022-10-09,1404.85,Location,,,,,Pacocha-Kiehn, +Schulist-Kulas,Compactor,XBE-4236649,Drywall & Acoustical (MOB),Abernathy-Stamm,Berge Inc,"Nitzsche, Gislason and Douglas",7811285882,Kizzee,,2875KP71D22,,2022-11-21,271.5,Location,,,,,"Wilkinson, Waters and Kerluke", +Hammes Inc,Crawler,NRV-3022526,Epoxy Flooring,Pollich LLC,"Upton, Feil and Jast",Powlowski LLC,8015369811,Ward,,6485BK61G70,facilisi cras non velit nec nisi vulputate nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget eros,2023-01-25,1757.26,,,,,,, +"Kemmer, Beier and Hegmann",Skid-Steer,EXG-6125653,Glass & Glazing,"Upton, Feil and Jast",Pacocha-Goodwin,Erdman-West,9025824302,Rolland,,8500KT88G80,,2022-09-27,3697.22,User,aweemsj,Antoinette,Weems,aweemsj@who.int,, +"Padberg, Kozey and Morar",Compactor,XYK-7848357,RF Shielding,Berge Inc,Abernathy-Stamm,Russel Group,2116700211,Robby,,,,2022-11-05,4059.57,Location,,,,,Lindgren-Marquardt, +Pfannerstill and Sons,Skid-Steer,GZW-6229249,Waterproofing & Caulking,Pollich LLC,"Kutch, Johnson and Olson","Torp, Kautzer and Rodriguez",4909263041,Debbi,,,lectus pellentesque eget nunc donec quis orci eget orci vehicula condimentum curabitur in,2022-08-11,1697.74,,,,,,, +Kunde-Hermiston,Compactor,VZD-7424699,Marlite Panels (FED),Mosciski Inc,"Upton, Feil and Jast","Jenkins, Goldner and Cruickshank",5611381559,Nial,,8991VD43T11,cras mi pede malesuada in imperdiet et commodo vulputate justo in blandit,2022-09-17,4957.55,Location,,,,,Powlowski LLC, +"Treutel, Schmidt and Koss",Compactor,RZO-4616134,Retaining Wall and Brick Pavers,"Stokes, Daniel and Johnson","Kutch, Johnson and Olson","O'Conner, Nitzsche and Aufderhar",0833202169,Nial,,6036SV40U93,,2023-04-18,3218.61,User,bvandalen5,Barbabas,Van Dalen,bvandalen5@smugmug.com,, +Nader Inc,Scraper,NXG-1697279,RF Shielding,"Botsford, Boyle and Herzog","Fritsch, Sauer and Conn",Waters LLC,7094568704,Robby,,7964JY42Z54,,2022-08-26,3628.55,Location,,,,,"McGlynn, Hagenes and Bruen", +"Cremin, Conroy and Kuphal",Crawler,OGN-4843235,Glass & Glazing,Okuneva Group,"Heaney, Altenwerth and Emmerich",Pacocha-Kiehn,6424257173,Beverly,,2880LU86I74,sagittis nam congue risus semper porta volutpat quam pede lobortis ligula sit amet eleifend pede libero quis orci,2022-09-22,4163.07,,,,,,, +"Douglas, Considine and Gerhold",Backhoe,BXI-1028705,Structural and Misc Steel (Fabrication),"Romaguera, Goldner and Crooks",Ankunding-Ledner,Roberts-Anderson,5388101251,Karry,,,,2023-02-20,976.02,,,,,,, +Emard Group,Scraper,MWK-5482358,Ornamental Railings,Mosciski Inc,"Heaney, Altenwerth and Emmerich","Mills, Gleichner and Schamberger",5509346090,Nobe,,8183RH74P65,donec dapibus duis at velit eu est congue elementum in hac habitasse platea dictumst morbi vestibulum,2022-06-28,1441.26,Location,,,,,Fritsch-Abernathy, +Runolfsdottir-Schulist,Compactor,ZSS-2076422,EIFS,Rippin-Schiller,"Upton, Feil and Jast",Ledner-Barrows,4163465013,Karry,dui luctus rutrum nulla tellus in sagittis dui vel nisl,,,2022-11-11,3688.09,User,rrappa27,Rabi,Rappa,rrappa27@cyberchimps.com,, +Stoltenberg-Cormier,Trencher,XNO-4048888,Roofing (Metal),Watsica LLC,Pollich LLC,Russel Group,3593822457,Robby,morbi quis tortor id nulla ultrices aliquet maecenas leo odio condimentum id luctus nec molestie sed,1583JR98T50,,2022-12-04,1780.96,,,,,,, +"Hand, Bogan and Cassin",Skid-Steer,SWP-7721219,Roofing (Metal),"Kunde, Doyle and Kozey",Krajcik LLC,Lindgren-Marquardt,1185234640,Nial,,4091TS60T07,,2023-05-09,1528.53,User,mskellingtonu,Marrilee,Skellington,mskellingtonu@abc.net.au,, +Kling-Homenick,Dragline,HGX-6549969,Asphalt Paving,Walker-Towne,"Fritsch, Sauer and Conn","Jenkins, Goldner and Cruickshank",2726185990,Bale,,9573TJ14G71,,2023-01-31,4930.43,User,asyne1a,Angela,Syne,asyne1a@icio.us,, +"Kilback, Towne and Stehr",Skid-Steer,ZTD-2850379,Drilled Shafts,Krajcik LLC,"Stokes, Daniel and Johnson","O'Conner, Nitzsche and Aufderhar",7192145952,Paloma,,,ipsum aliquam non mauris morbi non lectus aliquam sit amet diam in magna bibendum imperdiet nullam orci,2022-12-25,2947.93,,,,,,, +Harvey-Moore,Dump Truck,QKC-1710236,Fire Protection,Berge Inc,Okuneva Group,Lynch and Sons,7337073255,Kizzee,,7010OW07S33,,2023-02-01,1617.55,Location,,,,,Ledner-Barrows, +Maggio-Hoppe,Excavator,LHL-3384861,Termite Control,Pacocha-Goodwin,Abernathy-Stamm,Lindgren-Marquardt,5642326493,Ward,amet sem fusce consequat nulla nisl nunc nisl duis bibendum felis sed interdum,0489GD18P85,,2022-07-29,4682.12,User,nscroggs2d,Naoma,Scroggs,nscroggs2d@yelp.com,, +Muller LLC,Skid-Steer,VYY-4278214,Rebar & Wire Mesh Install,"Kunde, Doyle and Kozey",Okuneva Group,Schinner Group,0946092591,Paloma,,,felis sed lacus morbi sem mauris laoreet ut rhoncus aliquet pulvinar sed nisl nunc rhoncus dui vel sem sed sagittis,2022-09-07,4400.17,Location,,,,,Lynch and Sons, +Kerluke Inc,Backhoe,HRP-6178185,Retaining Wall and Brick Pavers,Mosciski Inc,Berge Inc,Treutel Inc,6532044804,Nial,,,,2022-09-07,1345.45,Location,,,,,Lindgren-Marquardt, +"Harvey, Schulist and Kemmer",Trencher,QND-9017996,Painting & Vinyl Wall Covering,Abernathy-Stamm,Pollich LLC,Fritsch-Abernathy,4977996097,Paloma,est donec odio justo sollicitudin ut suscipit a feugiat et eros vestibulum ac est lacinia nisi venenatis tristique fusce,2497DB20W09,habitasse platea dictumst morbi vestibulum velit id pretium iaculis diam erat fermentum justo,2023-01-27,317.08,,,,,,, +Schimmel and Sons,Dragline,DAX-0085828,Drywall & Acoustical (MOB),Okuneva Group,Watsica LLC,"Jenkins, Goldner and Cruickshank",7739403397,Flo,,,,2022-12-28,4521.28,User,pwippers,Peirce,Wipper,pwippers@liveinternet.ru,, +Grimes-Lowe,Dragline,VED-7099927,Masonry,Rippin-Schiller,"Stokes, Daniel and Johnson",Lynch and Sons,3351670325,Karry,,,ullamcorper purus sit amet nulla quisque arcu libero rutrum ac lobortis vel dapibus at,2023-02-04,2620.69,,,,,,, +Krajcik-Cruickshank,Bulldozer,ZHU-0211288,Painting & Vinyl Wall Covering,"Fritsch, Sauer and Conn",Pacocha-Goodwin,Lindgren-Marquardt,5042401893,Paloma,vestibulum rutrum rutrum neque aenean auctor gravida sem praesent id massa id nisl venenatis lacinia aenean sit amet justo,0511VF33D78,proin eu mi nulla ac enim in tempor turpis nec euismod scelerisque quam turpis adipiscing lorem,2022-09-27,923.99,,,,,,, +Donnelly-Maggio,Compactor,HFG-7624506,Electrical and Fire Alarm,Pollich LLC,"Fritsch, Sauer and Conn","Torp, Kautzer and Rodriguez",3157087051,Illa,libero convallis eget eleifend luctus ultricies eu nibh quisque id justo sit amet sapien dignissim,5980WY24F50,tortor sollicitudin mi sit amet lobortis sapien sapien non mi integer ac neque duis bibendum morbi non,2022-12-05,4670.18,Location,,,,,"Jenkins, Goldner and Cruickshank", +Pfeffer-Zieme,Trencher,AZY-1682920,Wall Protection,Abernathy-Stamm,Mosciski Inc,"Torp, Kautzer and Rodriguez",5186444169,Beverly,consequat nulla nisl nunc nisl duis bibendum felis sed interdum venenatis turpis enim,6620HZ14T83,aliquam convallis nunc proin at turpis a pede posuere nonummy,2022-06-27,1674.34,User,cstamper1h,Celie,Stamper,cstamper1h@usda.gov,, +"Cormier, Glover and Hickle",Compactor,EYP-3561396,Termite Control,Rippin-Schiller,"Kunde, Doyle and Kozey","Torp, Kautzer and Rodriguez",4964027369,Paloma,quam suspendisse potenti nullam porttitor lacus at turpis donec posuere metus,4352FH58S52,,2022-11-21,136.12,,,,,,, +Rau Group,Skid-Steer,DYU-7769483,Marlite Panels (FED),Krajcik LLC,Krajcik LLC,Fritsch-Abernathy,2457705388,Sumner,,2286KF82X10,,2023-05-14,1147.8,User,flewins10,Filip,Lewins,flewins10@amazonaws.com,, +"Shanahan, Bradtke and Rowe",Backhoe,JYC-0713874,Hard Tile & Stone,"Upton, Feil and Jast",Ankunding-Ledner,Waters LLC,0372144769,Rolland,porta volutpat quam pede lobortis ligula sit amet eleifend pede libero quis orci nullam molestie nibh in,7775EM65V29,phasellus sit amet erat nulla tempus vivamus in felis eu sapien cursus vestibulum proin eu,2022-05-27,1691.36,,,,,,, +Bogisich LLC,Bulldozer,MIQ-6169091,Soft Flooring and Base,"Romaguera, Goldner and Crooks",Mosciski Inc,Powlowski LLC,8984702329,Ward,,,justo eu massa donec dapibus duis at velit eu est,2022-10-15,1578.57,User,ndubery1e,Neda,Dubery,ndubery1e@fotki.com,, +Ondricka-Schuster,Dump Truck,KEN-1184794,"Doors, Frames & Hardware",Walker-Towne,Watsica LLC,Pacocha-Kiehn,4745674969,Mabelle,ultrices posuere cubilia curae nulla dapibus dolor vel est donec odio justo sollicitudin ut suscipit a feugiat,,,2022-12-10,4298.98,,,,,,, +Mayer-Abernathy,Scraper,ZMD-6316371,Rebar & Wire Mesh Install,"Stokes, Daniel and Johnson",Abernathy-Stamm,Powlowski LLC,2526471701,Flo,,1918JS43H32,,2023-03-24,1171.9,User,gbassingdenm,Godwin,Bassingden,gbassingdenm@dedecms.com,, +Towne-Adams,Scraper,QCU-6826093,Prefabricated Aluminum Metal Canopies,Abernathy-Stamm,Rippin-Schiller,Waters LLC,4899233400,Debbi,arcu sed augue aliquam erat volutpat in congue etiam justo etiam pretium iaculis justo in hac habitasse platea dictumst,6487ZI24B86,nec molestie sed justo pellentesque viverra pede ac diam cras pellentesque volutpat dui maecenas,2023-02-22,602.41,Location,,,,,"McGlynn, Hagenes and Bruen", +Kunde Group,Compactor,ITE-9907827,Sitework & Site Utilities,Mosciski Inc,"Romaguera, Goldner and Crooks",Treutel Inc,4671828697,Kizzee,,3508XP22O98,sed interdum venenatis turpis enim blandit mi in porttitor pede justo eu massa donec dapibus duis at velit eu est,2023-02-18,3119.76,Location,,,,,Fritsch-Abernathy, +Welch Group,Compactor,GXW-3680693,Drywall & Acoustical (MOB),Okuneva Group,Watsica LLC,"Nitzsche, Gislason and Douglas",4413339149,Karry,mi integer ac neque duis bibendum morbi non quam nec,,,2023-02-16,1570.25,User,ewhitebrook1r,Emmalyn,Whitebrook,ewhitebrook1r@webnode.com,, +"Zieme, Metz and Schamberger",Grader,GDQ-7108837,Masonry,"Heaney, Altenwerth and Emmerich","Upton, Feil and Jast","Jenkins, Goldner and Cruickshank",8560447476,Chase,,4743VU30T06,nibh ligula nec sem duis aliquam convallis nunc proin at turpis a pede posuere nonummy integer non,2022-08-09,1530.46,User,pwoollons2q,Pyotr,Woollons,pwoollons2q@nps.gov,, +"Kuhlman, Walker and Denesik",Dragline,QZO-1370026,Asphalt Paving,Krajcik LLC,"Botsford, Boyle and Herzog",Erdman-West,5748443707,Mabelle,,2693SH27I09,accumsan felis ut at dolor quis odio consequat varius integer ac leo pellentesque ultrices mattis,2022-08-23,2694.26,,,,,,, +Cormier Inc,Bulldozer,AQW-7617737,RF Shielding,"Heaney, Altenwerth and Emmerich",Watsica LLC,"Mills, Gleichner and Schamberger",1502376915,Rolland,,8461WN86M04,sed sagittis nam congue risus semper porta volutpat quam pede lobortis ligula sit amet eleifend pede libero quis,2023-03-04,1055.33,,,,,,, +Beier LLC,Dragline,NDV-6470913,Masonry,Mosciski Inc,"Kutch, Johnson and Olson",Lynch and Sons,3437983455,Ward,volutpat quam pede lobortis ligula sit amet eleifend pede libero quis orci nullam molestie nibh in lectus,9621QF22B21,,2022-07-27,1251.77,User,rrappa27,Rabi,Rappa,rrappa27@cyberchimps.com,, +Skiles and Sons,Compactor,FOR-0865018,Exterior Signage,"Stokes, Daniel and Johnson","Kutch, Johnson and Olson","Torp, Kautzer and Rodriguez",3955766875,Tommy,,6385GQ84X66,quisque id justo sit amet sapien dignissim vestibulum vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere,2023-04-26,1109.44,Location,,,,,Frami and Sons, +"Corkery, Kunde and Schulist",Crawler,SCK-0860481,Rebar & Wire Mesh Install,"Fritsch, Sauer and Conn",Walker-Towne,Lynch and Sons,5146662358,Karry,amet nulla quisque arcu libero rutrum ac lobortis vel dapibus at diam nam tristique tortor eu,,,2022-10-05,2029.54,Location,,,,,"Torp, Kautzer and Rodriguez", +Brakus LLC,Backhoe,DAI-7864625,Casework,Pollich LLC,"Kunde, Doyle and Kozey","Jenkins, Goldner and Cruickshank",3421666270,Mabelle,,3209SC49Y83,vitae mattis nibh ligula nec sem duis aliquam convallis nunc proin at turpis,2022-06-01,3611.16,User,tcicutto1l,Thornie,Cicutto,tcicutto1l@irs.gov,, +"Donnelly, Daniel and Kuhn",Skid-Steer,AXA-8500000,Glass & Glazing,Pacocha-Goodwin,"Upton, Feil and Jast",Powlowski LLC,5545256559,Chase,,5792EV06A79,,2023-01-19,3029.64,,,,,,, +Wehner-Wyman,Compactor,MGX-6732943,Fire Sprinkler System,Berge Inc,Mosciski Inc,Ledner-Barrows,3758714940,Jeana,ut massa quis augue luctus tincidunt nulla mollis molestie lorem quisque ut erat curabitur,,,2023-01-09,2587.65,User,dhailwood18,Duffy,Hailwood,dhailwood18@ask.com,, +Hauck-Bosco,Bulldozer,NIS-2897343,Electrical,"Kunde, Doyle and Kozey","Botsford, Boyle and Herzog","Jenkins, Goldner and Cruickshank",8342227248,Illa,,0783AR26Y44,,2023-01-11,904.38,Location,,,,,Pacocha-Kiehn, +Shields Inc,Bulldozer,QZL-7700638,Construction Clean and Final Clean,Okuneva Group,Ankunding-Ledner,Pacocha-Kiehn,3955442162,Mabelle,et ultrices posuere cubilia curae duis faucibus accumsan odio curabitur convallis duis consequat dui nec nisi,2293HV69Q96,,2022-05-30,2920.56,Location,,,,,Erdman-West, +"Willms, O'Connell and Cormier",Grader,RJS-7752630,Fire Sprinkler System,Ankunding-Ledner,Ankunding-Ledner,Treutel Inc,1717119122,Bondon,,2846FL33H31,mauris laoreet ut rhoncus aliquet pulvinar sed nisl nunc rhoncus dui,2022-11-12,3998.15,,,,,,, +Schinner-Price,Compactor,MDX-2483852,Construction Clean and Final Clean,Abernathy-Stamm,"Kutch, Johnson and Olson",Lindgren-Marquardt,9849455784,Bondon,,8475JD14T05,in faucibus orci luctus et ultrices posuere cubilia curae nulla dapibus dolor vel,2023-04-21,2625.55,Location,,,,,"Wilkinson, Waters and Kerluke", +"Treutel, Berge and Batz",Grader,FZI-9158744,Marlite Panels (FED),"Kutch, Johnson and Olson","Kutch, Johnson and Olson",Russel Group,7516475364,Nial,,2620DL01N52,,2023-02-02,4272.01,Location,,,,,"Jenkins, Goldner and Cruickshank", +"McDermott, Koch and Skiles",Skid-Steer,RDJ-1659181,Marlite Panels (FED),Walker-Towne,"Romaguera, Goldner and Crooks","Jenkins, Goldner and Cruickshank",2002850756,Robby,congue eget semper rutrum nulla nunc purus phasellus in felis donec semper sapien a libero nam dui proin leo,4676YV46O78,,2022-07-24,3087.45,User,kharbage1w,Kinny,Harbage,kharbage1w@wordpress.org,, +Tremblay-Collier,Compactor,XCQ-5970453,Drilled Shafts,"Legros, Paucek and Collier",Mosciski Inc,Roberts-Anderson,7468627157,Kizzee,turpis nec euismod scelerisque quam turpis adipiscing lorem vitae mattis nibh ligula nec sem duis,2895CV95N88,velit eu est congue elementum in hac habitasse platea dictumst morbi vestibulum velit id pretium iaculis diam,2022-10-23,2726.04,,,,,,, +"Shanahan, Bergnaum and Buckridge",Trencher,IRM-4172620,Drywall & Acoustical (MOB),"Romaguera, Goldner and Crooks","Fritsch, Sauer and Conn","Mills, Gleichner and Schamberger",8440065950,Debbi,habitasse platea dictumst morbi vestibulum velit id pretium iaculis diam erat fermentum justo nec condimentum neque sapien placerat ante,4296XD75M64,,2023-05-18,2627.55,User,pwoollons2q,Pyotr,Woollons,pwoollons2q@nps.gov,, +"Dickens, Huel and Reilly",Compactor,DPP-2739545,Sitework & Site Utilities,"Botsford, Boyle and Herzog",Okuneva Group,Russel Group,1933518954,Debbi,sit amet nulla quisque arcu libero rutrum ac lobortis vel dapibus,4147HN29Y26,,2023-02-01,4571.33,,,,,,, +Shields Inc,Scraper,EBH-1609775,Roofing (Asphalt),Watsica LLC,Pollich LLC,"Nitzsche, Gislason and Douglas",3751836527,Debbi,,,,2023-01-16,3195.45,User,ahugonnet1f,Alvis,Hugonnet,ahugonnet1f@vinaora.com,, diff --git a/sample_csvs/consumables-sample.csv b/sample_csvs/consumables-sample.csv new file mode 100644 index 0000000000..962f55ebdd --- /dev/null +++ b/sample_csvs/consumables-sample.csv @@ -0,0 +1,51 @@ +Company,Name,Category,Supplier,Manufacturer,QTY,Location,Order Number,Min Amount,Model Number,Item Number,Notes,Purchase Date,Purchase Cost +Simonis and Sons,Dragline,Soft Flooring and Base,Towne Group,McLaughlin-Wisozk,50,Cherven Bryag,529027385-9,51,3533069599460644,6381392801995066,tortor sollicitudin mi sit amet lobortis sapien sapien non mi integer ac neque duis bibendum morbi non quam nec,2022-11-05,35.2 +Bruen-Hagenes,Trencher,Sitework & Site Utilities,Hoeger and Sons,"Davis, Koelpin and Lockman",54,Pampas,009135228-2,92,5602248408566255894,3563525570373877,fusce lacus purus aliquet at feugiat non pretium quis lectus suspendisse potenti in eleifend quam a odio in hac habitasse,2022-09-17,53.1 +Tremblay Inc,Crawler,Drywall & Acoustical (FED),"Cormier, Ryan and Cummerata","Morar, Stracke and Gusikowski",97,Penteado,492530513-1,20,201715355999350,3555797000872238,massa tempor convallis nulla neque libero convallis eget eleifend luctus ultricies eu,2023-02-05,25.08 +Franecki-Mitchell,Crawler,Soft Flooring and Base,"Bahringer, Hessel and Lynch",Mann Inc,11,Junliangcheng,006782510-9,13,201638625742873,3585582428645232,orci luctus et ultrices posuere cubilia curae nulla dapibus dolor vel est donec,2023-01-25,47.44 +Morar Group,Trencher,Masonry,Metz-Runte,Batz and Sons,35,Migori,410276787-8,2,201574561109571,4041593804503,sit amet consectetuer adipiscing elit proin risus praesent lectus vestibulum quam sapien varius,2022-08-24,29.06 +Rau and Sons,Crawler,Exterior Signage,"Metz, Reynolds and Pagac",Roberts-Haley,36,Lužani,251681998-6,37,6389706621209402,67627057199235112,mauris ullamcorper purus sit amet nulla quisque arcu libero rutrum ac lobortis vel dapibus,2022-09-25,70.5 +"Parisian, Feil and Brown",Dragline,Site Furnishings,"Wisozk, Rowe and Runolfsson",Marks-Lindgren,24,Carvalho,783104099-4,96,4132814261323213,5460576255071343,justo sollicitudin ut suscipit a feugiat et eros vestibulum ac,2022-06-06,23.48 +Upton Group,Skid-Steer,Wall Protection,Sawayn-Predovic,Padberg-Schaefer,86,Drumcondra,691167736-X,20,6709802030161855,5100134953726119,luctus et ultrices posuere cubilia curae mauris viverra diam vitae,2022-09-13,24.6 +Olson-Bradtke,Skid-Steer,Structural and Misc Steel (Fabrication),Dietrich Group,"Doyle, Yost and Kerluke",52,PiÅ‚awa Górna,993878516-6,99,6380721061950737,201534062027276,sed sagittis nam congue risus semper porta volutpat quam pede lobortis ligula sit amet eleifend,2023-03-09,64.2 +Pouros-Veum,Backhoe,Elevator,Gulgowski-Bahringer,Legros-Morissette,85,Tawangsari,863628920-3,27,5100136112680129,5602259943208580,aliquet ultrices erat tortor sollicitudin mi sit amet lobortis sapien sapien non mi integer ac neque duis bibendum morbi non,2022-09-10,73.65 +Purdy-Rice,Grader,Drywall & Acoustical (MOB),Bernhard-Cummings,Price and Sons,81,Trzebinia,184211928-1,43,5602251551839830,56022542102331797,mauris laoreet ut rhoncus aliquet pulvinar sed nisl nunc rhoncus dui,2022-08-20,12.11 +Kihn and Sons,Trencher,EIFS,"Auer, Kutch and Kutch","Murazik, Crooks and Pollich",73,Duru,843046019-5,32,3570560403003822,3544501358533481,tincidunt in leo maecenas pulvinar lobortis est phasellus sit amet erat nulla tempus,2023-04-03,26.73 +Hessel-Yundt,Bulldozer,Wall Protection,O'Reilly Inc,Volkman LLC,40,Yelizavetino,358328061-X,50,5108759813450674,5602215719381357,nulla dapibus dolor vel est donec odio justo sollicitudin ut suscipit a feugiat et eros,2023-05-14,83.76 +Kunze and Sons,Bulldozer,Drywall & Acoustical (MOB),Pollich-Mertz,Kertzmann-Leffler,91,HoeryÅng,676937901-5,50,5602259559696912,3536485956609903,eget massa tempor convallis nulla neque libero convallis eget eleifend luctus ultricies eu nibh quisque id justo sit amet sapien,2023-02-06,14.1 +"Satterfield, Kutch and Bartell",Crawler,Construction Clean and Final Clean,Prohaska-Rosenbaum,O'Conner and Sons,8,Verenchanka,822285715-0,59,36675923976242,3555834547028303,nec dui luctus rutrum nulla tellus in sagittis dui vel nisl duis ac nibh fusce lacus purus aliquet,2023-03-22,36.15 +Stanton-Olson,Dump Truck,Marlite Panels (FED),"Stiedemann, Jones and Heathcote",Bartoletti Inc,17,Mórrope,230874108-2,70,3573310923840155,5602252823412497,tellus in sagittis dui vel nisl duis ac nibh fusce lacus purus aliquet at feugiat non pretium,2022-11-03,14.22 +Kshlerin-Hane,Trencher,Curb & Gutter,Goodwin and Sons,Bechtelar-Gleason,45,ShÅ«sh,451367111-4,46,3536005374229934,5438118681407699,eleifend quam a odio in hac habitasse platea dictumst maecenas ut massa quis augue luctus tincidunt nulla mollis molestie lorem,2023-05-03,15.14 +"Mills, Greenfelder and Spinka",Crawler,HVAC,Reichel Inc,"Kuhic, Marquardt and Beier",10,Arcena Pequena,779807440-6,13,3552031889209913,5018365634108542731,pellentesque ultrices mattis odio donec vitae nisi nam ultrices libero non mattis pulvinar nulla pede ullamcorper augue a suscipit,2022-11-09,57.04 +Buckridge Inc,Skid-Steer,Asphalt Paving,Abernathy-Doyle,Hirthe-Gislason,23,Cardal,152277775-X,65,5602239497538228,6304534980889636,et commodo vulputate justo in blandit ultrices enim lorem ipsum dolor sit amet consectetuer adipiscing elit,2023-05-02,48.68 +Boehm Inc,Skid-Steer,Electrical and Fire Alarm,O'Kon Group,Schuster-Kunze,63,Tafraout,072198056-2,67,3536207535355414,5602242007322746026,donec ut mauris eget massa tempor convallis nulla neque libero convallis eget eleifend luctus ultricies eu nibh quisque id,2022-07-20,28.04 +Maggio Inc,Crawler,Curb & Gutter,West-Batz,Homenick-Schamberger,45,Boa Vista,779589028-8,16,3544589458568846,374622740975098,dolor vel est donec odio justo sollicitudin ut suscipit a,2023-05-14,32.86 +Koelpin Inc,Skid-Steer,"Doors, Frames & Hardware",VonRueden Group,Dickinson Group,6,Cachoeirinha,474127645-7,3,633110118810265152,4936757035760145,ante vel ipsum praesent blandit lacinia erat vestibulum sed magna at nunc commodo placerat praesent blandit nam nulla integer,2022-08-13,5.24 +Shields-Mohr,Bulldozer,Overhead Doors,Lesch-Daniel,Kunde-Dickens,11,Požarevac,658756723-1,19,3574428328762235,3582676636622299,semper sapien a libero nam dui proin leo odio porttitor id consequat in consequat ut nulla sed accumsan felis,2023-03-10,73.7 +Bechtelar-Littel,Skid-Steer,RF Shielding,Smitham-Yost,Cormier Inc,44,Cikendi,650173691-9,30,630469977163834077,4917512293781355,condimentum id luctus nec molestie sed justo pellentesque viverra pede ac diam cras,2022-05-23,99.17 +"Botsford, Rogahn and Heathcote",Compactor,Termite Control,Marquardt-Fadel,Klein-Kohler,71,KuÄevo,792535885-8,51,4903441380678636477,5602259771893871162,lorem vitae mattis nibh ligula nec sem duis aliquam convallis nunc proin,2023-04-20,70.22 +"Fadel, Abbott and Renner",Grader,Curb & Gutter,Collier-Halvorson,Heidenreich-Wisozk,34,Siparia,344682803-6,19,3558289528892176,5341222466132468,dui luctus rutrum nulla tellus in sagittis dui vel nisl duis ac nibh fusce lacus purus aliquet,2022-12-01,68.35 +"Klein, Stehr and Hansen",Bulldozer,Electrical,Heathcote-Hegmann,"Schoen, Schinner and Bashirian",70,Kobyzhcha,747563100-1,3,67615918374813979,3552267916495436,iaculis justo in hac habitasse platea dictumst etiam faucibus cursus urna ut tellus,2022-11-23,36.02 +Dibbert-Sawayn,Excavator,Epoxy Flooring,Purdy Inc,Jacobs LLC,90,Fukumitsu,684367116-0,89,3575124426928351,675940744647511521,vulputate ut ultrices vel augue vestibulum ante ipsum primis in faucibus orci luctus et ultrices,2022-08-25,11.9 +Smith-Greenholt,Skid-Steer,Roofing (Metal),Leannon-Swaniawski,Brown-O'Connell,48,Nanwai,182125694-8,21,3549394673501796,3534352097481265,vestibulum sed magna at nunc commodo placerat praesent blandit nam nulla integer pede justo lacinia eget,2022-09-29,74.89 +Ratke and Sons,Trencher,Painting & Vinyl Wall Covering,Pfannerstill LLC,Williamson and Sons,62,ShÅ«kat aÅŸ ŞūfÄ«,894744686-6,63,67627557381842583,3575485459195667,elit ac nulla sed vel enim sit amet nunc viverra dapibus nulla suscipit ligula in,2023-02-09,58.2 +Hammes Group,Compactor,Elevator,Hammes-Ortiz,Gusikowski Group,49,Norton,142503650-3,16,4041373495456,3567365631500028,morbi vel lectus in quam fringilla rhoncus mauris enim leo,2023-02-04,77.95 +Bednar-Tillman,Grader,Structural & Misc Steel Erection,Hackett-Rice,"Roberts, Grimes and Feest",48,Aengceleng,027615376-6,77,5038353282556401349,379827830457023,venenatis lacinia aenean sit amet justo morbi ut odio cras mi pede malesuada in imperdiet,2022-06-14,66.55 +Conn Inc,Backhoe,Elevator,"Satterfield, Dietrich and Dibbert","Gislason, Turner and Weissnat",49,Dajianchang,012542390-X,22,4175009765287943,633422970094073881,eleifend luctus ultricies eu nibh quisque id justo sit amet sapien dignissim vestibulum vestibulum ante ipsum primis,2023-04-24,39.65 +Baumbach-Hayes,Scraper,HVAC,Larkin-Stamm,Romaguera Group,60,Pshada,975951565-2,17,3558709467530792,201740170261422,mattis odio donec vitae nisi nam ultrices libero non mattis pulvinar nulla pede,2023-03-28,2.13 +Johnson-Hamill,Bulldozer,Electrical and Fire Alarm,Brekke-Rau,"Doyle, Crist and Kulas",87,Kwikila,420068378-4,44,3572846952864955,5610350660093584,maecenas pulvinar lobortis est phasellus sit amet erat nulla tempus,2022-11-13,55.16 +Weber-Spencer,Trencher,Structural & Misc Steel Erection,Jenkins-Nienow,Crona Group,17,Shiojiri,607345620-4,88,6387217570136176,6371741739648696,est phasellus sit amet erat nulla tempus vivamus in felis eu sapien cursus,2022-12-05,91.78 +Bartell-Wisozk,Dump Truck,Framing (Steel),Robel LLC,Bartell Inc,60,Bourg-en-Bresse,285063128-0,65,348798971763230,3529839890242313,in congue etiam justo etiam pretium iaculis justo in hac habitasse,2022-09-10,15.19 +"Blick, Wunsch and Kreiger",Backhoe,Electrical,Conn and Sons,Lockman and Sons,14,FalÄvarjÄn,141897809-4,15,3559869406780764,5602254605681417512,adipiscing elit proin interdum mauris non ligula pellentesque ultrices phasellus,2022-06-14,72.14 +Cassin and Sons,Trencher,Curb & Gutter,Weimann Inc,"Schumm, Smith and Ortiz",83,Caramuca,054429347-9,39,3584248684102141,3533548146815194,quis turpis sed ante vivamus tortor duis mattis egestas metus aenean fermentum donec ut mauris eget massa tempor,2022-06-22,34.35 +"Miller, Boehm and Kovacek",Grader,Structural & Misc Steel Erection,Heller-Simonis,"Harvey, Schiller and Schultz",60,Puerto Alto,463560912-X,99,3540539014169112,6304647611299330905,tristique in tempus sit amet sem fusce consequat nulla nisl nunc,2023-04-25,56.23 +Gulgowski Inc,Dump Truck,Prefabricated Aluminum Metal Canopies,Watsica-Bogan,"Toy, Fahey and McKenzie",77,Petrikov,662823771-0,41,3532416020888375,201871804426022,orci luctus et ultrices posuere cubilia curae duis faucibus accumsan,2023-01-07,28.79 +Beahan-Boyer,Bulldozer,Fire Protection,"Mayer, Stark and Blanda","Grant, Huel and O'Connell",40,Los Angeles,388651272-X,7,3540719793054384,3565693812607524,mauris viverra diam vitae quam suspendisse potenti nullam porttitor lacus at turpis donec posuere metus,2023-03-11,94.98 +"Rau, Sanford and Dach",Compactor,Fire Protection,"Ziemann, Hudson and Berge",Rempel Inc,32,Dapdap,257178615-6,100,3567797431604172,36788101946745,vel enim sit amet nunc viverra dapibus nulla suscipit ligula in lacus curabitur at ipsum,2022-10-22,62.54 +"Hettinger, Pollich and Gleason",Dump Truck,Drywall & Acoustical (MOB),Huel Group,Friesen LLC,26,LodhrÄn,942559838-X,53,375713644821956,4913216453197543,tristique fusce congue diam id ornare imperdiet sapien urna pretium nisl ut volutpat sapien arcu,2023-01-01,96.74 +Schultz and Sons,Crawler,Masonry & Precast,"Dach, Doyle and Dickens","Skiles, Fisher and Koepp",44,Xinxing,003878835-7,20,676195016446613968,6304826401904593789,nisl ut volutpat sapien arcu sed augue aliquam erat volutpat in congue etiam justo etiam pretium iaculis justo in hac,2023-02-04,33.83 +McLaughlin-Ratke,Backhoe,Electrical and Fire Alarm,Berge-Wilkinson,Maggio and Sons,36,Bantar,651286142-6,47,5100130235429332,6706505880344645,sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus mus etiam vel augue vestibulum rutrum rutrum neque aenean,2022-11-22,3.68 +Wehner-Kuhlman,Excavator,Construction Clean and Final Clean,"Thiel, Roob and Corwin","McGlynn, Padberg and Bogan",83,Kirgili,561724673-9,31,374288950680370,3580557960145994,amet turpis elementum ligula vehicula consequat morbi a ipsum integer,2023-02-28,7.54 +Koelpin-Dicki,Grader,Ornamental Railings,Herzog LLC,"Marquardt, Berge and Corkery",19,Srubec,758267626-2,12,6396431946998362,4175005352440697,semper rutrum nulla nunc purus phasellus in felis donec semper sapien a libero nam dui proin leo odio porttitor id,2023-03-12,18.2 +Adams and Sons,Backhoe,Roofing (Asphalt),"Will, Paucek and Luettgen",Hane-Heaney,81,Huifeng,925057692-7,51,5350965259826013,5381044667065516,sed lacus morbi sem mauris laoreet ut rhoncus aliquet pulvinar sed nisl,2022-07-24,80.03 +Greenholt and Sons,Crawler,Ornamental Railings,Grant-Stroman,Gottlieb-Lebsack,27,Trondheim,344790479-8,39,5100143050486022,67715312291635427,velit vivamus vel nulla eget eros elementum pellentesque quisque porta,2023-04-18,12.77 diff --git a/sample_csvs/MOCK_LICENSES.csv b/sample_csvs/licenses-sample.csv similarity index 100% rename from sample_csvs/MOCK_LICENSES.csv rename to sample_csvs/licenses-sample.csv diff --git a/sample_csvs/MOCK_LOCATIONS.csv b/sample_csvs/locations-sample.csv similarity index 100% rename from sample_csvs/MOCK_LOCATIONS.csv rename to sample_csvs/locations-sample.csv diff --git a/sample_csvs/users-sample.csv b/sample_csvs/users-sample.csv new file mode 100644 index 0000000000..8ee2359cbc --- /dev/null +++ b/sample_csvs/users-sample.csv @@ -0,0 +1,101 @@ +First Name,Last Name,Email,Username,Activated,Location,Address,City,State,Country,Postal Code,Website,Phone,Job Title,Notes,Employee Number,Company,Manager,Remote,VIP,Start Date,End Date,Gravatar +Reagen,Tenant,rtenant0@istockphoto.com,rtenant0,true,"Braun, Ullrich and O'Hara",0406 Emmet Drive,,,,,https://netvibes.com,895-137-2034,Nurse Practicioner,ac tellus semper interdum mauris ullamcorper purus sit amet nulla quisque arcu libero rutrum,"",,Reagen Tenant,true,true,2022-03-18,2022-12-04,rtenant0@soup.io +Kenneth,Lefeuvre,klefeuvre1@woothemes.com,klefeuvre1,false,Mueller LLC,404 Dennis Alley,,,,,,,Senior Quality Engineer,lacus at turpis donec posuere metus vitae ipsum,"",,,true,,,, +Kiel,Eland,keland2@tinyurl.com,keland2,false,"Mayer, Jacobi and Gibson",3058 Declaration Park,,,,,,,Web Developer I,eu est congue elementum in hac habitasse platea dictumst morbi vestibulum velit id,"",,,true,,,, +Susana,Kinneally,skinneally3@purevolume.com,skinneally3,false,Block Inc,1 Chive Alley,,,,,,,Software Consultant,phasellus in felis donec semper sapien a libero nam dui proin leo odio porttitor id consequat,"",,,true,,,, +Wallis,Hadcock,whadcock4@abc.net.au,whadcock4,false,Roob-Bartoletti,496 Hazelcrest Terrace,,,,,,,Information Systems Manager,nam congue risus semper porta volutpat quam pede lobortis ligula sit amet eleifend,"","Hand, Rohan and Oberbrunner",,false,,,, +Audry,Piell,apiell5@china.com.cn,apiell5,false,Schoen-Hilpert,3136 Talmadge Circle,,,,,,,Chemical Engineer,congue risus semper porta volutpat quam pede lobortis ligula sit amet eleifend pede libero quis orci nullam,"",,,false,,,, +Veronike,Poytress,vpoytress6@scribd.com,vpoytress6,false,"D'Amore, Schoen and Rogahn",3 Northwestern Hill,,,,,http://acquirethisname.com,198-980-1723,Compensation Analyst,non mattis pulvinar nulla pede ullamcorper augue a suscipit nulla elit ac nulla sed vel enim sit amet,"",,Veronike Poytress,true,true,2022-05-10,2022-08-14,vpoytress6@ed.gov +Sibel,Serris,sserris7@mayoclinic.com,sserris7,true,Swaniawski and Sons,090 Summit Road,,,,,,,,,,,,false,,,, +Aldis,Smardon,asmardon8@ifeng.com,asmardon8,false,Huels-Reinger,2 Brown Trail,,,,,,,,,,,,true,,,, +Juan,,,jlowseley9,true,Nicolas and Sons,0 Village Pass,,,,,,,Cost Accountant,phasellus id sapien in sapien,"",,,true,,,, +Thaine,Coger,tcogera@naver.com,tcogera,false,Zieme-Davis,31 Little Fleur Park,,,,,,,,,,,,true,,,, +Adrien,Ciotti,aciottib@ucoz.ru,aciottib,true,"Ward, Wolff and King",61 Graedel Hill,,,,,,,,,,,,false,,,, +Harmony,Vanetti,hvanettic@wordpress.com,hvanettic,true,"Kulas, Heller and Cormier",35 Hintze Drive,,,,,,,,,,Wyman-Jenkins,,true,,,, +Pansie,Morston,pmorstond@ebay.com,pmorstond,false,Mosciski-Graham,73222 Walton Street,,,,,,,,,,,,false,,,, +Court,Halse,chalsee@walmart.com,chalsee,true,"O'Reilly, Hilll and Stokes",61514 Pepper Wood Trail,,,,,,,,,,,,false,,,, +Gerardo,Scrowby,gscrowbyf@multiply.com,gscrowbyf,false,Cruickshank-Kling,18314 Schlimgen Point,,,,,,,,,,,,false,,,, +Imojean,Veare,iveareg@chronoengine.com,iveareg,false,Schultz-Lakin,9 Blaine Street,,,,,https://soup.io,502-710-6094,Environmental Tech,consequat lectus in est risus auctor sed,"",,Imojean Veare,false,true,2022-01-24,2022-08-06,iveareg@answers.com +Samuel,Bickley,sbickleyh@gravatar.com,sbickleyh,true,Langworth-Friesen,5891 Bunker Hill Crossing,,,,,,,,,,Corkery and Sons,,false,,,, +Niall,Ethington,nethingtoni@google.fr,nethingtoni,true,"Ledner, Klocko and Sipes",511 Springs Parkway,,,,,,,,,,,,false,,,, +Elfreda,Ilyukhov,eilyukhovj@mashable.com,eilyukhovj,false,Spinka-Ortiz,475 Bluestem Point,,,,,,,Clinical Specialist,felis ut at dolor quis odio consequat varius integer ac,"",,,true,,,, +Serena,Josling,sjoslingk@oracle.com,sjoslingk,false,Wuckert-Schuster,10512 Fuller Avenue,,,,,,,,,,"Cruickshank, Ziemann and Kreiger",,false,,,, +Sheelah,Dockwray,sdockwrayl@narod.ru,sdockwrayl,true,Orn-Koss,4241 Scofield Circle,,,,,,,Nurse,vestibulum sed magna at nunc commodo placerat praesent blandit nam nulla integer pede,"",Hermann-Anderson,,true,,,, +Lanette,Remnant,lremnantm@vinaora.com,lremnantm,false,Abshire LLC,8 Buena Vista Junction,,,,,,,General Manager,mauris ullamcorper purus sit amet,"",Bahringer-Lockman,,false,,,, +Jordan,Pritty,jprittyn@reverbnation.com,jprittyn,false,Stroman LLC,36 Becker Plaza,,,,,,,,,,Durgan-Bosco,,true,,,, +Lorrin,Boni,lbonio@msn.com,lbonio,true,Bogisich-Nolan,7 Messerschmidt Point,,,,,,,,,,,,false,,,, +Marie-ann,Waind,mwaindp@ucoz.ru,mwaindp,true,"Hartmann, Weissnat and Cassin",68082 Schmedeman Court,,,,,,,Nurse,duis faucibus accumsan odio curabitur convallis duis consequat,"",,,true,,,, +Marti,Daens,mdaensq@huffingtonpost.com,mdaensq,false,Robel-Roob,7 Knutson Pass,,,,,http://gov.uk,381-382-8857,Electrical Engineer,sapien a libero nam dui proin leo,"",,Marti Daens,false,true,2021-12-19,2022-12-08,mdaensq@themeforest.net +Scotti,Gillison,sgillisonr@intel.com,sgillisonr,true,Howe-Collins,44 Elka Terrace,,,,,,,Financial Advisor,viverra eget congue eget semper rutrum nulla nunc purus,"",,,true,,,, +Bobbe,Alven,balvens@csmonitor.com,balvens,true,Weimann-Bernhard,71 Manley Park,,,,,,,,,,Champlin-Turcotte,,false,,,, +Ferdy,Kincade,fkincadet@wsj.com,fkincadet,true,Bode Inc,3023 Novick Alley,,,,,,,Statistician IV,amet nulla quisque arcu libero,"",,,true,,,, +Rachael,Briers,rbriersu@vimeo.com,rbriersu,false,Koch and Sons,71 Shoshone Court,,,,,,,,,,,,true,,,, +Ado,Bartholin,abartholinv@ft.com,abartholinv,false,"Buckridge, Klein and Johnston",11 Jenna Avenue,,,,,,,,,,,,false,,,, +Tessy,,,tsailerw,false,Parker-Beer,0964 Vidon Way,,,,,,,Structural Engineer,aliquet ultrices erat tortor sollicitudin,"",,,true,,,, +Chloe,Gebbie,cgebbiex@cam.ac.uk,cgebbiex,false,Smith Inc,8 5th Alley,,,,,,,,,,,,true,,,, +Emlyn,Cusick,ecusicky@homestead.com,ecusicky,true,Dickens LLC,43 Canary Point,,,,,,,Health Coach II,eu tincidunt in leo maecenas pulvinar lobortis est phasellus sit amet erat nulla tempus vivamus in,"",,,true,,,, +Kenneth,McGuire,kmcguirez@scribd.com,kmcguirez,true,"Torphy, Bednar and Davis",2 Stone Corner Park,,,,,,,Nurse Practicioner,tortor duis mattis egestas metus aenean fermentum donec ut mauris eget massa,"",,,false,,,, +Berri,Forson,bforson10@pcworld.com,bforson10,true,"D'Amore, Larkin and O'Keefe",266 Alpine Road,,,,,,,Tax Accountant,in purus eu magna vulputate luctus cum sociis natoque penatibus et,"",,,true,,,, +Bree,Jiru,bjiru11@gravatar.com,bjiru11,true,Altenwerth LLC,58 4th Alley,,,,,,,,,,Farrell and Sons,,true,,,, +Kathie,Dignan,kdignan12@bbb.org,kdignan12,true,"Pagac, Effertz and Padberg",973 Banding Point,,,,,,,,,,"Marquardt, Cummings and Bosco",,true,,,, +Filbert,Van Der Straaten,fvanderstraaten13@sourceforge.net,fvanderstraaten13,false,"Cronin, Mante and Klein",0 Stuart Junction,,,,,,,Research Assistant I,ut massa volutpat convallis morbi odio odio elementum eu interdum eu tincidunt in leo maecenas,"",Bauch-Kling,,true,,,, +Miguel,Farbrace,mfarbrace14@barnesandnoble.com,mfarbrace14,false,O'Keefe Inc,1 Mallory Avenue,,,,,,,Assistant Manager,a libero nam dui proin leo odio porttitor id consequat in consequat ut nulla sed accumsan felis,"",Schmeler-Krajcik,,false,,,, +Em,,,ekittley15,false,Schuppe-Barrows,9946 Canary Pass,,,,,,,Nuclear Power Engineer,odio curabitur convallis duis consequat dui nec nisi,"","Abshire, Goyette and Spinka",,true,,,, +Bill,Morales,bmorales16@addtoany.com,bmorales16,true,Leannon Inc,0 Hudson Circle,,,,,,,,,,,,true,,,, +Spenser,Branca,sbranca17@tripod.com,sbranca17,false,Halvorson Inc,7904 Green Road,,,,,,,,,,,,false,,,, +Dawna,Ainslee,dainslee18@nifty.com,dainslee18,true,"Strosin, Collier and O'Connell",17 Granby Center,,,,,,,,,,,,false,,,, +Lind,Fontanet,lfontanet19@lycos.com,lfontanet19,false,Bins-Kunde,84 Kenwood Junction,,,,,,,,,,"Romaguera, Adams and Schoen",,false,,,, +Ashli,Gheorghie,agheorghie1a@blogtalkradio.com,agheorghie1a,false,Hauck Inc,8 Warrior Drive,,,,,,,,,,Wisozk Inc,,false,,,, +Janice,Freddi,jfreddi1b@behance.net,jfreddi1b,true,"Stoltenberg, Aufderhar and Schaden",0 Esch Alley,,,,,,,,,,,,false,,,, +Reinwald,Ivakhin,rivakhin1c@ask.com,rivakhin1c,false,Jacobson-Mosciski,24 New Castle Alley,,,,,,,,,,Welch and Sons,,true,,,, +Lindsey,Trevance,ltrevance1d@prnewswire.com,ltrevance1d,true,Hansen-Luettgen,17443 Lindbergh Plaza,,,,,,,Web Designer II,interdum eu tincidunt in leo maecenas pulvinar lobortis est phasellus,"",,,false,,,, +Cordi,Frostdyke,cfrostdyke1e@weather.com,cfrostdyke1e,false,Kohler Inc,53066 Merchant Plaza,,,,,,,,,,,,false,,,, +Jayme,Piatto,jpiatto1f@slate.com,jpiatto1f,true,Johnson and Sons,5598 Fuller Avenue,,,,,,,Graphic Designer,accumsan felis ut at dolor quis odio consequat varius integer,"",,,true,,,, +Henriette,Glanvill,hglanvill1g@ifeng.com,hglanvill1g,true,"Mills, Kautzer and Stiedemann",6461 Stang Center,,,,,,,,,,Gibson and Sons,,true,,,, +Izak,Legen,ilegen1h@berkeley.edu,ilegen1h,true,"Schiller, Runolfsson and Gottlieb",86874 Hovde Avenue,,,,,,,,,,,,false,,,, +Silas,Vallentin,svallentin1i@zdnet.com,svallentin1i,false,Wolff Inc,15041 Graceland Park,,,,,,,,,,,,true,,,, +Ansel,Augustus,aaugustus1j@oaic.gov.au,aaugustus1j,true,"Rippin, Frami and Parker",843 Nova Alley,,,,,,,Assistant Media Planner,ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae donec,"",Morissette and Sons,,false,,,, +Annemarie,Reburn,areburn1k@networksolutions.com,areburn1k,true,Dach Group,05 Crowley Avenue,,,,,,,Administrative Assistant IV,vivamus tortor duis mattis egestas metus aenean fermentum donec,"","Murazik, Wyman and Yost",,false,,,, +Fee,Bissell,fbissell1l@comsenz.com,fbissell1l,true,Braun Group,678 Nelson Hill,,,,,,,Health Coach IV,sed augue aliquam erat volutpat in congue etiam justo etiam pretium iaculis justo in,"","Lindgren, Quitzon and Heathcote",,true,,,, +Zenia,Kordt,zkordt1m@com.com,zkordt1m,true,"Harvey, Fay and Yundt",6337 Mayer Trail,,,,,,,Associate Professor,congue etiam justo etiam pretium iaculis justo in hac habitasse platea dictumst etiam faucibus cursus urna,"",,,true,,,, +Winthrop,Eales,weales1n@stanford.edu,weales1n,true,Johns-Kshlerin,79780 Helena Lane,,,,,,,Accountant I,platea dictumst etiam faucibus cursus urna ut,"",,,false,,,, +Davis,,,dnatalie1o,false,Tillman-Marquardt,3893 Express Junction,,,,,https://answers.com,266-409-9118,Teacher,suspendisse accumsan tortor quis turpis sed ante vivamus tortor,"",Casper-Grimes,Davis Natalie,false,false,2022-04-08,2022-09-24,dnatalie1o@altervista.org +Massimiliano,Fardo,mfardo1p@imgur.com,mfardo1p,true,Kozey Group,72 Hauk Terrace,,,,,,,Account Coordinator,parturient montes nascetur ridiculus mus etiam vel augue vestibulum rutrum rutrum neque aenean auctor gravida sem praesent id,"",Bednar Inc,,false,,,, +Dacey,Larrie,dlarrie1q@digg.com,dlarrie1q,false,Jerde-Ullrich,70371 Del Mar Center,,,,,,,,,,,,true,,,, +Pattie,Melvin,pmelvin1r@discovery.com,pmelvin1r,false,"McDermott, Mayert and Kemmer",845 Anderson Point,,,,,,,Safety Technician II,sit amet erat nulla tempus vivamus in felis eu sapien cursus vestibulum proin,"",,,true,,,, +Franny,Laughlin,flaughlin1s@blogspot.com,flaughlin1s,false,Renner-Terry,94 Northfield Pass,,,,,,,,,,"Parisian, Hudson and Bahringer",,false,,,, +Mariel,Roelvink,mroelvink1t@timesonline.co.uk,mroelvink1t,true,Russel-Blanda,434 Crest Line Road,,,,,http://amazon.de,429-401-0847,Quality Engineer,vel pede morbi porttitor lorem id ligula suspendisse ornare consequat lectus in est risus auctor sed tristique in tempus,"",,Mariel Roelvink,true,false,2022-02-21,2022-09-01,mroelvink1t@feedburner.com +Josee,Ghirardi,jghirardi1u@arizona.edu,jghirardi1u,true,Koss-Schmitt,96 Kingsford Avenue,,,,,,,,,,,,true,,,, +Felix,Pheasant,fpheasant1v@soundcloud.com,fpheasant1v,true,Rogahn Group,43 Northridge Place,,,,,,,,,,Hoppe-O'Reilly,,false,,,, +Stearne,Gwatkins,sgwatkins1w@jugem.jp,sgwatkins1w,false,"Koch, Ferry and Marks",6 Victoria Circle,,,,,,,,,,,,false,,,, +Nathaniel,Parkinson,nparkinson1x@springer.com,nparkinson1x,true,Harber Group,4589 Clarendon Way,,,,,,,Software Consultant,non mattis pulvinar nulla pede ullamcorper augue a suscipit nulla elit ac nulla sed vel enim sit amet nunc viverra,"",Koepp LLC,,true,,,, +Blondelle,Crawley,bcrawley1y@ezinearticles.com,bcrawley1y,true,"Hirthe, Bashirian and Feeney",17149 Ridge Oak Park,,,,,,,Help Desk Technician,sit amet cursus id turpis integer aliquet massa id lobortis convallis tortor risus dapibus augue vel accumsan tellus nisi eu,"",,,true,,,, +Pammi,Yair,pyair1z@virginia.edu,pyair1z,true,"Pfeffer, Schmeler and Dibbert",541 Beilfuss Alley,,,,,,,,,,,,true,,,, +Tremain,Lattimer,tlattimer20@go.com,tlattimer20,true,"Kuvalis, Kris and Howell",19 Red Cloud Parkway,,,,,,,,,,Gutkowski LLC,,true,,,, +Moreen,Fermin,mfermin21@w3.org,mfermin21,false,"Romaguera, Spinka and Bayer",50 Eagle Crest Circle,,,,,,,,,,,,true,,,, +Jena,Taborre,jtaborre22@pinterest.com,jtaborre22,true,Raynor Group,97 Helena Center,,,,,,,Dental Hygienist,vel enim sit amet nunc viverra dapibus nulla suscipit ligula in lacus curabitur at ipsum ac tellus semper interdum mauris,"",,,false,,,, +Odille,Stede,ostede23@biglobe.ne.jp,ostede23,true,"Boyle, Schowalter and Mayer",88993 Sunbrook Street,,,,,http://live.com,464-815-6024,,,,,Odille Stede,true,false,2021-12-12,2022-06-27,ostede23@lycos.com +Kori,Fulk,kfulk24@tiny.cc,kfulk24,true,Konopelski and Sons,43 Mockingbird Avenue,,,,,,,,,,"Nikolaus, Cassin and Streich",,true,,,, +Pollyanna,Cardon,pcardon25@infoseek.co.jp,pcardon25,false,Hickle Group,52088 Almo Junction,,,,,,,,,,"Cronin, Cummerata and Tromp",,false,,,, +Austin,McGeneay,amcgeneay26@tmall.com,amcgeneay26,true,Yundt-Howell,021 4th Park,,,,,,,Social Worker,est phasellus sit amet erat nulla tempus vivamus in felis eu sapien cursus vestibulum proin eu mi nulla,"",,,true,,,, +Imogen,Hamill,ihamill27@w3.org,ihamill27,false,Herman-Schulist,802 Sunbrook Place,,,,,http://vistaprint.com,344-665-1217,Registered Nurse,curabitur convallis duis consequat dui nec nisi volutpat,"",,Imogen Hamill,false,false,2021-12-03,2023-04-16,ihamill27@over-blog.com +Hershel,Bielfeld,hbielfeld28@soup.io,hbielfeld28,true,Kreiger and Sons,6944 Buell Trail,,,,,,,Actuary,curae donec pharetra magna vestibulum aliquet ultrices erat,"",Schmitt-Abbott,,true,,,, +Korry,,,ksteckings29,true,Bogan Inc,800 Ilene Parkway,,,,,,,,,,,,true,,,, +Cookie,Warret,cwarret2a@sakura.ne.jp,cwarret2a,false,Murphy-Yost,9 Vera Terrace,,,,,,,Mechanical Systems Engineer,semper rutrum nulla nunc purus phasellus in felis donec semper sapien a libero nam dui proin leo odio,"","Lueilwitz, Beahan and Raynor",,false,,,, +Wynne,Gonnely,wgonnely2b@facebook.com,wgonnely2b,true,"Frami, Erdman and Mayert",051 Kenwood Junction,,,,,,,,,,,,true,,,, +Ethelred,Bogey,ebogey2c@infoseek.co.jp,ebogey2c,true,Christiansen and Sons,7414 2nd Pass,,,,,,,Media Manager II,donec odio justo sollicitudin ut suscipit a feugiat et eros vestibulum ac est lacinia,"",,,false,,,, +Alvera,Tunuy,atunuy2d@bizjournals.com,atunuy2d,false,"Vandervort, Veum and Walker",152 Division Park,,,,,,,,,,"Bartell, Hartmann and Jerde",,false,,,, +Martino,Rosenbusch,mrosenbusch2e@seesaa.net,mrosenbusch2e,true,Parisian-Romaguera,130 Westridge Alley,,,,,http://bravesites.com,626-888-4072,,,,"Keebler, Walter and Senger",Martino Rosenbusch,false,false,2022-01-20,2022-09-11,mrosenbusch2e@about.me +Susi,Trapp,strapp2f@ft.com,strapp2f,false,"Rogahn, Volkman and McDermott",72958 Vahlen Avenue,,,,,,,,,,Nitzsche-Langosh,,true,,,, +Gordon,Alflatt,galflatt2g@marriott.com,galflatt2g,false,Effertz-Howell,0681 International Plaza,,,,,,,Research Associate,duis aliquam convallis nunc proin,"","Farrell, Hoeger and O'Keefe",,true,,,, +Derrick,Braidon,dbraidon2h@blogs.com,dbraidon2h,false,Prosacco-Walker,8760 Del Sol Circle,,,,,,,,,,Koss-Bogisich,,true,,,, +Minnnie,Bonar,mbonar2i@biglobe.ne.jp,mbonar2i,true,Daniel LLC,5424 Karstens Alley,,,,,,,Web Designer III,faucibus orci luctus et ultrices posuere cubilia curae mauris,"",,,true,,,, +Casper,Sinnatt,csinnatt2j@hc360.com,csinnatt2j,true,Barrows-Mertz,79 Melvin Center,,,,,,,,,,,,false,,,, +Chloe,Fist,cfist2k@auda.org.au,cfist2k,false,"Kling, Streich and Kertzmann",47064 Glacier Hill Court,,,,,,,,,,Altenwerth-Leuschke,,true,,,, +Elysha,Gosenell,egosenell2l@ft.com,egosenell2l,true,Hirthe LLC,70 Old Gate Alley,,,,,,,Nuclear Power Engineer,amet cursus id turpis integer aliquet massa id lobortis convallis tortor risus dapibus augue vel accumsan tellus,"",Macejkovic and Sons,,false,,,, +Gerianna,Feirn,gfeirn2m@bing.com,gfeirn2m,true,"Collier, Kirlin and Armstrong",881 Summer Ridge Circle,,,,,,,,,,,,false,,,, +Lucas,Mulvany,lmulvany2n@deliciousdays.com,lmulvany2n,false,Bahringer Group,639 La Follette Circle,,,,,,,,,,,,true,,,, +Lenore,Menendez,lmenendez2o@cdbaby.com,lmenendez2o,true,"Nader, Harvey and Casper",02 Springs Avenue,,,,,https://a8.net,926-526-1742,Community Outreach Specialist,ut odio cras mi pede malesuada in imperdiet et commodo vulputate justo in blandit ultrices enim lorem ipsum dolor,"",,Lenore Menendez,true,false,2022-01-11,2022-07-29,lmenendez2o@ifeng.com +Collete,Brandes,cbrandes2p@accuweather.com,cbrandes2p,true,Koepp-Parisian,5 Haas Way,,,,,,,,,,,,false,,,, +Osborne,Gummory,ogummory2q@1und1.de,ogummory2q,false,Mertz and Sons,91749 Haas Alley,,,,,,,,,,,,true,,,, +Emiline,Grossman,egrossman2r@dmoz.org,egrossman2r,true,Parisian and Sons,3872 Eagle Crest Court,,,,,https://uiuc.edu,338-830-8647,Food Chemist,dolor vel est donec odio justo sollicitudin ut suscipit a feugiat et eros vestibulum ac est lacinia nisi venenatis,"",Williamson-Casper,Emiline Grossman,true,true,2021-11-10,2022-10-01,egrossman2r@spiegel.de diff --git a/tests/Browser/LoginTest.php b/tests/Browser/LoginTest.php deleted file mode 100644 index 18f5172f15..0000000000 --- a/tests/Browser/LoginTest.php +++ /dev/null @@ -1,46 +0,0 @@ -make(); - - // We override the existing password to use a hash of one we know - $user->password = '$2y$10$8o5W8fgAKJbN3Kz4taepeeRVgKsG8pkZ1L4eJfdEKrn2mgI/JgCJy'; - - // We want a user that is a superuser - $user->permissions = '{"superuser": 1}'; - - $user->save(); - - Setting::factory()->create(); - - $this->browse(function (Browser $browser) { - $browser->visitRoute('login') - ->assertSee(trans('auth/general.login_prompt')); - }); - - $this->browse(function ($browser) use ($user) { - $browser->visitRoute('login') - ->type('username', $user->username) - ->type('password', 'password') - ->press(trans('auth/general.login')) - ->assertPathIs('/'); - $browser->screenshot('dashboard'); - }); - } -} diff --git a/tests/Browser/Pages/HomePage.php b/tests/Browser/Pages/HomePage.php deleted file mode 100644 index 26bf174f3d..0000000000 --- a/tests/Browser/Pages/HomePage.php +++ /dev/null @@ -1,41 +0,0 @@ - '#selector', - ]; - } -} diff --git a/tests/Browser/Pages/Page.php b/tests/Browser/Pages/Page.php deleted file mode 100644 index f8d76222c0..0000000000 --- a/tests/Browser/Pages/Page.php +++ /dev/null @@ -1,20 +0,0 @@ - '#selector', - ]; - } -} diff --git a/tests/Browser/console/.gitignore b/tests/Browser/console/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/tests/Browser/console/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/Browser/screenshots/.gitignore b/tests/Browser/screenshots/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/tests/Browser/screenshots/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/Browser/source/.gitignore b/tests/Browser/source/.gitignore deleted file mode 100644 index d6b7ef32c8..0000000000 --- a/tests/Browser/source/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/DuskTestCase.php b/tests/DuskTestCase.php deleted file mode 100644 index e591a7c771..0000000000 --- a/tests/DuskTestCase.php +++ /dev/null @@ -1,63 +0,0 @@ -addArguments(collect([ - '--window-size=1920,1080', - ])->unless($this->hasHeadlessDisabled(), function ($items) { - return $items->merge([ - '--disable-gpu', - '--headless', - ]); - })->all()); - - return RemoteWebDriver::create( - $_ENV['DUSK_DRIVER_URL'] ?? 'http://127.0.0.1:9515', - DesiredCapabilities::chrome()->setCapability( - ChromeOptions::CAPABILITY, $options - ) - ); - } - - /** - * Determine whether the Dusk command has disabled headless mode. - * - * @return bool - */ - protected function hasHeadlessDisabled() - { - return isset($_SERVER['DUSK_HEADLESS_DISABLED']) || - isset($_ENV['DUSK_HEADLESS_DISABLED']); - } -} diff --git a/tests/Feature/Api/Assets/AssetCheckinTest.php b/tests/Feature/Api/Assets/AssetCheckinTest.php new file mode 100644 index 0000000000..f71191d80c --- /dev/null +++ b/tests/Feature/Api/Assets/AssetCheckinTest.php @@ -0,0 +1,30 @@ +superuser()->create(); + $asset = Asset::factory()->create(['last_checkin' => null]); + + $asset->checkOut(User::factory()->create(), $admin, now()); + + $this->actingAsForApi($admin) + ->postJson(route('api.asset.checkin', $asset)) + ->assertOk(); + + $this->assertNotNull( + $asset->fresh()->last_checkin, + 'last_checkin field should be set on checkin' + ); + } +} diff --git a/tests/Feature/Api/Assets/AssetIndexTest.php b/tests/Feature/Api/Assets/AssetIndexTest.php index 3618c6e01e..778483c1c9 100644 --- a/tests/Feature/Api/Assets/AssetIndexTest.php +++ b/tests/Feature/Api/Assets/AssetIndexTest.php @@ -3,9 +3,9 @@ namespace Tests\Feature\Api\Assets; use App\Models\Asset; +use App\Models\Company; use App\Models\User; use Illuminate\Testing\Fluent\AssertableJson; -use Laravel\Passport\Passport; use Tests\Support\InteractsWithSettings; use Tests\TestCase; @@ -17,14 +17,14 @@ class AssetIndexTest extends TestCase { Asset::factory()->count(3)->create(); - Passport::actingAs(User::factory()->superuser()->create()); - $this->getJson( - route('api.assets.index', [ - 'sort' => 'name', - 'order' => 'asc', - 'offset' => '0', - 'limit' => '20', - ])) + $this->actingAsForApi(User::factory()->superuser()->create()) + ->getJson( + route('api.assets.index', [ + 'sort' => 'name', + 'order' => 'asc', + 'offset' => '0', + 'limit' => '20', + ])) ->assertOk() ->assertJsonStructure([ 'total', @@ -32,4 +32,50 @@ class AssetIndexTest extends TestCase ]) ->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc()); } + + public function testAssetIndexAdheresToCompanyScoping() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $assetA = Asset::factory()->for($companyA)->create(); + $assetB = Asset::factory()->for($companyB)->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewAssets()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewAssets()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.assets.index')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.assets.index')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.assets.index')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.assets.index')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.assets.index')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseDoesNotContainInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.assets.index')) + ->assertResponseDoesNotContainInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + } } diff --git a/tests/Feature/Api/Assets/AssetStoreTest.php b/tests/Feature/Api/Assets/AssetStoreTest.php new file mode 100644 index 0000000000..5a68aebccb --- /dev/null +++ b/tests/Feature/Api/Assets/AssetStoreTest.php @@ -0,0 +1,19 @@ +actingAsForApi(User::factory()->create()) + ->postJson(route('api.assets.store')) + ->assertForbidden(); + } +} diff --git a/tests/Feature/Api/Assets/AssetsForSelectListTest.php b/tests/Feature/Api/Assets/AssetsForSelectListTest.php new file mode 100644 index 0000000000..cccae38d38 --- /dev/null +++ b/tests/Feature/Api/Assets/AssetsForSelectListTest.php @@ -0,0 +1,76 @@ +create(['asset_tag' => '0001']); + Asset::factory()->create(['asset_tag' => '0002']); + + $response = $this->actingAsForApi(User::factory()->create()) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertOk(); + + $results = collect($response->json('results')); + + $this->assertEquals(2, $results->count()); + $this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, '0001'))); + $this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, '0002'))); + } + + public function testAssetsAreScopedToCompanyWhenMultipleCompanySupportEnabled() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $assetA = Asset::factory()->for($companyA)->create(['asset_tag' => '0001']); + $assetB = Asset::factory()->for($companyB)->create(['asset_tag' => '0002']); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewAssets()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewAssets()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertResponseContainsInResults($assetA) + ->assertResponseContainsInResults($assetB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertResponseContainsInResults($assetA) + ->assertResponseContainsInResults($assetB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertResponseContainsInResults($assetA) + ->assertResponseContainsInResults($assetB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertResponseContainsInResults($assetA) + ->assertResponseContainsInResults($assetB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertResponseContainsInResults($assetA) + ->assertResponseDoesNotContainInResults($assetB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('assets.selectlist', ['search' => '000'])) + ->assertResponseDoesNotContainInResults($assetA) + ->assertResponseContainsInResults($assetB); + } +} diff --git a/tests/Feature/Api/Assets/RequestableAssetsTest.php b/tests/Feature/Api/Assets/RequestableAssetsTest.php new file mode 100644 index 0000000000..8649b1b00b --- /dev/null +++ b/tests/Feature/Api/Assets/RequestableAssetsTest.php @@ -0,0 +1,79 @@ +actingAsForApi(User::factory()->create()) + ->getJson(route('api.assets.requestable')) + ->assertForbidden(); + } + + public function testReturnsRequestableAssets() + { + $requestableAsset = Asset::factory()->requestable()->create(['asset_tag' => 'requestable']); + $nonRequestableAsset = Asset::factory()->nonrequestable()->create(['asset_tag' => 'non-requestable']); + + $this->actingAsForApi(User::factory()->viewRequestableAssets()->create()) + ->getJson(route('api.assets.requestable')) + ->assertOk() + ->assertResponseContainsInRows($requestableAsset, 'asset_tag') + ->assertResponseDoesNotContainInRows($nonRequestableAsset, 'asset_tag'); + } + + public function testRequestableAssetsAreScopedToCompanyWhenMultipleCompanySupportEnabled() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $assetA = Asset::factory()->requestable()->for($companyA)->create(['asset_tag' => '0001']); + $assetB = Asset::factory()->requestable()->for($companyB)->create(['asset_tag' => '0002']); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewRequestableAssets()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewRequestableAssets()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.assets.requestable')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.assets.requestable')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.assets.requestable')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.assets.requestable')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.assets.requestable')) + ->assertResponseContainsInRows($assetA, 'asset_tag') + ->assertResponseDoesNotContainInRows($assetB, 'asset_tag'); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.assets.requestable')) + ->assertResponseDoesNotContainInRows($assetA, 'asset_tag') + ->assertResponseContainsInRows($assetB, 'asset_tag'); + } +} diff --git a/tests/Feature/Api/Components/ComponentIndexTest.php b/tests/Feature/Api/Components/ComponentIndexTest.php new file mode 100644 index 0000000000..ee83b7a46d --- /dev/null +++ b/tests/Feature/Api/Components/ComponentIndexTest.php @@ -0,0 +1,60 @@ +count(2)->create(); + + $componentA = Component::factory()->for($companyA)->create(); + $componentB = Component::factory()->for($companyB)->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewComponents()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewComponents()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.components.index')) + ->assertResponseContainsInRows($componentA) + ->assertResponseContainsInRows($componentB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.components.index')) + ->assertResponseContainsInRows($componentA) + ->assertResponseContainsInRows($componentB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.components.index')) + ->assertResponseContainsInRows($componentA) + ->assertResponseContainsInRows($componentB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.components.index')) + ->assertResponseContainsInRows($componentA) + ->assertResponseContainsInRows($componentB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.components.index')) + ->assertResponseContainsInRows($componentA) + ->assertResponseDoesNotContainInRows($componentB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.components.index')) + ->assertResponseDoesNotContainInRows($componentA) + ->assertResponseContainsInRows($componentB); + } +} diff --git a/tests/Feature/Api/Consumables/ConsumablesIndexTest.php b/tests/Feature/Api/Consumables/ConsumablesIndexTest.php new file mode 100644 index 0000000000..33c10ed078 --- /dev/null +++ b/tests/Feature/Api/Consumables/ConsumablesIndexTest.php @@ -0,0 +1,60 @@ +count(2)->create(); + + $consumableA = Consumable::factory()->for($companyA)->create(); + $consumableB = Consumable::factory()->for($companyB)->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewConsumables()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewConsumables()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.consumables.index')) + ->assertResponseContainsInRows($consumableA) + ->assertResponseContainsInRows($consumableB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.consumables.index')) + ->assertResponseContainsInRows($consumableA) + ->assertResponseContainsInRows($consumableB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.consumables.index')) + ->assertResponseContainsInRows($consumableA) + ->assertResponseContainsInRows($consumableB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.consumables.index')) + ->assertResponseContainsInRows($consumableA) + ->assertResponseContainsInRows($consumableB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.consumables.index')) + ->assertResponseContainsInRows($consumableA) + ->assertResponseDoesNotContainInRows($consumableB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.consumables.index')) + ->assertResponseDoesNotContainInRows($consumableA) + ->assertResponseContainsInRows($consumableB); + } +} diff --git a/tests/Feature/Api/Departments/DepartmentIndexTest.php b/tests/Feature/Api/Departments/DepartmentIndexTest.php new file mode 100644 index 0000000000..1a3884308f --- /dev/null +++ b/tests/Feature/Api/Departments/DepartmentIndexTest.php @@ -0,0 +1,94 @@ +getJson(route('api.departments.index'))->assertRedirect(); + } + + public function testViewingDepartmentIndexRequiresPermission() + { + $this->actingAsForApi(User::factory()->create()) + ->getJson(route('api.departments.index')) + ->assertForbidden(); + } + + public function testDepartmentIndexReturnsExpectedDepartments() + { + Department::factory()->count(3)->create(); + + $this->actingAsForApi(User::factory()->superuser()->create()) + ->getJson( + route('api.departments.index', [ + 'sort' => 'name', + 'order' => 'asc', + 'offset' => '0', + 'limit' => '20', + ])) + ->assertOk() + ->assertJsonStructure([ + 'total', + 'rows', + ]) + ->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc()); + } + + public function testDepartmentIndexAdheresToCompanyScoping() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $departmentA = Department::factory()->for($companyA)->create(); + $departmentB = Department::factory()->for($companyB)->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewDepartments()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewDepartments()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.departments.index')) + ->assertResponseContainsInRows($departmentA) + ->assertResponseContainsInRows($departmentB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.departments.index')) + ->assertResponseContainsInRows($departmentA) + ->assertResponseContainsInRows($departmentB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.departments.index')) + ->assertResponseContainsInRows($departmentA) + ->assertResponseContainsInRows($departmentB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.departments.index')) + ->assertResponseContainsInRows($departmentA) + ->assertResponseContainsInRows($departmentB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.departments.index')) + ->assertResponseContainsInRows($departmentA) + ->assertResponseDoesNotContainInRows($departmentB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.departments.index')) + ->assertResponseDoesNotContainInRows($departmentA) + ->assertResponseContainsInRows($departmentB); + } +} diff --git a/tests/Feature/Api/Groups/GroupStoreTest.php b/tests/Feature/Api/Groups/GroupStoreTest.php new file mode 100644 index 0000000000..9ffba51913 --- /dev/null +++ b/tests/Feature/Api/Groups/GroupStoreTest.php @@ -0,0 +1,41 @@ +actingAsForApi(User::factory()->create()) + ->postJson(route('api.groups.store')) + ->assertForbidden(); + } + + public function testCanStoreGroup() + { + $this->actingAsForApi(User::factory()->superuser()->create()) + ->postJson(route('api.groups.store'), [ + 'name' => 'My Awesome Group', + 'permissions' => [ + 'admin' => '1', + 'import' => '1', + 'reports.view' => '0', + ], + ]) + ->assertOk(); + + $group = Group::where('name', 'My Awesome Group')->first(); + + $this->assertNotNull($group); + $this->assertEquals('1', $group->decodePermissions()['admin']); + $this->assertEquals('1', $group->decodePermissions()['import']); + $this->assertEquals('0', $group->decodePermissions()['reports.view']); + } +} diff --git a/tests/Feature/Api/Licenses/LicensesIndexTest.php b/tests/Feature/Api/Licenses/LicensesIndexTest.php new file mode 100644 index 0000000000..a21a27da73 --- /dev/null +++ b/tests/Feature/Api/Licenses/LicensesIndexTest.php @@ -0,0 +1,60 @@ +count(2)->create(); + + $licenseA = License::factory()->for($companyA)->create(); + $licenseB = License::factory()->for($companyB)->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->viewLicenses()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->viewLicenses()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.licenses.index')) + ->assertResponseContainsInRows($licenseA) + ->assertResponseContainsInRows($licenseB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.licenses.index')) + ->assertResponseContainsInRows($licenseA) + ->assertResponseContainsInRows($licenseB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.licenses.index')) + ->assertResponseContainsInRows($licenseA) + ->assertResponseContainsInRows($licenseB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAsForApi($superUser) + ->getJson(route('api.licenses.index')) + ->assertResponseContainsInRows($licenseA) + ->assertResponseContainsInRows($licenseB); + + $this->actingAsForApi($userInCompanyA) + ->getJson(route('api.licenses.index')) + ->assertResponseContainsInRows($licenseA) + ->assertResponseDoesNotContainInRows($licenseB); + + $this->actingAsForApi($userInCompanyB) + ->getJson(route('api.licenses.index')) + ->assertResponseDoesNotContainInRows($licenseA) + ->assertResponseContainsInRows($licenseB); + } +} diff --git a/tests/Feature/Api/Locations/LocationsForSelectListTest.php b/tests/Feature/Api/Locations/LocationsForSelectListTest.php new file mode 100644 index 0000000000..4170cfc7f7 --- /dev/null +++ b/tests/Feature/Api/Locations/LocationsForSelectListTest.php @@ -0,0 +1,48 @@ +actingAsForApi(User::factory()->create()) + ->getJson(route('api.locations.selectlist')) + ->assertForbidden(); + } + + public function testLocationsReturned() + { + Location::factory()->create(); + + // see the where the "view.selectlists" is defined in the AuthServiceProvider + // for info on why "createUsers()" is used here. + $this->actingAsForApi(User::factory()->createUsers()->create()) + ->getJson(route('api.locations.selectlist')) + ->assertOk() + ->assertJsonStructure([ + 'results', + 'pagination', + 'total_count', + 'page', + 'page_count', + ]) + ->assertJson(fn(AssertableJson $json) => $json->has('results', 1)->etc()); + } + + public function testLocationsAreReturnedWhenUserIsUpdatingTheirProfileAndHasPermissionToUpdateLocation() + { + $this->actingAsForApi(User::factory()->canEditOwnLocation()->create()) + ->withHeader('referer', route('profile')) + ->getJson(route('api.locations.selectlist')) + ->assertOk(); + } +} diff --git a/tests/Feature/Api/Users/UpdateUserApiTest.php b/tests/Feature/Api/Users/UpdateUserApiTest.php new file mode 100644 index 0000000000..81c1154648 --- /dev/null +++ b/tests/Feature/Api/Users/UpdateUserApiTest.php @@ -0,0 +1,68 @@ +superuser()->create(); + $user = User::factory()->create(['activated' => 0]); + + $this->actingAsForApi($admin) + ->patch(route('api.users.update', $user), [ + 'activated' => 1, + ]); + + $this->assertEquals(1, $user->refresh()->activated); + } + + public function testApiUsersCanBeActivatedWithBooleanTrue() + { + $admin = User::factory()->superuser()->create(); + $user = User::factory()->create(['activated' => false]); + + $this->actingAsForApi($admin) + ->patch(route('api.users.update', $user), [ + 'activated' => true, + ]); + + $this->assertEquals(1, $user->refresh()->activated); + } + + public function testApiUsersCanBeDeactivatedWithNumber() + { + $admin = User::factory()->superuser()->create(); + $user = User::factory()->create(['activated' => true]); + + $this->actingAsForApi($admin) + ->patch(route('api.users.update', $user), [ + 'activated' => 0, + ]); + + $this->assertEquals(0, $user->refresh()->activated); + } + + public function testApiUsersCanBeDeactivatedWithBooleanFalse() + { + $admin = User::factory()->superuser()->create(); + $user = User::factory()->create(['activated' => true]); + + $this->actingAsForApi($admin) + ->patch(route('api.users.update', $user), [ + 'activated' => false, + ]); + + $this->assertEquals(0, $user->refresh()->activated); + } + +} diff --git a/tests/Feature/Api/Users/UsersForSelectListTest.php b/tests/Feature/Api/Users/UsersForSelectListTest.php index 6ab5bf9a85..8cdf700f04 100644 --- a/tests/Feature/Api/Users/UsersForSelectListTest.php +++ b/tests/Feature/Api/Users/UsersForSelectListTest.php @@ -30,6 +30,19 @@ class UsersForSelectListTest extends TestCase ->assertJson(fn(AssertableJson $json) => $json->has('results', 3)->etc()); } + public function testUsersCanBeSearchedByFirstAndLastName() + { + User::factory()->create(['first_name' => 'Luke', 'last_name' => 'Skywalker']); + + Passport::actingAs(User::factory()->create()); + $response = $this->getJson(route('api.users.selectlist', ['search' => 'luke sky']))->assertOk(); + + $results = collect($response->json('results')); + + $this->assertEquals(1, $results->count()); + $this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, 'Luke'))); + } + public function testUsersScopedToCompanyWhenMultipleFullCompanySupportEnabled() { $this->settings->enableMultipleFullCompanySupport(); diff --git a/tests/Feature/Api/Users/UsersSearchTest.php b/tests/Feature/Api/Users/UsersSearchTest.php new file mode 100644 index 0000000000..723a115db1 --- /dev/null +++ b/tests/Feature/Api/Users/UsersSearchTest.php @@ -0,0 +1,150 @@ +create(['first_name' => 'Luke', 'last_name' => 'Skywalker']); + User::factory()->create(['first_name' => 'Darth', 'last_name' => 'Vader']); + + Passport::actingAs(User::factory()->viewUsers()->create()); + $response = $this->getJson(route('api.users.index', ['search' => 'luke sky']))->assertOk(); + + $results = collect($response->json('rows')); + + $this->assertEquals(1, $results->count()); + $this->assertTrue($results->pluck('name')->contains(fn($text) => str_contains($text, 'Luke'))); + $this->assertFalse($results->pluck('name')->contains(fn($text) => str_contains($text, 'Darth'))); + } + + public function testResultsWhenSearchingForActiveUsers() + { + User::factory()->create(['first_name' => 'Active', 'last_name' => 'User']); + User::factory()->create(['first_name' => 'Deleted', 'last_name' => 'User'])->delete(); + + $response = $this->actingAsForApi(User::factory()->viewUsers()->create()) + ->getJson(route('api.users.index', [ + 'deleted' => 'false', + 'company_id' => '', + 'search' => 'user', + 'order' => 'asc', + 'offset' => '0', + 'limit' => '20', + ])) + ->assertOk(); + + $firstNames = collect($response->json('rows'))->pluck('first_name'); + + $this->assertTrue( + $firstNames->contains('Active'), + 'Expected user does not appear in results' + ); + + $this->assertFalse( + $firstNames->contains('Deleted'), + 'Unexpected deleted user appears in results' + ); + } + + public function testResultsWhenSearchingForDeletedUsers() + { + User::factory()->create(['first_name' => 'Active', 'last_name' => 'User']); + User::factory()->create(['first_name' => 'Deleted', 'last_name' => 'User'])->delete(); + + $response = $this->actingAsForApi(User::factory()->viewUsers()->create()) + ->getJson(route('api.users.index', [ + 'deleted' => 'true', + 'company_id' => '', + 'search' => 'user', + 'order' => 'asc', + 'offset' => '0', + 'limit' => '20', + ])) + ->assertOk(); + + $firstNames = collect($response->json('rows'))->pluck('first_name'); + + $this->assertFalse( + $firstNames->contains('Active'), + 'Unexpected active user appears in results' + ); + + $this->assertTrue( + $firstNames->contains('Deleted'), + 'Expected deleted user does not appear in results' + ); + } + + public function testUsersScopedToCompanyWhenMultipleFullCompanySupportEnabled() + { + $this->settings->enableMultipleFullCompanySupport(); + + $companyA = Company::factory() + ->has(User::factory(['first_name' => 'Company A', 'last_name' => 'User'])) + ->create(); + + Company::factory() + ->has(User::factory(['first_name' => 'Company B', 'last_name' => 'User'])) + ->create(); + + $response = $this->actingAsForApi(User::factory()->for($companyA)->viewUsers()->create()) + ->getJson(route('api.users.index')) + ->assertOk(); + + $results = collect($response->json('rows')); + + $this->assertTrue( + $results->pluck('name')->contains(fn($text) => str_contains($text, 'Company A')), + 'User index does not contain expected user' + ); + $this->assertFalse( + $results->pluck('name')->contains(fn($text) => str_contains($text, 'Company B')), + 'User index contains unexpected user from another company' + ); + } + + public function testUsersScopedToCompanyDuringSearchWhenMultipleFullCompanySupportEnabled() + { + $this->settings->enableMultipleFullCompanySupport(); + + $companyA = Company::factory() + ->has(User::factory(['first_name' => 'Company A', 'last_name' => 'User'])) + ->create(); + + Company::factory() + ->has(User::factory(['first_name' => 'Company B', 'last_name' => 'User'])) + ->create(); + + $response = $this->actingAsForApi(User::factory()->for($companyA)->viewUsers()->create()) + ->getJson(route('api.users.index', [ + 'deleted' => 'false', + 'company_id' => null, + 'search' => 'user', + 'order' => 'asc', + 'offset' => '0', + 'limit' => '20', + ])) + ->assertOk(); + + $results = collect($response->json('rows')); + + $this->assertTrue( + $results->pluck('name')->contains(fn($text) => str_contains($text, 'Company A')), + 'User index does not contain expected user' + ); + $this->assertFalse( + $results->pluck('name')->contains(fn($text) => str_contains($text, 'Company B')), + 'User index contains unexpected user from another company' + ); + } +} diff --git a/tests/Feature/Api/Users/UsersUpdateTest.php b/tests/Feature/Api/Users/UsersUpdateTest.php new file mode 100644 index 0000000000..953a671cf1 --- /dev/null +++ b/tests/Feature/Api/Users/UsersUpdateTest.php @@ -0,0 +1,87 @@ +superuser()->create(); + $manager = User::factory()->create(); + $company = Company::factory()->create(); + $department = Department::factory()->create(); + $location = Location::factory()->create(); + [$groupA, $groupB] = Group::factory()->count(2)->create(); + + $user = User::factory()->create([ + 'activated' => false, + 'remote' => false, + 'vip' => false, + ]); + + $this->actingAsForApi($admin) + ->patchJson(route('api.users.update', $user), [ + 'first_name' => 'Mabel', + 'last_name' => 'Mora', + 'username' => 'mabel', + 'password' => 'super-secret', + 'email' => 'mabel@onlymurderspod.com', + 'permissions' => '{"a.new.permission":"1"}', + 'activated' => true, + 'phone' => '619-555-5555', + 'jobtitle' => 'Host', + 'manager_id' => $manager->id, + 'employee_num' => '1111', + 'notes' => 'Pretty good artist', + 'company_id' => $company->id, + 'department_id' => $department->id, + 'location_id' => $location->id, + 'remote' => true, + 'groups' => $groupA->id, + 'vip' => true, + 'start_date' => '2021-08-01', + 'end_date' => '2025-12-31', + ]) + ->assertOk(); + + $user->refresh(); + $this->assertEquals('Mabel', $user->first_name, 'First name was not updated'); + $this->assertEquals('Mora', $user->last_name, 'Last name was not updated'); + $this->assertEquals('mabel', $user->username, 'Username was not updated'); + $this->assertTrue(Hash::check('super-secret', $user->password), 'Password was not updated'); + $this->assertEquals('mabel@onlymurderspod.com', $user->email, 'Email was not updated'); + $this->assertArrayHasKey('a.new.permission', $user->decodePermissions(), 'Permissions were not updated'); + $this->assertTrue((bool)$user->activated, 'User not marked as activated'); + $this->assertEquals('619-555-5555', $user->phone, 'Phone was not updated'); + $this->assertEquals('Host', $user->jobtitle, 'Job title was not updated'); + $this->assertTrue($user->manager->is($manager), 'Manager was not updated'); + $this->assertEquals('1111', $user->employee_num, 'Employee number was not updated'); + $this->assertEquals('Pretty good artist', $user->notes, 'Notes was not updated'); + $this->assertTrue($user->company->is($company), 'Company was not updated'); + $this->assertTrue($user->department->is($department), 'Department was not updated'); + $this->assertTrue($user->location->is($location), 'Location was not updated'); + $this->assertEquals(1, $user->remote, 'Remote was not updated'); + $this->assertTrue($user->groups->contains($groupA), 'Groups were not updated'); + $this->assertEquals(1, $user->vip, 'VIP was not updated'); + $this->assertEquals('2021-08-01', $user->start_date, 'Start date was not updated'); + $this->assertEquals('2025-12-31', $user->end_date, 'End date was not updated'); + + // `groups` can be an id or array or ids + $this->patch(route('api.users.update', $user), ['groups' => [$groupA->id, $groupB->id]]); + + $user->refresh(); + $this->assertTrue($user->groups->contains($groupA), 'Not part of expected group'); + $this->assertTrue($user->groups->contains($groupB), 'Not part of expected group'); + } +} diff --git a/tests/Feature/Assets/AssetCheckinTest.php b/tests/Feature/Assets/AssetCheckinTest.php new file mode 100644 index 0000000000..059fb1294f --- /dev/null +++ b/tests/Feature/Assets/AssetCheckinTest.php @@ -0,0 +1,32 @@ +superuser()->create(); + $asset = Asset::factory()->create(['last_checkin' => null]); + + $asset->checkOut(User::factory()->create(), $admin, now()); + + $this->actingAs($admin) + ->post(route('hardware.checkin.store', [ + 'assetId' => $asset->id, + ])) + ->assertRedirect(); + + $this->assertNotNull( + $asset->fresh()->last_checkin, + 'last_checkin field should be set on checkin' + ); + } +} diff --git a/tests/Feature/CheckoutAcceptances/AccessoryAcceptanceTest.php b/tests/Feature/CheckoutAcceptances/AccessoryAcceptanceTest.php new file mode 100644 index 0000000000..a49b1167cb --- /dev/null +++ b/tests/Feature/CheckoutAcceptances/AccessoryAcceptanceTest.php @@ -0,0 +1,82 @@ +settings->enableAlertEmail(); + + $acceptance = CheckoutAcceptance::factory() + ->pending() + ->for(Accessory::factory()->appleMouse(), 'checkoutable') + ->create(); + + $this->actingAs($acceptance->assignedTo) + ->post(route('account.store-acceptance', $acceptance), ['asset_acceptance' => 'accepted']) + ->assertSessionHasNoErrors(); + + $this->assertNotNull($acceptance->fresh()->accepted_at); + + Notification::assertSentTo( + $acceptance, + function (AcceptanceAssetAcceptedNotification $notification) use ($acceptance) { + $this->assertStringContainsString( + $acceptance->assignedTo->present()->fullName, + $notification->toMail()->render() + ); + + return true; + } + ); + } + + /** + * This can be absorbed into a bigger test + */ + public function testUsersNameIsIncludedInAccessoryDeclinedNotification() + { + Notification::fake(); + + $this->settings->enableAlertEmail(); + + $acceptance = CheckoutAcceptance::factory() + ->pending() + ->for(Accessory::factory()->appleMouse(), 'checkoutable') + ->create(); + + $this->actingAs($acceptance->assignedTo) + ->post(route('account.store-acceptance', $acceptance), ['asset_acceptance' => 'declined']) + ->assertSessionHasNoErrors(); + + $this->assertNotNull($acceptance->fresh()->declined_at); + + Notification::assertSentTo( + $acceptance, + function (AcceptanceAssetDeclinedNotification $notification) use ($acceptance) { + $this->assertStringContainsString( + $acceptance->assignedTo->present()->fullName, + $notification->toMail($acceptance)->render() + ); + + return true; + } + ); + } +} diff --git a/tests/Feature/Checkouts/LicenseCheckoutTest.php b/tests/Feature/Checkouts/LicenseCheckoutTest.php new file mode 100644 index 0000000000..978fac28f2 --- /dev/null +++ b/tests/Feature/Checkouts/LicenseCheckoutTest.php @@ -0,0 +1,62 @@ +superuser()->create(); + $asset = Asset::factory()->create(); + $licenseSeat = LicenseSeat::factory()->create(); + + $this->actingAs($admin) + ->post("/licenses/{$licenseSeat->license->id}/checkout", [ + 'checkout_to_type' => 'asset', + 'assigned_to' => null, + 'asset_id' => $asset->id, + 'notes' => 'oh hi there', + ]); + + $this->assertDatabaseHas('action_logs', [ + 'action_type' => 'checkout', + 'target_id' => $asset->id, + 'target_type' => Asset::class, + 'item_id' => $licenseSeat->license->id, + 'item_type' => License::class, + 'note' => 'oh hi there', + ]); + } + + public function testNotesAreStoredInActionLogOnCheckoutToUser() + { + $admin = User::factory()->superuser()->create(); + $licenseSeat = LicenseSeat::factory()->create(); + + $this->actingAs($admin) + ->post("/licenses/{$licenseSeat->license->id}/checkout", [ + 'checkout_to_type' => 'user', + 'assigned_to' => $admin->id, + 'asset_id' => null, + 'notes' => 'oh hi there', + ]); + + $this->assertDatabaseHas('action_logs', [ + 'action_type' => 'checkout', + 'target_id' => $admin->id, + 'target_type' => User::class, + 'item_id' => $licenseSeat->license->id, + 'item_type' => License::class, + 'note' => 'oh hi there', + ]); + } +} diff --git a/tests/Feature/DashboardTest.php b/tests/Feature/DashboardTest.php new file mode 100644 index 0000000000..4e9459fb06 --- /dev/null +++ b/tests/Feature/DashboardTest.php @@ -0,0 +1,19 @@ +actingAs(User::factory()->create()) + ->get(route('home')) + ->assertRedirect(route('view-assets')); + } +} diff --git a/tests/Feature/Livewire/CategoryEditFormTest.php b/tests/Feature/Livewire/CategoryEditFormTest.php new file mode 100644 index 0000000000..26719e4041 --- /dev/null +++ b/tests/Feature/Livewire/CategoryEditFormTest.php @@ -0,0 +1,105 @@ +assertStatus(200); + } + + public function testSendEmailCheckboxIsCheckedOnLoadWhenSendEmailIsExistingSetting() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => true, + 'eulaText' => '', + 'useDefaultEula' => false, + ])->assertSet('sendCheckInEmail', true); + } + + public function testSendEmailCheckboxIsCheckedOnLoadWhenCategoryEulaSet() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => false, + 'eulaText' => 'Some Content', + 'useDefaultEula' => false, + ])->assertSet('sendCheckInEmail', true); + } + + public function testSendEmailCheckboxIsCheckedOnLoadWhenUsingDefaultEula() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => false, + 'eulaText' => '', + 'useDefaultEula' => true, + ])->assertSet('sendCheckInEmail', true); + } + + public function testSendEmailCheckBoxIsUncheckedOnLoadWhenSendEmailIsFalseNoCategoryEulaSetAndNotUsingDefaultEula() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => false, + 'eulaText' => '', + 'useDefaultEula' => false, + ])->assertSet('sendCheckInEmail', false); + } + + public function testSendEmailCheckboxIsCheckedWhenCategoryEulaEntered() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => false, + 'useDefaultEula' => false, + ])->assertSet('sendCheckInEmail', false) + ->set('eulaText', 'Some Content') + ->assertSet('sendCheckInEmail', true); + } + + public function testSendEmailCheckboxCheckedAndDisabledAndEulaTextDisabledWhenUseDefaultEulaSelected() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => false, + 'useDefaultEula' => false, + ])->assertSet('sendCheckInEmail', false) + ->set('useDefaultEula', true) + ->assertSet('sendCheckInEmail', true) + ->assertSet('eulaTextDisabled', true) + ->assertSet('sendCheckInEmailDisabled', true); + } + + public function testSendEmailCheckboxEnabledAndSetToOriginalValueWhenNoCategoryEulaAndNotUsingGlobalEula() + { + Livewire::test(CategoryEditForm::class, [ + 'eulaText' => 'Some Content', + 'sendCheckInEmail' => false, + 'useDefaultEula' => true, + ]) + ->set('useDefaultEula', false) + ->set('eulaText', '') + ->assertSet('sendCheckInEmail', false) + ->assertSet('sendCheckInEmailDisabled', false); + + Livewire::test(CategoryEditForm::class, [ + 'eulaText' => 'Some Content', + 'sendCheckInEmail' => true, + 'useDefaultEula' => true, + ]) + ->set('useDefaultEula', false) + ->set('eulaText', '') + ->assertSet('sendCheckInEmail', true) + ->assertSet('sendCheckInEmailDisabled', false); + } + + public function testEulaFieldEnabledOnLoadWhenNotUsingDefaultEula() + { + Livewire::test(CategoryEditForm::class, [ + 'sendCheckInEmail' => false, + 'eulaText' => '', + 'useDefaultEula' => false, + ])->assertSet('eulaTextDisabled', false); + } +} diff --git a/tests/Feature/Reports/CustomReportTest.php b/tests/Feature/Reports/CustomReportTest.php new file mode 100644 index 0000000000..dd3199212e --- /dev/null +++ b/tests/Feature/Reports/CustomReportTest.php @@ -0,0 +1,136 @@ +streamedContent())->getRecords()) + ->pluck(0) + ->contains($needle) + ); + + return $this; + } + ); + + TestResponse::macro( + 'assertDontSeeTextInStreamedResponse', + function (string $needle) { + Assert::assertFalse( + collect(Reader::createFromString($this->streamedContent())->getRecords()) + ->pluck(0) + ->contains($needle) + ); + + return $this; + } + ); + } + + public function testCustomAssetReport() + { + Asset::factory()->create(['name' => 'Asset A']); + Asset::factory()->create(['name' => 'Asset B']); + + $this->actingAs(User::factory()->canViewReports()->create()) + ->post('reports/custom', [ + 'asset_name' => '1', + 'asset_tag' => '1', + 'serial' => '1', + ])->assertOk() + ->assertHeader('content-type', 'text/csv; charset=UTF-8') + ->assertSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B'); + } + + public function testCustomAssetReportAdheresToCompanyScoping() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + Asset::factory()->for($companyA)->create(['name' => 'Asset A']); + Asset::factory()->for($companyB)->create(['name' => 'Asset B']); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->canViewReports()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->canViewReports()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAs($superUser) + ->post('reports/custom', ['asset_name' => '1', 'asset_tag' => '1', 'serial' => '1']) + ->assertSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B'); + + $this->actingAs($userInCompanyA) + ->post('reports/custom', ['asset_name' => '1', 'asset_tag' => '1', 'serial' => '1']) + ->assertSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B'); + + $this->actingAs($userInCompanyB) + ->post('reports/custom', ['asset_name' => '1', 'asset_tag' => '1', 'serial' => '1']) + ->assertSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B'); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAs($superUser) + ->post('reports/custom', ['asset_name' => '1', 'asset_tag' => '1', 'serial' => '1']) + ->assertSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B'); + + $this->actingAs($userInCompanyA) + ->post('reports/custom', ['asset_name' => '1', 'asset_tag' => '1', 'serial' => '1']) + ->assertSeeTextInStreamedResponse('Asset A') + ->assertDontSeeTextInStreamedResponse('Asset B'); + + $this->actingAs($userInCompanyB) + ->post('reports/custom', ['asset_name' => '1', 'asset_tag' => '1', 'serial' => '1']) + ->assertDontSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B'); + } + + public function testCanLimitAssetsByLastCheckIn() + { + Asset::factory()->create(['name' => 'Asset A', 'last_checkin' => '2023-08-01']); + Asset::factory()->create(['name' => 'Asset B', 'last_checkin' => '2023-08-02']); + Asset::factory()->create(['name' => 'Asset C', 'last_checkin' => '2023-08-03']); + Asset::factory()->create(['name' => 'Asset D', 'last_checkin' => '2023-08-04']); + Asset::factory()->create(['name' => 'Asset E', 'last_checkin' => '2023-08-05']); + + $this->actingAs(User::factory()->canViewReports()->create()) + ->post('reports/custom', [ + 'asset_name' => '1', + 'asset_tag' => '1', + 'serial' => '1', + 'checkin_date' => '1', + 'checkin_date_start' => '2023-08-02', + 'checkin_date_end' => '2023-08-04', + ])->assertOk() + ->assertHeader('content-type', 'text/csv; charset=UTF-8') + ->assertDontSeeTextInStreamedResponse('Asset A') + ->assertSeeTextInStreamedResponse('Asset B') + ->assertSeeTextInStreamedResponse('Asset C') + ->assertSeeTextInStreamedResponse('Asset D') + ->assertDontSeeTextInStreamedResponse('Asset E'); + } +} diff --git a/tests/Feature/Users/UpdateUserTest.php b/tests/Feature/Users/UpdateUserTest.php index 9ddb323625..92245059ef 100644 --- a/tests/Feature/Users/UpdateUserTest.php +++ b/tests/Feature/Users/UpdateUserTest.php @@ -10,10 +10,10 @@ class UpdateUserTest extends TestCase { use InteractsWithSettings; - public function testUsersCanBeActivated() + public function testUsersCanBeActivatedWithNumber() { $admin = User::factory()->superuser()->create(); - $user = User::factory()->create(['activated' => false]); + $user = User::factory()->create(['activated' => 0]); $this->actingAs($admin) ->put(route('users.update', $user), [ @@ -22,10 +22,25 @@ class UpdateUserTest extends TestCase 'activated' => 1, ]); - $this->assertTrue($user->refresh()->activated); + $this->assertEquals(1, $user->refresh()->activated); } - public function testUsersCanBeDeactivated() + public function testUsersCanBeActivatedWithBooleanTrue() + { + $admin = User::factory()->superuser()->create(); + $user = User::factory()->create(['activated' => false]); + + $this->actingAs($admin) + ->put(route('users.update', $user), [ + 'first_name' => $user->first_name, + 'username' => $user->username, + 'activated' => true, + ]); + + $this->assertEquals(1, $user->refresh()->activated); + } + + public function testUsersCanBeDeactivatedWithNumber() { $admin = User::factory()->superuser()->create(); $user = User::factory()->create(['activated' => true]); @@ -34,12 +49,25 @@ class UpdateUserTest extends TestCase ->put(route('users.update', $user), [ 'first_name' => $user->first_name, 'username' => $user->username, - // checkboxes that are not checked are - // not included in the request payload - // 'activated' => 0, + 'activated' => 0, ]); - $this->assertFalse($user->refresh()->activated); + $this->assertEquals(0, $user->refresh()->activated); + } + + public function testUsersCanBeDeactivatedWithBooleanFalse() + { + $admin = User::factory()->superuser()->create(); + $user = User::factory()->create(['activated' => true]); + + $this->actingAs($admin) + ->put(route('users.update', $user), [ + 'first_name' => $user->first_name, + 'username' => $user->username, + 'activated' => false, + ]); + + $this->assertEquals(0, $user->refresh()->activated); } public function testUsersUpdatingThemselvesDoNotDeactivateTheirAccount() @@ -50,12 +78,8 @@ class UpdateUserTest extends TestCase ->put(route('users.update', $admin), [ 'first_name' => $admin->first_name, 'username' => $admin->username, - // checkboxes that are disabled are not - // included in the request payload - // even if they are checked - // 'activated' => 0, ]); - $this->assertTrue($admin->refresh()->activated); + $this->assertEquals(1, $admin->refresh()->activated); } } diff --git a/tests/Support/CustomTestMacros.php b/tests/Support/CustomTestMacros.php new file mode 100644 index 0000000000..4242b28653 --- /dev/null +++ b/tests/Support/CustomTestMacros.php @@ -0,0 +1,78 @@ +{$property})) { + throw new RuntimeException( + "The property ({$property}) either does not exist or is null on the model which isn't helpful for comparison." + ); + } + }; + + TestResponse::macro( + 'assertResponseContainsInRows', + function (Model $model, string $property = 'name') use ($guardAgainstNullProperty) { + $guardAgainstNullProperty($model, $property); + + Assert::assertTrue( + collect($this['rows'])->pluck($property)->contains(e($model->{$property})), + "Response did not contain the expected value: {$model->{$property}}" + ); + + return $this; + } + ); + + TestResponse::macro( + 'assertResponseDoesNotContainInRows', + function (Model $model, string $property = 'name') use ($guardAgainstNullProperty) { + $guardAgainstNullProperty($model, $property); + + Assert::assertFalse( + collect($this['rows'])->pluck($property)->contains(e($model->{$property})), + "Response contained unexpected value: {$model->{$property}}" + ); + + return $this; + } + ); + + TestResponse::macro( + 'assertResponseContainsInResults', + function (Model $model, string $property = 'id') use ($guardAgainstNullProperty) { + $guardAgainstNullProperty($model, $property); + + Assert::assertTrue( + collect($this->json('results'))->pluck('id')->contains(e($model->{$property})), + "Response did not contain the expected value: {$model->{$property}}" + ); + + return $this; + } + ); + + TestResponse::macro( + 'assertResponseDoesNotContainInResults', + function (Model $model, string $property = 'id') use ($guardAgainstNullProperty) { + $guardAgainstNullProperty($model, $property); + + Assert::assertFalse( + collect($this->json('results'))->pluck('id')->contains(e($model->{$property})), + "Response contained unexpected value: {$model->{$property}}" + ); + + return $this; + } + ); + } +} diff --git a/tests/Support/InteractsWithAuthentication.php b/tests/Support/InteractsWithAuthentication.php new file mode 100644 index 0000000000..27b20e382b --- /dev/null +++ b/tests/Support/InteractsWithAuthentication.php @@ -0,0 +1,16 @@ +update(['alert_email' => $email]); + } + + public function disableAlertEmail(): Settings + { + return $this->update(['alert_email' => null]); + } + public function enableMultipleFullCompanySupport(): Settings { return $this->update(['full_multiple_companies_support' => 1]); } + public function disableMultipleFullCompanySupport(): Settings + { + return $this->update(['full_multiple_companies_support' => 0]); + } + public function enableWebhook(): Settings { return $this->update([ @@ -41,6 +56,17 @@ class Settings ]); } + public function enableAutoIncrement(): Settings + { + return $this->update([ + 'auto_increment_assets' => 1, + 'auto_increment_prefix' => 'ABCD', + 'next_auto_tag_base' => '123', + 'zerofill_count' => 5 + ]); + + } + /** * @param array $attributes Attributes to modify in the application's settings. */ diff --git a/tests/TestCase.php b/tests/TestCase.php index 28051c7c7f..03f273ad6f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,11 +5,16 @@ namespace Tests; use App\Http\Middleware\SecurityHeaders; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; +use RuntimeException; +use Tests\Support\CustomTestMacros; +use Tests\Support\InteractsWithAuthentication; use Tests\Support\InteractsWithSettings; abstract class TestCase extends BaseTestCase { use CreatesApplication; + use CustomTestMacros; + use InteractsWithAuthentication; use LazilyRefreshDatabase; private array $globallyDisabledMiddleware = [ @@ -18,6 +23,12 @@ abstract class TestCase extends BaseTestCase protected function setUp(): void { + if (!file_exists(realpath(__DIR__ . '/../') . '/.env.testing')) { + throw new RuntimeException( + '.env.testing file does not exist. Aborting to avoid wiping your local database' + ); + } + parent::setUp(); $this->withoutMiddleware($this->globallyDisabledMiddleware); @@ -25,5 +36,7 @@ abstract class TestCase extends BaseTestCase if (collect(class_uses_recursive($this))->contains(InteractsWithSettings::class)) { $this->initializeSettings(); } + + $this->registerCustomMacros(); } } diff --git a/tests/Unit/AssetTest.php b/tests/Unit/AssetTest.php index 432165dc07..7670d10753 100644 --- a/tests/Unit/AssetTest.php +++ b/tests/Unit/AssetTest.php @@ -12,23 +12,130 @@ class AssetTest extends TestCase { use InteractsWithSettings; - // public function testAutoIncrementMixed() - // { - // $expected = '123411'; - // $next = Asset::nextAutoIncrement( - // collect([ - // ['asset_tag' => '0012345'], - // ['asset_tag' => 'WTF00134'], - // ['asset_tag' => 'WTF-745'], - // ['asset_tag' => '0012346'], - // ['asset_tag' => '00123410'], - // ['asset_tag' => 'U8T7597h77'], - // ]) - // ); + public function testAutoIncrement() + { + $this->settings->enableAutoIncrement(); - // \Log::debug('Next: '.$next); - // $this->assertEquals($expected, $next); - // } + $a = Asset::factory()->create(['asset_tag' => Asset::autoincrement_asset() ]); + $b = Asset::factory()->create(['asset_tag' => Asset::autoincrement_asset() ]); + + $this->assertModelExists($a); + $this->assertModelExists($b); + + } + public function testAutoIncrementCollision() + { + $this->settings->enableAutoIncrement(); + + // we have to do this by hand to 'simulate' two web pages being open at the same time + $a = Asset::factory()->make(['asset_tag' => Asset::autoincrement_asset() ]); + $b = Asset::factory()->make(['asset_tag' => Asset::autoincrement_asset() ]); + + $this->assertTrue($a->save()); + $this->assertFalse($b->save()); + } + + public function testAutoIncrementDouble() + { + // make one asset with the autoincrement *ONE* higher than the next auto-increment + // make sure you can then still make another + $this->settings->enableAutoIncrement(); + + $gap_number = Asset::autoincrement_asset(1); + $final_number = Asset::autoincrement_asset(2); + $a = Asset::factory()->make(['asset_tag' => $gap_number]); //make an asset with an ID that is one *over* the next increment + $b = Asset::factory()->make(['asset_tag' => Asset::autoincrement_asset()]); //but also make one with one that is *at* the next increment + $this->assertTrue($a->save()); + $this->assertTrue($b->save()); + + //and ensure a final asset ends up at *two* over what would've been the next increment at the start + $c = Asset::factory()->make(['asset_tag' => Asset::autoincrement_asset()]); + $this->assertTrue($c->save()); + $this->assertEquals($c->asset_tag, $final_number); + } + + public function testAutoIncrementGapAndBackfill() + { + // make one asset 3 higher than the next auto-increment + // manually make one that's 1 lower than that + // make sure the next one is one higher than the 3 higher one. + $this->settings->enableAutoIncrement(); + + $big_gap = Asset::autoincrement_asset(3); + $final_result = Asset::autoincrement_asset(4); + $backfill_one = Asset::autoincrement_asset(0); + $backfill_two = Asset::autoincrement_asset(1); + $backfill_three = Asset::autoincrement_asset(2); + $a = Asset::factory()->create(['asset_tag' => $big_gap]); + $this->assertModelExists($a); + + $b = Asset::factory()->create(['asset_tag' => $backfill_one]); + $this->assertModelExists($b); + + $c = Asset::factory()->create(['asset_tag' => $backfill_two]); + $this->assertModelExists($c); + + $d = Asset::factory()->create(['asset_tag' => $backfill_three]); + $this->assertModelExists($d); + + $final = Asset::factory()->create(['asset_tag' => Asset::autoincrement_asset()]); + $this->assertModelExists($final); + $this->assertEquals($final->asset_tag, $final_result); + } + + public function testPrefixlessAutoincrementBackfill() + { + // TODO: COPYPASTA FROM above, is there a way to still run this test but not have it be so duplicative? + $this->settings->enableAutoIncrement()->set(['auto_increment_prefix' => '']); + + $big_gap = Asset::autoincrement_asset(3); + $final_result = Asset::autoincrement_asset(4); + $backfill_one = Asset::autoincrement_asset(0); + $backfill_two = Asset::autoincrement_asset(1); + $backfill_three = Asset::autoincrement_asset(2); + $a = Asset::factory()->create(['asset_tag' => $big_gap]); + $this->assertModelExists($a); + + $b = Asset::factory()->create(['asset_tag' => $backfill_one]); + $this->assertModelExists($b); + + $c = Asset::factory()->create(['asset_tag' => $backfill_two]); + $this->assertModelExists($c); + + $d = Asset::factory()->create(['asset_tag' => $backfill_three]); + $this->assertModelExists($d); + + $final = Asset::factory()->create(['asset_tag' => Asset::autoincrement_asset()]); + $this->assertModelExists($final); + $this->assertEquals($final->asset_tag, $final_result); + } + + public function testUnzerofilledPrefixlessAutoincrementBackfill() + { + // TODO: COPYPASTA FROM above (AGAIN), is there a way to still run this test but not have it be so duplicative? + $this->settings->enableAutoIncrement()->set(['auto_increment_prefix' => '','zerofill_count' => 0]); + + $big_gap = Asset::autoincrement_asset(3); + $final_result = Asset::autoincrement_asset(4); + $backfill_one = Asset::autoincrement_asset(0); + $backfill_two = Asset::autoincrement_asset(1); + $backfill_three = Asset::autoincrement_asset(2); + $a = Asset::factory()->create(['asset_tag' => $big_gap]); + $this->assertModelExists($a); + + $b = Asset::factory()->create(['asset_tag' => $backfill_one]); + $this->assertModelExists($b); + + $c = Asset::factory()->create(['asset_tag' => $backfill_two]); + $this->assertModelExists($c); + + $d = Asset::factory()->create(['asset_tag' => $backfill_three]); + $this->assertModelExists($d); + + $final = Asset::factory()->create(['asset_tag' => Asset::autoincrement_asset()]); + $this->assertModelExists($final); + $this->assertEquals($final->asset_tag, $final_result); + } public function testWarrantyExpiresAttribute() { diff --git a/tests/Unit/CompanyScopingTest.php b/tests/Unit/CompanyScopingTest.php new file mode 100644 index 0000000000..669dd5ed41 --- /dev/null +++ b/tests/Unit/CompanyScopingTest.php @@ -0,0 +1,169 @@ + [Accessory::class], + 'Assets' => [Asset::class], + 'Components' => [Component::class], + 'Consumables' => [Consumable::class], + 'Licenses' => [License::class], + ]; + } + + /** @dataProvider models */ + public function testCompanyScoping($model) + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $modelA = $model::factory()->for($companyA)->create(); + $modelB = $model::factory()->for($companyB)->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAs($superUser); + $this->assertCanSee($modelA); + $this->assertCanSee($modelB); + + $this->actingAs($userInCompanyA); + $this->assertCanSee($modelA); + $this->assertCanSee($modelB); + + $this->actingAs($userInCompanyB); + $this->assertCanSee($modelA); + $this->assertCanSee($modelB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAs($superUser); + $this->assertCanSee($modelA); + $this->assertCanSee($modelB); + + $this->actingAs($userInCompanyA); + $this->assertCanSee($modelA); + $this->assertCannotSee($modelB); + + $this->actingAs($userInCompanyB); + $this->assertCannotSee($modelA); + $this->assertCanSee($modelB); + } + + public function testAssetMaintenanceCompanyScoping() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $assetMaintenanceForCompanyA = AssetMaintenance::factory()->for(Asset::factory()->for($companyA))->create(); + $assetMaintenanceForCompanyB = AssetMaintenance::factory()->for(Asset::factory()->for($companyB))->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAs($superUser); + $this->assertCanSee($assetMaintenanceForCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyB); + + $this->actingAs($userInCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyB); + + $this->actingAs($userInCompanyB); + $this->assertCanSee($assetMaintenanceForCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAs($superUser); + $this->assertCanSee($assetMaintenanceForCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyB); + + $this->actingAs($userInCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyA); + $this->assertCannotSee($assetMaintenanceForCompanyB); + + $this->actingAs($userInCompanyB); + $this->assertCannotSee($assetMaintenanceForCompanyA); + $this->assertCanSee($assetMaintenanceForCompanyB); + } + + public function testLicenseSeatCompanyScoping() + { + [$companyA, $companyB] = Company::factory()->count(2)->create(); + + $licenseSeatA = LicenseSeat::factory()->for(Asset::factory()->for($companyA))->create(); + $licenseSeatB = LicenseSeat::factory()->for(Asset::factory()->for($companyB))->create(); + + $superUser = $companyA->users()->save(User::factory()->superuser()->make()); + $userInCompanyA = $companyA->users()->save(User::factory()->make()); + $userInCompanyB = $companyB->users()->save(User::factory()->make()); + + $this->settings->disableMultipleFullCompanySupport(); + + $this->actingAs($superUser); + $this->assertCanSee($licenseSeatA); + $this->assertCanSee($licenseSeatB); + + $this->actingAs($userInCompanyA); + $this->assertCanSee($licenseSeatA); + $this->assertCanSee($licenseSeatB); + + $this->actingAs($userInCompanyB); + $this->assertCanSee($licenseSeatA); + $this->assertCanSee($licenseSeatB); + + $this->settings->enableMultipleFullCompanySupport(); + + $this->actingAs($superUser); + $this->assertCanSee($licenseSeatA); + $this->assertCanSee($licenseSeatB); + + $this->actingAs($userInCompanyA); + $this->assertCanSee($licenseSeatA); + $this->assertCannotSee($licenseSeatB); + + $this->actingAs($userInCompanyB); + $this->assertCannotSee($licenseSeatA); + $this->assertCanSee($licenseSeatB); + } + + private function assertCanSee(Model $model) + { + $this->assertTrue( + get_class($model)::all()->contains($model), + 'User was not able to see expected model' + ); + } + + private function assertCannotSee(Model $model) + { + $this->assertFalse( + get_class($model)::all()->contains($model), + 'User was able to see model from a different company' + ); + } +} diff --git a/tests/Unit/Helpers/HelperTest.php b/tests/Unit/Helpers/HelperTest.php new file mode 100644 index 0000000000..0b5fba986c --- /dev/null +++ b/tests/Unit/Helpers/HelperTest.php @@ -0,0 +1,19 @@ +assertIsString(Helper::defaultChartColors(1000)); + } + + public function testDefaultChartColorsMethodHandlesNegativeNumbers() + { + $this->assertIsString(Helper::defaultChartColors(-1)); + } +} diff --git a/tests/Unit/_bootstrap.php b/tests/Unit/_bootstrap.php deleted file mode 100644 index 62817a53c4..0000000000 --- a/tests/Unit/_bootstrap.php +++ /dev/null @@ -1,3 +0,0 @@ -loadSessionSnapshot('login')) return; - - // logging in - $I->amOnPage('/login'); - $I->fillField('username', 'snipe'); - $I->fillField('password', 'password'); - $I->click('Login'); - //$I->saveSessionSnapshot('login'); - } -} diff --git a/tests/_support/ApiTester.php b/tests/_support/ApiTester.php deleted file mode 100644 index f76364bc19..0000000000 --- a/tests/_support/ApiTester.php +++ /dev/null @@ -1,58 +0,0 @@ -createPersonalAccessClient($user->id, 'Codeception API Test Client', - 'http://localhost/'); - - \Illuminate\Support\Facades\DB::table('oauth_personal_access_clients')->insert([ - 'client_id' => $client->id, - 'created_at' => new DateTime, - 'updated_at' => new DateTime, - ]); - - $user->permissions = json_encode(['superuser' => true]); - $user->save(); - - $token = $user->createToken('CodeceptionAPItestToken')->accessToken; - - return $token; - } - - /** - * Remove Timestamps from transformed array - * This fixes false negatives when comparing json due to timestamp second rounding issues - * @param array $array Array returned from the transformer - * @return array Transformed item striped of created_at and updated_at - */ - public function removeTimeStamps($array) - { - unset($array['created_at']); - unset($array['updated_at']); - - return $array; - } -} diff --git a/tests/_support/FunctionalTester.php b/tests/_support/FunctionalTester.php deleted file mode 100644 index 7eabf4cf4b..0000000000 --- a/tests/_support/FunctionalTester.php +++ /dev/null @@ -1,151 +0,0 @@ - first()->id; - } - - public function getCategoryId() - { - return Category::inRandomOrder()->first()->id; - } - - public function getManufacturerId() - { - return Manufacturer::inRandomOrder()->first()->id; - } - - public function getLocationId() - { - return Location::inRandomOrder()->first()->id; - } - - /** - * @return mixed Random Accessory Id - */ - public function getAccessoryId() - { - return Accessory::inRandomOrder()->first()->id; - } - - /** - * @return mixed Random Asset Model Id; - */ - public function getModelId() - { - return AssetModel::inRandomOrder()->first()->id; - } - - /** - * @return mixed Id of Empty Asset Model - */ - public function getEmptyModelId() - { - return AssetModel::doesntHave('assets')->first()->id; - } - - /** - * @return mixed Id of random status - */ - public function getStatusId() - { - return StatusLabel::inRandomOrder()->first()->id; - } - - /** - * Id of random user - * @return mixed - */ - public function getUserId() - { - return User::inRandomOrder()->first()->id; - } - - /** - * @return mixed Id of random supplier - */ - public function getSupplierId() - { - return Supplier::inRandomOrder()->first()->id; - } - - /** - * @return mixed Id of Random Asset - */ - public function getAssetId() - { - return Asset::inRandomOrder()->first()->id; - } - - /** - * An Empty category - * @return mixed Id of Empty Category - */ - public function getEmptyCategoryId() - { - return Category::where('category_type', 'asset')->doesntHave('models')->first()->id; - } - - /** - * A random component id for testing - * @return mixed Id of random component - */ - public function getComponentId() - { - return Component::inRandomOrder()->first()->id; - } - - /** - * A random consumable Id for testing - * @return mixed - */ - public function getConsumableId() - { - return Consumable::inRandomOrder()->first()->id; - } - - /** - * Return a random depreciation id for tests. - * @return mixed - */ - public function getDepreciationId() - { - return Depreciation::inRandomOrder()->first()->id; - } -} diff --git a/tests/_support/Helper/Acceptance.php b/tests/_support/Helper/Acceptance.php deleted file mode 100644 index 2dd1562b13..0000000000 --- a/tests/_support/Helper/Acceptance.php +++ /dev/null @@ -1,10 +0,0 @@ -validateHTML(); - * - * Validate the HTML of the current page, but ignore errors containing the string "Ignoreit": - * $I->validateHTML(["Ignoreme"]); - * - * - * - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @author Tobias Hößl - */ - -namespace Helper; - -use Codeception\TestCase; - -class HTMLValidator extends \Codeception\Module -{ - /** - * @param string $html - * @return array - * @throws \Exception - */ - private function validateByVNU($html) - { - $javaPath = $this->_getConfig('javaPath'); - if (! $javaPath) { - $javaPath = 'java'; - } - $vnuPath = $this->_getConfig('vnuPath'); - if (! $vnuPath) { - $vnuPath = '/usr/local/bin/vnu.jar'; - } - - $filename = DIRECTORY_SEPARATOR.'tmp'.DIRECTORY_SEPARATOR.uniqid('html-validate').'.html'; - file_put_contents($filename, $html); - exec($javaPath.' -Xss1024k -jar '.$vnuPath.' --format json '.$filename.' 2>&1', $return); - $data = json_decode($return[0], true); - if (! $data || ! isset($data['messages']) || ! is_array($data['messages'])) { - throw new \Exception('Invalid data returned from validation service: '.$return); - } - - return $data['messages']; - } - - /** - * @return string - * @throws \Codeception\Exception\ModuleException - * @throws \Exception - */ - private function getPageSource() - { - if (! $this->hasModule('WebDriver')) { - throw new \Exception('This validator needs WebDriver to work'); - } - - /** @var \Codeception\Module\WebDriver $webdriver */ - $webdriver = $this->getModule('WebDriver'); - - return $webdriver->webDriver->getPageSource(); - } - - /** - * @param string[] $ignoreMessages - */ - public function validateHTML($ignoreMessages = []) - { - $source = $this->getPageSource(); - try { - $messages = $this->validateByVNU($source); - } catch (\Exception $e) { - $this->fail($e->getMessage()); - - return; - } - $failMessages = []; - $lines = explode("\n", $source); - foreach ($messages as $message) { - if ($message['type'] == 'error') { - $formattedMsg = '- Line '.$message['lastLine'].', column '.$message['lastColumn'].': '. - $message['message']."\n > ".$lines[$message['lastLine'] - 1]; - $ignoring = false; - foreach ($ignoreMessages as $ignoreMessage) { - if (mb_stripos($formattedMsg, $ignoreMessage) !== false) { - $ignoring = true; - } - } - if (! $ignoring) { - $failMessages[] = $formattedMsg; - } - } - } - if (count($failMessages) > 0) { - \PHPUnit_Framework_Assert::fail('Invalid HTML: '."\n".implode("\n", $failMessages)); - } - } -} diff --git a/tests/_support/Helper/Unit.php b/tests/_support/Helper/Unit.php deleted file mode 100644 index 4d27aa3599..0000000000 --- a/tests/_support/Helper/Unit.php +++ /dev/null @@ -1,10 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the accessories listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/accessories'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="accessories"] tr', [5, 30]); -$I->seeInTitle('Accessories'); -$I->see('Accessories'); -$I->seeInPageSource('accessories/create'); -$I->dontSee('Accessories', '.page-header'); -$I->see('Accessories', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create accessories form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/accessories/create'); -$I->dontSee('Create Accessory', '.page-header'); -$I->see('Create Accessory', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/AssetsCept.php b/tests/acceptance/AssetsCept.php deleted file mode 100644 index 6cc39e028c..0000000000 --- a/tests/acceptance/AssetsCept.php +++ /dev/null @@ -1,24 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that assets page loads without errors'); -$I->amGoingTo('go to the assets listing page'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/hardware'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="assets"] tr', [5, 50]); -$I->seeInTitle('Assets'); -$I->see('Assets'); -$I->seeInPageSource('hardware/create'); -$I->dontSee('Assets', '.page-header'); -$I->see('Assets', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create assets form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/hardware/create'); -$I->dontSee('Create Asset', '.page-header'); -$I->see('Create Asset', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/AssetsCest.php b/tests/acceptance/AssetsCest.php deleted file mode 100644 index 51011ce830..0000000000 --- a/tests/acceptance/AssetsCest.php +++ /dev/null @@ -1,33 +0,0 @@ -am('logged in user'); - $I->wantTo('ensure that assets page loads without errors'); - $I->amGoingTo('go to the assets listing page'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage('/hardware'); - $I->waitForElement('.table', 20); // secs - $I->seeNumberOfElements('table[name="assets"] tr', [5, 50]); - $I->seeInTitle(trans('general.assets')); - $I->see(trans('general.assets')); - $I->seeInPageSource('hardware/create'); - $I->see(trans('general.assets'), 'h1.pull-left'); - } - - public function createAsset(AcceptanceTester $I) - { - $I->wantTo('ensure that the create assets form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage('/hardware/create'); - $I->dontSee('Create Asset', '.page-header'); - $I->see('Create Asset', 'h1.pull-left'); - } -} diff --git a/tests/acceptance/CategoriesCept.php b/tests/acceptance/CategoriesCept.php deleted file mode 100644 index 75a6d2be11..0000000000 --- a/tests/acceptance/CategoriesCept.php +++ /dev/null @@ -1,24 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the categories listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/categories'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="categories"] tr', [5, 30]); -$I->seeInTitle('Categories'); -$I->see('Categories'); -$I->seeInPageSource('/categories/create'); -$I->dontSee('Categories', '.page-header'); -$I->see('Categories', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create category form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/categories/create'); -$I->dontSee('Create Category', '.page-header'); -$I->see('Create Category', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/CompaniesCept.php b/tests/acceptance/CompaniesCept.php deleted file mode 100644 index 98a06a6bfb..0000000000 --- a/tests/acceptance/CompaniesCept.php +++ /dev/null @@ -1,25 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the company listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/companies'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="companies"] tr', [5, 30]); -$I->seeInTitle('Companies'); -$I->see('Companies'); -$I->seeInPageSource('companies/create'); -$I->dontSee('Companies', '.page-header'); -$I->see('Companies', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create company form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/companies/create'); -$I->dontSee('Create Company', '.page-header'); -$I->see('Create Company', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/ConsumablesCept.php b/tests/acceptance/ConsumablesCept.php deleted file mode 100644 index 9461c232c3..0000000000 --- a/tests/acceptance/ConsumablesCept.php +++ /dev/null @@ -1,24 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the consumables listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/consumables'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="consumables"] tr', [5, 30]); -$I->seeInTitle('Consumables'); -$I->see('Consumables'); -$I->seeInPageSource('/consumables/create'); -$I->dontSee('Consumables', '.page-header'); -$I->see('Consumables', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create consumables form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/consumables/create'); -$I->dontSee('Create Consumable', '.page-header'); -$I->see('Create Consumable', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/CustomfieldsCept.php b/tests/acceptance/CustomfieldsCept.php deleted file mode 100644 index 9526316999..0000000000 --- a/tests/acceptance/CustomfieldsCept.php +++ /dev/null @@ -1,14 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the custom fields page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/fields'); -$I->seeInTitle('Custom Fields'); -$I->see('Custom Fields'); -$I->seeInPageSource('/fields/create'); -$I->dontSee('Custom Fields', '.page-header'); -$I->dontSee('Fieldsets', '.page-header'); -$I->see('Manage Custom Fields', 'h1.pull-left'); diff --git a/tests/acceptance/DepartmentsCept.php b/tests/acceptance/DepartmentsCept.php deleted file mode 100644 index e9f6fd6b9c..0000000000 --- a/tests/acceptance/DepartmentsCept.php +++ /dev/null @@ -1,25 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the department listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/departments'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="departments"] tr', [5, 30]); -$I->seeInTitle('Departments'); -$I->see('Departments'); -$I->seeInPageSource('departments/create'); -$I->dontSee('Departments', '.page-header'); -$I->see('Departments', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create department form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/department/create'); -$I->dontSee('Create Department', '.page-header'); -$I->see('Create Department', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/DepreciationsCept.php b/tests/acceptance/DepreciationsCept.php deleted file mode 100644 index d0f43ad4e5..0000000000 --- a/tests/acceptance/DepreciationsCept.php +++ /dev/null @@ -1,25 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that depreciations page loads without errors'); -$I->amGoingTo('go to the depreciations listing page'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/depreciations'); -$I->seeInTitle('Depreciations'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="depreciations"] tbody tr', [1, 5]); -$I->seeInPageSource('/depreciations/create'); -$I->dontSee('Depreciations', '.page-header'); -$I->see('Depreciations', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create depreciation form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/depreciations/create'); -$I->seeInTitle('Create Depreciation'); -$I->dontSee('Create Depreciation', '.page-header'); -$I->see('Create Depreciation', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/LocationsCept.php b/tests/acceptance/LocationsCept.php deleted file mode 100644 index 600b5c2822..0000000000 --- a/tests/acceptance/LocationsCept.php +++ /dev/null @@ -1,24 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the locations listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/locations'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('tr', [5, 30]); -$I->seeInTitle('Locations'); -$I->see('Locations'); -$I->seeInPageSource('/locations/create'); -$I->dontSee('Locations', '.page-header'); -$I->see('Locations', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create location form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/locations/create'); -$I->dontSee('Create Location', '.page-header'); -$I->see('Create Location', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/LoginCest.php b/tests/acceptance/LoginCest.php deleted file mode 100644 index 97fdbf5921..0000000000 --- a/tests/acceptance/LoginCest.php +++ /dev/null @@ -1,18 +0,0 @@ -wantTo('sign in'); - $I->amOnPage('/login'); - $I->see(trans('auth/general.login_prompt')); - $I->seeElement('input[type=text]'); - $I->seeElement('input[type=password]'); - } -} diff --git a/tests/acceptance/ManufacturersCept.php b/tests/acceptance/ManufacturersCept.php deleted file mode 100644 index 69d0c0e6da..0000000000 --- a/tests/acceptance/ManufacturersCept.php +++ /dev/null @@ -1,23 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the manufacturers listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/manufacturers'); -$I->seeNumberOfElements('table[name="manufacturers"] tr', [5, 30]); -$I->see('Manufacturers'); -$I->seeInTitle('Manufacturers'); -$I->seeInPageSource('manufacturers/create'); -$I->dontSee('Manufacturers', '.page-header'); -$I->see('Manufacturers', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure that the create manufacturer form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/manufacturers/create'); -$I->dontSee('Create Manufacturer', '.page-header'); -$I->see('Create Manufacturer', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/StatuslabelsCept.php b/tests/acceptance/StatuslabelsCept.php deleted file mode 100644 index 224a713250..0000000000 --- a/tests/acceptance/StatuslabelsCept.php +++ /dev/null @@ -1,24 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure the status labels listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/statuslabels'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('tr', [1, 30]); -$I->seeInTitle('Status Labels'); -$I->see('Status Labels'); -$I->seeInPageSource('statuslabels/create'); -$I->dontSee('Status Labels', '.page-header'); -$I->see('Status Labels', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure the create status labels form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/statuslabels/create'); -$I->dontSee('Create Status Label', '.page-header'); -$I->see('Create Status Label', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/SuppliersCept.php b/tests/acceptance/SuppliersCept.php deleted file mode 100644 index 59840dfd9d..0000000000 --- a/tests/acceptance/SuppliersCept.php +++ /dev/null @@ -1,24 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the suppliers listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/suppliers'); -$I->waitForElement('.table', 5); // secs -$I->seeNumberOfElements('table[name="suppliers"] tr', [5, 25]); -$I->seeInTitle('Suppliers'); -$I->see('Suppliers'); -$I->seeInPageSource('suppliers/create'); -$I->dontSee('Suppliers', '.page-header'); -$I->see('Suppliers', 'h1.pull-left'); - -/* Create Form */ -$I->wantTo('ensure the create supplier form loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->click(['link' => 'Create New']); -$I->amOnPage('/suppliers/create'); -$I->dontSee('Create Supplier', '.page-header'); -$I->see('Create Supplier', 'h1.pull-left'); -$I->dontSee('<span class="'); diff --git a/tests/acceptance/UsersCept.php b/tests/acceptance/UsersCept.php deleted file mode 100644 index ddad283ef1..0000000000 --- a/tests/acceptance/UsersCept.php +++ /dev/null @@ -1,63 +0,0 @@ -am('logged in user'); -$I->wantTo('ensure that the users listing page loads without errors'); -$I->lookForwardTo('seeing it load without errors'); -$I->amOnPage('/users'); -//$I->waitForJS("return $.active == 0;", 60); -$I->waitForElement('.table', 5); // secs -//$I->seeNumberOfElements('tr', [1,10]); -$I->seeInTitle('Users'); -$I->see('Users'); -$I->seeInPageSource('users/create'); -$I->dontSee('Users', '.page-header'); -$I->see('Users', 'h1.pull-left'); -$I->seeLink('Create New'); // matches Logout - -/* Create form */ -$I->am('logged in admin'); -$I->wantTo('ensure that you get errors when you submit an incomplete form'); -$I->lookForwardTo('seeing errors display'); -$I->click(['link' => 'Create New']); -$I->amOnPage('users/create'); -$I->dontSee('Create User', '.page-header'); -$I->see('Create User', 'h1.pull-left'); - -/* Submit form and expect errors */ -$I->click(['name' => 'email']); -$I->submitForm('#userForm', [ - 'email' => 'me@example.com', -]); -$I->seeElement('.alert-danger'); -$I->dontSeeInSource('<br><'); - -/* Submit form and expect errors */ -$I->click(['name' => 'email']); -$I->click(['name' => 'username']); -$I->submitForm('#userForm', [ - 'email' => Helper::generateRandomString(15).'@example.com', - 'first_name' => 'Joe', - 'last_name' => 'Smith', - 'username' => Helper::generateRandomString(15), -]); - -$I->seeElement('.alert-danger'); -$I->dontSeeInSource('<br><'); - -/* Submit form and expect success */ -$I->wantTo('submit the form successfully'); -$I->click(['name' => 'email']); -$I->fillField(['name' => 'email'], Helper::generateRandomString(15).'@example.com'); -$I->fillField(['name' => 'first_name'], 'Joe'); -$I->fillField(['name' => 'last_name'], 'Smith'); -$I->click(['name' => 'username']); -$I->fillField(['name' => 'username'], Helper::generateRandomString(15)); -$I->click(['name' => 'password']); -$I->fillField(['name' => 'password'], 'password'); -$I->click(['name' => 'password_confirmation']); -$I->fillField(['name' => 'password_confirmation'], 'password'); -$I->click('Save'); -$I->seeElement('.alert-success'); -$I->dontSeeInSource('<br><'); diff --git a/tests/acceptance/_bootstrap.php b/tests/acceptance/_bootstrap.php deleted file mode 100644 index 62817a53c4..0000000000 --- a/tests/acceptance/_bootstrap.php +++ /dev/null @@ -1,3 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexAccessories(ApiTester $I) - { - $I->wantTo('Get a list of accessories'); - - // call - $I->sendGET('/accessories?limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $accessory = App\Models\Accessory::orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new AccessoriesTransformer)->transformAccessory($accessory))); - } - - /** @test */ - public function createAccessory(ApiTester $I, $scenario) - { - $I->wantTo('Create a new accessory'); - - $temp_accessory = \App\Models\Accessory::factory()->appleBtKeyboard()->make([ - 'name' => 'Test Accessory Name', - 'company_id' => 2, - ]); - - // setup - $data = [ - 'category_id' => $temp_accessory->category_id, - 'company_id' => $temp_accessory->company->id, - 'location_id' => $temp_accessory->location_id, - 'name' => $temp_accessory->name, - 'order_number' => $temp_accessory->order_number, - 'purchase_cost' => $temp_accessory->purchase_cost, - 'purchase_date' => $temp_accessory->purchase_date, - 'model_number' => $temp_accessory->model_number, - 'manufacturer_id' => $temp_accessory->manufacturer_id, - 'supplier_id' => $temp_accessory->supplier_id, - 'qty' => $temp_accessory->qty, - ]; - - // create - $I->sendPOST('/accessories', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateAccessoryWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an accessory with PATCH'); - - // create - $accessory = \App\Models\Accessory::factory()->appleBtKeyboard()->create([ - 'name' => 'Original Accessory Name', - 'company_id' => 2, - 'location_id' => 3, - ]); - $I->assertInstanceOf(\App\Models\Accessory::class, $accessory); - - $temp_accessory = \App\Models\Accessory::factory()->microsoftMouse()->make([ - 'company_id' => 3, - 'name' => 'updated accessory name', - 'location_id' => 1, - ]); - - $data = [ - 'category_id' => $temp_accessory->category_id, - 'company_id' => $temp_accessory->company->id, - 'location_id' => $temp_accessory->location_id, - 'name' => $temp_accessory->name, - 'order_number' => $temp_accessory->order_number, - 'purchase_cost' => $temp_accessory->purchase_cost, - 'purchase_date' => $temp_accessory->purchase_date, - 'model_number' => $temp_accessory->model_number, - 'manufacturer_id' => $temp_accessory->manufacturer_id, - 'supplier_id' => $temp_accessory->supplier_id, - 'image' => $temp_accessory->image, - 'qty' => $temp_accessory->qty, - ]; - - $I->assertNotEquals($accessory->name, $data['name']); - - // update - $I->sendPATCH('/accessories/'.$accessory->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/accessories/message.update.success'), $response->messages); - $I->assertEquals($accessory->id, $response->payload->id); // accessory id does not change - $I->assertEquals($temp_accessory->company_id, $response->payload->company_id); // company_id updated - $I->assertEquals($temp_accessory->name, $response->payload->name); // accessory name updated - $I->assertEquals($temp_accessory->location_id, $response->payload->location_id); // accessory location_id updated - $temp_accessory->created_at = Carbon::parse($response->payload->created_at); - $temp_accessory->updated_at = Carbon::parse($response->payload->updated_at); - $temp_accessory->id = $accessory->id; - // verify - $I->sendGET('/accessories/'.$accessory->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new AccessoriesTransformer)->transformAccessory($temp_accessory)); - } - - /** @test */ - public function deleteAccessoryTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an accessory'); - - // create - $accessory = \App\Models\Accessory::factory()->appleBtKeyboard()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Accessory::class, $accessory); - - // delete - $I->sendDELETE('/accessories/'.$accessory->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/accessories/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/accessories/'.$accessory->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiAssetsCest.php b/tests/api/ApiAssetsCest.php deleted file mode 100644 index 554d4c4230..0000000000 --- a/tests/api/ApiAssetsCest.php +++ /dev/null @@ -1,168 +0,0 @@ -faker = \Faker\Factory::create(); - $this->user = \App\Models\User::find(1); - Setting::getSettings()->time_display_format = 'H:i'; - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexAssets(ApiTester $I) - { - $I->wantTo('Get a list of assets'); - - // call - $I->sendGET('/hardware?limit=20&sort=id&order=desc'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - // FIXME: This is disabled because the statuslabel join is doing something weird in Api/AssetsController@index - // However, it's hiding other real test errors in other parts of the code, so disabling this for now until we can fix. -// $response = json_decode($I->grabResponse(), true); - - // sample verify -// $asset = Asset::orderByDesc('id')->take(20)->get()->first(); - - // -// $I->seeResponseContainsJson($I->removeTimestamps((new AssetsTransformer)->transformAsset($asset))); - } - - /** @test */ - public function createAsset(ApiTester $I, $scenario) - { - $I->wantTo('Create a new asset'); - - $temp_asset = \App\Models\Asset::factory()->laptopMbp()->make([ - 'asset_tag' => 'Test Asset Tag', - 'company_id' => 2, - ]); - - // setup - $data = [ - 'asset_tag' => $temp_asset->asset_tag, - 'assigned_to' => $temp_asset->assigned_to, - 'company_id' => $temp_asset->company->id, - 'image' => $temp_asset->image, - 'model_id' => $temp_asset->model_id, - 'name' => $temp_asset->name, - 'notes' => $temp_asset->notes, - 'purchase_cost' => $temp_asset->purchase_cost, - 'purchase_date' => $temp_asset->purchase_date, - 'rtd_location_id' => $temp_asset->rtd_location_id, - 'serial' => $temp_asset->serial, - 'status_id' => $temp_asset->status_id, - 'supplier_id' => $temp_asset->supplier_id, - 'warranty_months' => $temp_asset->warranty_months, - ]; - - // create - $I->sendPOST('/hardware', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - /** @test */ - public function updateAssetWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an asset with PATCH'); - - // create - $asset = \App\Models\Asset::factory()->laptopMbp()->create([ - 'company_id' => 2, - 'rtd_location_id' => 3, - ]); - $I->assertInstanceOf(\App\Models\Asset::class, $asset); - - $temp_asset = \App\Models\Asset::factory()->laptopAir()->make([ - 'company_id' => 3, - 'name' => 'updated asset name', - 'rtd_location_id' => 1, - ]); - - $data = [ - 'asset_tag' => $temp_asset->asset_tag, - 'assigned_to' => $temp_asset->assigned_to, - 'company_id' => $temp_asset->company->id, - 'image' => $temp_asset->image, - 'model_id' => $temp_asset->model_id, - 'name' => $temp_asset->name, - 'notes' => $temp_asset->notes, - 'order_number' => $temp_asset->order_number, - 'purchase_cost' => $temp_asset->purchase_cost, - 'purchase_date' => $temp_asset->purchase_date->format('Y-m-d'), - 'rtd_location_id' => $temp_asset->rtd_location_id, - 'serial' => $temp_asset->serial, - 'status_id' => $temp_asset->status_id, - 'supplier_id' => $temp_asset->supplier_id, - 'warranty_months' => $temp_asset->warranty_months, - ]; - - $I->assertNotEquals($asset->name, $data['name']); - - // update - $I->sendPATCH('/hardware/'.$asset->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - // dd($response); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/hardware/message.update.success'), $response->messages); - $I->assertEquals($asset->id, $response->payload->id); // asset id does not change - $I->assertEquals($temp_asset->asset_tag, $response->payload->asset_tag); // asset tag updated - $I->assertEquals($temp_asset->name, $response->payload->name); // asset name updated - $I->assertEquals($temp_asset->rtd_location_id, $response->payload->rtd_location_id); // asset rtd_location_id updated - $temp_asset->created_at = Carbon::parse($response->payload->created_at); - $temp_asset->updated_at = Carbon::parse($response->payload->updated_at); - $temp_asset->id = $asset->id; - $temp_asset->location_id = $response->payload->rtd_location_id; - - // verify - $I->sendGET('/hardware/'.$asset->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new AssetsTransformer)->transformAsset($temp_asset)); - } - - /** @test */ - public function deleteAssetTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an asset'); - - // create - $asset = \App\Models\Asset::factory()->laptopMbp()->create(); - $I->assertInstanceOf(\App\Models\Asset::class, $asset); - - // delete - $I->sendDELETE('/hardware/'.$asset->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/hardware/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/hardware/'.$asset->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - - // Make sure we're soft deleted. - $response = json_decode($I->grabResponse()); - $I->assertNotNull($response->deleted_at); - } -} diff --git a/tests/api/ApiCategoriesCest.php b/tests/api/ApiCategoriesCest.php deleted file mode 100644 index 7605872a17..0000000000 --- a/tests/api/ApiCategoriesCest.php +++ /dev/null @@ -1,141 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexCategorys(ApiTester $I) - { - $I->wantTo('Get a list of categories'); - - // call - $I->sendGET('/categories?order_by=id&limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $category = App\Models\Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count') - ->orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new CategoriesTransformer)->transformCategory($category))); - } - - /** @test */ - public function createCategory(ApiTester $I, $scenario) - { - $I->wantTo('Create a new category'); - - $temp_category = \App\Models\Category::factory()->assetLaptopCategory()->make([ - 'name' => 'Test Category Tag', - ]); - - // setup - $data = [ - 'category_type' => $temp_category->category_type, - 'checkin_email' => $temp_category->checkin_email, - 'eula_text' => $temp_category->eula_text, - 'name' => $temp_category->name, - 'require_acceptance' => $temp_category->require_acceptance, - 'use_default_eula' => $temp_category->use_default_eula, - ]; - - // create - $I->sendPOST('/categories', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateCategoryWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an category with PATCH'); - - // create - $category = \App\Models\Category::factory()->assetLaptopCategory() - ->create([ - 'name' => 'Original Category Name', - ]); - $I->assertInstanceOf(\App\Models\Category::class, $category); - - $temp_category = \App\Models\Category::factory()->accessoryMouseCategory()->make([ - 'name' => 'updated category name', - ]); - - $data = [ - 'category_type' => $temp_category->category_type, - 'checkin_email' => $temp_category->checkin_email, - 'eula_text' => $temp_category->eula_text, - 'name' => $temp_category->name, - 'require_acceptance' => $temp_category->require_acceptance, - 'use_default_eula' => $temp_category->use_default_eula, - ]; - - $I->assertNotEquals($category->name, $data['name']); - - // update - $I->sendPATCH('/categories/'.$category->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/categories/message.update.success'), $response->messages); - $I->assertEquals($category->id, $response->payload->id); // category id does not change - $I->assertEquals($temp_category->name, $response->payload->name); // category name updated - // Some manual copying to compare against - $temp_category->created_at = Carbon::parse($response->payload->created_at); - $temp_category->updated_at = Carbon::parse($response->payload->updated_at); - $temp_category->id = $category->id; - - // verify - $I->sendGET('/categories/'.$category->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new CategoriesTransformer)->transformCategory($temp_category)); - } - - /** @test */ - public function deleteCategoryTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an category'); - - // create - $category = \App\Models\Category::factory()->assetLaptopCategory()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Category::class, $category); - - // delete - $I->sendDELETE('/categories/'.$category->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/categories/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/categories/'.$category->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiCheckoutAssetsCest.php b/tests/api/ApiCheckoutAssetsCest.php deleted file mode 100644 index 48b4bd2335..0000000000 --- a/tests/api/ApiCheckoutAssetsCest.php +++ /dev/null @@ -1,147 +0,0 @@ -user = \App\Models\User::find(1); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function checkoutAssetToUser(ApiTester $I) - { - $I->wantTo('Check out an asset to a user'); - //Grab an asset from the database that isn't checked out. - $asset = Asset::whereNull('assigned_to')->first(); - $targetUser = \App\Models\User::factory()->create(); - $data = [ - 'assigned_user' => $targetUser->id, - 'note' => 'This is a test checkout note', - 'expected_checkin' => '2018-05-24', - 'name' => 'Updated Asset Name', - 'checkout_to_type' => 'user', - ]; - $I->sendPOST("/hardware/{$asset->id}/checkout", $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/hardware/message.checkout.success'), $response->messages); - - // Confirm results. - $I->sendGET("/hardware/{$asset->id}"); - $I->seeResponseContainsJson([ - 'assigned_to' => [ - 'id' => $targetUser->id, - 'type' => 'user', - ], - 'name' => 'Updated Asset Name', - 'expected_checkin' => Helper::getFormattedDateObject('2018-05-24', 'date'), - ]); - } - - /** @test */ - public function checkoutAssetToAsset(ApiTester $I) - { - $I->wantTo('Check out an asset to an asset'); - //Grab an asset from the database that isn't checked out. - $asset = Asset::whereNull('assigned_to') - ->where('model_id', 8) - ->where('status_id', Statuslabel::deployable()->first()->id) - ->first(); // We need to make sure that this is an asset/model that doesn't require acceptance - $targetAsset = \App\Models\Asset::factory()->desktopMacpro()->create([ - 'name' => 'Test Asset For Checkout to', - ]); - $data = [ - 'assigned_asset' => $targetAsset->id, - 'checkout_to_type' => 'asset', - ]; - $I->sendPOST("/hardware/{$asset->id}/checkout", $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/hardware/message.checkout.success'), $response->messages); - - // Confirm results. - $I->sendGET("/hardware/{$asset->id}"); - $I->seeResponseContainsJson([ - 'assigned_to' => [ - 'id' => $targetAsset->id, - 'type' => 'asset', - ], - ]); - } - - /** @test */ - public function checkoutAssetToLocation(ApiTester $I) - { - $I->wantTo('Check out an asset to an asset'); - //Grab an asset from the database that isn't checked out. - $asset = Asset::whereNull('assigned_to') - ->where('model_id', 8) - ->where('status_id', Statuslabel::deployable()->first()->id) - ->first(); // We need to make sure that this is an asset/model that doesn't require acceptance - $targetLocation = \App\Models\Location::factory()->create([ - 'name' => 'Test Location for Checkout', - ]); - $data = [ - 'assigned_location' => $targetLocation->id, - 'checkout_to_type' => 'location', - ]; - $I->sendPOST("/hardware/{$asset->id}/checkout", $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/hardware/message.checkout.success'), $response->messages); - - // Confirm results. - $I->sendGET("/hardware/{$asset->id}"); - $I->seeResponseContainsJson([ - 'assigned_to' => [ - 'id' => $targetLocation->id, - 'type' => 'location', - ], - ]); - } - - /** @test */ - public function checkinAsset(ApiTester $I) - { - $I->wantTo('Checkin an asset that is currently checked out'); - - $asset = Asset::whereNotNull('assigned_to')->first(); - - $I->sendPOST("/hardware/{$asset->id}/checkin", [ - 'note' => 'Checkin Note', - ]); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/hardware/message.checkin.success'), $response->messages); - - // Verify - $I->sendGET("/hardware/{$asset->id}"); - $I->seeResponseContainsJson([ - 'assigned_to' => null, - ]); - } -} diff --git a/tests/api/ApiCompaniesCest.php b/tests/api/ApiCompaniesCest.php deleted file mode 100644 index 55fa5bc218..0000000000 --- a/tests/api/ApiCompaniesCest.php +++ /dev/null @@ -1,132 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexCompanys(ApiTester $I) - { - $I->wantTo('Get a list of companies'); - - // call - $I->sendGET('/companies?order_by=id&limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // dd($response); - // sample verify - $company = App\Models\Company::withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count') - ->orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - - $I->seeResponseContainsJson($I->removeTimestamps((new CompaniesTransformer)->transformCompany($company))); - } - - /** @test */ - public function createCompany(ApiTester $I, $scenario) - { - $I->wantTo('Create a new company'); - - $temp_company = \App\Models\Company::factory()->make([ - 'name' => 'Test Company Tag', - ]); - - // setup - $data = [ - 'name' => $temp_company->name, - ]; - - // create - $I->sendPOST('/companies', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateCompanyWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an company with PATCH'); - - // create - $company = \App\Models\Company::factory()->create([ - 'name' => 'Original Company Name', - ]); - $I->assertInstanceOf(\App\Models\Company::class, $company); - - $temp_company = \App\Models\Company::factory()->make([ - 'name' => 'updated company name', - ]); - - $data = [ - 'name' => $temp_company->name, - ]; - - $I->assertNotEquals($company->name, $data['name']); - - // update - $I->sendPATCH('/companies/'.$company->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/companies/message.update.success'), $response->messages); - $I->assertEquals($company->id, $response->payload->id); // company id does not change - $I->assertEquals($temp_company->name, $response->payload->name); // company name updated - // Some manual copying to compare against - $temp_company->created_at = Carbon::parse($response->payload->created_at->datetime); - $temp_company->updated_at = Carbon::parse($response->payload->updated_at->datetime); - $temp_company->id = $company->id; - - // verify - $I->sendGET('/companies/'.$company->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new CompaniesTransformer)->transformCompany($temp_company)); - } - - /** @test */ - public function deleteCompanyTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an company'); - - // create - $company = \App\Models\Company::factory()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Company::class, $company); - - // delete - $I->sendDELETE('/companies/'.$company->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/companies/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/companies/'.$company->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiComponentsAssetsCest.php b/tests/api/ApiComponentsAssetsCest.php deleted file mode 100644 index c7e2e043b9..0000000000 --- a/tests/api/ApiComponentsAssetsCest.php +++ /dev/null @@ -1,80 +0,0 @@ -faker = \Faker\Factory::create(); - // $this->user = \App\Models\User::find(1); - - // $I->amBearerAuthenticated($I->getToken($this->user)); - // } - - // // /** @test */ - // // public function indexComponentsAssets(ApiTester $I) - // // { - // // $I->wantTo('Get a list of assets related to a component'); - - // // // generate component - // // $component = factory(\App\Models\Component::class) - // // ->create(['user_id' => $this->user->id, 'qty' => 20]); - - // // // generate assets and associate component - // // $assets = factory(\App\Models\Asset::class, 2) - // // ->create(['user_id' => $this->user->id]) - // // ->each(function ($asset) use ($component) { - // // $component->assets()->attach($component->id, [ - // // 'component_id' => $component->id, - // // 'user_id' => $this->user->id, - // // 'created_at' => date('Y-m-d H:i:s'), - // // 'assigned_qty' => 2, - // // 'asset_id' => $asset->id - // // ]); - // // }); - - // // // verify - // // $I->sendGET('/components/' . $component->id . '/assets/'); - // // $I->seeResponseIsJson(); - // // $I->seeResponseCodeIs(200); - - // // $response = json_decode($I->grabResponse()); - // // $I->assertEquals(2, $response->total); - - // // $I->assertInstanceOf(\Illuminate\Database\Eloquent\Collection::class, $assets); - - // // $I->seeResponseContainsJson(['rows' => [ - // // 0 => [ - // // 'name' => $assets[0]->name, - // // 'id' => $assets[0]->id, - // // 'created_at' => $assets[0]->created_at->format('Y-m-d'), - // // ], - // // 1 => [ - // // 'name' => $assets[1]->name, - // // 'id' => $assets[1]->id, - // // 'created_at' => $assets[1]->created_at->format('Y-m-d'), - // // ], - // // ] - // // ]); - // // } - - // // /** @test */ - // // public function expectEmptyResponseWithoutAssociatedAssets(ApiTester $I, $scenario) - // // { - // // $I->wantTo('See an empty response when there are no associated assets to a component'); - - // // $component = factory(\App\Models\Component::class) - // // ->create(['user_id' => $this->user->id, 'qty' => 20]); - - // // $I->sendGET('/components/' . $component->id . '/assets'); - // // $I->seeResponseCodeIs(200); - // // $I->seeResponseIsJson(); - - // // $response = json_decode($I->grabResponse()); - // // $I->assertEquals(0, $response->total); - // // $I->assertEquals([], $response->rows); - // // $I->seeResponseContainsJson(['total' => 0, 'rows' => []]); - // // } -} diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php deleted file mode 100644 index dea9ece186..0000000000 --- a/tests/api/ApiComponentsCest.php +++ /dev/null @@ -1,156 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexComponents(ApiTester $I) - { - $I->wantTo('Get a list of components'); - - // call - $I->sendGET('/components?limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $component = App\Models\Component::orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - - $I->seeResponseContainsJson($I->removeTimestamps((new ComponentsTransformer)->transformComponent($component))); - } - - /** @test */ - public function createComponent(ApiTester $I, $scenario) - { - $I->wantTo('Create a new component'); - - $temp_component = \App\Models\Component::factory()->ramCrucial4()->make([ - 'name' => 'Test Component Name', - 'company_id' => 2, - ]); - - // setup - $data = [ - 'category_id' => $temp_component->category_id, - 'company_id' => $temp_component->company->id, - 'location_id' => $temp_component->location_id, - 'manufacturer_id' => $temp_component->manufacturer_id, - 'model_number' => $temp_component->model_number, - 'name' => $temp_component->name, - 'order_number' => $temp_component->order_number, - 'purchase_cost' => $temp_component->purchase_cost, - 'purchase_date' => $temp_component->purchase_date, - 'qty' => $temp_component->qty, - 'serial' => $temp_component->serial, - ]; - - // create - $I->sendPOST('/components', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateComponentWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an component with PATCH'); - - // create - $component = \App\Models\Component::factory()->ramCrucial4()->create([ - 'name' => 'Original Component Name', - 'company_id' => 2, - 'location_id' => 3, - ]); - $I->assertInstanceOf(\App\Models\Component::class, $component); - - $temp_component = \App\Models\Component::factory()->ssdCrucial240()->make([ - 'company_id' => 3, - 'name' => 'updated component name', - 'location_id' => 1, - ]); - - $data = [ - 'category_id' => $temp_component->category_id, - 'company_id' => $temp_component->company->id, - 'location_id' => $temp_component->location_id, - 'min_amt' => $temp_component->min_amt, - 'name' => $temp_component->name, - 'order_number' => $temp_component->order_number, - 'purchase_cost' => $temp_component->purchase_cost, - 'purchase_date' => $temp_component->purchase_date, - 'qty' => $temp_component->qty, - 'serial' => $temp_component->serial, - ]; - - $I->assertNotEquals($component->name, $data['name']); - - // update - $I->sendPATCH('/components/'.$component->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/components/message.update.success'), $response->messages); - $I->assertEquals($component->id, $response->payload->id); // component id does not change - $I->assertEquals($temp_component->company_id, $response->payload->company_id); // company_id updated - $I->assertEquals($temp_component->name, $response->payload->name); // component name updated - $I->assertEquals($temp_component->location_id, $response->payload->location_id); // component location_id updated - $temp_component->created_at = Carbon::parse($response->payload->created_at); - $temp_component->updated_at = Carbon::parse($response->payload->updated_at); - $temp_component->id = $component->id; - // verify - $I->sendGET('/components/'.$component->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new ComponentsTransformer)->transformComponent($temp_component)); - } - - /** @test */ - public function deleteComponentTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an component'); - - // create - $component = \App\Models\Component::factory()->ramCrucial4()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Component::class, $component); - - // delete - $I->sendDELETE('/components/'.$component->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - // dd($response); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/components/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/components/'.$component->id); - - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiConsumablesCest.php b/tests/api/ApiConsumablesCest.php deleted file mode 100644 index 8490c3a56e..0000000000 --- a/tests/api/ApiConsumablesCest.php +++ /dev/null @@ -1,155 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexConsumables(ApiTester $I) - { - $I->wantTo('Get a list of consumables'); - - // call - $I->sendGET('/consumables?limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $consumable = App\Models\Consumable::orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - - $I->seeResponseContainsJson($I->removeTimestamps((new ConsumablesTransformer)->transformConsumable($consumable))); - } - - /** @test */ - public function createConsumable(ApiTester $I, $scenario) - { - $I->wantTo('Create a new consumable'); - - $temp_consumable = \App\Models\Consumable::factory()->ink()->make([ - 'name' => 'Test Consumable Name', - 'company_id' => 2, - ]); - - // setup - $data = [ - 'category_id' => $temp_consumable->category_id, - 'company_id' => $temp_consumable->company->id, - 'location_id' => $temp_consumable->location_id, - 'manufacturer_id' => $temp_consumable->manufacturer_id, - 'name' => $temp_consumable->name, - 'order_number' => $temp_consumable->order_number, - 'purchase_cost' => $temp_consumable->purchase_cost, - 'purchase_date' => $temp_consumable->purchase_date, - 'qty' => $temp_consumable->qty, - 'model_number' => $temp_consumable->model_number, - ]; - - // create - $I->sendPOST('/consumables', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateConsumableWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an consumable with PATCH'); - - // create - $consumable = \App\Models\Consumable::factory()->ink()->create([ - 'name' => 'Original Consumable Name', - 'company_id' => 2, - 'location_id' => 3, - ]); - $I->assertInstanceOf(\App\Models\Consumable::class, $consumable); - - $temp_consumable = \App\Models\Consumable::factory()->cardstock()->make([ - 'company_id' => 3, - 'name' => 'updated consumable name', - 'location_id' => 1, - ]); - - $data = [ - 'category_id' => $temp_consumable->category_id, - 'company_id' => $temp_consumable->company->id, - 'item_no' => $temp_consumable->item_no, - 'location_id' => $temp_consumable->location_id, - 'name' => $temp_consumable->name, - 'order_number' => $temp_consumable->order_number, - 'purchase_cost' => $temp_consumable->purchase_cost, - 'purchase_date' => $temp_consumable->purchase_date, - 'model_number' => $temp_consumable->model_number, - 'manufacturer_id' => $temp_consumable->manufacturer_id, - 'supplier_id' => $temp_consumable->supplier_id, - 'qty' => $temp_consumable->qty, - ]; - - $I->assertNotEquals($consumable->name, $data['name']); - - // update - $I->sendPATCH('/consumables/'.$consumable->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/consumables/message.update.success'), $response->messages); - $I->assertEquals($consumable->id, $response->payload->id); // consumable id does not change - $I->assertEquals($temp_consumable->company_id, $response->payload->company_id); // company_id updated - $I->assertEquals($temp_consumable->name, $response->payload->name); // consumable name updated - $I->assertEquals($temp_consumable->location_id, $response->payload->location_id); // consumable location_id updated - $temp_consumable->created_at = Carbon::parse($response->payload->created_at); - $temp_consumable->updated_at = Carbon::parse($response->payload->updated_at); - $temp_consumable->id = $consumable->id; - // verify - $I->sendGET('/consumables/'.$consumable->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new ConsumablesTransformer)->transformConsumable($temp_consumable)); - } - - /** @test */ - public function deleteConsumableTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an consumable'); - - // create - $consumable = \App\Models\Consumable::factory()->ink()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Consumable::class, $consumable); - - // delete - $I->sendDELETE('/consumables/'.$consumable->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/consumables/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/consumables/'.$consumable->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiLicenseSeatsCest.php b/tests/api/ApiLicenseSeatsCest.php deleted file mode 100644 index 664915bb4c..0000000000 --- a/tests/api/ApiLicenseSeatsCest.php +++ /dev/null @@ -1,185 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexLicenseSeats(ApiTester $I) - { - $I->wantTo('Get a list of license seats for a specific license'); - - // call - $I->sendGET('/licenses/1/seats?limit=10&order=desc'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - // sample verify - $licenseSeats = App\Models\LicenseSeat::where('license_id', 1) - ->orderBy('id', 'desc')->take(10)->get(); - // pick a random seat - $licenseSeat = $licenseSeats->random(); - // need the index in the original list so that the "name" field is determined correctly - $licenseSeatNumber = 0; - foreach ($licenseSeats as $index=>$seat) { - if ($licenseSeat === $seat) { - $licenseSeatNumber = $index + 1; - } - } - $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat, $licenseSeatNumber))); - } - - /** @test */ - public function showLicenseSeat(ApiTester $I) - { - $I->wantTo('Get a license seat'); - - // call - $I->sendGET('/licenses/1/seats/10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - // sample verify - $licenseSeat = App\Models\LicenseSeat::findOrFail(10); - $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); - } - - /** @test */ - public function checkoutLicenseSeatToUser(ApiTester $I) - { - $I->wantTo('Checkout a license seat to a user'); - - $user = App\Models\User::all()->random(); - $licenseSeat = App\Models\LicenseSeat::all()->random(); - $endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id; - - $data = [ - 'assigned_to' => $user->id, - 'note' => 'Test Checkout to User via API', - ]; - - // update - $I->sendPATCH($endpoint, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); - $I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change - $I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change - - // verify - $licenseSeat = $licenseSeat->fresh(); - $I->sendGET($endpoint); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); - - // verify that the last logged action is a checkout - $I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'action_type' => 'checkout', - ]); - } - - /** @test */ - public function checkoutLicenseSeatToAsset(ApiTester $I) - { - $I->wantTo('Checkout a license seat to an asset'); - - $asset = App\Models\Asset::all()->random(); - $licenseSeat = App\Models\LicenseSeat::all()->random(); - $endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id; - - $data = [ - 'asset_id' => $asset->id, - 'note' => 'Test Checkout to Asset via API', - ]; - - // update - $I->sendPATCH($endpoint, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); - $I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change - $I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change - - // verify - $licenseSeat = $licenseSeat->fresh(); - $I->sendGET($endpoint); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); - - // verify that the last logged action is a checkout - $I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'action_type' => 'checkout', - ]); - } - - /** @test */ - public function checkoutLicenseSeatToUserAndAsset(ApiTester $I) - { - $I->wantTo('Checkout a license seat to a user AND an asset'); - - $asset = App\Models\Asset::all()->random(); - $user = App\Models\User::all()->random(); - $licenseSeat = App\Models\LicenseSeat::all()->random(); - $endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id; - - $data = [ - 'asset_id' => $asset->id, - 'assigned_to' => $user->id, - 'note' => 'Test Checkout to User and Asset via API', - ]; - - // update - $I->sendPATCH($endpoint, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); - $I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change - $I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change - - // verify - $licenseSeat = $licenseSeat->fresh(); - $I->sendGET($endpoint); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); - - // verify that the last logged action is a checkout - $I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson([ - 'action_type' => 'checkout', - ]); - } -} diff --git a/tests/api/ApiLicensesCest.php b/tests/api/ApiLicensesCest.php deleted file mode 100644 index 0668e87628..0000000000 --- a/tests/api/ApiLicensesCest.php +++ /dev/null @@ -1,194 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexLicenses(ApiTester $I) - { - $I->wantTo('Get a list of licenses'); - - // call - $I->sendGET('/licenses?limit=10&sort=created_at'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $license = App\Models\License::orderByDesc('created_at') - ->withCount('freeSeats as free_seats_count') - ->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new LicensesTransformer)->transformLicense($license))); - } - - /** @test */ - public function createLicense(ApiTester $I, $scenario) - { - $I->wantTo('Create a new license'); - - $temp_license = \App\Models\License::factory()->acrobat()->make([ - 'name' => 'Test License Name', - 'depreciation_id' => 3, - 'company_id' => 2, - ]); - - // setup - $data = [ - 'company_id' => $temp_license->company_id, - 'depreciation_id' => $temp_license->depreciation_id, - 'expiration_date' => $temp_license->expiration_date, - 'license_email' => $temp_license->license_email, - 'license_name' => $temp_license->license_name, - 'maintained' => $temp_license->maintained, - 'manufacturer_id' => $temp_license->manufacturer_id, - 'name' => $temp_license->name, - 'notes' => $temp_license->notes, - 'order_number' => $temp_license->order_number, - 'purchase_cost' => $temp_license->purchase_cost, - 'purchase_date' => $temp_license->purchase_date, - 'purchase_order' => $temp_license->purchase_order, - 'reassignable' => $temp_license->reassignable, - 'seats' => $temp_license->seats, - 'serial' => $temp_license->serial, - 'supplier_id' => $temp_license->supplier_id, - 'termination_date' => $temp_license->termination_date, - ]; - - // create - $I->sendPOST('/licenses', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateLicenseWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update a license with PATCH'); - - // create - $license = \App\Models\License::factory()->acrobat()->create([ - 'name' => 'Original License Name', - 'depreciation_id' => 3, - 'company_id' => 2, - ]); - $I->assertInstanceOf(\App\Models\License::class, $license); - - $temp_license = \App\Models\License::factory()->office()->make([ - 'company_id' => 3, - 'depreciation_id' => 2, - ]); - - $data = [ - 'company_id' => $temp_license->company_id, - 'depreciation_id' => $temp_license->depreciation_id, - 'expiration_date' => $temp_license->expiration_date, - 'license_email' => $temp_license->license_email, - 'license_name' => $temp_license->license_name, - 'maintained' => $temp_license->maintained, - 'manufacturer_id' => $temp_license->manufacturer_id, - 'name' => $temp_license->name, - 'notes' => $temp_license->notes, - 'order_number' => $temp_license->order_number, - 'purchase_cost' => $temp_license->purchase_cost, - 'purchase_date' => $temp_license->purchase_date, - 'purchase_order' => $temp_license->purchase_order, - 'reassignable' => $temp_license->reassignable, - 'seats' => $temp_license->seats, - 'serial' => $temp_license->serial, - 'supplier_id' => $temp_license->supplier_id, - 'category_id' => $temp_license->category_id, - 'termination_date' => $temp_license->termination_date, - ]; - // We aren't checking anyhting out in this test, so this fakes the withCount() that happens on a normal db fetch. - $temp_license->free_seats_count = $temp_license->seats; - $I->assertNotEquals($license->name, $data['name']); - - // update - $I->sendPATCH('/licenses/'.$license->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); - $I->assertEquals($license->id, $response->payload->id); // license id does not change - $I->assertEquals($temp_license->name, $response->payload->name); // license name - $temp_license->created_at = Carbon::parse($response->payload->created_at); - $temp_license->updated_at = Carbon::parse($response->payload->updated_at); - $temp_license->id = $license->id; - // verify - $I->sendGET('/licenses/'.$license->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new LicensesTransformer)->transformLicense($temp_license)); - } - - /** @test */ - public function deleteLicenseWithUsersTest(ApiTester $I, $scenario) - { - $I->wantTo('Ensure a license with seats checked out cannot be deleted'); - - // create - $license = \App\Models\License::factory()->acrobat()->create([ - 'name' => 'Soon to be deleted', - ]); - $licenseSeat = $license->freeSeat(); - $licenseSeat->assigned_to = $this->user->id; - $licenseSeat->save(); - $I->assertInstanceOf(\App\Models\License::class, $license); - - // delete - $I->sendDELETE('/licenses/'.$license->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('error', $response->status); - $I->assertEquals(trans('admin/licenses/message.assoc_users'), $response->messages); - } - - /** @test */ - public function deleteLicenseTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an license'); - - // create - $license = \App\Models\License::factory()->acrobat()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\License::class, $license); - - // delete - $I->sendDELETE('/licenses/'.$license->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/licenses/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/licenses/'.$license->id); - - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiLocationsCest.php b/tests/api/ApiLocationsCest.php deleted file mode 100644 index 536830f00d..0000000000 --- a/tests/api/ApiLocationsCest.php +++ /dev/null @@ -1,154 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexLocations(ApiTester $I) - { - $I->wantTo('Get a list of locations'); - - // call - $I->sendGET('/locations?limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $location = App\Models\Location::orderByDesc('created_at') - ->withCount('assignedAssets as assigned_assets_count', 'assets as assets_count', 'users as users_count') - ->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new LocationsTransformer)->transformLocation($location))); - } - - /** @test */ - public function createLocation(ApiTester $I, $scenario) - { - $I->wantTo('Create a new location'); - - $temp_location = \App\Models\Location::factory()->make([ - 'name' => 'Test Location Tag', - ]); - - // setup - $data = [ - 'name' => $temp_location->name, - 'image' => $temp_location->image, - 'address' => $temp_location->address, - 'address2' => $temp_location->address2, - 'city' => $temp_location->city, - 'state' => $temp_location->state, - 'country' => $temp_location->country, - 'zip' => $temp_location->zip, - 'parent_id' => $temp_location->parent_id, - 'parent_id' => $temp_location->parent_id, - 'manager_id' => $temp_location->manager_id, - 'currency' => $temp_location->currency, - ]; - - // create - $I->sendPOST('/locations', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateLocationWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an location with PATCH'); - - // create - $location = \App\Models\Location::factory()->create([ - 'name' => 'Original Location Name', - ]); - $I->assertInstanceOf(\App\Models\Location::class, $location); - - $temp_location = \App\Models\Location::factory()->make([ - 'name' => 'updated location name', - ]); - - $data = [ - 'name' => $temp_location->name, - 'image' => $temp_location->image, - 'address' => $temp_location->address, - 'address2' => $temp_location->address2, - 'city' => $temp_location->city, - 'state' => $temp_location->state, - 'country' => $temp_location->country, - 'zip' => $temp_location->zip, - 'parent_id' => $temp_location->parent_id, - 'parent_id' => $temp_location->parent_id, - 'manager_id' => $temp_location->manager_id, - 'currency' => $temp_location->currency, - ]; - - $I->assertNotEquals($location->name, $data['name']); - - // update - $I->sendPATCH('/locations/'.$location->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/locations/message.update.success'), $response->messages); - $I->assertEquals($location->id, $response->payload->id); // location id does not change - $I->assertEquals($temp_location->name, $response->payload->name); // location name updated - - // Some necessary manual copying - $temp_location->created_at = Carbon::parse($response->payload->created_at->datetime); - $temp_location->updated_at = Carbon::parse($response->payload->updated_at->datetime); - $temp_location->id = $location->id; - - // verify - $I->sendGET('/locations/'.$location->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new LocationsTransformer)->transformLocation($temp_location)); - } - - /** @test */ - public function deleteLocationTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an location'); - - // create - $location = \App\Models\Location::factory()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Location::class, $location); - - // delete - $I->sendDELETE('/locations/'.$location->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/locations/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/locations/'.$location->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiManufacturersCest.php b/tests/api/ApiManufacturersCest.php deleted file mode 100644 index be2f4cc483..0000000000 --- a/tests/api/ApiManufacturersCest.php +++ /dev/null @@ -1,142 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexManufacturers(ApiTester $I) - { - $I->wantTo('Get a list of manufacturers'); - - // call - $I->sendGET('/manufacturers?order_by=id&limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $manufacturer = App\Models\Manufacturer::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'licenses as licenses_count') - ->orderByDesc('created_at')->take(10)->get()->shuffle()->first(); - - $I->seeResponseContainsJson($I->removeTimestamps((new ManufacturersTransformer)->transformManufacturer($manufacturer))); - } - - /** @test */ - public function createManufacturer(ApiTester $I, $scenario) - { - $I->wantTo('Create a new manufacturer'); - - $temp_manufacturer = \App\Models\Manufacturer::factory()->apple()->make([ - 'name' => 'Test Manufacturer Tag', - ]); - - // setup - $data = [ - 'image' => $temp_manufacturer->image, - 'name' => $temp_manufacturer->name, - 'support_email' => $temp_manufacturer->support_email, - 'support_phone' => $temp_manufacturer->support_phone, - 'support_url' => $temp_manufacturer->support_url, - 'url' => $temp_manufacturer->url, - ]; - - // create - $I->sendPOST('/manufacturers', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateManufacturerWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an manufacturer with PATCH'); - - // create - $manufacturer = \App\Models\Manufacturer::factory()->apple() - ->create([ - 'name' => 'Original Manufacturer Name', - ]); - $I->assertInstanceOf(\App\Models\Manufacturer::class, $manufacturer); - - $temp_manufacturer = \App\Models\Manufacturer::factory()->dell()->make([ - 'name' => 'updated manufacturer name', - ]); - - $data = [ - 'image' => $temp_manufacturer->image, - 'name' => $temp_manufacturer->name, - 'support_email' => $temp_manufacturer->support_email, - 'support_phone' => $temp_manufacturer->support_phone, - 'support_url' => $temp_manufacturer->support_url, - 'url' => $temp_manufacturer->url, - ]; - - $I->assertNotEquals($manufacturer->name, $data['name']); - - // update - $I->sendPATCH('/manufacturers/'.$manufacturer->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/manufacturers/message.update.success'), $response->messages); - $I->assertEquals($manufacturer->id, $response->payload->id); // manufacturer id does not change - $I->assertEquals($temp_manufacturer->name, $response->payload->name); // manufacturer name updated - // Some manual copying to compare against - $temp_manufacturer->created_at = Carbon::parse($response->payload->created_at); - $temp_manufacturer->updated_at = Carbon::parse($response->payload->updated_at); - $temp_manufacturer->id = $manufacturer->id; - - // verify - $I->sendGET('/manufacturers/'.$manufacturer->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new ManufacturersTransformer)->transformManufacturer($temp_manufacturer)); - } - - /** @test */ - public function deleteManufacturerTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an manufacturer'); - - // create - $manufacturer = \App\Models\Manufacturer::factory()->apple()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Manufacturer::class, $manufacturer); - - // delete - $I->sendDELETE('/manufacturers/'.$manufacturer->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/manufacturers/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/manufacturers/'.$manufacturer->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiModelsCest.php b/tests/api/ApiModelsCest.php deleted file mode 100644 index 1d3d6fc788..0000000000 --- a/tests/api/ApiModelsCest.php +++ /dev/null @@ -1,145 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexAssetModels(ApiTester $I) - { - $I->wantTo('Get a list of assetmodels'); - - // call - $I->sendGET('/models?limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - $assetmodel = App\Models\AssetModel::orderByDesc('created_at') - ->withCount('assets as assets_count')->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new AssetModelsTransformer)->transformAssetModel($assetmodel))); - } - - /** @test */ - public function createAssetModel(ApiTester $I, $scenario) - { - $I->wantTo('Create a new assetmodel'); - - $temp_assetmodel = \App\Models\AssetModel::factory()->mbp13Model()->make([ - 'name' => 'Test AssetModel Tag', - ]); - - // setup - $data = [ - 'category_id' => $temp_assetmodel->category_id, - 'depreciation_id' => $temp_assetmodel->depreciation_id, - 'eol' => $temp_assetmodel->eol, - 'image' => $temp_assetmodel->image, - 'manufacturer_id' => $temp_assetmodel->manufacturer_id, - 'model_number' => $temp_assetmodel->model_number, - 'name' => $temp_assetmodel->name, - 'notes' => $temp_assetmodel->notes, - ]; - - // create - $I->sendPOST('/models', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateAssetModelWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an assetmodel with PATCH'); - - // create - $assetmodel = \App\Models\AssetModel::factory()->mbp13Model()->create([ - 'name' => 'Original AssetModel Name', - ]); - $I->assertInstanceOf(\App\Models\AssetModel::class, $assetmodel); - - $temp_assetmodel = \App\Models\AssetModel::factory()->polycomcxModel()->make([ - 'name' => 'updated AssetModel name', - 'fieldset_id' => 2, - ]); - - $data = [ - 'category_id' => $temp_assetmodel->category_id, - 'depreciation_id' => $temp_assetmodel->depreciation_id, - 'eol' => $temp_assetmodel->eol, - 'image' => $temp_assetmodel->image, - 'manufacturer_id' => $temp_assetmodel->manufacturer_id, - 'model_number' => $temp_assetmodel->model_number, - 'name' => $temp_assetmodel->name, - 'notes' => $temp_assetmodel->notes, - 'fieldset' => $temp_assetmodel->fieldset->id, - ]; - - $I->assertNotEquals($assetmodel->name, $data['name']); - - // update - $I->sendPATCH('/models/'.$assetmodel->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/models/message.update.success'), $response->messages); - $I->assertEquals($assetmodel->id, $response->payload->id); // assetmodel id does not change - $I->assertEquals($temp_assetmodel->name, $response->payload->name); // assetmodel name updated - - // Some necessary manual copying - $temp_assetmodel->created_at = Carbon::parse($response->payload->created_at); - $temp_assetmodel->updated_at = Carbon::parse($response->payload->updated_at); - $temp_assetmodel->id = $assetmodel->id; - // verify - $I->sendGET('/models/'.$assetmodel->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new AssetModelsTransformer)->transformAssetModel($temp_assetmodel)); - } - - /** @test */ - public function deleteAssetModelTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an assetmodel'); - - // create - $assetmodel = \App\Models\AssetModel::factory()->mbp13Model()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\AssetModel::class, $assetmodel); - - // delete - $I->sendDELETE('/models/'.$assetmodel->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/models/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/models/'.$assetmodel->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiStatusLabelsCest.php b/tests/api/ApiStatusLabelsCest.php deleted file mode 100644 index fabb71d51f..0000000000 --- a/tests/api/ApiStatusLabelsCest.php +++ /dev/null @@ -1,142 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexStatuslabels(ApiTester $I) - { - $I->wantTo('Get a list of statuslabels'); - - // call - $I->sendGET('/statuslabels?limit=10'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $statuslabel = App\Models\Statuslabel::orderByDesc('created_at') - ->withCount('assets as assets_count') - ->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new StatuslabelsTransformer)->transformStatuslabel($statuslabel))); - } - - /** @test */ - public function createStatuslabel(ApiTester $I, $scenario) - { - $I->wantTo('Create a new statuslabel'); - - $temp_statuslabel = \App\Models\Statuslabel::factory()->make([ - 'name' => 'Test Statuslabel Tag', - ]); - - // setup - $data = [ - 'name' => $temp_statuslabel->name, - 'archived' => $temp_statuslabel->archived, - 'deployable' => $temp_statuslabel->deployable, - 'notes' => $temp_statuslabel->notes, - 'pending' => $temp_statuslabel->pending, - 'type' => 'deployable', - ]; - - // create - $I->sendPOST('/statuslabels', $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateStatuslabelWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an statuslabel with PATCH'); - - // create - $statuslabel = \App\Models\Statuslabel::factory()->rtd()->create([ - 'name' => 'Original Statuslabel Name', - ]); - $I->assertInstanceOf(\App\Models\Statuslabel::class, $statuslabel); - - $temp_statuslabel = \App\Models\Statuslabel::factory()->pending()->make([ - 'name' => 'updated statuslabel name', - 'type' => 'pending', - ]); - - $data = [ - 'name' => $temp_statuslabel->name, - 'archived' => $temp_statuslabel->archived, - 'deployable' => $temp_statuslabel->deployable, - 'notes' => $temp_statuslabel->notes, - 'pending' => $temp_statuslabel->pending, - 'type' => $temp_statuslabel->type, - ]; - - $I->assertNotEquals($statuslabel->name, $data['name']); - - // update - $I->sendPATCH('/statuslabels/'.$statuslabel->id, $data); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - // dd($response); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/statuslabels/message.update.success'), $response->messages); - $I->assertEquals($statuslabel->id, $response->payload->id); // statuslabel id does not change - $I->assertEquals($temp_statuslabel->name, $response->payload->name); // statuslabel name updated - // Some manual copying to compare against - $temp_statuslabel->created_at = Carbon::parse($response->payload->created_at); - $temp_statuslabel->updated_at = Carbon::parse($response->payload->updated_at); - $temp_statuslabel->id = $statuslabel->id; - - // verify - $I->sendGET('/statuslabels/'.$statuslabel->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new StatuslabelsTransformer)->transformStatuslabel($temp_statuslabel)); - } - - /** @test */ - public function deleteStatuslabelTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an statuslabel'); - - // create - $statuslabel = \App\Models\Statuslabel::factory()->create([ - 'name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\Statuslabel::class, $statuslabel); - - // delete - $I->sendDELETE('/statuslabels/'.$statuslabel->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/statuslabels/message.delete.success'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/statuslabels/'.$statuslabel->id); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } -} diff --git a/tests/api/ApiUsersCest.php b/tests/api/ApiUsersCest.php deleted file mode 100644 index 912653be2d..0000000000 --- a/tests/api/ApiUsersCest.php +++ /dev/null @@ -1,205 +0,0 @@ -user = \App\Models\User::find(1); - $I->haveHttpHeader('Accept', 'application/json'); - $I->amBearerAuthenticated($I->getToken($this->user)); - } - - /** @test */ - public function indexUsers(ApiTester $I) - { - $I->wantTo('Get a list of users'); - - // call - $I->sendGET('/users?limit=10&sort=created_at'); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse(), true); - // sample verify - $user = App\Models\User::orderByDesc('created_at') - ->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count') - ->take(10)->get()->shuffle()->first(); - $I->seeResponseContainsJson($I->removeTimestamps((new UsersTransformer)->transformUser($user))); - } - - /** @test */ - public function createUser(ApiTester $I, $scenario) - { - $I->wantTo('Create a new user'); - - $temp_user = \App\Models\User::factory()->make([ - 'name' => 'Test User Name', - ]); - Group::factory()->count(2)->create(); - $groups = Group::pluck('id'); - // setup - $data = [ - 'activated' => $temp_user->activated, - 'address' => $temp_user->address, - 'city' => $temp_user->city, - 'company_id' => $temp_user->company_id, - 'country' => $temp_user->country, - 'department_id' => $temp_user->department_id, - 'email' => $temp_user->email, - 'employee_num' => $temp_user->employee_num, - 'first_name' => $temp_user->first_name, - 'jobtitle' => $temp_user->jobtitle, - 'last_name' => $temp_user->last_name, - 'locale' => $temp_user->locale, - 'location_id' => $temp_user->location_id, - 'notes' => $temp_user->notes, - 'manager_id' => $temp_user->manager_id, - 'password' => $temp_user->password, - 'password_confirmation' => $temp_user->password, - 'phone' => $temp_user->phone, - 'state' => $temp_user->state, - 'username' => $temp_user->username, - 'zip' => $temp_user->zip, - 'groups' => $groups, - ]; - - // create - $I->sendPOST('/users', $data); - $I->seeResponseIsJson(); - $user = User::where('username', $temp_user->username)->first(); - $I->assertEquals($groups, $user->groups()->pluck('id')); - $I->seeResponseCodeIs(200); - } - - // Put is routed to the same method in the controller - // DO we actually need to test both? - - /** @test */ - public function updateUserWithPatch(ApiTester $I, $scenario) - { - $I->wantTo('Update an user with PATCH'); - - // create - $user = \App\Models\User::factory()->create([ - 'first_name' => 'Original User Name', - 'company_id' => 2, - 'location_id' => 3, - ]); - $I->assertInstanceOf(\App\Models\User::class, $user); - - $temp_user = \App\Models\User::factory()->make([ - 'company_id' => 3, - 'first_name' => 'updated user name', - 'location_id' => 1, - ]); - - Group::factory()->count(2)->create(); - $groups = Group::pluck('id'); - - $data = [ - 'activated' => $temp_user->activated, - 'address' => $temp_user->address, - 'city' => $temp_user->city, - 'company_id' => $temp_user->company_id, - 'country' => $temp_user->country, - 'department_id' => $temp_user->department_id, - 'email' => $temp_user->email, - 'employee_num' => $temp_user->employee_num, - 'first_name' => $temp_user->first_name, - 'groups' => $groups, - 'jobtitle' => $temp_user->jobtitle, - 'last_name' => $temp_user->last_name, - 'locale' => $temp_user->locale, - 'location_id' => $temp_user->location_id, - 'notes' => $temp_user->notes, - 'manager_id' => $temp_user->manager_id, - 'password' => $temp_user->password, - 'phone' => $temp_user->phone, - 'state' => $temp_user->state, - 'username' => $temp_user->username, - 'zip' => $temp_user->zip, - ]; - - $I->assertNotEquals($user->first_name, $data['first_name']); - - // update - $I->sendPATCH('/users/'.$user->id, $data); - $I->seeResponseIsJson(); - - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/users/message.success.update'), $response->messages); - $I->assertEquals($user->id, $response->payload->id); // user id does not change - $I->assertEquals($temp_user->company_id, $response->payload->company->id); // company_id updated - $I->assertEquals($temp_user->first_name, $response->payload->first_name); // user name updated - $I->assertEquals($temp_user->location_id, $response->payload->location->id); // user location_id updated - $newUser = User::where('username', $temp_user->username)->first(); - $I->assertEquals($groups, $newUser->groups()->pluck('id')); - $temp_user->created_at = Carbon::parse($response->payload->created_at->datetime); - $temp_user->updated_at = Carbon::parse($response->payload->updated_at->datetime); - $temp_user->id = $user->id; - // verify - $I->sendGET('/users/'.$user->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - $I->seeResponseContainsJson((new UsersTransformer)->transformUser($temp_user)); - } - - /** @test */ - public function deleteUserTest(ApiTester $I, $scenario) - { - $I->wantTo('Delete an user'); - - // create - $user = \App\Models\User::factory()->create([ - 'first_name' => 'Soon to be deleted', - ]); - $I->assertInstanceOf(\App\Models\User::class, $user); - - // delete - $I->sendDELETE('/users/'.$user->id); - $I->seeResponseIsJson(); - $I->seeResponseCodeIs(200); - - $response = json_decode($I->grabResponse()); - // dd($response); - $I->assertEquals('success', $response->status); - $I->assertEquals(trans('admin/users/message.success.delete'), $response->messages); - - // verify, expect a 200 - $I->sendGET('/users/'.$user->id); - - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - } - - /** @test */ - public function fetchUserAssetsTest(ApiTester $I, $scenario) - { - $I->wantTo('Fetch assets for a user'); - - $user = User::has('assets')->first(); - $asset = $user->assets->shuffle()->first(); - $I->sendGET("/users/{$user->id}/assets"); - $response = json_decode($I->grabResponse()); - $I->seeResponseCodeIs(200); - $I->seeResponseIsJson(); - - // Just test a random one. - $I->seeResponseContainsJson([ - 'asset_tag' => $asset->asset_tag, - ]); - } -} diff --git a/tests/api/_bootstrap.php b/tests/api/_bootstrap.php deleted file mode 100644 index 62817a53c4..0000000000 --- a/tests/api/_bootstrap.php +++ /dev/null @@ -1,3 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - $I->seeAuthentication(); - } - - // tests - public function loadsFormWithoutErrors(FunctionalTester $I) - { - $I->wantTo('ensure that the create accessories form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage('/accessories/create'); - $I->seeResponseCodeIs(200); - $I->dontSee('Create Accessory', '.page-header'); - $I->see('Create Accessory', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage('/accessories/create'); - $I->seeResponseCodeIs(200); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The category id field is required.', '.alert-msg'); - $I->see('The qty field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage('/accessories/create'); - $I->seeResponseCodeIs(200); - $I->fillField('name', 't2'); - $I->fillField('qty', '-15'); - $I->fillField('min_amt', '-15'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 3 characters', '.alert-msg'); - $I->see('The category id field is required', '.alert-msg'); - $I->see('The qty must be at least 1', '.alert-msg'); - $I->see('The min amt must be at least 0', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $accessory = \App\Models\Accessory::factory()->appleBtKeyboard()->make(); - $values = [ - 'category_id' => $accessory->category_id, - 'location_id' => $accessory->location_id, - 'manufacturer_id' => $accessory->manufacturer_id, - 'min_amt' => $accessory->min_amt, - 'name' => 'Test Accessory', - 'order_number' => $accessory->order_number, - 'purchase_date' => '2016-01-01', - 'qty' => $accessory->qty, - ]; - - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage('/accessories/create'); - $I->seeResponseCodeIs(200); - - $I->submitForm('form#create-form', $values); - $I->seeRecord('accessories', $values); - - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete an accessory'); - $I->sendDelete(route('accessories.destroy', $I->getAccessoryId()), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/AssetModelsCest.php b/tests/functional/AssetModelsCest.php deleted file mode 100644 index fbf43a5c57..0000000000 --- a/tests/functional/AssetModelsCest.php +++ /dev/null @@ -1,63 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('Test Asset Model Creation'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('models.create')); - $I->seeInTitle('Create Asset Model'); - $I->see('Create Asset Model', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('models.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The manufacturer id field is required.', '.alert-msg'); - $I->see('The category id field is required.', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $model = \App\Models\AssetModel::factory()->mbp13Model()->make(['name'=>'Test Model']); - $values = [ - 'category_id' => $model->category_id, - 'depreciation_id' => $model->depreciation_id, - 'eol' => $model->eol, - 'manufacturer_id' => $model->manufacturer_id, - 'model_number' => $model->model_number, - 'name' => $model->name, - 'notes' => $model->notes, - ]; - - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('models.create')); - - $I->submitForm('form#create-form', $values); - $I->seeRecord('models', $values); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete an asset model'); - $model = \App\Models\AssetModel::factory()->mbp13Model()->create(['name' => 'Test Model']); - $I->sendDelete(route('models.destroy', $model->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/AssetsCest.php b/tests/functional/AssetsCest.php deleted file mode 100644 index 3c7ffc1282..0000000000 --- a/tests/functional/AssetsCest.php +++ /dev/null @@ -1,94 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create assets form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('hardware.create')); - $I->dontSee('Create Asset', '.page-header'); - $I->see('Create Asset', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('hardware.create')); - // Settings factory can enable auto prefixes, which generate a random asset id. Lets clear it out for the sake of this test. - $I->fillField('#asset_tag', ''); - $I->click('Save'); - $I->see('The asset tag field is required.', '.alert-msg'); - $I->see('The model id field is required.', '.alert-msg'); - $I->see('The status id field is required.', '.alert-msg'); - } - - public function passesCreateAndCheckout(FunctionalTester $I) - { - $asset = \App\Models\Asset::factory()->laptopMbp()->make([ - 'asset_tag'=>'test tag', - 'name'=> 'test asset', - 'company_id'=>1, - 'warranty_months'=>15, - ]); - $userId = $I->getUserId(); - $values = [ - 'asset_tags[1]' => $asset->asset_tag, - 'assigned_user' => $userId, - 'company_id' => $asset->company_id, - 'model_id' => $asset->model_id, - 'name' => $asset->name, - 'notes' => $asset->notes, - 'order_number' => $asset->order_number, - 'purchase_cost' => $asset->purchase_cost, - 'purchase_date' => '2016-01-01', - 'requestable' => $asset->requestable, - 'rtd_location_id' => $asset->rtd_location_id, - 'serials[1]' => $asset->serial, - 'status_id' => $asset->status_id, - 'supplier_id' => $asset->supplier_id, - 'warranty_months' => $asset->warranty_months, - ]; - - $seenValues = [ - 'asset_tag' => $asset->asset_tag, - 'assigned_to' => $userId, - 'assigned_type' => \App\Models\User::class, - 'company_id' => $asset->company_id, - 'model_id' => $asset->model_id, - 'name' => $asset->name, - 'notes' => $asset->notes, - 'order_number' => $asset->order_number, - 'purchase_cost' => $asset->purchase_cost, - 'purchase_date' => '2016-01-01', - 'requestable' => $asset->requestable, - 'rtd_location_id' => $asset->rtd_location_id, - 'serial' => $asset->serial, - 'status_id' => $asset->status_id, - 'supplier_id' => $asset->supplier_id, - 'warranty_months' => $asset->warranty_months, - ]; - - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('hardware.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('assets', $seenValues); - $I->seeResponseCodeIs(200); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete an asset'); - $I->sendDelete(route('hardware.destroy', $I->getAssetId()), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/CategoriesCest.php b/tests/functional/CategoriesCest.php deleted file mode 100644 index ff89fcc152..0000000000 --- a/tests/functional/CategoriesCest.php +++ /dev/null @@ -1,66 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - public function _after(FunctionalTester $I) - { - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('Test Category Creation'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('categories.create')); - $I->seeInTitle('Create Category'); - $I->see('Create Category', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('categories.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The category type field is required.', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $category = \App\Models\Category::factory()->assetLaptopCategory()->make([ - 'name' => 'Test Category', - ]); - $values = [ - 'category_type' => $category->category_type, - 'checkin_email' => $category->checkin_email, - 'eula_text' => $category->eula_text, - 'name' => $category->name, - 'require_acceptance' => $category->require_acceptance, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('categories.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('categories', $values); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a category'); - $category = \App\Models\Category::factory()->assetLaptopCategory()->create([ - 'name'=>'Deletable Test Category', - ]); - $I->sendDelete(route('categories.destroy', $category->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/CompaniesCest.php b/tests/functional/CompaniesCest.php deleted file mode 100644 index 75724aa206..0000000000 --- a/tests/functional/CompaniesCest.php +++ /dev/null @@ -1,46 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('Test Company Creation'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('companies.create')); - $I->seeInTitle('Create Company'); - $I->see('Create Company', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('companies.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $company = \App\Models\Company::factory()->make(); - $values = [ - 'name' => $company->name, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('companies.create')); - $I->fillField('name', 'TestCompany'); - $I->submitForm('form#create-form', $values); - $I->seeRecord('companies', $values); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } -} diff --git a/tests/functional/ComponentsCest.php b/tests/functional/ComponentsCest.php deleted file mode 100644 index 6e2a7f136b..0000000000 --- a/tests/functional/ComponentsCest.php +++ /dev/null @@ -1,79 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create components form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('components.create')); - $I->dontSee('Create Component', '.page-header'); - $I->see('Create Component', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('components.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The category id field is required.', '.alert-msg'); - $I->see('The qty field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('components.create')); - $I->fillField('name', 't2'); - $I->fillField('qty', '-15'); - $I->fillField('min_amt', '-15'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 3 characters', '.alert-msg'); - $I->see('The qty must be at least 1', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $component = \App\Models\Component::factory()->ramCrucial4()->make([ - 'name' => 'Test Component', - 'serial' => '3523-235325-1350235', - ]); - $values = [ - 'category_id' => $component->category_id, - 'company_id' => $component->company_id, - 'location_id' => $component->location_id, - 'min_amt' => $component->min_amt, - 'name' => $component->name, - 'order_number' => $component->order_number, - 'purchase_cost' => $component->purchase_cost, - 'purchase_date' => '2016-01-01', - 'qty' => $component->qty, - 'serial' => $component->serial, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('components.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('components', $values); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a component'); - $I->sendDelete(route('components.destroy', $I->getComponentId()), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/ConsumablesCest.php b/tests/functional/ConsumablesCest.php deleted file mode 100644 index c47eb23c0f..0000000000 --- a/tests/functional/ConsumablesCest.php +++ /dev/null @@ -1,81 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create consumables form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('consumables.create')); - $I->dontSee('Create Consumable', '.page-header'); - $I->see('Create Consumable', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('consumables.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The category id field is required.', '.alert-msg'); - $I->see('The qty field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('consumables.create')); - $I->fillField('name', 't2'); - $I->fillField('qty', '-15'); - $I->fillField('min_amt', '-15'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 3 characters', '.alert-msg'); - $I->see('The qty must be at least 0', '.alert-msg'); - $I->see('The min amt must be at least 0', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $consumable = \App\Models\Consumable::factory()->cardstock()->make([ - 'name' => 'Test Consumable', - 'model_number' => 23520, - ]); - // dd($consumable); - $values = [ - 'category_id' => $consumable->category_id, - 'company_id' => $consumable->company_id, - 'item_no' => $consumable->item_no, - 'manufacturer_id' => $consumable->manufacturer_id, - 'min_amt' => $consumable->min_amt, - 'model_number' => $consumable->model_number, - 'name' => $consumable->name, - 'order_number' => $consumable->order_number, - 'purchase_cost' => $consumable->purchase_cost, - 'purchase_date' => '2016-01-01', - 'qty' => $consumable->qty, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('consumables.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('consumables', $values); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a consumable'); - $I->sendDelete(route('consumables.destroy', $I->getConsumableId()), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/DepreciationsCest.php b/tests/functional/DepreciationsCest.php deleted file mode 100644 index 25dcfaef1f..0000000000 --- a/tests/functional/DepreciationsCest.php +++ /dev/null @@ -1,66 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('Test Depreciation Creation'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('depreciations.create')); - $I->seeInTitle('Create Depreciation'); - $I->dontSee('Create Depreciation', '.page-header'); - $I->see('Create Depreciation', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('depreciations.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The months field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('depreciations.create')); - $I->fillField('name', 't2'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 3 characters', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $depreciation = \App\Models\Depreciation::factory()->computer()->make([ - 'name'=>'Test Depreciation', - ]); - $values = [ - 'name' => $depreciation->name, - 'months' => $depreciation->months, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('depreciations.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('depreciations', $values); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a depreciation'); - $I->sendDelete(route('depreciations.destroy', $I->getDepreciationId()), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/GroupsCest.php b/tests/functional/GroupsCest.php deleted file mode 100644 index aa9ba9872a..0000000000 --- a/tests/functional/GroupsCest.php +++ /dev/null @@ -1,74 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function loadsFormWithoutErrors(FunctionalTester $I) - { - $I->wantTo('ensure that the create groups form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('groups.create')); - $I->seeResponseCodeIs(200); - $I->dontSee('Create New Group', '.page-header'); - $I->see('Create New Group', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('groups.create')); - $I->seeResponseCodeIs(200); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('groups.create')); - $I->seeResponseCodeIs(200); - $I->fillField('name', 't'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 2 characters', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('groups.create')); - $I->seeResponseCodeIs(200); - $I->fillField('name', 'TestGroup'); - $I->click('Save'); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I, $scenario) - { - $I->wantTo('Ensure I can delete a group'); - // create a group - $I->amOnPage(route('groups.create')); - $I->seeResponseCodeIs(200); - $I->fillField('name', 'TestGroup'); - $I->click('Save'); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - - $I->sendDelete(route('groups.destroy', Group::whereName('TestGroup')->doesntHave('users')->first()->id)); - $I->seeResponseCodeIs(200); - $I->seeElement('.alert-success'); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/LicensesCest.php b/tests/functional/LicensesCest.php deleted file mode 100644 index 5add67f51d..0000000000 --- a/tests/functional/LicensesCest.php +++ /dev/null @@ -1,88 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create licenses form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('licenses.create')); - $I->dontSee('Create License', '.page-header'); - $I->see('Create License', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('licenses.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - $I->see('The seats field is required.', '.alert-msg'); - $I->see('The category id field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('licenses.create')); - $I->fillField('name', 't2'); - $I->fillField('seats', '-15'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 3 characters', '.alert-msg'); - $I->see('The seats must be at least 1', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $license = \App\Models\License::factory()->photoshop()->make([ - 'name' => 'Test License', - 'company_id' => 3, - ]); - $values = [ - 'company_id' => $license->company_id, - 'expiration_date' => '2018-01-01', - 'license_email' => $license->license_email, - 'license_name' => $license->license_name, - 'maintained' => true, - 'manufacturer_id' => $license->manufacturer_id, - 'category_id' => $license->category_id, - 'name' => $license->name, - 'notes' => $license->notes, - 'order_number' => $license->order_number, - 'purchase_cost' => $license->purchase_cost, - 'purchase_date' => '2016-01-01', - 'purchase_order' => $license->purchase_order, - 'reassignable' => true, - 'seats' => $license->seats, - 'serial' => $license->serial, - 'termination_date' => '2020-01-01', - ]; - - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('licenses.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('licenses', $values); - $I->dontSee('<span class="'); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a license'); - $I->sendDelete(route('licenses.destroy', License::doesntHave('assignedUsers')->first()->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/LocationsCest.php b/tests/functional/LocationsCest.php deleted file mode 100644 index 9177c6ae4b..0000000000 --- a/tests/functional/LocationsCest.php +++ /dev/null @@ -1,74 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - /* Create Form */ - $I->wantTo('Test Location Creation'); - $I->lookForwardTo('Finding no Failures'); - $I->amOnPage(route('locations.create')); - $I->dontSee('Create Location', '.page-header'); - $I->see('Create Location', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('locations.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short values'); - $I->amOnPage(route('locations.create')); - $I->fillField('name', 't'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 2 characters', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $location = \App\Models\Location::factory()->make(); - $values = [ - 'name' => $location->name, - 'parent_id' => $I->getLocationId(), - 'currency' => $location->currency, - 'address' => $location->address, - 'address2' => $location->address2, - 'city' => $location->city, - 'state' => $location->state, - 'country' => $location->country, - 'zip' => $location->zip, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('locations.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('locations', $values); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a location'); - $location = \App\Models\Location::factory()->create(); - $I->sendDelete(route('locations.destroy', $location->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/ManufacturersCest.php b/tests/functional/ManufacturersCest.php deleted file mode 100644 index 65c8b1d190..0000000000 --- a/tests/functional/ManufacturersCest.php +++ /dev/null @@ -1,66 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('Test Manufacturer Creation'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('manufacturers.create')); - $I->seeInTitle('Create Manufacturer'); - $I->see('Create Manufacturer', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('manufacturers.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('manufacturers.create')); - $I->fillField('name', 't'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name must be at least 2 characters', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $manufacturer = \App\Models\Manufacturer::factory()->microsoft()->make([ - 'name' => 'Test Manufacturer', - ]); - $values = [ - 'name' => $manufacturer->name, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('manufacturers.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('manufacturers', $values); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a manufacturer'); - $manufacturerId = \App\Models\Manufacturer::factory()->microsoft()->create(['name' => 'Deletable Test Manufacturer'])->id; - $I->sendDelete(route('manufacturers.destroy', $manufacturerId), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/StatusLabelsCest.php b/tests/functional/StatusLabelsCest.php deleted file mode 100644 index ff1ec90961..0000000000 --- a/tests/functional/StatusLabelsCest.php +++ /dev/null @@ -1,67 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create statuslabels form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('statuslabels.create')); - $I->dontSee('Create Status Label', '.page-header'); - $I->see('Create Status Label', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('statuslabels.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $status = \App\Models\Statuslabel::factory()->pending()->make(); - $submitValues = [ - 'name' => 'Testing Status', - 'statuslabel_types' => 'pending', - 'color' => '#b46262', - 'notes' => $status->notes, - 'show_in_nav' => true, - ]; - - $recordValues = [ - 'name' => 'Testing Status', - 'pending' => $status->pending, - 'deployable' => $status->archived, - 'archived' => $status->deployable, - 'color' => '#b46262', - 'notes' => $status->notes, - 'show_in_nav' => true, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('statuslabels.create')); - $I->submitForm('form#create-form', $submitValues); - $I->seeRecord('status_labels', $recordValues); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a Status Label'); - $I->sendDelete(route('statuslabels.destroy', Statuslabel::doesntHave('assets')->first()->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/SuppliersCest.php b/tests/functional/SuppliersCest.php deleted file mode 100644 index 69746f86a5..0000000000 --- a/tests/functional/SuppliersCest.php +++ /dev/null @@ -1,65 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create settings/suppliers form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('suppliers.create')); - $I->dontSee('Create Supplier', '.page-header'); - $I->see('Create Supplier', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('suppliers.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The name field is required.', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $supplier = \App\Models\Supplier::factory()->make(); - - $values = [ - 'name' => $supplier->name, - 'address' => $supplier->address, - 'address2' => $supplier->address2, - 'city' => $supplier->city, - 'state' => $supplier->state, - 'zip' => $supplier->zip, - 'country' => $supplier->country, - 'contact' => $supplier->contact, - 'phone' => $supplier->phone, - 'fax' => $supplier->fax, - 'email' => $supplier->email, - 'url' => $supplier->url, - 'notes' => $supplier->notes, - ]; - $I->wantTo('Test Validation Succeeds'); - $I->amOnPage(route('suppliers.create')); - $I->submitForm('form#create-form', $values); - $I->seeRecord('suppliers', $values); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $I->wantTo('Ensure I can delete a supplier'); - $supplier = \App\Models\Supplier::factory()->create(); - $I->sendDelete(route('suppliers.destroy', $supplier->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/UsersCest.php b/tests/functional/UsersCest.php deleted file mode 100644 index e861694918..0000000000 --- a/tests/functional/UsersCest.php +++ /dev/null @@ -1,98 +0,0 @@ -amOnPage('/login'); - $I->fillField('username', 'admin'); - $I->fillField('password', 'password'); - $I->click('Login'); - } - - // tests - public function tryToTest(FunctionalTester $I) - { - $I->wantTo('ensure that the create users form loads without errors'); - $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('users.create')); - $I->dontSee('Create User', '.page-header'); - $I->see('Create User', 'h1.pull-left'); - } - - public function failsEmptyValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with blank elements'); - $I->amOnPage(route('users.create')); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The first name field is required.', '.alert-msg'); - $I->see('The username field is required unless ldap import is in 1.', '.alert-msg'); - $I->see('The password field is required.', '.alert-msg'); - } - - public function failsShortValidation(FunctionalTester $I) - { - $I->wantTo('Test Validation Fails with short name'); - $I->amOnPage(route('users.create')); - $I->fillField('first_name', 't2'); - $I->fillField('last_name', 't2'); - $I->fillField('username', 'a'); - $I->fillField('password', '12345'); - $I->click('Save'); - $I->seeElement('.alert-danger'); - $I->see('The password must be at least 8 characters', '.alert-msg'); - } - - public function passesCorrectValidation(FunctionalTester $I) - { - $user = \App\Models\User::factory()->make(); - $submitValues = [ - 'first_name' => $user->first_name, - 'last_name' => $user->last_name, - 'username' => $user->username, - 'password' => $user->password, - 'password_confirmation' => $user->password, - 'email' => $user->email, - 'company_id' => $user->company_id, - 'locale' => $user->locale, - 'employee_num' => $user->employee_num, - 'jobtitle' => $user->jobtitle, - 'manager_id' => $user->manager_id, - 'location_id' => $user->location_id, - 'phone' => $user->phone, - 'activated' => true, - 'notes' => $user->notes, - ]; - $storedValues = [ - 'first_name' => $user->first_name, - 'last_name' => $user->last_name, - 'username' => $user->username, - 'email' => $user->email, - 'company_id' => $user->company_id, - 'locale' => $user->locale, - 'employee_num' => $user->employee_num, - 'jobtitle' => $user->jobtitle, - 'manager_id' => $user->manager_id, - 'location_id' => $user->location_id, - 'phone' => $user->phone, - 'activated' => true, - 'notes' => $user->notes, - ]; - $I->amOnPage(route('users.create')); - $I->wantTo('Test Validation Succeeds'); - $I->submitForm('form#userForm', $submitValues); - $I->seeRecord('users', $storedValues); - $I->seeElement('.alert-success'); - } - - public function allowsDelete(FunctionalTester $I) - { - $user = \App\Models\User::factory()->create(); - $I->wantTo('Ensure I can delete a user'); - $I->sendDelete(route('users.destroy', $user->id), ['_token' => csrf_token()]); - $I->seeResponseCodeIs(200); - } -} diff --git a/tests/functional/_bootstrap.php b/tests/functional/_bootstrap.php deleted file mode 100644 index 62817a53c4..0000000000 --- a/tests/functional/_bootstrap.php +++ /dev/null @@ -1,3 +0,0 @@ -=". $php_min_works. " - <".$php_max_wontwork.") \n"; echo "--------------------------------------------------------\n\n"; -if (version_compare(PHP_VERSION, $required_php_min, '<')) { - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; - echo "This version of PHP (".PHP_VERSION.") is not compatible with Snipe-IT.\n"; - echo "Snipe-IT requires PHP version ".$required_php_min." or greater. Please upgrade \n"; - echo "your version of PHP (web/php-fcgi and cli) and try running this script again.\n\n\n"; - exit; +if ((version_compare(phpversion(), $php_min_works, '>=')) && (version_compare(phpversion(), $php_max_wontwork, '<'))) { + + echo "√ Current PHP version: (" . phpversion() . ") is at least " . $php_min_works . " and less than ".$php_max_wontwork."! Continuing... \n"; + echo sprintf("FYI: The php.ini used by this PHP is: %s\n\n", get_cfg_var('cfg_file_path')); } else { - echo "Current PHP version: (" . PHP_VERSION . ") is at least ".$required_php_min." - continuing... \n"; - echo sprintf("FYI: The php.ini used by this PHP is: %s\n\n", get_cfg_var('cfg_file_path')); + echo "!!!!!!!!!!!!!!!!!!!!!!!!! PHP VERSION ERROR !!!!!!!!!!!!!!!!!!!!!!!!!\n"; + echo "This version of PHP (".phpversion().") is NOT compatible with Snipe-IT.\n"; + echo "Snipe-IT requires PHP versions between ".$php_min_works." and ".$php_max_wontwork.".\n"; + echo "Please install a compatible version of PHP and re-run this script again. \n"; + echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n"; + exit; } - echo "Checking Required PHP extensions... \n\n"; // Get the list of installed extensions diff --git a/webpack.mix.js b/webpack.mix.js index 2cda1ee50f..0aaa0f632c 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -143,6 +143,7 @@ mix [ "./node_modules/bootstrap-table/dist/bootstrap-table.css", "./node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.css", + "./resources/assets/css/dragtable.css", ], "public/css/dist/bootstrap-table.css" ) @@ -170,6 +171,7 @@ mix.combine( "./resources/assets/js/signature_pad.js", "./node_modules/jquery-form-validator/form-validator/jquery.form-validator.js", //problem? "./node_modules/list.js/dist/list.js", + "./node_modules/clipboard/dist/clipboard.js", ], "public/js/build/vendor.js" // this file seems OK! ); @@ -180,10 +182,13 @@ mix.combine( mix .combine( [ + "./resources/assets/js/dragtable.js", './node_modules/bootstrap-table/dist/bootstrap-table.js', + "./resources/assets/js/bootstrap-table-reorder-columns.js", './node_modules/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.js', './node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.js', './node_modules/bootstrap-table/dist/extensions/cookie/bootstrap-table-cookie.js', + './node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.js', './resources/assets/js/extensions/jquery.base64.js', './node_modules/tableexport.jquery.plugin/tableExport.min.js', './node_modules/tableexport.jquery.plugin/libs/jsPDF/jspdf.umd.min.js', @@ -202,6 +207,13 @@ mix ) .version(); +mix + .combine( + ['./node_modules/alpinejs/dist/cdn.js'], + './public/js/dist/all-defer.js' + ) + .version(); + /** * Copy, minify and version skins */
    @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - + + + + + + @endif @@ -1385,6 +1436,16 @@ @stop @section('moar_scripts') + @include ('partials.bootstrap-table') @stop diff --git a/resources/views/layouts/basic.blade.php b/resources/views/layouts/basic.blade.php index 1a56a77b0a..759ffd52a8 100644 --- a/resources/views/layouts/basic.blade.php +++ b/resources/views/layouts/basic.blade.php @@ -8,10 +8,9 @@ {{ ($snipeSettings) && ($snipeSettings->site_name) ? $snipeSettings->site_name : 'Snipe-IT' }} - + {{-- stylesheets --}} - + @@ -960,6 +959,11 @@ \ No newline at end of file diff --git a/resources/views/models/custom_fields_form.blade.php b/resources/views/models/custom_fields_form.blade.php index cbc6a731ab..bae98373e4 100644 --- a/resources/views/models/custom_fields_form.blade.php +++ b/resources/views/models/custom_fields_form.blade.php @@ -53,7 +53,7 @@ @else - @if (($field->field_encrypted=='0') || (Gate::allows('admin'))) + @if (($field->field_encrypted=='0') || (Gate::allows('assets.view.encrypted_custom_fields'))) @else @@ -85,3 +85,12 @@ @endforeach @endif + + + diff --git a/resources/views/models/custom_fields_form_bulk_edit.blade.php b/resources/views/models/custom_fields_form_bulk_edit.blade.php new file mode 100644 index 0000000000..f30c60d331 --- /dev/null +++ b/resources/views/models/custom_fields_form_bulk_edit.blade.php @@ -0,0 +1,118 @@ +@php +//set array up before loop so it doesn't get wiped at every iteration + $fields = []; +@endphp +@foreach($models as $model) +@if (($model) && ($model->fieldset)) + @foreach($model->fieldset->fields AS $field) + @php + //prevents some duplicate queries - open to a better way of skipping dupes in output + //its ugly, but if we'd rather deal with duplicate queries we can get rid of this. + if (in_array($field->db_column_name(), $fields)) { + $duplicate = true; + continue; + } else { + $duplicate = false; + } + $fields[] = $field->db_column_name(); + @endphp + +
    + +
    + + @if ($field->element!='text') + + @if ($field->element=='listbox') + {{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(), + Request::old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, htmlspecialchars($item->{$field->db_column_name()}, ENT_QUOTES)) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }} + + @elseif ($field->element=='textarea') + @if($field->is_unique) + + @endif + @if(!$field->is_unique) + + @endif + @elseif ($field->element=='checkbox') + + @foreach ($field->formatFieldValuesAsArray() as $key => $value) + + + @endforeach + @elseif ($field->element=='radio') + @foreach ($field->formatFieldValuesAsArray() as $value) + + + + @endforeach + + @endif + + @else + + + @if ($field->format=='DATE') + +
    +
    + + +
    +
    + + + @else + + @if (($field->field_encrypted=='0') || (Gate::allows('admin'))) + @if ($field->is_unique) + + @endif + @if(!$field->is_unique) + + @endif + @else + + @endif + + @endif + + @endif + + @if ($field->help_text!='') +

    {{ $field->help_text }}

    + @endif + +

    {{ trans('admin/hardware/form.bulk_update_model_prefix') }}: + {{$field->assetModels()->pluck('name')->intersect($modelNames)->implode(', ')}} +

    + + + + + first($field->db_column_name()); + if ($errormessage) { + $errormessage=preg_replace('/ snipeit /', '', $errormessage); + print(''); + } + ?> +
    + + @if ($field->field_encrypted) +
    + +
    + @endif + + +
    + @endforeach +@endif + @endforeach diff --git a/resources/views/models/edit.blade.php b/resources/views/models/edit.blade.php index 74d3c1b83c..14a7e839e1 100755 --- a/resources/views/models/edit.blade.php +++ b/resources/views/models/edit.blade.php @@ -15,6 +15,7 @@ @include ('partials.forms.edit.manufacturer-select', ['translated_name' => trans('general.manufacturer'), 'fieldname' => 'manufacturer_id']) @include ('partials.forms.edit.model_number') @include ('partials.forms.edit.depreciation') +@include ('partials.forms.edit.minimum_quantity') @@ -34,7 +35,8 @@ -@livewire('custom-field-set-default-values-for-model',["model_id" => $item->id]) + +@livewire('custom-field-set-default-values-for-model',["model_id" => $item->id ?? $model_id ?? null ]) @include ('partials.forms.edit.notes') @include ('partials.forms.edit.requestable', ['requestable_text' => trans('admin/models/general.requestable')]) diff --git a/resources/views/models/view.blade.php b/resources/views/models/view.blade.php index 8bce7365fd..29cfd99c11 100755 --- a/resources/views/models/view.blade.php +++ b/resources/views/models/view.blade.php @@ -168,9 +168,14 @@
    @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - + + + + + + @endif @@ -231,6 +236,12 @@ @endif + @if ($model->min_amt) +
  • {{ trans('general.min_amt') }}: + {{$model->min_amt }} +
  • + @endif + @if ($model->manufacturer)
  • {{ trans('general.manufacturer') }}: @@ -334,12 +345,12 @@ @if ($model->assets->count() > 0)
    - {{ trans('general.delete') }} +
    @else -
    - {{ trans('general.delete') }} + + {{ trans('general.delete') }}
    @endif @endcan @@ -352,6 +363,16 @@ @stop @section('moar_scripts') + + + @include ('partials.bootstrap-table', ['exportFile' => 'manufacturer' . $model->name . '-export', 'search' => false]) @stop diff --git a/resources/views/notifications.blade.php b/resources/views/notifications.blade.php index 0935c28582..0205e3e10f 100755 --- a/resources/views/notifications.blade.php +++ b/resources/views/notifications.blade.php @@ -3,7 +3,7 @@
    - {{ trans('general.notification_error') }} + {{ trans('general.notification_error') }}: {{ trans('general.notification_error_hint') }}
    @@ -16,7 +16,7 @@
    - {{ trans('general.notification_success') }} + {{ trans('general.notification_success') }}: {{ $message }}
    @@ -28,20 +28,32 @@
    - {{ trans('general.notification_success') }} + {{ trans('general.notification_success') }}: {{ $message }}
    @endif +@if ($message = Session::get('success-unescaped')) +
    +
    + + + {{ trans('general.notification_success') }}: + {!! $message !!} +
    +
    +@endif + + @if ($assets = Session::get('assets')) @foreach ($assets as $asset)
    - {{ trans('general.asset_information') }} + {{ trans('general.asset_information') }}:
      @isset ($asset->model->name)
    • {{ trans('general.model_name') }} {{ $asset->model->name }}
    • @@ -67,7 +79,7 @@
      - {{ trans('general.consumable_information') }} + {{ trans('general.consumable_information') }}:
      • {{ trans('general.consumable_name') }} {{ $consumable->name }}
    @@ -81,7 +93,7 @@
    - {{ trans('general.accessory_information') }} + {{ trans('general.accessory_information') }}:
    • {{ trans('general.accessory_name') }} {{ $accessory->name }}
    @@ -94,7 +106,7 @@
    - {{ trans('general.error') }} + {{ trans('general.error') }}: {{ $message }}
    @@ -107,7 +119,7 @@
    - {{ trans('general.notification_error') }} + {{ trans('general.notification_error') }}: {{ $message }}
    @@ -115,12 +127,31 @@ @endif +@if ($messages = Session::get('bulk_asset_errors')) +
    +
    + + + {{ trans('general.notification_error') }}: + {{ trans('general.notification_bulk_error_hint') }} + @foreach($messages as $key => $message) + @for ($x = 0; $x < count($message); $x++) +
      +
    • {{ $message[$x] }}
    • +
    + @endfor + @endforeach +
    +
    +@endif + + @if ($message = Session::get('warning'))
    - {{ trans('general.notification_warning') }} + {{ trans('general.notification_warning') }}: {{ $message }}
    @@ -132,7 +163,7 @@
    - {{ trans('general.notification_info') }} + {{ trans('general.notification_info') }}: {{ $message }}
    diff --git a/resources/views/notifications/markdown/asset-acceptance.blade.php b/resources/views/notifications/markdown/asset-acceptance.blade.php index 5616525b13..50191d4a37 100644 --- a/resources/views/notifications/markdown/asset-acceptance.blade.php +++ b/resources/views/notifications/markdown/asset-acceptance.blade.php @@ -11,7 +11,7 @@ | **{{ ucfirst(trans('general.accepted')) }}** | {{ $accepted_date }} | @endif @if (isset($declined_date)) -| **{{ trans('general.declined') }}** | {{ $declined_date }} | +| **{{ ucfirst(trans('general.declined')) }}** | {{ $declined_date }} | @endif @if ((isset($item_tag)) && ($item_tag!='')) | **{{ trans('mail.asset_tag') }}** | {{ $item_tag }} | diff --git a/resources/views/notifications/markdown/checkin-accessory.blade.php b/resources/views/notifications/markdown/checkin-accessory.blade.php index cf1536518b..98a49bf7e2 100644 --- a/resources/views/notifications/markdown/checkin-accessory.blade.php +++ b/resources/views/notifications/markdown/checkin-accessory.blade.php @@ -3,7 +3,7 @@ {{ trans('mail.the_following_item') }} -@if ($item->getImageUrl()) +@if (($snipeSettings->show_images_in_email =='1') && $item->getImageUrl())
    Asset
    @endif diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 0a4ac1b031..0122096f80 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -1,24 +1,15 @@ @push('css') - - + @endpush @push('js') + +@endpush +@php + $selector = '[x-data="'.$name.'"]'; +@endphp +@push('css') + +@endpush + +
    + +
    +

    Fields

    +
    + +
    +
    + + + + +
    + +

    Options

    +
    + + +
    +
    + + + + +
    +
    +
    diff --git a/resources/views/partials/label2-preview.blade.php b/resources/views/partials/label2-preview.blade.php new file mode 100644 index 0000000000..36617bcc37 --- /dev/null +++ b/resources/views/partials/label2-preview.blade.php @@ -0,0 +1,98 @@ +@once + @push('css') + + @endpush +@endonce + +@push('js') + +@endpush + +
    +
    + + +
    + +
    diff --git a/resources/views/reports/asset_maintenances.blade.php b/resources/views/reports/asset_maintenances.blade.php index e8d86fcb36..ea12df34d1 100644 --- a/resources/views/reports/asset_maintenances.blade.php +++ b/resources/views/reports/asset_maintenances.blade.php @@ -48,6 +48,7 @@
  • {{ trans('admin/asset_maintenances/form.cost') }} {{ trans('general.location') }} {{ trans('admin/hardware/form.default_location') }}{{ trans('admin/asset_maintenances/table.is_warranty') }} {{ trans('general.admin') }} {{ trans('admin/asset_maintenances/form.notes') }}
    {{ trans('admin/hardware/table.image') }}{{ trans('admin/hardware/table.image') }} {{ trans('general.audit') }} {{ trans('general.admin') }} {{ trans('general.item') }}{!! $item['assetItem']->present()->nameUrl() !!} {{ $item['assetItem']->asset_tag }} assignedTo === null || $item['acceptance']->assignedTo->trashed()) style="text-decoration: line-through" @endif>{!! ($item['acceptance']->assignedTo) ? $item['acceptance']->assignedTo->present()->nameUrl() : trans('admin/reports/general.deleted_user') !!} + + @if(!$item['acceptance']->trashed()) - @if ($item['acceptance']->assignedTo){{ trans('admin/reports/general.send_reminder') }}@endif + + @if ($item['acceptance']->assignedTo) + @csrf + + + + @endif + @endif + +
    {{ trans('admin/settings/table.created') }} {{ trans('admin/settings/table.size') }}{{ trans('general.delete') }}{{ trans('table.actions') }}