diff --git a/.all-contributorsrc b/.all-contributorsrc index 8ea582b9b4..9058deba03 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -3217,6 +3217,33 @@ "contributions": [ "bug" ] + }, + { + "login": "DarrenRainey", + "name": "Darren Rainey", + "avatar_url": "https://avatars.githubusercontent.com/u/6136439?v=4", + "profile": "https://darrenraineys.co.uk", + "contributions": [ + "code" + ] + }, + { + "login": "maciej-poleszczyk", + "name": "maciej-poleszczyk", + "avatar_url": "https://avatars.githubusercontent.com/u/133033121?v=4", + "profile": "https://github.com/maciej-poleszczyk", + "contributions": [ + "code" + ] + }, + { + "login": "sgross-emlix", + "name": "Sebastian Groß", + "avatar_url": "https://avatars.githubusercontent.com/u/143394709?v=4", + "profile": "https://github.com/sgross-emlix", + "contributions": [ + "code" + ] } ] } diff --git a/.env.docker b/.env.docker index 9e50383014..4865f8e13a 100644 --- a/.env.docker +++ b/.env.docker @@ -97,7 +97,7 @@ API_TOKEN_EXPIRATION_YEARS=40 # -------------------------------------------- # OPTIONAL: SECURITY HEADER SETTINGS # -------------------------------------------- -APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.0.0.0/8 +APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.16.0.0/12 ALLOW_IFRAMING=false REFERRER_POLICY=same-origin ENABLE_CSP=false diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index b5f067cc68..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,38 +0,0 @@ -#### Expected Behavior (or desired behavior if a feature request) - -(what you expect to happen goes here) - ------ - -#### Actual Behavior - -(what actually happens goes here) - ------ - -#### Please confirm you have done the following before posting your bug report: - -- [ ] I have enabled debug mode -- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues) - ------ -#### Provide answers to these questions: - -- Is this a fresh install or an upgrade? -- Version of Snipe-IT you're running -- Version of PHP you're running -- Version of MySQL/MariaDB you're running -- What OS and web server you're running Snipe-IT on -- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc) -- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error -- What specific Snipe-IT page you're on, and what specific element you're interacting with to trigger the error -- If a stacktrace is provided in the error, include that too. -- Any errors that appear in your browser's error console. -- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo. -- Include any additional information you can find in `storage/logs` and your webserver's logs. -- Include what you've done so far in the installation, and if you got any error messages along the way. -- Indicate whether or not you've manually edited any data directly in the database - -Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you. - -https://snipe-it.readme.io/docs/getting-help diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 5cc00c2a31..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,129 +0,0 @@ -name: Bug Report -description: Create a report to help us improve -body: - - type: checkboxes - attributes: - label: Debug mode - description: Please confirm you have done the following before posting your bug report - options: - - label: I have enabled debug mode - required: true - - label: I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues) - required: true - - type: textarea - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. - validations: - required: true - - type: textarea - attributes: - label: Reproduction steps - description: Steps to reproduce the behavior. - value: | - 1. - 2. - 3. - ... - validations: - required: true - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - validations: - required: true - - type: textarea - attributes: - label: Screenshots - description: 'If applicable, add screenshots to help explain your problem.' - - type: markdown - attributes: - value: "### Server" - - type: input - attributes: - label: Snipe-IT Version - validations: - required: true - - type: input - id: server_operatingSystem - attributes: - label: Operating System - description: 'e.g. Ubuntu, Windows' - validations: - required: true - - type: input - attributes: - label: Web Server - description: 'e.g. Apache, IIS' - validations: - required: true - - type: input - attributes: - label: PHP Version - validations: - required: true - - type: markdown - attributes: - value: "### Desktop" - - type: input - id: desktop_operatingSystem - attributes: - label: Operating System - description: 'e.g. Ubuntu, Windows' - - type: input - id: desktop_browser - attributes: - label: Browser - description: 'e.g. Google Chrome, Safari' - - type: input - id: desktop_version - attributes: - label: Version - description: 'e.g. 93' - - type: markdown - attributes: - value: "### Mobile" - - type: input - attributes: - label: Device - description: 'e.g. iPhone 6, Pixel 4a' - - type: input - id: mobile_operatingSystem - attributes: - label: Operating System - description: 'e.g. iOS 8.1, Android 9' - - type: input - id: mobile_browser - attributes: - label: Browser - description: 'e.g. Google Chrome, Safari' - - type: input - id: mobile_version - attributes: - label: Version - description: 'e.g. 93' - - type: textarea - attributes: - label: Error messages - description: | - WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error - If a stacktrace is provided in the error, include that too. - Any errors that appear in your browser's error console. - Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo. - Include any additional information you can find in `storage/logs` and your webserver's logs. - Include the output from `php -m` (this should display what modules you have enabled.) - render: shell - - type: textarea - attributes: - label: Additional context - description: | - Is this a fresh install or an upgrade? - What OS and web server you're running Snipe-IT on - What method you used to install Snipe-IT (install.sh, manual installation, docker, etc) - Include what you've done so far in the installation, and if you got any error messages along the way. - Indicate whether or not you've manually edited any data directly in the database - Add any other context about the problem here. - - type: markdown - attributes: - value: Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0cec..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index dec3a8fe08..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Feature Request -description: Suggest an idea for this project -title: "[Feature Request]: " -labels: ["feature request"] -body: - - type: textarea - attributes: - label: Is your feature request related to a problem? Please describe. - description: A clear and concise description of what the problem is. The more information you can provide about your use-case, the more liklely we are to consider your feature. - validations: - required: true - - type: textarea - attributes: - label: Describe the solution you'd like - description: A clear and concise description of what you want to happen. - validations: - required: true - - type: textarea - attributes: - label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've considered. - - type: textarea - attributes: - label: Additional context - description: Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 7ef56a0d5f..0000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,40 +0,0 @@ -# Description - -Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change. - -Fixes # (issue) - -## Type of change - -Please delete options that are not relevant. - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update - -# How Has This Been Tested? - -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - -- [ ] Test A -- [ ] Test B - -**Test Configuration**: -* PHP version: -* MySQL version -* Webserver version -* OS version - - -# Checklist: - -- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview -- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines -- [ ] My code follows the style guidelines of this project -- [ ] I have performed a self-review of my own code -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes diff --git a/.github/travis-memory.ini b/.github/travis-memory.ini deleted file mode 100644 index 6bd8019ec4..0000000000 --- a/.github/travis-memory.ini +++ /dev/null @@ -1 +0,0 @@ -memory_limit= 2048M diff --git a/.github/weekly-digest.yml b/.github/weekly-digest.yml deleted file mode 100644 index fe502fbc98..0000000000 --- a/.github/weekly-digest.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Configuration for weekly-digest - https://github.com/apps/weekly-digest -publishDay: sun -canPublishIssues: true -canPublishPullRequests: true -canPublishContributors: true -canPublishStargazers: true -canPublishCommits: true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 14ddea2258..5a4042aee4 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,8 @@ jobs: - uses: actions/stale@v9 with: debug-only: true - operations-per-run: 100 # just while we're debugging + ascending: true + operations-per-run: 1000 # just while we're debugging repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 60 days-before-close: 7 diff --git a/.github/workflows/tests-mysql.yml b/.github/workflows/tests-mysql.yml index 737a86dca3..310414cda6 100644 --- a/.github/workflows/tests-mysql.yml +++ b/.github/workflows/tests-mysql.yml @@ -76,4 +76,4 @@ jobs: DB_DATABASE: snipeit DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_USERNAME: root - run: php artisan test --parallel + run: php artisan test diff --git a/.github/workflows/tests-postgres.yml b/.github/workflows/tests-postgres.yml index 0c361511b8..ae48277be3 100644 --- a/.github/workflows/tests-postgres.yml +++ b/.github/workflows/tests-postgres.yml @@ -74,4 +74,4 @@ jobs: DB_PORT: ${{ job.services.postgresql.ports[5432] }} DB_USERNAME: snipeit DB_PASSWORD: password - run: php artisan test --parallel + run: php artisan test diff --git a/.github/workflows/tests-sqlite.yml b/.github/workflows/tests-sqlite.yml index 49c7c92d8e..220194314f 100644 --- a/.github/workflows/tests-sqlite.yml +++ b/.github/workflows/tests-sqlite.yml @@ -43,6 +43,9 @@ jobs: cp -v .env.testing.example .env cp -v .env.testing.example .env.testing + - name: Create database file + run: touch database/database.sqlite + - name: Install Dependencies run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist @@ -57,5 +60,5 @@ jobs: - name: Execute tests (Unit and Feature tests) via PHPUnit env: - DB_CONNECTION: sqlite_testing - run: php artisan test --parallel + DB_CONNECTION: sqlite + run: php artisan test diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f821c1f17f..f61e73facb 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -52,7 +52,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken | [
bilias](https://github.com/bilias)
[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [
coach1988](https://github.com/coach1988)
[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [
MrM](https://github.com/mauro-miatello)
[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [
koiakoia](https://github.com/koiakoia)
[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [
Mustafa Online](https://github.com/mustafa-online)
[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [
franceslui](https://github.com/franceslui)
[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [
Q4kK](https://github.com/Q4kK)
[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | | [
squintfox](https://github.com/squintfox)
[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [
Jeff Clay](https://github.com/jeffclay)
[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [
Phil J R](https://github.com/PP-JN-RL)
[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [
i_virus](https://www.corelight.com/)
[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [
Paul Grime](https://github.com/gitgrimbo)
[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [
Lee Porte](https://leeporte.co.uk)
[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [
BRYAN ](https://github.com/bryanlopezinc)
[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | | [
U-H-T](https://github.com/U-H-T)
[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [
Matt Tyree](https://github.com/Tyree)
[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [
Florent Bervas](http://spoontux.net)
[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [
Daniel Albertsen](https://ditscheri.com)
[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [
r-xyz](https://github.com/r-xyz)
[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [
Steven Mainor](https://github.com/DrekiDegga)
[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [
arne-kroeger](https://github.com/arne-kroeger)
[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | -| [
Glukose1](https://github.com/Glukose1)
[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [
Scarzy](https://github.com/Scarzy)
[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [
setpill](https://github.com/setpill)
[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [
swift2512](https://github.com/swift2512)
[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | +| [
Glukose1](https://github.com/Glukose1)
[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [
Scarzy](https://github.com/Scarzy)
[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [
setpill](https://github.com/setpill)
[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [
swift2512](https://github.com/swift2512)
[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [
Darren Rainey](https://darrenraineys.co.uk)
[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [
maciej-poleszczyk](https://github.com/maciej-poleszczyk)
[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [
Sebastian Groß](https://github.com/sgross-emlix)
[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/README.md b/README.md index 0086c7b327..7914b15834 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![snipe-it-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602) -[![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://app.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml) +[![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/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml) [![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributing) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) ## Snipe-IT - Open Source Asset Management System @@ -14,6 +14,21 @@ Snipe-IT is actively developed and we [release quite frequently](https://github. > [!TIP] > __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into. +----- + +### Table of Contents +* [Installation](#installation) +* [User's Manual](#users-manual) +* [Bug Reports & Feature Requests](#bug-reports--feature-requests) +* [Security](#security) +* [Upgrading](#upgrading) +* [Translations!](#translations-) +* [Libraries, Modules & Related Projects](#libraries-modules--related-projects) +* [Join the Community!](#join-the-community) +* [Contributing](#contributing) +* [Announcement List](#announcement-list) + + ----- ### Installation @@ -22,8 +37,6 @@ For instructions on installing and configuring Snipe-IT on your server, check ou If you're having trouble with the installation, please check the [Common Issues](https://snipe-it.readme.io/docs/common-issues) and [Getting Help](https://snipe-it.readme.io/docs/getting-help) documentation, and search this repository's open *and* closed issues for help. - - ----- ### User's Manual For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview). @@ -35,20 +48,21 @@ Feel free to check out the [GitHub Issues for this project](https://github.com/s > [!IMPORTANT] > **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.** -> + ----- +### Security + +> [!IMPORTANT] +> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.** +----- + + ### Upgrading Please see the [upgrading documentation](https://snipe-it.readme.io/docs/upgrading) for instructions on upgrading Snipe-IT. ------ -### Announcement List - -To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important. - ------- - ### Translations! Please see the [translations documentation](https://snipe-it.readme.io/docs/translations) for information about available languages and how to add translations to Snipe-IT. @@ -82,19 +96,33 @@ Since the release of the JSON REST API, several third-party developers have been ----- -### Contributing +### Join the Community! -Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview). - -Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. - -The ERD is available [online here](https://drawsql.app/templates/snipe-it). - -[Here is a list](CONTRIBUTORS.md) of the wonderful people that have contributed to the Snipe-IT. +- **[Join our Discord](https://discord.gg/yZFtShAcKk)!** It’s full of great people. We even wrote about it [here](https://grokstar.dev/culture/2024/06/the-unlikely-rise-of-discord-as-a-support-channel/)! +- **Follow us on Bluesky** at [@snipeitapp.com](https://bsky.app/profile/snipeitapp.com) +- **Follow us on Mastodon** at [hachyderm.io/@grokability](https://hachyderm.io/@grokability) +- **Follow our blog** at [Grokstar.Dev](https://grokstar.dev) +- **Subscribe here** on Github for notifications about new releases. (We recommend selecting "Releases" only for most users - this repo can get noisy.) ----- -### Security +### Contributing + +**Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.** + +Contributions should follow from a human-to-human discussion in the form of an issue for the best chances of being merged into the core project. (Sometimes we might already be working on that feature, sometimes we've decided against ) + +Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview). + +This project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + +The ERD is available [online here](https://drawsql.app/templates/snipe-it). + +Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years! + +------ +### Announcement List + +To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important. + -> [!IMPORTANT] -> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.** diff --git a/SECURITY.md b/SECURITY.md index dcbfd3e6f0..e7ea431592 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,10 +10,12 @@ however there are times when library dependencies and/or PHP/MySQL dependencies make it impossible to backport security fixes on older versions. | Version | Supported | -| ------- | ------------------ | -| 5.1.x | :white_check_mark: | +|---------| ------------------ | +| 7.x | :white_check_mark: | +| 6.x | :x: | +| 5.1.x | :x: | | 5.0.x | :x: | -| 4.0.x | :white_check_mark: | +| 4.0.x | :x: | | < 4.0 | :x: | ## Reporting a Vulnerability diff --git a/app/Console/Commands/LdapSync.php b/app/Console/Commands/LdapSync.php index 62fda07892..e57fab19ab 100644 --- a/app/Console/Commands/LdapSync.php +++ b/app/Console/Commands/LdapSync.php @@ -2,6 +2,7 @@ namespace App\Console\Commands; +use App\Models\Asset; use App\Models\Department; use App\Models\Group; use Illuminate\Console\Command; @@ -137,23 +138,24 @@ 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 + $default_location = null; if ($this->option('location') != '') { - if ($location = Location::where('name', '=', $this->option('location'))->first()) { + if ($default_location = Location::where('name', '=', $this->option('location'))->first()) { Log::debug('Location name ' . $this->option('location') . ' passed'); - Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')'); + Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')'); } } elseif ($this->option('location_id')) { + //TODO - figure out how or why this is an array? foreach($this->option('location_id') as $location_id) { - if ($location = Location::where('id', '=', $location_id)->first()) { + if ($default_location = Location::where('id', '=', $location_id)->first()) { Log::debug('Location ID ' . $location_id . ' passed'); - Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')'); + Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')'); } } } - if (! isset($location)) { + if (!isset($default_location)) { Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.'); } @@ -229,43 +231,44 @@ class LdapSync extends Command for ($i = 0; $i < $results['count']; $i++) { - $item = []; - $item['username'] = $results[$i][$ldap_map["username"]][0] ?? ''; - $item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? ''; - $item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? ''; - $item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? ''; - $item['email'] = $results[$i][$ldap_map["email"]][0] ?? ''; - $item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? ''; - $item['location_id'] = $results[$i]['location_id'] ?? ''; - $item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? ''; - $item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? ''; - $item['country'] = $results[$i][$ldap_map["country"]][0] ?? ''; - $item['department'] = $results[$i][$ldap_map["dept"]][0] ?? ''; - $item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? ''; - $item['location'] = $results[$i][$ldap_map["location"]][0] ?? ''; + $item = []; + $item['username'] = $results[$i][$ldap_map["username"]][0] ?? ''; + $item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? ''; + $item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? ''; + $item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? ''; + $item['email'] = $results[$i][$ldap_map["email"]][0] ?? ''; + $item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? ''; + $item['location_id'] = $results[$i]['location_id'] ?? ''; + $item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? ''; + $item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? ''; + $item['country'] = $results[$i][$ldap_map["country"]][0] ?? ''; + $item['department'] = $results[$i][$ldap_map["dept"]][0] ?? ''; + $item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? ''; + $item['location'] = $results[$i][$ldap_map["location"]][0] ?? ''; + $location = $default_location; //initially, set '$location' to the default_location (which may just be `null`) - // ONLY if you are using the "ldap_location" option *AND* you have an actual result - if ($ldap_map["location"] && $item['location']) { - $location = Location::firstOrCreate([ - 'name' => $item['location'], - ]); - } - $department = Department::firstOrCreate([ - 'name' => $item['department'], + // ONLY if you are using the "ldap_location" option *AND* you have an actual result + if ($ldap_map["location"] && $item['location']) { + $location = Location::firstOrCreate([ + 'name' => $item['location'], ]); + } + $department = Department::firstOrCreate([ + 'name' => $item['department'], + ]); - $user = User::where('username', $item['username'])->first(); - if ($user) { - // Updating an existing user. - $item['createorupdate'] = 'updated'; - } else { - // Creating a new user. - $user = new User; - $user->password = $user->noPassword(); - $user->locale = app()->getLocale(); - $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 = User::where('username', $item['username'])->first(); + if ($user) { + // Updating an existing user. + $item['createorupdate'] = 'updated'; + } else { + // Creating a new user. + $user = new User; + $user->password = $user->noPassword(); + $user->locale = app()->getLocale(); + $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'; + } //If a sync option is not filled in on the LDAP settings don't populate the user field if($ldap_map["username"] != null){ @@ -296,7 +299,7 @@ class LdapSync extends Command $user->department_id = $department->id; } if($ldap_map["location"] != null){ - $user->location_id = $location ? $location->id : null; + $user->location_id = $location?->id; } if($ldap_map["manager"] != null){ @@ -320,59 +323,66 @@ class LdapSync extends Command ] ]; } - + + $add_manager_to_cache = true; if ($ldap_manager["count"] > 0) { + try { + // Get the Manager's username + // PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array. + $ldapManagerUsername = $ldap_manager[0][$ldap_map["username"]][0]; - // Get the Manager's username - // PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array. - $ldapManagerUsername = $ldap_manager[0][$ldap_map["username"]][0]; + // Get User from Manager username. + $ldap_manager = User::where('username', $ldapManagerUsername)->first(); - // Get User from Manager username. - $ldap_manager = User::where('username', $ldapManagerUsername)->first(); - - if ($ldap_manager && isset($ldap_manager->id)) { - // Link user to manager id. - $user->manager_id = $ldap_manager->id; + if ($ldap_manager && isset($ldap_manager->id)) { + // Link user to manager id. + $user->manager_id = $ldap_manager->id; + } + } catch (\Exception $e) { + $add_manager_to_cache = false; + \Log::warning('Handling ldap manager ' . $item['manager'] . ' caused an exception: ' . $e->getMessage() . '. Continuing synchronization.'); } } - $manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed' + if ($add_manager_to_cache) { + $manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed' + } } } } - // Sync activated state for Active Directory. - if ( !empty($ldap_map["active_flag"])) { // IF we have an 'active' flag set.... - // ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them. - // (Specifically, we don't handle a value of '0.0' correctly) - $raw_value = @$results[$i][$ldap_map["active_flag"]][0]; - $filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - $boolean_cast = (bool)$raw_value; + // Sync activated state for Active Directory. + if (!empty($ldap_map["active_flag"])) { // IF we have an 'active' flag set.... + // ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them. + // (Specifically, we don't handle a value of '0.0' correctly) + $raw_value = @$results[$i][$ldap_map["active_flag"]][0]; + $filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + $boolean_cast = (bool) $raw_value; - $user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast + $user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast - } elseif (array_key_exists('useraccountcontrol', $results[$i]) ) { - // ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists, - // ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in + } elseif (array_key_exists('useraccountcontrol', $results[$i])) { + // ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists, + // ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in - /* The following is _probably_ the correct logic, but we can't use it because - some users may have been dependent upon the previous behavior, and this - could cause additional access to be available to users they don't want - to allow to log in. + /* The following is _probably_ the correct logic, but we can't use it because + some users may have been dependent upon the previous behavior, and this + could cause additional access to be available to users they don't want + to allow to log in. - $useraccountcontrol = $results[$i]['useraccountcontrol'][0]; - if( - // based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties - ($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT - !($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE - !($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT - ) { - $user->activated = 1; - } else { - $user->activated = 0; - } */ - $enabled_accounts = [ + $useraccountcontrol = $results[$i]['useraccountcontrol'][0]; + if( + // based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties + ($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT + !($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE + !($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT + ) { + $user->activated = 1; + } else { + $user->activated = 0; + } */ + $enabled_accounts = [ '512', // 0x200 NORMAL_ACCOUNT '544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD '66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD @@ -385,44 +395,49 @@ class LdapSync extends Command '4260352', // 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH '1049088', // 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED '1114624', // 0x110200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, NOT_DELEGATED, - ]; - $user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0; + ]; + $user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0; // If we're not using AD, and there isn't an activated flag set, activate all users - } /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active. - already-existing accounts will be however the administrator has set them */ + } /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active. + already-existing accounts will be however the administrator has set them */ - if ($item['ldap_location_override'] == true) { - $user->location_id = $item['location_id']; - } elseif ((isset($location)) && (! empty($location))) { - if ((is_array($location)) && (array_key_exists('id', $location))) { - $user->location_id = $location['id']; - } elseif (is_object($location)) { - $user->location_id = $location->id; - } + if ($item['ldap_location_override'] == true) { + $user->location_id = $item['location_id']; + } elseif ((isset($location)) && (!empty($location))) { + if ((is_array($location)) && (array_key_exists('id', $location))) { + $user->location_id = $location['id']; + } elseif (is_object($location)) { + $user->location_id = $location->id; //THIS is the magic line, this should do it. } - $location = null; - $user->ldap_import = 1; + } + // TODO - should we be NULLING locations if $location is really `null`, and that's what we came up with? + // will that conflict with any overriding setting that the user set? Like, if they moved someone from + // the 'null' location to somewhere, we wouldn't want to try to override that, right? + $location = null; + $user->ldap_import = 1; - $errors = ''; + $errors = ''; - if ($user->save()) { - $item['note'] = $item['createorupdate']; - $item['status'] = 'success'; - if ( $item['createorupdate'] === 'created' && $ldap_default_group) { - $user->groups()->attach($ldap_default_group); - } - - } else { - foreach ($user->getErrors()->getMessages() as $key => $err) { - $errors .= $err[0]; - } - $item['note'] = $errors; - $item['status'] = 'error'; + if ($user->save()) { + $item['note'] = $item['createorupdate']; + $item['status'] = 'success'; + if ($item['createorupdate'] === 'created' && $ldap_default_group) { + $user->groups()->attach($ldap_default_group); } + //updates assets location based on user's location + Asset::where('assigned_to', '=', $user->id)->where('assigned_type', '=', User::class)->update(['location_id' => $user->location_id]); - array_push($summary, $item); + } else { + foreach ($user->getErrors()->getMessages() as $key => $err) { + $errors .= $err[0]; + } + $item['note'] = $errors; + $item['status'] = 'error'; + } + + array_push($summary, $item); } if ($this->option('summary')) { diff --git a/app/Console/Commands/ObjectImportCommand.php b/app/Console/Commands/ObjectImportCommand.php index 8370e7c050..b79949521a 100644 --- a/app/Console/Commands/ObjectImportCommand.php +++ b/app/Console/Commands/ObjectImportCommand.php @@ -6,6 +6,7 @@ use Illuminate\Console\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Illuminate\Support\Facades\Log; +use Symfony\Component\Console\Helper\ProgressIndicator; ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M')); @@ -29,6 +30,11 @@ class ObjectImportCommand extends Command */ protected $description = 'Import Items from CSV'; + /** + * The progress indicator instance. + */ + protected ProgressIndicator $progressIndicator; + /** * Create a new command instance. * @@ -39,8 +45,6 @@ class ObjectImportCommand extends Command parent::__construct(); } - private $bar; - /** * Execute the console command. * @@ -48,12 +52,14 @@ class ObjectImportCommand extends Command */ public function handle() { + $this->progressIndicator = new ProgressIndicator($this->output); + $filename = $this->argument('filename'); $class = title_case($this->option('item-type')); $classString = "App\\Importer\\{$class}Importer"; $importer = new $classString($filename); $importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback']) - ->setUserId($this->option('user_id')) + ->setCreatedBy($this->option('user_id')) ->setUpdating($this->option('update')) ->setShouldNotify($this->option('send-welcome')) ->setUsernameFormat($this->option('username_format')); @@ -61,46 +67,25 @@ class ObjectImportCommand extends Command // This $logFile/useFiles() bit is currently broken, so commenting it out for now // $logFile = $this->option('logfile'); // Log::useFiles($logFile); - $this->comment('======= Importing Items from '.$filename.' ========='); + $this->progressIndicator->start('======= Importing Items from '.$filename.' ========='); + $importer->import(); - $this->bar = null; - - if (! empty($this->errors)) { - $this->comment('The following Errors were encountered.'); - foreach ($this->errors as $asset => $error) { - $this->comment('Error: Item: '.$asset.' failed validation: '.json_encode($error)); - } - } else { - $this->comment('All Items imported successfully!'); - } - $this->comment(''); + $this->progressIndicator->finish('Import finished.'); } - public function errorCallback($item, $field, $errorString) + public function errorCallback($item, $field, $error) { - $this->errors[$item->name][$field] = $errorString; + $this->output->write("\x0D\x1B[2K"); + + $this->warn('Error: Item: '.$item->name.' failed validation: '.json_encode($error)); } - public function progress($count) + public function progress($importedItemsCount) { - if (! $this->bar) { - $this->bar = $this->output->createProgressBar($count); - } - static $index = 0; - $index++; - if ($index < $count) { - $this->bar->advance(); - } else { - $this->bar->finish(); - } + $this->progressIndicator->advance(); } - // Tracks the current item for error messages - private $updating; - // An array of errors encountered while parsing - private $errors; - /** * Log a message to file, configurable by the --log-file parameter. * If a warning message is passed, we'll spit it to the console as well. diff --git a/app/Console/Commands/SendAcceptanceReminder.php b/app/Console/Commands/SendAcceptanceReminder.php index 1551348046..f4ab2c5b68 100644 --- a/app/Console/Commands/SendAcceptanceReminder.php +++ b/app/Console/Commands/SendAcceptanceReminder.php @@ -2,15 +2,15 @@ namespace App\Console\Commands; +use App\Mail\UnacceptedAssetReminderMail; use App\Models\Asset; use App\Models\CheckoutAcceptance; use App\Models\Setting; use App\Models\User; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CurrentInventory; -use App\Notifications\UnacceptedAssetReminderNotification; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Facades\Mail; class SendAcceptanceReminder extends Command { @@ -65,42 +65,29 @@ class SendAcceptanceReminder extends Command return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : ''; }); - $no_mail_address = []; - foreach($unacceptedAssetGroups as $unacceptedAssetGroup) { + // The [0] is weird, but it allows for the item_count to work and grabs the appropriate info for each user. + // Collapsing and flattening the collection doesn't work above. + $acceptance = $unacceptedAssetGroup[0]['acceptance']; + $locale = $acceptance->assignedTo?->locale; + $email = $acceptance->assignedTo?->email; + if(!$email){ + $this->info($acceptance->assignedTo?->present()->fullName().' has no email address.'); + } $item_count = $unacceptedAssetGroup->count(); - foreach ($unacceptedAssetGroup as $unacceptedAsset) { -// if ($unacceptedAsset['acceptance']->assignedTo->email == ''){ -// $no_mail_address[] = $unacceptedAsset['checkoutable']->assignedTo->present()->fullName; -// } - if ($unacceptedAsset['acceptance']->assignedTo) { - if (!$unacceptedAsset['acceptance']->assignedTo->locale) { - Notification::locale(Setting::getSettings()->locale)->send( - $unacceptedAsset['acceptance']->assignedTo, - new UnacceptedAssetReminderNotification($unacceptedAsset['assetItem'], $count) - ); - } else { - Notification::send( - $unacceptedAsset['acceptance']->assignedTo, - new UnacceptedAssetReminderNotification($unacceptedAsset, $item_count) - ); - } - $count++; - } + if ($locale && $email) { + Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale)); + + } elseif ($email) { + Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))); } + $count++; } - if (!empty($no_mail_address)) { - foreach($no_mail_address as $user) { - return $user.' has no email.'; - } - - - } - - - $this->info($count.' users notified.'); + + return 0; } + } diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 18e149b57d..95a344dce9 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Crypt; use Illuminate\Contracts\Encryption\DecryptException; use Carbon\Carbon; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; use Intervention\Image\ImageManagerStatic as Image; use Illuminate\Support\Facades\Session; @@ -708,6 +709,28 @@ class Helper return $randomString; } + /** + * A method to be used to handle deprecations notifications, currently handling MS Teams. more can be added when needed. + * + * + * @author [Godfrey Martinez] + * @since [v7.0.14] + * @return array + */ + public static function deprecationCheck() : array { + // The check and message that the user is still using the deprecated version + $deprecations = [ + 'ms_teams_deprecated' => array( + 'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'), + 'message' => 'The Microsoft Teams webhook URL being used will be deprecated Jan 31st, 2025. Change webhook endpoint'), + ]; + + // if item of concern is being used and its being used with the deprecated values return the notification array. + if(Setting::getSettings()->webhook_selected === 'microsoft' && $deprecations['ms_teams_deprecated']['check']) { + return $deprecations; + } + return []; + } /** * This nasty little method gets the low inventory info for the @@ -1123,6 +1146,7 @@ class Helper 'png' => 'far fa-image', 'webp' => 'far fa-image', 'avif' => 'far fa-image', + 'svg' => 'fas fa-vector-square', // word 'doc' => 'far fa-file-word', 'docx' => 'far fa-file-word', @@ -1135,7 +1159,7 @@ class Helper //Text 'txt' => 'far fa-file-alt', 'rtf' => 'far fa-file-alt', - 'xml' => 'far fa-file-alt', + 'xml' => 'fas fa-code', // Misc 'pdf' => 'far fa-file-pdf', 'lic' => 'far fa-save', @@ -1148,41 +1172,7 @@ class Helper return 'far fa-file'; } - public static function show_file_inline($filename) - { - $extension = substr(strrchr($filename, '.'), 1); - if ($extension) { - switch ($extension) { - case 'jpg': - case 'jpeg': - case 'gif': - case 'png': - case 'webp': - case 'avif': - return true; - break; - default: - return false; - } - } - - return false; - } - - /** - * Generate a random encrypted password. - * - * @author Wes Hulette - * - * @since 5.0.0 - * - * @return string - */ - public static function generateEncyrptedPassword(): string - { - return bcrypt(self::generateUnencryptedPassword()); - } /** * Get a random unencrypted password. diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php index 2cdab1d66c..47700f913a 100644 --- a/app/Helpers/StorageHelper.php +++ b/app/Helpers/StorageHelper.php @@ -7,6 +7,7 @@ use Illuminate\Http\Response; use Illuminate\Http\RedirectResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\StreamedResponse; +use Illuminate\Contracts\Filesystem\FileNotFoundException; class StorageHelper { public static function downloader($filename, $disk = 'default') : BinaryFileResponse | RedirectResponse | StreamedResponse @@ -25,4 +26,64 @@ class StorageHelper return Storage::disk($disk)->download($filename); } } + + + /** + * This determines the file types that should be allowed inline and checks their fileinfo extension + * to determine that they are safe to display inline. + * + * @author [ + * @since v7.0.14 + * @param $file_with_path + * @return bool + */ + public static function allowSafeInline($file_with_path) { + + $allowed_inline = [ + 'pdf', + 'svg', + 'jpg', + 'gif', + 'svg', + 'avif', + 'webp', + 'png', + ]; + + + // The file exists and is allowed to be displayed inline + if (Storage::exists($file_with_path) && (in_array(pathinfo($file_with_path, PATHINFO_EXTENSION), $allowed_inline))) { + return true; + } + return false; + + } + + /** + * Decide whether to show the file inline or download it. + */ + public static function showOrDownloadFile($file, $filename) { + + $headers = []; + + if (request('inline') == 'true') { + + $headers = [ + 'Content-Disposition' => 'inline', + ]; + + // This is NOT allowed as inline - force it to be displayed as text in the browser + if (self::allowSafeInline($file) != true) { + $headers = array_merge($headers, ['Content-Type' => 'text/plain']); + } + } + + // Everything else seems okay, but the file doesn't exist on the server. + if (Storage::missing($file)) { + throw new FileNotFoundException(); + } + + return Storage::download($file, $filename, $headers); + + } } diff --git a/app/Http/Controllers/Accessories/AccessoriesFilesController.php b/app/Http/Controllers/Accessories/AccessoriesFilesController.php index b63c202d30..ebc1e4b8e0 100644 --- a/app/Http/Controllers/Accessories/AccessoriesFilesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesFilesController.php @@ -106,50 +106,29 @@ class AccessoriesFilesController extends Controller * @param int $accessoryId * @param int $fileId */ - public function show($accessoryId = null, $fileId = null, $download = true) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse + public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse { - Log::debug('Private filesystem is: '.config('filesystems.default')); - $accessory = Accessory::find($accessoryId); - - // the accessory is valid - if (isset($accessory->id)) { + if ($accessory = Accessory::find($accessoryId)) { $this->authorize('view', $accessory); $this->authorize('accessories.files', $accessory); - 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')); - } + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) { + $file = 'private_uploads/accessories/'.$log->filename; - $file = 'private_uploads/accessories/'.$log->filename; - - if (Storage::missing($file)) { - Log::debug('FILE DOES NOT EXISTS for '.$file); - Log::debug('URL should be '.Storage::url($file)); - - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->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); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found')); } } + + return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.log_record_not_found')); + } - return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId])); + return redirect()->route('accessories.index')->with('error', trans('general.file_not_found')); } } diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index c6cdf9bbf8..278d7e2081 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -240,7 +240,7 @@ class AcceptanceController extends Controller try { $acceptance->notify(new AcceptanceAssetAcceptedNotification($data)); } catch (\Exception $e) { - Log::error($e); + Log::warning($e); } event(new CheckoutAccepted($acceptance)); @@ -338,4 +338,5 @@ class AcceptanceController extends Controller return redirect()->to('account/accept')->with('success', $return_msg); } + } diff --git a/app/Http/Controllers/ActionlogController.php b/app/Http/Controllers/ActionlogController.php index f143c4b73b..f2580c96fc 100644 --- a/app/Http/Controllers/ActionlogController.php +++ b/app/Http/Controllers/ActionlogController.php @@ -37,10 +37,16 @@ class ActionlogController extends Controller } } - public function getStoredEula($filename) : Response | BinaryFileResponse + public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse { $this->authorize('view', \App\Models\Asset::class); $file = config('app.private_uploads').'/eula-pdfs/'.$filename; - return response()->download($file); + + if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) { + return response()->download($file); + } + + return redirect()->back()->with('error', trans('general.file_does_not_exist')); + } } diff --git a/app/Http/Controllers/Api/AccessoriesController.php b/app/Http/Controllers/Api/AccessoriesController.php index 19951b6589..d1ef72bcca 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -137,7 +137,6 @@ class AccessoriesController extends Controller */ public function store(StoreAccessoryRequest $request) { - $this->authorize('create', Accessory::class); $accessory = new Accessory; $accessory->fill($request->all()); $accessory = $request->handleImages($accessory); @@ -197,9 +196,6 @@ class AccessoriesController extends Controller $this->authorize('view', Accessory::class); $accessory = Accessory::with('lastCheckout')->findOrFail($id); - if (! Company::isCurrentUserHasAccess($accessory)) { - return ['total' => 0, 'rows' => []]; - } $offset = request('offset', 0); $limit = request('limit', 50); @@ -325,7 +321,7 @@ class AccessoriesController extends Controller $accessory = Accessory::find($accessory_checkout->accessory_id); $this->authorize('checkin', $accessory); - $logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note')); + $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note')); // Was the accessory updated? if ($accessory_checkout->delete()) { @@ -333,14 +329,6 @@ class AccessoriesController extends Controller $user = User::find($accessory_checkout->assigned_to); } - $data['log_id'] = $logaction->id; - $data['first_name'] = $user->first_name; - $data['last_name'] = $user->last_name; - $data['item_name'] = $accessory->name; - $data['checkin_date'] = $logaction->created_at; - $data['item_tag'] = ''; - $data['note'] = $logaction->note; - return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkin.success'))); } diff --git a/app/Http/Controllers/Api/AssetModelsController.php b/app/Http/Controllers/Api/AssetModelsController.php index e1ae0c12d3..33a00d1d3f 100644 --- a/app/Http/Controllers/Api/AssetModelsController.php +++ b/app/Http/Controllers/Api/AssetModelsController.php @@ -56,20 +56,21 @@ class AssetModelsController extends Controller 'models.id', 'models.image', 'models.name', - 'model_number', - 'min_amt', - 'eol', - 'requestable', + 'models.model_number', + 'models.min_amt', + 'models.eol', + 'models.created_by', + 'models.requestable', 'models.notes', 'models.created_at', - 'category_id', - 'manufacturer_id', - 'depreciation_id', - 'fieldset_id', + 'models.category_id', + 'models.manufacturer_id', + 'models.depreciation_id', + 'models.fieldset_id', 'models.deleted_at', 'models.updated_at', ]) - ->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues','adminuser') + ->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues', 'adminuser') ->withCount('assets as assets_count'); if ($request->input('status')=='deleted') { @@ -95,7 +96,7 @@ class AssetModelsController extends Controller $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'models.created_at'; - switch ($sort) { + switch ($request->input('sort')) { case 'manufacturer': $assetmodels->OrderManufacturer($order); break; @@ -105,6 +106,9 @@ class AssetModelsController extends Controller case 'fieldset': $assetmodels->OrderFieldset($order); break; + case 'created_by': + $assetmodels->OrderByCreatedByName($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 514f4484c8..7a6566d34e 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -33,6 +33,8 @@ use Illuminate\Http\Request; use App\Http\Requests\ImageUploadRequest; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Route; +use App\View\Label; +use Illuminate\Support\Facades\Storage; /** @@ -80,10 +82,10 @@ class AssetsController extends Controller $this->authorize('reports.view'); } else { $transformer = 'App\Http\Transformers\AssetsTransformer'; - $this->authorize('index', Asset::class); + $this->authorize('index', Asset::class); } - - + + $settings = Setting::getSettings(); $allowed_columns = [ @@ -126,8 +128,19 @@ class AssetsController extends Controller } $assets = Asset::select('assets.*') - ->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo', 'adminuser','model.depreciation', - 'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users. + ->with( + 'location', + 'assetstatus', + 'company', + 'defaultLoc', + 'assignedTo', + 'adminuser', + 'model.depreciation', + 'model.category', + 'model.manufacturer', + 'model.fieldset', + 'supplier' + ); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users. if ($filter_non_deprecable_assets) { @@ -159,8 +172,8 @@ class AssetsController extends Controller * Handle due and overdue audits and checkin dates */ switch ($action) { - // Audit (singular) is left over from earlier legacy APIs - case 'audits' : + // Audit (singular) is left over from earlier legacy APIs + case 'audits': switch ($upcoming_status) { case 'due': $assets->DueForAudit($settings); @@ -187,7 +200,7 @@ class AssetsController extends Controller break; } break; - } + } /** * End handling due and overdue audits and checkin dates @@ -265,7 +278,6 @@ class AssetsController extends Controller $join->on('status_alias.id', '=', 'assets.status_id'); }); } - } @@ -345,7 +357,7 @@ class AssetsController extends Controller $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); @@ -395,11 +407,10 @@ class AssetsController extends Controller // This may not work for all databases, but it works for MySQL if ($numeric_sort) { - $assets->orderByRaw($sort_override . ' * 1 ' . $order); + $assets->orderByRaw(DB::getTablePrefix() . 'assets.' . $sort_override . ' * 1 ' . $order); } else { $assets->orderBy($sort_override, $order); } - } else { $assets->orderBy($column_sort, $order); } @@ -413,11 +424,11 @@ class AssetsController extends Controller $total = $assets->count(); $assets = $assets->skip($offset)->take($limit)->get(); - + /** * Include additional associated relationships - */ + */ if ($request->input('components')) { $assets->loadMissing(['components' => function ($query) { $query->orderBy('created_at', 'desc'); @@ -441,7 +452,7 @@ class AssetsController extends Controller * @since [v4.2.1] * @author [A. Gianotto] [] */ - public function showByTag(Request $request, $tag) : JsonResponse | array + public function showByTag(Request $request, $tag): JsonResponse | array { $this->authorize('index', Asset::class); $assets = Asset::where('asset_tag', $tag)->with('assetstatus')->with('assignedTo'); @@ -463,12 +474,10 @@ class AssetsController extends Controller } else { return (new AssetsTransformer)->transformAssets($assets, $assets->count()); } - } // If there are 0 results, return the "no such asset" response return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); - } /** @@ -479,7 +488,7 @@ class AssetsController extends Controller * @since [v4.2.1] * @return \Illuminate\Http\JsonResponse */ - public function showBySerial(Request $request, $serial) : JsonResponse | array + public function showBySerial(Request $request, $serial): JsonResponse | array { $this->authorize('index', Asset::class); $assets = Asset::where('serial', $serial)->with('assetstatus')->with('assignedTo'); @@ -488,14 +497,13 @@ class AssetsController extends Controller if ($request->input('deleted', 'false') == 'true') { $assets = $assets->withTrashed(); } - + if (($assets = $assets->get()) && ($assets->count()) > 0) { - return (new AssetsTransformer)->transformAssets($assets, $assets->count()); + return (new AssetsTransformer)->transformAssets($assets, $assets->count()); } // If there are 0 results, return the "no such asset" response return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); - } /** @@ -506,20 +514,20 @@ class AssetsController extends Controller * @since [v4.0] * @return \Illuminate\Http\JsonResponse */ - public function show(Request $request, $id) : JsonResponse | array + public function show(Request $request, $id): JsonResponse | array { if ($asset = Asset::with('assetstatus') ->with('assignedTo')->withTrashed() - ->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)) { + ->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id) + ) { $this->authorize('view', $asset); - return (new AssetsTransformer)->transformAsset($asset, $request->input('components') ); + return (new AssetsTransformer)->transformAsset($asset, $request->input('components')); } return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); - } - public function licenses(Request $request, $id) : array + public function licenses(Request $request, $id): array { $this->authorize('view', Asset::class); $this->authorize('view', License::class); @@ -527,7 +535,7 @@ class AssetsController extends Controller $licenses = $asset->licenses()->get(); return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count()); - } + } /** @@ -537,7 +545,7 @@ class AssetsController extends Controller * @since [v4.0.16] * @see \App\Http\Transformers\SelectlistTransformer */ - public function selectlist(Request $request) : array + public function selectlist(Request $request): array { $assets = Asset::select([ @@ -548,7 +556,7 @@ class AssetsController extends Controller 'assets.assigned_to', 'assets.assigned_type', 'assets.status_id', - ])->with('model', 'assetstatus', 'assignedTo')->NotArchived(); + ])->with('model', 'assetstatus', 'assignedTo')->NotArchived(); if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') { $assets = $assets->RTD(); @@ -570,12 +578,12 @@ class AssetsController extends Controller $asset->use_text = $asset->present()->fullName; if (($asset->checkedOutToUser()) && ($asset->assigned)) { - $asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute(); + $asset->use_text .= ' → ' . $asset->assigned->getFullNameAttribute(); } if ($asset->assetstatus->getStatuslabelType() == 'pending') { - $asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')'; + $asset->use_text .= '(' . $asset->assetstatus->getStatuslabelType() . ')'; } $asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null; @@ -601,12 +609,12 @@ class AssetsController extends Controller $asset->created_by = auth()->id(); /** - * this is here just legacy reasons. Api\AssetController - * used image_source once to allow encoded image uploads. - */ + * this is here just legacy reasons. Api\AssetController + * used image_source once to allow encoded image uploads. + */ if ($request->has('image_source')) { $request->offsetSet('image', $request->offsetGet('image_source')); - } + } $asset = $request->handleImages($asset); @@ -623,9 +631,9 @@ class AssetsController extends Controller // If input value is null, use custom field's default value if ($field_val == null) { - Log::debug('Field value for '.$field->db_column.' is null'); + Log::debug('Field value for ' . $field->db_column . ' is null'); $field_val = $field->defaultValue($request->get('model_id')); - Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id'))); + Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->get('model_id'))); } // if the field is set to encrypted, make sure we encrypt the value @@ -643,7 +651,7 @@ class AssetsController extends Controller } } if ($field->element == 'checkbox') { - if(is_array($field_val)) { + if (is_array($field_val)) { $field_val = implode(',', $field_val); } } @@ -702,64 +710,64 @@ class AssetsController extends Controller } /** - * this is here just legacy reasons. Api\AssetController - * used image_source once to allow encoded image uploads. - */ + * this is here just legacy reasons. Api\AssetController + * used image_source once to allow encoded image uploads. + */ if ($request->has('image_source')) { $request->offsetSet('image', $request->offsetGet('image_source')); } $asset = $request->handleImages($asset); $model = $asset->model; - - // Update custom fields - $problems_updating_encrypted_custom_fields = false; - if (($model) && (isset($model->fieldset))) { - foreach ($model->fieldset->fields as $field) { - $field_val = $request->input($field->db_column, null); - if ($request->has($field->db_column)) { - if ($field->element == 'checkbox') { - if(is_array($field_val)) { - $field_val = implode(',', $field_val); - } + // Update custom fields + $problems_updating_encrypted_custom_fields = false; + if (($model) && (isset($model->fieldset))) { + foreach ($model->fieldset->fields as $field) { + $field_val = $request->input($field->db_column, null); + + if ($request->has($field->db_column)) { + if ($field->element == 'checkbox') { + if (is_array($field_val)) { + $field_val = implode(',', $field_val); } - if ($field->field_encrypted == '1') { - if (Gate::allows('assets.view.encrypted_custom_fields')) { - $field_val = Crypt::encrypt($field_val); - } else { - $problems_updating_encrypted_custom_fields = true; - continue; - } - } - $asset->{$field->db_column} = $field_val; } + if ($field->field_encrypted == '1') { + if (Gate::allows('assets.view.encrypted_custom_fields')) { + $field_val = Crypt::encrypt($field_val); + } else { + $problems_updating_encrypted_custom_fields = true; + continue; + } + } + $asset->{$field->db_column} = $field_val; } } - if ($asset->save()) { - if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) { - $location = $target->location_id; - } elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) { - $location = $target->location_id; + } + if ($asset->save()) { + if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) { + $location = $target->location_id; + } elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) { + $location = $target->location_id; - Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id) - ->update(['location_id' => $target->location_id]); - } elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) { - $location = $target->id; - } + Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id) + ->update(['location_id' => $target->location_id]); + } elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) { + $location = $target->id; + } - if (isset($target)) { - $asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location); - } + if (isset($target)) { + $asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location); + } - if ($asset->image) { - $asset->image = $asset->getImageUrl(); - } + if ($asset->image) { + $asset->image = $asset->getImageUrl(); + } if ($problems_updating_encrypted_custom_fields) { - return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning'))); + return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.encrypted_warning'))); } else { - return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success'))); + return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success'))); } } return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200); @@ -773,16 +781,23 @@ class AssetsController extends Controller * @param int $assetId * @since [v4.0] */ - public function destroy($id) : JsonResponse + public function destroy($id): JsonResponse { $this->authorize('delete', Asset::class); if ($asset = Asset::find($id)) { $this->authorize('delete', $asset); - DB::table('assets') - ->where('id', $asset->id) - ->update(['assigned_to' => null]); + if ($asset->assignedTo) { + + $target = $asset->assignedTo; + $checkin_at = date('Y-m-d H:i:s'); + $originalValues = $asset->getRawOriginal(); + event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on delete', $checkin_at, $originalValues)); + DB::table('assets') + ->where('id', $asset->id) + ->update(['assigned_to' => null]); + } $asset->delete(); @@ -792,7 +807,7 @@ class AssetsController extends Controller return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); } - + /** * Restore a soft-deleted asset. @@ -801,7 +816,7 @@ class AssetsController extends Controller * @param int $assetId * @since [v5.1.18] */ - public function restore(Request $request, $assetId = null) : JsonResponse + public function restore(Request $request, $assetId = null): JsonResponse { if ($asset = Asset::withTrashed()->find($assetId)) { @@ -820,7 +835,6 @@ class AssetsController extends Controller } return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); - } /** @@ -830,7 +844,7 @@ class AssetsController extends Controller * @param string $tag * @since [v6.0.5] */ - public function checkoutByTag(AssetCheckoutRequest $request, $tag) : JsonResponse + public function checkoutByTag(AssetCheckoutRequest $request, $tag): JsonResponse { if ($asset = Asset::where('asset_tag', $tag)->first()) { return $this->checkout($request, $asset->id); @@ -845,13 +859,13 @@ class AssetsController extends Controller * @param int $assetId * @since [v4.0] */ - public function checkout(AssetCheckoutRequest $request, $asset_id) : JsonResponse + public function checkout(AssetCheckoutRequest $request, $asset_id): JsonResponse { $this->authorize('checkout', Asset::class); $asset = Asset::findOrFail($asset_id); if (! $asset->availableForCheckout()) { - return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available'))); + return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available'))); } $this->authorize('checkout', $asset); @@ -868,14 +882,12 @@ class AssetsController extends Controller $asset->location_id = ($target) ? $target->id : ''; $error_payload['target_id'] = $request->input('assigned_location'); $error_payload['target_type'] = 'location'; - } elseif (request('checkout_to_type') == 'asset') { $target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset')); // Override with the asset's location_id if it has one $asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : ''; $error_payload['target_id'] = $request->input('assigned_asset'); $error_payload['target_type'] = 'asset'; - } elseif (request('checkout_to_type') == 'user') { // Fetch the target and set the asset's new location_id $target = User::find(request('assigned_user')); @@ -889,7 +901,7 @@ class AssetsController extends Controller } if (! isset($target)) { - return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.')); + return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset ' . e($asset->asset_tag) . ' is invalid - ' . $error_payload['target_type'] . ' does not exist.')); } $checkout_at = request('checkout_at', date('Y-m-d H:i:s')); @@ -903,15 +915,15 @@ class AssetsController extends Controller // TODO: Follow up here. WTF. Commented out for now. -// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) { -// $asset->location_id = $target->rtd_location_id; -// } + // if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) { + // $asset->location_id = $target->rtd_location_id; + // } if ($asset->checkOut($target, auth()->user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) { - return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success'))); + return response()->json(Helper::formatStandardApiResponse('success', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.success'))); } - return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error'))); + return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.error'))); } @@ -922,7 +934,7 @@ class AssetsController extends Controller * @param int $assetId * @since [v4.0] */ - public function checkin(Request $request, $asset_id) : JsonResponse + public function checkin(Request $request, $asset_id): JsonResponse { $asset = Asset::with('model')->findOrFail($asset_id); $this->authorize('checkin', $asset); @@ -930,7 +942,7 @@ class AssetsController extends Controller $target = $asset->assignedTo; if (is_null($target)) { return response()->json(Helper::formatStandardApiResponse('error', [ - 'asset_tag'=> e($asset->asset_tag), + 'asset_tag' => e($asset->asset_tag), 'model' => e($asset->model->name), 'model_number' => e($asset->model->model_number) ], trans('admin/hardware/message.checkin.already_checked_in'))); @@ -953,7 +965,7 @@ class AssetsController extends Controller if ($request->filled('location_id')) { $asset->location_id = $request->input('location_id'); - if ($request->input('update_default_location')){ + if ($request->input('update_default_location')) { $asset->rtd_location_id = $request->input('location_id'); } } @@ -961,8 +973,8 @@ class AssetsController extends Controller if ($request->filled('status_id')) { $asset->status_id = $request->input('status_id'); } - - $checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at').' '. date('H:i:s') : date('Y-m-d H:i:s'); + + $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'))) { @@ -980,7 +992,8 @@ class AssetsController extends Controller [Asset::class], function (Builder $query) use ($asset) { $query->where('id', $asset->id); - }) + } + ) ->get() ->map(function ($acceptance) { $acceptance->delete(); @@ -990,13 +1003,13 @@ class AssetsController extends Controller event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues)); return response()->json(Helper::formatStandardApiResponse('success', [ - 'asset_tag'=> e($asset->asset_tag), + 'asset_tag' => e($asset->asset_tag), 'model' => e($asset->model->name), 'model_number' => e($asset->model->model_number) ], trans('admin/hardware/message.checkin.success'))); } - return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error'))); + return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkin.error'))); } /** @@ -1005,7 +1018,7 @@ class AssetsController extends Controller * @author [A. Janes] [] * @since [v6.0] */ - public function checkinByTag(Request $request, $tag = null) : JsonResponse + public function checkinByTag(Request $request, $tag = null): JsonResponse { $this->authorize('checkin', Asset::class); if (null == $tag && null !== ($request->input('asset_tag'))) { @@ -1018,8 +1031,8 @@ class AssetsController extends Controller } return response()->json(Helper::formatStandardApiResponse('error', [ - 'asset'=> e($tag) - ], 'Asset with tag '.e($tag).' not found')); + 'asset' => e($tag) + ], 'Asset with tag ' . e($tag) . ' not found')); } @@ -1030,7 +1043,7 @@ class AssetsController extends Controller * @param int $id * @since [v4.0] */ - public function audit(Request $request) : JsonResponse + public function audit(Request $request): JsonResponse { $this->authorize('audit', Asset::class); @@ -1041,8 +1054,8 @@ class AssetsController extends Controller // No tag passed - return an error if (!$request->filled('asset_tag')) { return response()->json(Helper::formatStandardApiResponse('error', [ - 'asset_tag'=> '', - 'error'=> trans('admin/hardware/message.no_tag'), + 'asset_tag' => '', + 'error' => trans('admin/hardware/message.no_tag'), ], trans('admin/hardware/message.no_tag')), 200); } @@ -1090,28 +1103,25 @@ class AssetsController extends Controller $asset->logAudit(request('note'), request('location_id')); return response()->json(Helper::formatStandardApiResponse('success', [ - 'asset_tag'=> e($asset->asset_tag), - 'note'=> e($request->input('note')), + 'asset_tag' => e($asset->asset_tag), + 'note' => e($request->input('note')), 'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date), ], trans('admin/hardware/message.audit.success'))); } // Asset failed validation or was not able to be saved return response()->json(Helper::formatStandardApiResponse('error', [ - 'asset_tag'=> e($asset->asset_tag), - 'error'=> $asset->getErrors()->first(), + 'asset_tag' => e($asset->asset_tag), + 'error' => $asset->getErrors()->first(), ], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200); - } // No matching asset for the asset tag that was passed. return response()->json(Helper::formatStandardApiResponse('error', [ - 'asset_tag'=> e($request->input('asset_tag')), - 'error'=> trans('admin/hardware/message.audit.error'), + 'asset_tag' => e($request->input('asset_tag')), + 'error' => trans('admin/hardware/message.audit.error'), ], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200); - - } @@ -1122,7 +1132,7 @@ class AssetsController extends Controller * @author [A. Gianotto] [] * @since [v4.0] */ - public function requestable(Request $request) : JsonResponse | array + public function requestable(Request $request): JsonResponse | array { $this->authorize('viewRequestable', Asset::class); @@ -1143,8 +1153,18 @@ class AssetsController extends Controller } $assets = Asset::select('assets.*') - ->with('location', 'assetstatus', 'assetlog', 'company','assignedTo', - 'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests'); + ->with( + 'location', + 'assetstatus', + 'assetlog', + 'company', + 'assignedTo', + 'model.category', + 'model.manufacturer', + 'model.fieldset', + 'supplier', + 'requests' + ); @@ -1152,7 +1172,7 @@ class AssetsController extends Controller 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())) { @@ -1193,4 +1213,89 @@ class AssetsController extends Controller return (new AssetsTransformer)->transformRequestedAssets($assets, $total); } + + /** + * Generate asset labels by tag + * + * @author [Nebelkreis] [https://github.com/NebelKreis] + * + * @param Request $request Contains asset_tags array of asset tags to generate labels for + * @return JsonResponse Returns base64 encoded PDF on success, error message on failure + */ + public function getLabels(Request $request): JsonResponse + { + try { + $this->authorize('view', Asset::class); + + // Validate that asset tags were provided in the request + if (!$request->filled('asset_tags')) { + return response()->json(Helper::formatStandardApiResponse('error', null, + trans('admin/hardware/message.no_assets_selected')), 400); + } + + // Convert asset tags from request into collection and fetch matching assets + $asset_tags = collect($request->input('asset_tags')); + $assets = Asset::whereIn('asset_tag', $asset_tags)->get(); + + // Return error if no assets were found for the provided tags + if ($assets->isEmpty()) { + return response()->json(Helper::formatStandardApiResponse('error', null, + trans('admin/hardware/message.does_not_exist')), 404); + } + + try { + $settings = Setting::getSettings(); + + // Check if logo file exists in storage and disable logo if not found + // This prevents errors when trying to include a non-existent logo in the PDF + $settings->label_logo = ($original_logo = $settings->label_logo) && !Storage::disk('public')->exists('/' . $original_logo) ? null : $settings->label_logo; + + + $label = new Label(); + + if (!$label) { + throw new \Exception('Label object could not be created'); + } + + // Configure label with assets and settings + // bulkedit=false and count=0 are default values for label generation + $label = $label->with('assets', $assets) + ->with('settings', $settings) + ->with('bulkedit', false) + ->with('count', 0); + + // Generate PDF using callback function + // The callback captures the PDF content in $pdf_content variable + $pdf_content = ''; + $label->render(function($pdf) use (&$pdf_content) { + $pdf_content = $pdf->Output('', 'S'); + return $pdf; + }); + + // Verify PDF was generated successfully + if (empty($pdf_content)) { + throw new \Exception('PDF content is empty'); + } + + $encoded_content = base64_encode($pdf_content); + + return response()->json(Helper::formatStandardApiResponse('success', [ + 'pdf' => $encoded_content + ], trans('admin/hardware/message.labels_generated'))); + + } catch (\Exception $e) { + return response()->json(Helper::formatStandardApiResponse('error', [ + 'error_message' => $e->getMessage(), + 'error_line' => $e->getLine(), + 'error_file' => $e->getFile() + ], trans('admin/hardware/message.error_generating_labels')), 500); + } + } catch (\Exception $e) { + return response()->json(Helper::formatStandardApiResponse('error', [ + 'error_message' => $e->getMessage(), + 'error_line' => $e->getLine(), + 'error_file' => $e->getFile() + ], $e->getMessage()), 500); + } + } } diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index 561e13c9cd..0f594f5e75 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -38,6 +38,7 @@ class ComponentsController extends Controller 'name', 'min_amt', 'order_number', + 'model_number', 'serial', 'purchase_date', 'purchase_cost', @@ -47,7 +48,7 @@ class ComponentsController extends Controller ]; $components = Component::select('components.*') - ->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser'); + ->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer'); if ($request->filled('search')) { $components = $components->TextSearch($request->input('search')); @@ -69,6 +70,14 @@ class ComponentsController extends Controller $components->where('supplier_id', '=', $request->input('supplier_id')); } + if ($request->filled('manufacturer_id')) { + $components->where('manufacturer_id', '=', $request->input('manufacturer_id')); + } + + if ($request->filled('model_number')) { + $components->where('model_number', '=', $request->input('model_number')); + } + if ($request->filled('location_id')) { $components->where('location_id', '=', $request->input('location_id')); } @@ -98,6 +107,9 @@ class ComponentsController extends Controller case 'supplier': $components = $components->OrderSupplier($order); break; + case 'manufacturer': + $components = $components->OrderManufacturer($order); + break; case 'created_by': $components = $components->OrderByCreatedBy($order); break; @@ -297,9 +309,7 @@ class ComponentsController extends Controller public function checkin(Request $request, $component_asset_id) : JsonResponse { if ($component_assets = DB::table('components_assets')->find($component_asset_id)) { - if (is_null($component = Component::find($component_assets->component_id))) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.not_found'))); } @@ -307,17 +317,13 @@ class ComponentsController extends Controller $max_to_checkin = $component_assets->assigned_qty; - if ($max_to_checkin > 1) { - - $validator = Validator::make($request->all(), [ - "checkin_qty" => "required|numeric|between:1,$max_to_checkin" - ]); - - if ($validator->fails()) { - return response()->json(Helper::formatStandardApiResponse('error', null, 'Checkin quantity must be between 1 and '.$max_to_checkin)); - } + $validator = Validator::make($request->all(), [ + "checkin_qty" => "required|numeric|between:1,$max_to_checkin" + ]); + + if ($validator->fails()) { + return response()->json(Helper::formatStandardApiResponse('error', null, 'Checkin quantity must be between 1 and ' . $max_to_checkin)); } - // Validation passed, so let's figure out what we have to do here. $qty_remaining_in_checkout = ($component_assets->assigned_qty - (int)$request->input('checkin_qty', 1)); @@ -327,28 +333,23 @@ class ComponentsController extends Controller $component_assets->assigned_qty = $qty_remaining_in_checkout; Log::debug($component_asset_id.' - '.$qty_remaining_in_checkout.' remaining in record '.$component_assets->id); - - DB::table('components_assets')->where('id', - $component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]); + + DB::table('components_assets')->where('id', $component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]); // If the checked-in qty is exactly the same as the assigned_qty, // we can simply delete the associated components_assets record - if ($qty_remaining_in_checkout == 0) { + if ($qty_remaining_in_checkout === 0) { DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } - $asset = Asset::find($component_assets->asset_id); event(new CheckoutableCheckedIn($component, $asset, auth()->user(), $request->input('note'), Carbon::now())); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkin.success'))); - } return response()->json(Helper::formatStandardApiResponse('error', null, 'No matching checkouts for that component join record')); - - } } diff --git a/app/Http/Controllers/Api/ConsumablesController.php b/app/Http/Controllers/Api/ConsumablesController.php index 8e7f321720..7ff676c7be 100644 --- a/app/Http/Controllers/Api/ConsumablesController.php +++ b/app/Http/Controllers/Api/ConsumablesController.php @@ -258,6 +258,8 @@ class ConsumablesController extends Controller $this->authorize('checkout', $consumable); + $consumable->checkout_qty = $request->input('checkout_qty', 1); + // Make sure there is at least one available to checkout if ($consumable->numRemaining() <= 0) { return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable'))); @@ -268,6 +270,12 @@ class ConsumablesController extends Controller return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.invalid_item_category_single', ['type' => trans('general.consumable')]))); } + // Make sure there is at least one available to checkout + if ($consumable->numRemaining() <= 0 || $consumable->checkout_qty > $consumable->numRemaining()) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable', ['requested' => $consumable->checkout_qty, 'remaining' => $consumable->numRemaining() ]))); + } + + // Check if the user exists - @TODO: this should probably be handled via validation, not here?? if (!$user = User::find($request->input('assigned_to'))) { @@ -278,7 +286,8 @@ class ConsumablesController extends Controller // Update the consumable data $consumable->assigned_to = $request->input('assigned_to'); - $consumable->users()->attach($consumable->id, + for ($i = 0; $i < $consumable->checkout_qty; $i++) { + $consumable->users()->attach($consumable->id, [ 'consumable_id' => $consumable->id, 'created_by' => $user->id, @@ -286,6 +295,8 @@ class ConsumablesController extends Controller 'note' => $request->input('note'), ] ); + } + event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note'))); diff --git a/app/Http/Controllers/Api/ImportController.php b/app/Http/Controllers/Api/ImportController.php index 13f4301217..ebf8b550b2 100644 --- a/app/Http/Controllers/Api/ImportController.php +++ b/app/Http/Controllers/Api/ImportController.php @@ -28,8 +28,7 @@ class ImportController extends Controller public function index() : JsonResponse | array { $this->authorize('import'); - $imports = Import::latest()->get(); - + $imports = Import::with('adminuser')->latest()->get(); return (new ImportsTransformer)->transformImports($imports); } @@ -133,7 +132,7 @@ class ImportController extends Controller } $import->filesize = filesize($path.'/'.$file_name); - + $import->created_by = auth()->id(); $import->save(); $results[] = $import; } @@ -177,6 +176,9 @@ class ImportController extends Controller case 'asset': $redirectTo = 'hardware.index'; break; + case 'assetModel': + $redirectTo = 'models.index'; + break; case 'accessory': $redirectTo = 'accessories.index'; break; diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index f111ef6c83..f716fbbf7f 100644 --- a/app/Http/Controllers/Api/ManufacturersController.php +++ b/app/Http/Controllers/Api/ManufacturersController.php @@ -60,7 +60,8 @@ class ManufacturersController extends Controller ->withCount('assets as assets_count') ->withCount('licenses as licenses_count') ->withCount('consumables as consumables_count') - ->withCount('accessories as accessories_count'); + ->withCount('accessories as accessories_count') + ->withCount('components as components_count'); if ($request->input('deleted') == 'true') { $manufacturers->onlyTrashed(); diff --git a/app/Http/Controllers/Api/ReportsController.php b/app/Http/Controllers/Api/ReportsController.php index 566911dd27..494c75104f 100644 --- a/app/Http/Controllers/Api/ReportsController.php +++ b/app/Http/Controllers/Api/ReportsController.php @@ -45,7 +45,7 @@ class ReportsController extends Controller } if ($request->filled('action_type')) { - $actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc'); + $actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type')); } if ($request->filled('created_by')) { @@ -53,15 +53,16 @@ class ReportsController extends Controller } if ($request->filled('action_source')) { - $actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc'); + $actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source')); + } + + if ($request->filled('remote_ip')) { + $actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip')); } - if ($request->filled('remote_ip')) { - $actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'))->orderBy('created_at', 'desc'); - } if ($request->filled('uploads')) { - $actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc'); + $actionlogs = $actionlogs->whereNotNull('filename'); } $allowed_columns = [ @@ -74,6 +75,8 @@ class ReportsController extends Controller 'note', 'remote_ip', 'user_agent', + 'target_type', + 'item_type', 'action_source', 'action_date', ]; @@ -91,7 +94,7 @@ class ReportsController extends Controller $actionlogs->OrderByCreatedBy($order); break; default: - $sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at'; + $sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'action_logs.created_at'; $actionlogs = $actionlogs->orderBy($sort, $order); break; } diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index 754ebf7323..7e4851ff5a 100644 --- a/app/Http/Controllers/Api/StatuslabelsController.php +++ b/app/Http/Controllers/Api/StatuslabelsController.php @@ -95,7 +95,8 @@ class StatuslabelsController extends Controller $request->except('deployable', 'pending', 'archived'); if (! $request->filled('type')) { - return response()->json(Helper::formatStandardApiResponse('error', null, ['type' => ['Status label type is required.']]), 500); + + return response()->json(Helper::formatStandardApiResponse('error', null, ['type' => ['Status label type is required.']])); } $statuslabel = new Statuslabel; diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index bfe8f44ef1..61aad10734 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -14,6 +14,7 @@ use App\Http\Transformers\UsersTransformer; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Accessory; +use App\Models\Company; use App\Models\Consumable; use App\Models\License; use App\Models\User; @@ -42,13 +43,14 @@ class UsersController extends Controller $users = User::select([ 'users.activated', - 'users.created_by', 'users.address', 'users.avatar', 'users.city', 'users.company_id', 'users.country', + 'users.created_by', 'users.created_at', + 'users.updated_at', 'users.deleted_at', 'users.department_id', 'users.email', @@ -67,7 +69,6 @@ class UsersController extends Controller 'users.state', 'users.two_factor_enrolled', 'users.two_factor_optin', - 'users.updated_at', 'users.username', 'users.zip', 'users.remote', @@ -255,6 +256,7 @@ class UsersController extends Controller 'groups', 'activated', 'created_at', + 'updated_at', 'two_factor_enrolled', 'two_factor_optin', 'last_login', @@ -281,6 +283,7 @@ class UsersController extends Controller 'autoassign_licenses', 'website', 'locale', + 'notes', ]; $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name'; @@ -370,6 +373,7 @@ class UsersController extends Controller $user = new User; $user->fill($request->all()); + $user->company_id = Company::getIdForCurrentUser($request->input('company_id')); $user->created_by = auth()->id(); if ($request->has('permissions')) { @@ -451,6 +455,10 @@ class UsersController extends Controller $user->fill($request->all()); + if ($request->filled('company_id')) { + $user->company_id = Company::getIdForCurrentUser($request->input('company_id')); + } + if ($user->id == $request->input('manager_id')) { return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager')); } @@ -473,10 +481,11 @@ class UsersController extends Controller $user->permissions = $permissions_array; } - // Update the location of any assets checked out to this user - Asset::where('assigned_type', User::class) - ->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]); - + if($request->has('location_id')) { + // Update the location of any assets checked out to this user + Asset::where('assigned_type', User::class) + ->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]); + } app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar'); if ($user->save()) { diff --git a/app/Http/Controllers/AssetModelsController.php b/app/Http/Controllers/AssetModelsController.php index 9d4c13afd9..aa083e1ae3 100755 --- a/app/Http/Controllers/AssetModelsController.php +++ b/app/Http/Controllers/AssetModelsController.php @@ -18,6 +18,7 @@ use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Log; use \Illuminate\Contracts\View\View; use \Illuminate\Http\RedirectResponse; +use Illuminate\Support\MessageBag; /** @@ -29,6 +30,7 @@ use \Illuminate\Http\RedirectResponse; */ class AssetModelsController extends Controller { + protected MessageBag $validatorErrors; /** * Returns a view that invokes the ajax tables which actually contains * the content for the accessories listing, which is generated in getDatatable. @@ -158,7 +160,7 @@ class AssetModelsController extends Controller if ($this->shouldAddDefaultValues($request->input())) { if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))) { - return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error')); + return redirect()->back()->withInput()->withErrors($this->validatorErrors); } } @@ -481,9 +483,15 @@ class AssetModelsController extends Controller $rules[$field] = $validation; } - $validator = Validator::make($data, $rules); + $attributes = []; + foreach ($model->fieldset->fields as $field) { + $attributes[$field->db_column] = trim(preg_replace('/_+|snipeit|\d+/', ' ', $field->db_column)); + } + + $validator = Validator::make($data, $rules)->setAttributeNames($attributes); if($validator->fails()){ + $this->validatorErrors = $validator->errors(); return false; } diff --git a/app/Http/Controllers/Assets/AssetFilesController.php b/app/Http/Controllers/Assets/AssetFilesController.php index b5a04759bb..d15055c4b2 100644 --- a/app/Http/Controllers/Assets/AssetFilesController.php +++ b/app/Http/Controllers/Assets/AssetFilesController.php @@ -61,43 +61,30 @@ class AssetFilesController extends Controller */ public function show($assetId = null, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse { - $asset = Asset::find($assetId); - // the asset is valid - if (isset($asset->id)) { + if ($asset = Asset::find($assetId)) { + $this->authorize('view', $asset); - 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'); + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) { + $file = 'private_uploads/assets/'.$log->filename; + + if ($log->action_type == 'audit') { + $file = 'private_uploads/audits/'.$log->filename; + } + + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.file_not_found')); + } + } - $file = 'private_uploads/assets/'.$log->filename; - - if ($log->action_type == 'audit') { - $file = 'private_uploads/audits/'.$log->filename; - } - - if (! Storage::exists($file)) { - return response('File '.$file.' not found on server', 404) - ->header('Content-Type', 'text/plain'); - } - - if (request('inline') == 'true') { - - $headers = [ - 'Content-Disposition' => 'inline', - ]; - - return Storage::download($file, $log->filename, $headers); - } - - return StorageHelper::downloader($file); + return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found')); } - // Prepare the error message - $error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]); - // Redirect to the hardware management page - return redirect()->route('hardware.index')->with('error', $error); + return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist')); + } /** diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index e236009d54..98f4b7e38e 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -17,7 +17,6 @@ use App\Models\Location; use App\Models\Setting; use App\Models\Statuslabel; use App\Models\User; -use Illuminate\Support\Facades\Auth; use App\View\Label; use Carbon\Carbon; use Illuminate\Support\Facades\DB; @@ -112,8 +111,10 @@ class AssetsController extends Controller $settings = Setting::getSettings(); - $success = false; + $successes = []; + $failures = []; $serials = $request->input('serials'); + $asset = null; for ($a = 1; $a <= count($asset_tags); $a++) { $asset = new Asset(); @@ -200,20 +201,35 @@ class AssetsController extends Controller $asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location); } - $success = true; - + $successes[] = " $asset->id]) . "' style='color: white;'>" . e($asset->asset_tag) . ""; + + } else { + $failures[] = join(",", $asset->getErrors()->all()); } } session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]); - if ($success) { + if ($successes) { + if ($failures) { + //some succeeded, some failed + return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested + ->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)])) + ->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)])); + } else { + if (count($successes) == 1) { + //the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed + //and re-translated + return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) + ->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)])); + } else { + //multi-success + return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) + ->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)])); + } + } - return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) - ->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)])); - - } return redirect()->back()->withInput()->withErrors($asset->getErrors()); @@ -428,7 +444,7 @@ class AssetsController extends Controller * @param int $assetId * @since [v1.0] */ - public function destroy($assetId) : RedirectResponse + public function destroy(Request $request, $assetId) : RedirectResponse { // Check if the asset exists if (is_null($asset = Asset::find($assetId))) { @@ -438,9 +454,17 @@ class AssetsController extends Controller $this->authorize('delete', $asset); - DB::table('assets') - ->where('id', $asset->id) - ->update(['assigned_to' => null]); + if ($asset->assignedTo) { + + $target = $asset->assignedTo; + $checkin_at = date('Y-m-d H:i:s'); + $originalValues = $asset->getRawOriginal(); + event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on delete', $checkin_at, $originalValues)); + DB::table('assets') + ->where('id', $asset->id) + ->update(['assigned_to' => null]); + } + if ($asset->image) { try { diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index 1ce08e65e9..93f7255c0b 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -52,6 +52,10 @@ class BulkAssetsController extends Controller } $asset_ids = $request->input('ids'); + if ($request->input('bulk_actions') === 'checkout') { + $request->session()->flashInput(['selected_assets' => $asset_ids]); + return redirect()->route('hardware.bulkcheckout.show'); + } // Figure out where we need to send the user after the update is complete, and store that in the session $bulk_back_url = request()->headers->get('referer'); @@ -241,10 +245,12 @@ class BulkAssetsController extends Controller || ($request->filled('status_id')) || ($request->filled('model_id')) || ($request->filled('next_audit_date')) + || ($request->filled('asset_eol_date')) || ($request->filled('null_name')) || ($request->filled('null_purchase_date')) || ($request->filled('null_expected_checkin_date')) || ($request->filled('null_next_audit_date')) + || ($request->filled('null_asset_eol_date')) || ($request->anyFilled($custom_field_columns)) ) { @@ -267,7 +273,8 @@ class BulkAssetsController extends Controller ->conditionallyAddItem('requestable') ->conditionallyAddItem('supplier_id') ->conditionallyAddItem('warranty_months') - ->conditionallyAddItem('next_audit_date'); + ->conditionallyAddItem('next_audit_date') + ->conditionallyAddItem('asset_eol_date'); foreach ($custom_field_columns as $key => $custom_field_column) { $this->conditionallyAddItem($custom_field_column); } @@ -312,6 +319,17 @@ class BulkAssetsController extends Controller $this->update_array['next_audit_date'] = null; } + if ($request->input('null_asset_eol_date')=='1') { + $this->update_array['asset_eol_date'] = null; + + // If they are nulling the EOL date to allow it to calculate, set eol explicit to 0 + if ($request->input('calc_eol')=='1') { + $this->update_array['eol_explicit'] = 0; + } + } + + + if ($request->filled('purchase_cost')) { $this->update_array['purchase_cost'] = $request->input('purchase_cost'); } @@ -571,31 +589,34 @@ class BulkAssetsController extends Controller } $errors = []; - DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) { + DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference! foreach ($asset_ids as $asset_id) { $asset = Asset::findOrFail($asset_id); $this->authorize('checkout', $asset); - $error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null); + $checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null); + //TODO - I think this logic is duplicated in the checkOut method? if ($target->location_id != '') { $asset->location_id = $target->location_id; - $asset->unsetEventDispatcher(); - $asset->save(); + // TODO - I don't know why this is being saved without events + $asset::withoutEvents(function () use ($asset) { + $asset->save(); + }); } - if ($error) { - array_merge_recursive($errors, $asset->getErrors()->toArray()); + if (!$checkout_success) { + $errors = array_merge_recursive($errors, $asset->getErrors()->toArray()); } } }); if (! $errors) { // Redirect to the new asset page - return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success')); + return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids)); } // Redirect to the asset management page with error - return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors); + return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors); } catch (ModelNotFoundException $e) { return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors()); } diff --git a/app/Http/Controllers/Components/ComponentsController.php b/app/Http/Controllers/Components/ComponentsController.php index 430984767e..62dc25cf1d 100644 --- a/app/Http/Controllers/Components/ComponentsController.php +++ b/app/Http/Controllers/Components/ComponentsController.php @@ -73,6 +73,8 @@ class ComponentsController extends Controller $component->name = $request->input('name'); $component->category_id = $request->input('category_id'); $component->supplier_id = $request->input('supplier_id'); + $component->manufacturer_id = $request->input('manufacturer_id'); + $component->model_number = $request->input('model_number'); $component->location_id = $request->input('location_id'); $component->company_id = Company::getIdForCurrentUser($request->input('company_id')); $component->order_number = $request->input('order_number', null); @@ -150,6 +152,8 @@ class ComponentsController extends Controller $component->name = $request->input('name'); $component->category_id = $request->input('category_id'); $component->supplier_id = $request->input('supplier_id'); + $component->manufacturer_id = $request->input('manufacturer_id'); + $component->model_number = $request->input('model_number'); $component->location_id = $request->input('location_id'); $component->company_id = Company::getIdForCurrentUser($request->input('company_id')); $component->order_number = $request->input('order_number'); @@ -189,7 +193,7 @@ class ComponentsController extends Controller $this->authorize('delete', $component); // Remove the image if one exists - if (Storage::disk('public')->exists('components/'.$component->image)) { + if ($component->image && Storage::disk('public')->exists('components/' . $component->image)) { try { Storage::disk('public')->delete('components/'.$component->image); } catch (\Exception $e) { diff --git a/app/Http/Controllers/Components/ComponentsFilesController.php b/app/Http/Controllers/Components/ComponentsFilesController.php index a7d42bb072..83468a0b10 100644 --- a/app/Http/Controllers/Components/ComponentsFilesController.php +++ b/app/Http/Controllers/Components/ComponentsFilesController.php @@ -112,40 +112,25 @@ class ComponentsFilesController extends Controller public function show($componentId = null, $fileId = null) { Log::debug('Private filesystem is: '.config('filesystems.default')); - $component = Component::find($componentId); + // the component is valid - if (isset($component->id)) { + if ($component = Component::find($componentId)) { $this->authorize('view', $component); $this->authorize('components.files', $component); - 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'); - } + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) { - $file = 'private_uploads/components/'.$log->filename; + $file = 'private_uploads/components/'.$log->filename; - if (Storage::missing($file)) { - Log::debug('FILE DOES NOT EXISTS for '.$file); - Log::debug('URL should be '.Storage::url($file)); - - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->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); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.file_not_found')); } - - if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer? - return StorageHelper::downloader($file); - } } + return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.log_record_not_found')); + } return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId])); diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index 3bf202733a..e08da41229 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -70,7 +70,7 @@ class ConsumableCheckoutController extends Controller $this->authorize('checkout', $consumable); // If the quantity is not present in the request or is not a positive integer, set it to 1 - $quantity = $request->input('qty'); + $quantity = $request->input('checkout_qty'); if (!isset($quantity) || !ctype_digit((string)$quantity) || $quantity <= 0) { $quantity = 1; } @@ -92,7 +92,7 @@ class ConsumableCheckoutController extends Controller // Update the consumable data $consumable->assigned_to = e($request->input('assigned_to')); - for($i = 0; $i < $quantity; $i++){ + for ($i = 0; $i < $quantity; $i++){ $consumable->users()->attach($consumable->id, [ 'consumable_id' => $consumable->id, 'created_by' => $admin_user->id, @@ -100,6 +100,8 @@ class ConsumableCheckoutController extends Controller 'note' => $request->input('note'), ]); } + + $consumable->checkout_qty = $quantity; event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note'))); $request->request->add(['checkout_to_type' => 'user']); diff --git a/app/Http/Controllers/Consumables/ConsumablesFilesController.php b/app/Http/Controllers/Consumables/ConsumablesFilesController.php index 35a4ae841e..054fdc0b81 100644 --- a/app/Http/Controllers/Consumables/ConsumablesFilesController.php +++ b/app/Http/Controllers/Consumables/ConsumablesFilesController.php @@ -104,7 +104,6 @@ class ConsumablesFilesController extends Controller * @since [v1.4] * @param int $consumableId * @param int $fileId - * @return \Symfony\Consumable\HttpFoundation\Response * @throws \Illuminate\Auth\Access\AuthorizationException */ public function show($consumableId = null, $fileId = null) @@ -116,36 +115,18 @@ class ConsumablesFilesController extends Controller $this->authorize('view', $consumable); $this->authorize('consumables.files', $consumable); - 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'); - } + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) { + $file = 'private_uploads/consumables/'.$log->filename; - $file = 'private_uploads/consumables/'.$log->filename; - - if (Storage::missing($file)) { - Log::debug('FILE DOES NOT EXISTS for '.$file); - Log::debug('URL should be '.Storage::url($file)); - - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->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); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found')); } } + // The log record doesn't exist somehow + return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.log_record_not_found')); + } return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId])); diff --git a/app/Http/Controllers/HealthController.php b/app/Http/Controllers/HealthController.php index c75b903b0b..dac1f17bf4 100644 --- a/app/Http/Controllers/HealthController.php +++ b/app/Http/Controllers/HealthController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use Illuminate\Routing\Controller as BaseController; +use Illuminate\Support\Facades\DB; /** * This controller provide the health route for @@ -15,13 +16,35 @@ use Illuminate\Routing\Controller as BaseController; */ class HealthController extends BaseController { + + public function __construct() + { + $this->middleware('health'); + } + + /** * Returns a fixed JSON content ({ "status": "ok"}) which indicate the app is up and running */ public function get() { - return response()->json([ - 'status' => 'ok', - ]); + try { + + if (DB::select('select 2 + 2')) { + return response()->json([ + 'status' => 'ok', + ]); + } + + } catch (\Exception $e) { + \Log::error('Could not connect to database'); + return response()->json([ + 'status' => 'database connection failed', + ], 500); + + } + + + } } diff --git a/app/Http/Controllers/Licenses/LicenseFilesController.php b/app/Http/Controllers/Licenses/LicenseFilesController.php index fa18e8cf48..6ab3cb7703 100644 --- a/app/Http/Controllers/Licenses/LicenseFilesController.php +++ b/app/Http/Controllers/Licenses/LicenseFilesController.php @@ -112,37 +112,19 @@ class LicenseFilesController extends Controller $this->authorize('view', $license); $this->authorize('licenses.files', $license); - 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'); - } - - $file = 'private_uploads/licenses/'.$log->filename; - - if (Storage::missing($file)) { - Log::debug('NOT EXISTS for '.$file); - Log::debug('NOT EXISTS URL should be '.Storage::url($file)); - - return response('File '.$file.' ('.Storage::url($file).') not found on server', 404) - ->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); + if ($log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) { + $file = 'private_uploads/licenses/'.$log->filename; + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.file_not_found')); } } + + // The log record doesn't exist somehow + return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found')); + } return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId])); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index abe09e8afa..896c253463 100755 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -194,14 +194,14 @@ class ProfileController extends Controller */ public function printInventory() : View { - $show_user = auth()->user(); + $show_users = User::where('id',auth()->user()->id)->get(); return view('users/print') - ->with('assets', auth()->user()->assets) - ->with('licenses', $show_user->licenses()->get()) - ->with('accessories', $show_user->accessories()->get()) - ->with('consumables', $show_user->consumables()->get()) - ->with('show_user', $show_user) + ->with('assets', auth()->user()->assets()) + ->with('licenses', auth()->user()->licenses()->get()) + ->with('accessories', auth()->user()->accessories()->get()) + ->with('consumables', auth()->user()->consumables()->get()) + ->with('users', $show_users) ->with('settings', Setting::getSettings()); } @@ -222,7 +222,12 @@ class ProfileController extends Controller return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email')); } - $user->notify((new CurrentInventory($user))); + try { + $user->notify((new CurrentInventory($user))); + } catch (\Exception $e) { + \Log::error($e); + } + return redirect()->back()->with('success', trans('admin/users/general.user_notified')); } } diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 105dac6350..03f7ec1c3f 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Helpers\Helper; +use App\Mail\CheckoutAssetMail; use App\Models\Accessory; use App\Models\Actionlog; use App\Models\Asset; @@ -18,6 +19,7 @@ use App\Notifications\CheckoutAssetNotification; use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use \Illuminate\Contracts\View\View; use League\Csv\Reader; @@ -259,7 +261,7 @@ class ReportsController extends Controller $executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']; Log::debug('Added headers: '.$executionTime); - $actionlogs = Actionlog::with('item', 'user', 'target', 'location') + $actionlogs = Actionlog::with('item', 'user', 'target', 'location', 'adminuser') ->orderBy('created_at', 'DESC') ->chunk(20, function ($actionlogs) use ($handle) { $executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']; @@ -286,7 +288,7 @@ class ReportsController extends Controller $row = [ $actionlog->created_at, - ($actionlog->admin) ? e($actionlog->admin->getFullNameAttribute()) : '', + ($actionlog->adminuser) ? e($actionlog->adminuser->getFullNameAttribute()) : '', $actionlog->present()->actionType(), e($actionlog->itemType()), ($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name, @@ -1150,24 +1152,17 @@ class ReportsController extends Controller } $logItem = $logItem_res[0]; } - + $email = $assetItem->assignedTo?->email; + $locale = $assetItem->assignedTo?->locale; // Only send notification if assigned - if ($assetItem->assignedTo) { + if ($locale && $email) { + Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $logItem->note, $acceptance))->locale($locale)); - if (!$assetItem->assignedTo->locale) { - Notification::locale(Setting::getSettings()->locale)->send( - $assetItem->assignedTo, - new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note) - ); - } else { - Notification::send( - $assetItem->assignedTo, - new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note) - ); + } elseif ($email) { + Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $logItem->note, $acceptance))); } - } - if ($assetItem->assignedTo->email == ''){ + if ($email == ''){ return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email')); } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index d0e4685ab7..b89a221faa 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -7,6 +7,11 @@ use App\Helpers\StorageHelper; use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\SettingsSamlRequest; use App\Http\Requests\SetupUserRequest; +use App\Http\Requests\StoreLdapSettings; +use App\Http\Requests\StoreLocalizationSettings; +use App\Http\Requests\StoreNotificationSettings; +use App\Http\Requests\StoreLabelSettings; +use App\Http\Requests\StoreSecuritySettings; use App\Models\CustomField; use App\Models\Group; use App\Models\Setting; @@ -273,20 +278,6 @@ class SettingsController extends Controller return view('settings/index', compact('settings')); } - /** - * Return the admin settings page. - * - * @author [A. Gianotto] [] - * - * @since [v1.0] - */ - public function getEdit() : View - - { - $setting = Setting::getSettings(); - - return view('settings/general', compact('setting')); - } /** * Return a form to allow a super admin to update settings. @@ -343,6 +334,8 @@ class SettingsController extends Controller $setting->depreciation_method = $request->input('depreciation_method'); $setting->dash_chart_type = $request->input('dash_chart_type'); $setting->profile_edit = $request->input('profile_edit', 0); + $setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0); + if ($request->input('per_page') != '') { $setting->per_page = $request->input('per_page'); @@ -486,7 +479,7 @@ class SettingsController extends Controller * * @since [v1.0] */ - public function postSecurity(Request $request) : RedirectResponse + public function postSecurity(StoreSecuritySettings $request) : RedirectResponse { $this->validate($request, [ 'pwd_secure_complexity' => 'array', @@ -556,7 +549,7 @@ class SettingsController extends Controller * * @since [v1.0] */ - public function postLocalization(Request $request) : RedirectResponse + public function postLocalization(StoreLocalizationSettings $request) : RedirectResponse { if (is_null($setting = Setting::getSettings())) { return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error')); @@ -599,7 +592,7 @@ class SettingsController extends Controller * @author [A. Gianotto] [] * @since [v1.0] */ - public function postAlerts(Request $request) : RedirectResponse + public function postAlerts(StoreNotificationSettings $request) : RedirectResponse { if (is_null($setting = Setting::getSettings())) { return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error')); @@ -741,7 +734,7 @@ class SettingsController extends Controller * @author [A. Gianotto] [] * @since [v4.0] */ - public function postLabels(Request $request) : RedirectResponse + public function postLabels(StoreLabelSettings $request) : RedirectResponse { if (is_null($setting = Setting::getSettings())) { return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error')); @@ -824,26 +817,7 @@ class SettingsController extends Controller { $setting = Setting::getSettings(); $groups = Group::pluck('name', 'id'); - - - /** - * This validator is only temporary (famous last words.) - @snipe - */ - $messages = [ - 'ldap_username_field.not_in' => 'sAMAccountName (mixed case) will likely not work. You should use samaccountname (lowercase) instead. ', - 'ldap_auth_filter_query.not_in' => 'uid=samaccountname is probably not a valid auth filter. You probably want uid= ', - 'ldap_filter.regex' => 'This value should probably not be wrapped in parentheses.', - ]; - - $validator = Validator::make($setting->toArray(), [ - 'ldap_username_field' => 'not_in:sAMAccountName', - 'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1', - 'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1', - ], $messages); - - - - return view('settings.ldap', compact('setting', 'groups'))->withErrors($validator); + return view('settings.ldap', compact('setting', 'groups')); } /** @@ -852,7 +826,7 @@ class SettingsController extends Controller * @author [A. Gianotto] [] * @since [v4.0] */ - public function postLdapSettings(Request $request) : RedirectResponse + public function postLdapSettings(StoreLdapSettings $request) : RedirectResponse { if (is_null($setting = Setting::getSettings())) { return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error')); diff --git a/app/Http/Controllers/Users/BulkUsersController.php b/app/Http/Controllers/Users/BulkUsersController.php index fbf08c9820..5541f7a15d 100644 --- a/app/Http/Controllers/Users/BulkUsersController.php +++ b/app/Http/Controllers/Users/BulkUsersController.php @@ -323,7 +323,7 @@ class BulkUsersController extends Controller $logAction->item_type = $itemType; $logAction->target_id = $item->assigned_to; $logAction->target_type = User::class; - $logAction->created_at = auth()->id(); + $logAction->created_by = auth()->id(); $logAction->note = 'Bulk checkin items'; $logAction->logaction('checkin from'); } diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php index 9e5f322c03..e99bfe298f 100644 --- a/app/Http/Controllers/Users/UserFilesController.php +++ b/app/Http/Controllers/Users/UserFilesController.php @@ -7,9 +7,6 @@ use App\Http\Controllers\Controller; use App\Http\Requests\UploadFileRequest; use App\Models\Actionlog; use App\Models\User; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Input; -use Illuminate\Support\Facades\Response; use Symfony\Component\HttpFoundation\JsonResponse; use Illuminate\Support\Facades\Storage; @@ -116,31 +113,30 @@ 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)) { + if ($user = User::find($userId)) { $this->authorize('view', $user); if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) { + $file = 'private_uploads/users/'.$log->filename; - // Display the file inline - if (request('inline') == 'true') { - $headers = [ - 'Content-Disposition' => 'inline', - ]; - return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers); + try { + return StorageHelper::showOrDownloadFile($file, $log->filename); + } catch (\Exception $e) { + return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.file_not_found')); } - - return Storage::download('private_uploads/users/'.$log->filename); } - return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found')); + // The log record doesn't exist somehow + return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.log_record_not_found')); + + + return redirect()->back()->with('error', trans('general.file_not_found')); } // Redirect to the user management page if the user doesn't exist diff --git a/app/Http/Controllers/Users/UsersController.php b/app/Http/Controllers/Users/UsersController.php index 051db1f4ef..397bfd16d8 100755 --- a/app/Http/Controllers/Users/UsersController.php +++ b/app/Http/Controllers/Users/UsersController.php @@ -288,33 +288,31 @@ class UsersController extends Controller $user->password = bcrypt($request->input('password')); } - // Update the location of any assets checked out to this user Asset::where('assigned_type', User::class) ->where('assigned_to', $user->id) ->update(['location_id' => $user->location_id]); - $permissions_array = $request->input('permission'); + $permissions_array = $request->input('permission'); + // Strip out the superuser permission if the user isn't a superadmin + if (! auth()->user()->isSuperUser()) { + unset($permissions_array['superuser']); + $permissions_array['superuser'] = $orig_superuser; + } - // Strip out the superuser permission if the user isn't a superadmin - if (! auth()->user()->isSuperUser()) { - unset($permissions_array['superuser']); - $permissions_array['superuser'] = $orig_superuser; - } + $user->permissions = json_encode($permissions_array); - $user->permissions = json_encode($permissions_array); + // Handle uploaded avatar + app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar'); + session()->put(['redirect_option' => $request->get('redirect_option')]); - // Handle uploaded avatar - app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar'); - session()->put(['redirect_option' => $request->get('redirect_option')]); - - if ($user->save()) { - // Redirect to the user page - return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users')) - ->with('success', trans('admin/users/message.success.update')); - } - return redirect()->back()->withInput()->withErrors($user->getErrors()); + if ($user->save()) { + // Redirect to the user page + return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users')) + ->with('success', trans('admin/users/message.success.update')); + } + return redirect()->back()->withInput()->withErrors($user->getErrors()); } /** diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index db4afc8322..12c300e5bd 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -13,6 +13,7 @@ use App\Notifications\RequestAssetNotification; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use \Illuminate\Contracts\View\View; +use Log; /** * This controller handles all actions related to the ability for users @@ -179,8 +180,11 @@ class ViewAssetsController extends Controller $asset->decrement('requests_counter', 1); $logaction->logaction('request canceled'); - $settings->notify(new RequestAssetCancelation($data)); - + try { + $settings->notify(new RequestAssetCancelation($data)); + } catch (\Exception $e) { + Log::warning($e); + } return redirect()->route('requestable-assets') ->with('success')->with('success', trans('admin/hardware/message.requests.canceled')); } @@ -188,7 +192,11 @@ class ViewAssetsController extends Controller $logaction->logaction('requested'); $asset->request(); $asset->increment('requests_counter', 1); - $settings->notify(new RequestAssetNotification($data)); + try { + $settings->notify(new RequestAssetNotification($data)); + } catch (\Exception $e) { + Log::warning($e); + } return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success')); } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 8c9289a799..b69e22e4f9 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -53,6 +53,10 @@ class Kernel extends HttpKernel \App\Http\Middleware\CheckLocale::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], + + 'health' => [ + + ], ]; /** @@ -69,5 +73,6 @@ class Kernel extends HttpKernel 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'health' => null, ]; } diff --git a/app/Http/Middleware/CheckForSetup.php b/app/Http/Middleware/CheckForSetup.php index 4e399ffcff..6cb593b5ff 100644 --- a/app/Http/Middleware/CheckForSetup.php +++ b/app/Http/Middleware/CheckForSetup.php @@ -7,14 +7,19 @@ use Closure; class CheckForSetup { + + protected $except = [ + '_debugbar*', + 'health' + ]; + public function handle($request, Closure $next, $guard = null) { /** - * This is dumb - * @todo Check on removing this, not sure if it's still needed + * Skip this middleware for the debugbar and health check */ - if ($request->is('_debugbar*')) { + if ($request->is($this->except)) { return $next($request); } @@ -25,7 +30,7 @@ class CheckForSetup return $next($request); } } else { - if (! ($request->is('setup*')) && ! ($request->is('.env')) && ! ($request->is('health'))) { + if (! ($request->is('setup*')) && ! ($request->is('.env'))) { return redirect(config('app.url').'/setup'); } diff --git a/app/Http/Requests/AssetCheckinRequest.php b/app/Http/Requests/AssetCheckinRequest.php index 116b8b39f2..8980cea095 100644 --- a/app/Http/Requests/AssetCheckinRequest.php +++ b/app/Http/Requests/AssetCheckinRequest.php @@ -21,9 +21,14 @@ class AssetCheckinRequest extends Request */ public function rules() { - return [ + $settings = \App\Models\Setting::getSettings(); - ]; + $rules = []; + + if($settings->require_checkinout_notes) { + $rules['note'] = 'string|required'; + } + return $rules; } public function response(array $errors) diff --git a/app/Http/Requests/AssetCheckoutRequest.php b/app/Http/Requests/AssetCheckoutRequest.php index f48a7d5e53..c8245682a2 100644 --- a/app/Http/Requests/AssetCheckoutRequest.php +++ b/app/Http/Requests/AssetCheckoutRequest.php @@ -21,6 +21,8 @@ class AssetCheckoutRequest extends Request */ public function rules() { + $settings = \App\Models\Setting::getSettings(); + $rules = [ 'assigned_user' => 'required_without_all:assigned_asset,assigned_location', 'assigned_asset' => 'required_without_all:assigned_user,assigned_location', @@ -35,7 +37,11 @@ class AssetCheckoutRequest extends Request 'nullable', 'date' ], - ]; + ]; + + if($settings->require_checkinout_notes) { + $rules['note'] = 'required|string'; + } return $rules; } diff --git a/app/Http/Requests/ItemImportRequest.php b/app/Http/Requests/ItemImportRequest.php index a6dc0ad7e5..59afdbe09f 100644 --- a/app/Http/Requests/ItemImportRequest.php +++ b/app/Http/Requests/ItemImportRequest.php @@ -38,10 +38,11 @@ class ItemImportRequest extends FormRequest $filename = config('app.private_uploads').'/imports/'.$import->file_path; $import->import_type = $this->input('import-type'); - $class = title_case($import->import_type); + $class = ucfirst($import->import_type); $classString = "App\\Importer\\{$class}Importer"; $importer = new $classString($filename); $import->field_map = request('column-mappings'); + $import->created_by = auth()->id(); $import->save(); $fieldMappings = []; @@ -60,7 +61,7 @@ class ItemImportRequest extends FormRequest $fieldMappings = array_change_key_case(array_flip($import->field_map), CASE_LOWER); } $importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback']) - ->setUserId(auth()->id()) + ->setCreatedBy(auth()->id()) ->setUpdating($this->get('import-update')) ->setShouldNotify($this->get('send-welcome')) ->setUsernameFormat('firstname.lastname') diff --git a/app/Http/Requests/StoreAssetRequest.php b/app/Http/Requests/StoreAssetRequest.php index e1665e2136..fb7469ac88 100644 --- a/app/Http/Requests/StoreAssetRequest.php +++ b/app/Http/Requests/StoreAssetRequest.php @@ -29,7 +29,8 @@ class StoreAssetRequest extends ImageUploadRequest // Guard against users passing in an array for company_id instead of an integer. // If the company_id is not an integer then we simply use what was // provided to be caught by model level validation later. - $idForCurrentUser = is_int($this->company_id) + // The use of is_numeric accounts for 1 and '1'. + $idForCurrentUser = is_numeric($this->company_id) ? Company::getIdForCurrentUser($this->company_id) : $this->company_id; diff --git a/app/Http/Requests/StoreLabelSettings.php b/app/Http/Requests/StoreLabelSettings.php new file mode 100644 index 0000000000..a203d2702d --- /dev/null +++ b/app/Http/Requests/StoreLabelSettings.php @@ -0,0 +1,41 @@ +|string> + */ + public function rules(): array + { + return [ + 'labels_per_page' => 'numeric', + 'labels_width' => 'numeric', + 'labels_height' => 'numeric', + 'labels_pmargin_left' => 'numeric|nullable', + 'labels_pmargin_right' => 'numeric|nullable', + 'labels_pmargin_top' => 'numeric|nullable', + 'labels_pmargin_bottom' => 'numeric|nullable', + 'labels_display_bgutter' => 'numeric|nullable', + 'labels_display_sgutter' => 'numeric|nullable', + 'labels_fontsize' => 'numeric|min:5', + 'labels_pagewidth' => 'numeric|nullable', + 'labels_pageheight' => 'numeric|nullable', + 'qr_text' => 'max:31|nullable', + ]; + } +} diff --git a/app/Http/Requests/StoreLdapSettings.php b/app/Http/Requests/StoreLdapSettings.php new file mode 100644 index 0000000000..4197145046 --- /dev/null +++ b/app/Http/Requests/StoreLdapSettings.php @@ -0,0 +1,38 @@ +|string> + */ + public function rules(): array + { + return [ + 'ldap_username_field' => 'not_in:sAMAccountName|required_if:ldap_enabled,1', + 'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1', + 'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1', + 'ldap_server' => 'nullable|required_if:ldap_enabled,1|starts_with:ldap://,ldaps://', + 'ldap_uname' => 'nullable|required_if:ldap_enabled,1', + 'ldap_pword' => 'nullable|required_if:ldap_enabled,1', + 'ldap_basedn' => 'nullable|required_if:ldap_enabled,1', + 'ldap_fname_field' => 'nullable|required_if:ldap_enabled,1', + 'custom_forgot_pass_url' => 'nullable|url', + ]; + } + +} diff --git a/app/Http/Requests/StoreLocalizationSettings.php b/app/Http/Requests/StoreLocalizationSettings.php new file mode 100644 index 0000000000..4cea8826e8 --- /dev/null +++ b/app/Http/Requests/StoreLocalizationSettings.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array + { + return [ + 'default_currency' => 'required', + 'locale' => 'required', + ]; + } +} diff --git a/app/Http/Requests/StoreNotificationSettings.php b/app/Http/Requests/StoreNotificationSettings.php new file mode 100644 index 0000000000..13ce5478ed --- /dev/null +++ b/app/Http/Requests/StoreNotificationSettings.php @@ -0,0 +1,37 @@ +|string> + */ + public function rules(): array + { + return [ + 'alert_email' => 'email_array|nullable', + 'admin_cc_email' => 'email_array|nullable', + 'alert_threshold' => 'numeric|nullable|gt:0', + 'alert_interval' => 'numeric|nullable|gt:0', + 'audit_warning_days' => 'numeric|nullable|gt:0', + 'due_checkin_days' => 'numeric|nullable|gt:0', + 'audit_interval' => 'numeric|nullable|gt:0', + ]; + } + +} diff --git a/app/Http/Requests/StoreSecuritySettings.php b/app/Http/Requests/StoreSecuritySettings.php new file mode 100644 index 0000000000..42a529aa57 --- /dev/null +++ b/app/Http/Requests/StoreSecuritySettings.php @@ -0,0 +1,35 @@ +|string> + */ + public function rules(): array + { + return [ + 'pwd_secure_min' => 'numeric|required|min:8', + 'custom_forgot_pass_url' => 'url|nullable', + 'privacy_policy_link' => 'nullable|url', + 'login_remote_user_enabled' => 'numeric|nullable', + 'login_common_disabled' => 'numeric|nullable', + 'login_remote_user_custom_logout_url' => 'string|nullable', + 'login_remote_user_header_name' => 'string|nullable', + ]; + } +} diff --git a/app/Http/Requests/Traits/MayContainCustomFields.php b/app/Http/Requests/Traits/MayContainCustomFields.php index 9a7f85e3a2..bbdf62893d 100644 --- a/app/Http/Requests/Traits/MayContainCustomFields.php +++ b/app/Http/Requests/Traits/MayContainCustomFields.php @@ -23,7 +23,7 @@ trait MayContainCustomFields return str_starts_with($attributes, '_snipeit_'); }); // if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag - if (count($request_fields) > 0) { + if (count($request_fields) > 0 && $validator->errors()->isEmpty()) { $request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column')) ->each(function ($request_field_name) use ($request_fields, $validator) { if (CustomField::where('db_column', $request_field_name)->exists()) { diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index d0605c747b..4e6341c8f3 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -141,6 +141,8 @@ class ActionlogsTransformer if ($actionlog->item) { if ($actionlog->itemType() == 'asset') { $file_url = route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]); + } elseif ($actionlog->itemType() == 'accessory') { + $file_url = route('show.accessoryfile', ['accessoryId' => $actionlog->item->id, 'fileId' => $actionlog->id]); } elseif ($actionlog->itemType() == 'license') { $file_url = route('show.licensefile', ['licenseId' => $actionlog->item->id, 'fileId' => $actionlog->id]); } elseif ($actionlog->itemType() == 'user') { @@ -158,7 +160,6 @@ class ActionlogsTransformer [ 'url' => $file_url, 'filename' => $actionlog->filename, - 'inlineable' => (bool) Helper::show_file_inline($actionlog->filename), ] : null, 'item' => ($actionlog->item) ? [ @@ -346,4 +347,4 @@ class ActionlogsTransformer -} \ No newline at end of file +} diff --git a/app/Http/Transformers/AssetMaintenancesTransformer.php b/app/Http/Transformers/AssetMaintenancesTransformer.php index c5f0abbaab..81b4a9eabb 100644 --- a/app/Http/Transformers/AssetMaintenancesTransformer.php +++ b/app/Http/Transformers/AssetMaintenancesTransformer.php @@ -66,7 +66,7 @@ class AssetMaintenancesTransformer 'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'date'), 'user_id' => ($assetmaintenance->adminuser) ? [ 'id' => $assetmaintenance->adminuser->id, - 'name'=> e($assetmaintenance->admin->getFullNameAttribute()) + 'name'=> e($assetmaintenance->adminuser->present()->fullName()) ] : null, // legacy to not change the shape of the API 'created_by' => ($assetmaintenance->adminuser) ? [ 'id' => (int) $assetmaintenance->adminuser->id, diff --git a/app/Http/Transformers/AssetModelsTransformer.php b/app/Http/Transformers/AssetModelsTransformer.php index a7b1c6a49f..dab21d9773 100644 --- a/app/Http/Transformers/AssetModelsTransformer.php +++ b/app/Http/Transformers/AssetModelsTransformer.php @@ -65,6 +65,10 @@ class AssetModelsTransformer 'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None', 'requestable' => ($assetmodel->requestable == '1') ? true : false, 'notes' => Helper::parseEscapedMarkedownInline($assetmodel->notes), + 'created_by' => ($assetmodel->adminuser) ? [ + 'id' => (int) $assetmodel->adminuser->id, + 'name'=> e($assetmodel->adminuser->present()->fullName()), + ] : null, '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/ComponentsTransformer.php b/app/Http/Transformers/ComponentsTransformer.php index 70572c9494..f98edd6e3f 100644 --- a/app/Http/Transformers/ComponentsTransformer.php +++ b/app/Http/Transformers/ComponentsTransformer.php @@ -38,6 +38,8 @@ class ComponentsTransformer 'name' => e($component->category->name), ] : null, 'supplier' => ($component->supplier) ? ['id' => $component->supplier->id, 'name'=> e($component->supplier->name)] : null, + 'manufacturer' => ($component->manufacturer) ? ['id' => $component->manufacturer->id, 'name'=> e($component->manufacturer->name)] : null, + 'model_number' => ($component->model_number) ? e($component->model_number) : null, 'order_number' => e($component->order_number), 'purchase_date' => Helper::getFormattedDateObject($component->purchase_date, 'date'), 'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost), diff --git a/app/Http/Transformers/DepreciationReportTransformer.php b/app/Http/Transformers/DepreciationReportTransformer.php index 47d1cbc476..33d9a1f5fd 100644 --- a/app/Http/Transformers/DepreciationReportTransformer.php +++ b/app/Http/Transformers/DepreciationReportTransformer.php @@ -63,15 +63,12 @@ class DepreciationReportTransformer */ if (($asset->model) && ($asset->model->depreciation)) { $depreciated_value = Helper::formatCurrencyOutput($asset->getDepreciatedValue()); - if($asset->model->eol==0 || $asset->model->eol==null ){ - $monthly_depreciation = Helper::formatCurrencyOutput($asset->purchase_cost / $asset->model->depreciation->months); - } - else { - $monthly_depreciation = Helper::formatCurrencyOutput(($asset->model->eol > 0 ? ($asset->purchase_cost / $asset->model->eol) : 0)); - } + $monthly_depreciation =Helper::formatCurrencyOutput($asset->purchase_cost / $asset->model->depreciation->months); $diff = Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue())); } - + else if($asset->model->eol !== null) { + $monthly_depreciation = Helper::formatCurrencyOutput(($asset->model->eol > 0 ? ($asset->purchase_cost / $asset->model->eol) : 0)); + } if ($asset->assigned) { $checkout_target = $asset->assigned->name; diff --git a/app/Http/Transformers/ManufacturersTransformer.php b/app/Http/Transformers/ManufacturersTransformer.php index e08aaa7436..d6954c1d24 100644 --- a/app/Http/Transformers/ManufacturersTransformer.php +++ b/app/Http/Transformers/ManufacturersTransformer.php @@ -36,6 +36,7 @@ class ManufacturersTransformer 'licenses_count' => (int) $manufacturer->licenses_count, 'consumables_count' => (int) $manufacturer->consumables_count, 'accessories_count' => (int) $manufacturer->accessories_count, + 'components_count' => (int) $manufacturer->components_count, 'created_by' => ($manufacturer->adminuser) ? [ 'id' => (int) $manufacturer->adminuser->id, 'name'=> e($manufacturer->adminuser->present()->fullName()), diff --git a/app/Importer/AccessoryImporter.php b/app/Importer/AccessoryImporter.php index eb17c5acad..bc9c1909ff 100644 --- a/app/Importer/AccessoryImporter.php +++ b/app/Importer/AccessoryImporter.php @@ -42,6 +42,7 @@ class AccessoryImporter extends ItemImporter } $this->log('No Matching Accessory, Creating a new one'); $accessory = new Accessory(); + $accessory->created_by = auth()->id(); $this->item['model_number'] = $this->findCsvMatch($row, "model_number"); $this->item['min_amt'] = $this->findCsvMatch($row, "min_amt"); $accessory->fill($this->sanitizeItemForStoring($accessory)); diff --git a/app/Importer/AssetModelImporter.php b/app/Importer/AssetModelImporter.php new file mode 100644 index 0000000000..7cfd8a530d --- /dev/null +++ b/app/Importer/AssetModelImporter.php @@ -0,0 +1,174 @@ +createAssetModelIfNotExists($row); + } + + /** + * Create a model if a duplicate does not exist. + * @todo Investigate how this should interact with Importer::createModelIfNotExists + * + * @author A. Gianotto + * @since 6.1.0 + * @param array $row + */ + public function createAssetModelIfNotExists(array $row) + { + + $editingAssetModel = false; + $assetModel = AssetModel::where('name', '=', $this->findCsvMatch($row, 'name'))->first(); + + if ($assetModel) { + if (! $this->updating) { + $this->log('A matching Model '.$this->item['name'].' already exists'); + return; + } + + $this->log('Updating Model'); + $editingAssetModel = true; + } else { + $this->log('No Matching Model, Create a new one'); + $assetModel = new AssetModel(); + } + + // Pull the records from the CSV to determine their values + $this->item['name'] = trim($this->findCsvMatch($row, 'name')); + $this->item['category'] = trim($this->findCsvMatch($row, 'category')); + $this->item['manufacturer'] = trim($this->findCsvMatch($row, 'manufacturer')); + $this->item['min_amt'] = trim($this->findCsvMatch($row, 'min_amt')); + $this->item['model_number'] = trim($this->findCsvMatch($row, 'model_number')); + $this->item['eol'] = trim($this->findCsvMatch($row, 'eol')); + $this->item['notes'] = trim($this->findCsvMatch($row, 'notes')); + $this->item['fieldset'] = trim($this->findCsvMatch($row, 'fieldset')); + $this->item['depreciation'] = trim($this->findCsvMatch($row, 'depreciation')); + $this->item['requestable'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'))) == 1) ? 1 : 0; + + if (!empty($this->item['category'])) { + if ($category = $this->createOrFetchCategory($this->item['category'])) { + $this->item['category_id'] = $category; + } + } + if (!empty($this->item['manufacturer'])) { + if ($manufacturer = $this->createOrFetchManufacturer($this->item['manufacturer'])) { + $this->item['manufacturer_id'] = $manufacturer; + } + } + + if (!empty($this->item['depreciation'])) { + if ($depreciation = $this->fetchDepreciation($this->item['depreciation'])) { + $this->item['depreciation_id'] = $depreciation; + } + } + + if (!empty($this->item['fieldset'])) { + if ($fieldset = $this->createOrFetchCustomFieldset($this->item['fieldset'])) { + $this->item['fieldset_id'] = $fieldset; + } + } + + Log::debug('Item array is: '); + Log::debug(print_r($this->item, true)); + + + if ($editingAssetModel) { + Log::debug('Updating existing model'); + $assetModel->update($this->sanitizeItemForUpdating($assetModel)); + } else { + Log::debug('Creating model'); + $assetModel->fill($this->sanitizeItemForStoring($assetModel)); + $assetModel->created_by = auth()->id(); + } + + if ($assetModel->save()) { + $this->log('AssetModel '.$assetModel->name.' created or updated from CSV import'); + return $assetModel; + + } else { + $this->log($assetModel->getErrors()->first()); + $this->addErrorToBag($assetModel, $assetModel->getErrors()->keys()[0], $assetModel->getErrors()->first()); + return $assetModel->getErrors(); + } + + } + + + /** + * Fetch an existing depreciation, or create new if it doesn't exist. + * + * We only do a fetch vs create here since Depreciations have additional fields required + * and cannot be created without them (months, for example.)) + * + * @author A. Gianotto + * @since 7.1.3 + * @param $depreciation_name string + * @return int id of depreciation created/found + */ + public function fetchDepreciation($depreciation_name) : ?int + { + if ($depreciation_name != '') { + + if ($depreciation = Depreciation::where('name', '=', $depreciation_name)->first()) { + $this->log('A matching Depreciation '.$depreciation_name.' already exists'); + return $depreciation->id; + } + } + + return null; + } + + /** + * Fetch an existing fieldset, or create new if it doesn't exist + * + * @author A. Gianotto + * @since 7.1.3 + * @param $fieldset_name string + * @return int id of fieldset created/found + */ + public function createOrFetchCustomFieldset($fieldset_name) : ?int + { + if ($fieldset_name != '') { + $fieldset = CustomFieldset::where('name', '=', $fieldset_name)->first(); + + if ($fieldset) { + $this->log('A matching fieldset '.$fieldset_name.' already exists'); + return $fieldset->id; + } + + $fieldset = new CustomFieldset(); + $fieldset->name = $fieldset_name; + + if ($fieldset->save()) { + $this->log('Fieldset '.$fieldset_name.' was created'); + + return $fieldset->id; + } + $this->logError($fieldset, 'Fieldset'); + } + + return null; + } +} \ No newline at end of file diff --git a/app/Importer/ComponentImporter.php b/app/Importer/ComponentImporter.php index 9687ec4f17..3979ba499d 100644 --- a/app/Importer/ComponentImporter.php +++ b/app/Importer/ComponentImporter.php @@ -47,6 +47,7 @@ class ComponentImporter extends ItemImporter } $this->log('No matching component, creating one'); $component = new Component; + $component->created_by = auth()->id(); $component->fill($this->sanitizeItemForStoring($component)); // This sets an attribute on the Loggable trait for the action log @@ -58,7 +59,7 @@ class ComponentImporter extends ItemImporter if (isset($this->item['asset_tag']) && ($asset = Asset::where('asset_tag', $this->item['asset_tag'])->first())) { $component->assets()->attach($component->id, [ 'component_id' => $component->id, - 'created_by' => $this->created_by, + 'created_by' => auth()->id(), 'created_at' => date('Y-m-d H:i:s'), 'assigned_qty' => 1, // Only assign the first one to the asset 'asset_id' => $asset->id, diff --git a/app/Importer/ConsumableImporter.php b/app/Importer/ConsumableImporter.php index 9e7019b086..10ffaaedfb 100644 --- a/app/Importer/ConsumableImporter.php +++ b/app/Importer/ConsumableImporter.php @@ -41,6 +41,7 @@ class ConsumableImporter extends ItemImporter } $this->log('No matching consumable, creating one'); $consumable = new Consumable(); + $consumable->created_by = auth()->id(); $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")); diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php index 678fb9ecb2..907c8b72c5 100644 --- a/app/Importer/Importer.php +++ b/app/Importer/Importer.php @@ -21,7 +21,6 @@ abstract class Importer * Id of User performing import * @var */ - protected $created_by; /** * Are we updating items in the import @@ -149,21 +148,28 @@ abstract class Importer { $headerRow = $this->csv->fetchOne(); $this->csv->setHeaderOffset(0); //explicitly sets the CSV document header record - $results = $this->normalizeInputArray($this->csv->getRecords($headerRow)); $this->populateCustomFields($headerRow); - DB::transaction(function () use (&$results) { + DB::transaction(function () use ($headerRow) { + $importedItemsCount = 0; Model::unguard(); - $resultsCount = count($results); - foreach ($results as $row) { + + foreach ($this->csv->getRecords($headerRow) as $row) { + //Lowercase header values to ensure we're comparing values properly. + $row = array_change_key_case($row, CASE_LOWER); + $this->handle($row); + + $importedItemsCount++; + if ($this->progressCallback) { - call_user_func($this->progressCallback, $resultsCount); + call_user_func($this->progressCallback, $importedItemsCount); } $this->log('------------- Action Summary ----------------'); } + Model::reguard(); }); } @@ -236,22 +242,6 @@ abstract class Importer return $key; } - /** - * Used to lowercase header values to ensure we're comparing values properly. - * - * @param $results - * @return array - */ - public function normalizeInputArray($results) - { - $newArray = []; - foreach ($results as $index => $arrayToNormalize) { - $newArray[$index] = array_change_key_case($arrayToNormalize); - } - - return $newArray; - } - /** * Figure out the fieldname of the custom field * @@ -373,6 +363,7 @@ abstract class Importer // No luck finding a user on username or first name, let's create one. $user = new User; + $user->first_name = $user_array['first_name']; $user->last_name = $user_array['last_name']; $user->username = $user_array['username']; @@ -416,7 +407,7 @@ abstract class Importer * * @return self */ - public function setUserId($created_by) + public function setCreatedBy($created_by) { $this->created_by = $created_by; @@ -502,6 +493,16 @@ abstract class Importer public function fetchHumanBoolean($value) { + $true = [ + 'yes', + 'y', + 'true', + ]; + + if (in_array(strtolower($value), $true)) { + return 1; + } + return (int) filter_var($value, FILTER_VALIDATE_BOOLEAN); } @@ -538,6 +539,7 @@ abstract class Importer return null; } + /** * Fetch an existing manager * diff --git a/app/Importer/ItemImporter.php b/app/Importer/ItemImporter.php index 360618f4f0..6b1d647136 100644 --- a/app/Importer/ItemImporter.php +++ b/app/Importer/ItemImporter.php @@ -94,7 +94,7 @@ class ItemImporter extends Importer $this->item['qty'] = $this->findCsvMatch($row, 'quantity'); $this->item['requestable'] = $this->findCsvMatch($row, 'requestable'); - $this->item['created_by'] = $this->created_by; + $this->item['created_by'] = auth()->id(); $this->item['serial'] = $this->findCsvMatch($row, 'serial'); // NO need to call this method if we're running the user import. // TODO: Merge these methods. @@ -113,7 +113,7 @@ class ItemImporter extends Importer protected function determineCheckout($row) { // Locations don't get checked out to anyone/anything - if (get_class($this) == LocationImporter::class) { + if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class)) { return; } @@ -249,6 +249,7 @@ class ItemImporter extends Importer $this->log('No Matching Model, Creating a new one'); $asset_model = new AssetModel(); + $asset_model->created_by = auth()->id(); $item = $this->sanitizeItemForStoring($asset_model, $editingModel); $item['name'] = $asset_model_name; $item['model_number'] = $asset_modelNumber; @@ -256,11 +257,8 @@ class ItemImporter extends Importer $item['category_id'] = $this->createOrFetchCategory($asset_model_category); $asset_model->fill($item); - //$asset_model = AssetModel::firstOrNew($item); $item = null; - - if ($asset_model->save()) { $this->log('Asset Model '.$asset_model_name.' with model number '.$asset_modelNumber.' was created'); @@ -287,21 +285,28 @@ class ItemImporter extends Importer $classname = class_basename(get_class($this)); $item_type = strtolower(substr($classname, 0, strpos($classname, 'Importer'))); + // If we're importing asset models only (without attached assets), override the category type to asset + if ($item_type == 'assetmodel') { + $item_type = 'asset'; + } + if (empty($asset_category)) { $asset_category = 'Unnamed Category'; } + + $category = Category::where(['name' => $asset_category, 'category_type' => $item_type])->first(); - if ($category) { - $this->log('A matching category: '.$asset_category.' already exists'); + if ($category) { + $this->log('A matching category: '.$category->name.' already exists'); return $category->id; } $category = new Category(); + $category->created_by = auth()->id(); $category->name = $asset_category; $category->category_type = $item_type; - $category->created_by = $this->created_by; if ($category->save()) { $this->log('Category '.$asset_category.' was created'); @@ -330,6 +335,7 @@ class ItemImporter extends Importer return $company->id; } $company = new Company(); + $company->created_by = auth()->id(); $company->name = $asset_company_name; if ($company->save()) { @@ -386,6 +392,7 @@ class ItemImporter extends Importer } $this->log('Creating a new status'); $status = new Statuslabel(); + $status->created_by = auth()->id(); $status->name = trim($asset_statuslabel_name); $status->deployable = 1; @@ -425,7 +432,7 @@ class ItemImporter extends Importer //Otherwise create a manufacturer. $manufacturer = new Manufacturer(); $manufacturer->name = trim($item_manufacturer); - $manufacturer->created_by = $this->created_by; + $manufacturer->created_by = auth()->id(); if ($manufacturer->save()) { $this->log('Manufacturer '.$manufacturer->name.' was created'); @@ -466,7 +473,7 @@ class ItemImporter extends Importer $location->city = ''; $location->state = ''; $location->country = ''; - $location->created_by = $this->created_by; + $location->created_by = auth()->id(); if ($location->save()) { $this->log('Location '.$asset_location.' was created'); @@ -502,7 +509,7 @@ class ItemImporter extends Importer $supplier = new Supplier(); $supplier->name = $item_supplier; - $supplier->created_by = $this->created_by; + $supplier->created_by = auth()->id(); if ($supplier->save()) { $this->log('Supplier '.$item_supplier.' was created'); diff --git a/app/Importer/LicenseImporter.php b/app/Importer/LicenseImporter.php index 3f7bb9f85c..0dc7475478 100644 --- a/app/Importer/LicenseImporter.php +++ b/app/Importer/LicenseImporter.php @@ -84,6 +84,7 @@ class LicenseImporter extends ItemImporter $license->update($this->sanitizeItemForUpdating($license)); } else { $license->fill($this->sanitizeItemForStoring($license)); + $license->created_by = auth()->id(); } // This sets an attribute on the Loggable trait for the action log diff --git a/app/Importer/LocationImporter.php b/app/Importer/LocationImporter.php index b3ef59d248..a9a5152234 100644 --- a/app/Importer/LocationImporter.php +++ b/app/Importer/LocationImporter.php @@ -51,6 +51,7 @@ class LocationImporter extends ItemImporter } else { $this->log('No Matching Location, Create a new one'); $location = new Location; + $location->created_by = auth()->id(); } // Pull the records from the CSV to determine their values @@ -65,7 +66,6 @@ class LocationImporter extends ItemImporter $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['created_by'] = auth()->id(); if ($this->findCsvMatch($row, 'parent_location')) { $this->item['parent_id'] = $this->createOrFetchLocation(trim($this->findCsvMatch($row, 'parent_location'))); diff --git a/app/Importer/UserImporter.php b/app/Importer/UserImporter.php index 036bf15c9a..77317b3d09 100644 --- a/app/Importer/UserImporter.php +++ b/app/Importer/UserImporter.php @@ -114,6 +114,7 @@ class UserImporter extends ItemImporter $this->log('No matching user, creating one'); $user = new User(); + $user->created_by = auth()->id(); $user->fill($this->sanitizeItemForStoring($user)); if ($user->save()) { diff --git a/app/Listeners/CheckoutableListener.php b/app/Listeners/CheckoutableListener.php index 5968632fdc..8e9c6c2565 100644 --- a/app/Listeners/CheckoutableListener.php +++ b/app/Listeners/CheckoutableListener.php @@ -3,14 +3,22 @@ namespace App\Listeners; use App\Events\CheckoutableCheckedOut; +use App\Mail\CheckinAccessoryMail; +use App\Mail\CheckinLicenseMail; +use App\Mail\CheckoutAccessoryMail; +use App\Mail\CheckoutAssetMail; +use App\Mail\CheckinAssetMail; +use App\Mail\CheckoutConsumableMail; +use App\Mail\CheckoutLicenseMail; use App\Models\Accessory; use App\Models\Asset; use App\Models\CheckoutAcceptance; use App\Models\Component; use App\Models\Consumable; use App\Models\LicenseSeat; -use App\Models\Recipients\AdminRecipient; +use App\Models\Location; use App\Models\Setting; +use App\Models\User; use App\Notifications\CheckinAccessoryNotification; use App\Notifications\CheckinAssetNotification; use App\Notifications\CheckinLicenseSeatNotification; @@ -19,9 +27,12 @@ use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseSeatNotification; use GuzzleHttp\Exception\ClientException; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Exception; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; +use Osama\LaravelTeamsNotification\TeamsNotification; class CheckoutableListener { @@ -42,32 +53,57 @@ class CheckoutableListener /** * Make a checkout acceptance and attach it in the notification */ + $settings = Setting::getSettings(); $acceptance = $this->getCheckoutAcceptance($event); + $adminCcEmailsArray = []; + if($settings->admin_cc_email !== '') { + $adminCcEmail = $settings->admin_cc_email; + $adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail)); + } + $ccEmails = array_filter($adminCcEmailsArray); + $mailable = $this->getCheckoutMailType($event, $acceptance); + $notifiable = $this->getNotifiables($event); + + if (!$event->checkedOutTo->locale){ + $mailable->locale($event->checkedOutTo->locale); + } + // Send email notifications try { - if (! $event->checkedOutTo->locale) { - Notification::locale(Setting::getSettings()->locale)->send( - $this->getNotifiables($event), - $this->getCheckoutNotification($event, $acceptance) - ); - } else { - Notification::send( - $this->getNotifiables($event), - $this->getCheckoutNotification($event, $acceptance) - ); - } + /** + * Send an email if any of the following conditions are met: + * 1. The asset requires acceptance + * 2. The item has a EULA + * 3. The item should send an email at check-in/check-out + */ - if ($this->shouldSendWebhookNotification()) { - - //slack doesn't include the url in its messaging format so this is needed to hit the endpoint - - if(Setting::getSettings()->webhook_selected =='slack' || Setting::getSettings()->webhook_selected =='general') { - - - Notification::route('slack', Setting::getSettings()->webhook_endpoint) - ->notify($this->getCheckoutNotification($event)); - } - } + if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() || + $this->checkoutableShouldSendEmail($event)) { + Log::info('Sending checkout email, Locale: ' . ($event->checkedOutTo->locale ?? 'default')); + if (!empty($notifiable)) { + Mail::to($notifiable)->cc($ccEmails)->send($mailable); + } elseif (!empty($ccEmails)) { + Mail::cc($ccEmails)->send($mailable); + } + Log::info('Checkout Mail sent.'); + } + } catch (ClientException $e) { + Log::debug("Exception caught during checkout email: " . $e->getMessage()); + } catch (Exception $e) { + Log::debug("Exception caught during checkout email: " . $e->getMessage()); + } +// Send Webhook notification + try{ + if ($this->shouldSendWebhookNotification()) { + if ($this->newMicrosoftTeamsWebhookEnabled()) { + $message = $this->getCheckoutNotification($event)->toMicrosoftTeams(); + $notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint); + $notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams + } else { + Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint) + ->notify($this->getCheckoutNotification($event, $acceptance)); + } + } } catch (ClientException $e) { Log::debug("Exception caught during checkout notification: " . $e->getMessage()); } catch (Exception $e) { @@ -75,6 +111,7 @@ class CheckoutableListener } } + /** * Notify the user and post to webhook about the checked in checkoutable */ @@ -100,31 +137,57 @@ class CheckoutableListener } } } + $settings = Setting::getSettings(); + $adminCcEmailsArray = []; + if($settings->admin_cc_email !== '') { + $adminCcEmail = $settings->admin_cc_email; + $adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail)); + } + $ccEmails = array_filter($adminCcEmailsArray); + $mailable = $this->getCheckinMailType($event); + $notifiable = $this->getNotifiables($event); + if (!$event->checkedOutTo->locale){ + $mailable->locale($event->checkedOutTo->locale); + } + // Send email notifications try { - // Use default locale - if (! $event->checkedOutTo->locale) { - Notification::locale(Setting::getSettings()->locale)->send( - $this->getNotifiables($event), - $this->getCheckinNotification($event) - ); - } else { - Notification::send( - $this->getNotifiables($event), - $this->getCheckinNotification($event) - ); - } - //slack doesn't include the url in its messaging format so this is needed to hit the endpoint - if(Setting::getSettings()->webhook_selected =='slack' || Setting::getSettings()->webhook_selected =='general') { - - if ($this->shouldSendWebhookNotification()) { - Notification::route('slack', Setting::getSettings()->webhook_endpoint) - ->notify($this->getCheckinNotification($event)); + /** + * Send an email if any of the following conditions are met: + * 1. The asset requires acceptance + * 2. The item has a EULA + * 3. The item should send an email at check-in/check-out + */ + if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() || + $this->checkoutableShouldSendEmail($event)) { + Log::info('Sending checkin email, Locale: ' . ($event->checkedOutTo->locale ?? 'default')); + if (!empty($notifiable)) { + Mail::to($notifiable)->cc($ccEmails)->send($mailable); + } elseif (!empty($ccEmails)){ + Mail::cc($ccEmails)->send($mailable); + } + Log::info('Checkin Mail sent.'); } - } - } catch (ClientException $e) { - Log::warning("Exception caught during checkout notification: " . $e->getMessage()); + Log::debug("Exception caught during checkin email: " . $e->getMessage()); + } catch (Exception $e) { + Log::debug("Exception caught during checkin email: " . $e->getMessage()); + } + + // Send Webhook notification + try { + if ($this->shouldSendWebhookNotification()) { + if ($this->newMicrosoftTeamsWebhookEnabled()) { + $message = $this->getCheckinNotification($event)->toMicrosoftTeams(); + $notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint); + $notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams + } else { + Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint) + ->notify($this->getCheckinNotification($event)); + } + } + } catch (ClientException $e) { + Log::warning("Exception caught during checkin notification: " . $e->getMessage()); } catch (Exception $e) { Log::warning("Exception caught during checkin notification: " . $e->getMessage()); } @@ -153,33 +216,6 @@ class CheckoutableListener return $acceptance; } - /** - * Gets the entities to be notified of the passed event - * - * @param Event $event - * @return Collection - */ - private function getNotifiables($event) - { - $notifiables = collect(); - - /** - * Notify who checked out the item as long as the model can route notifications - */ - if (method_exists($event->checkedOutTo, 'routeNotificationFor')) { - $notifiables->push($event->checkedOutTo); - } - - /** - * Notify Admin users if the settings is activated - */ - if ((Setting::getSettings()) && (Setting::getSettings()->admin_cc_email != '')) { - $notifiables->push(new AdminRecipient()); - } - - return $notifiables; - } - /** * Get the appropriate notification for the event * @@ -228,7 +264,7 @@ class CheckoutableListener break; case Consumable::class: $notificationClass = CheckoutConsumableNotification::class; - break; + break; case LicenseSeat::class: $notificationClass = CheckoutLicenseSeatNotification::class; break; @@ -237,6 +273,50 @@ class CheckoutableListener return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note); } + private function getCheckoutMailType($event, $acceptance){ + $lookup = [ + Accessory::class => CheckoutAccessoryMail::class, + Asset::class => CheckoutAssetMail::class, + LicenseSeat::class => CheckoutLicenseMail::class, + Consumable::class => CheckoutConsumableMail::class, + ]; + $mailable= $lookup[get_class($event->checkoutable)]; + + return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note); + + } + private function getCheckinMailType($event){ + $lookup = [ + Accessory::class => CheckinAccessoryMail::class, + Asset::class => CheckinAssetMail::class, + LicenseSeat::class => CheckinLicenseMail::class, + ]; + + $mailable= $lookup[get_class($event->checkoutable)]; + + return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note); + + } + private function getNotifiables($event){ + + if($event->checkedOutTo instanceof Asset){ + $event->checkedOutTo->load('assignedTo'); + return $event->checkedOutTo->assignedto?->email ?? ''; + } + else if($event->checkedOutTo instanceof Location) { + return $event->checkedOutTo->manager?->email ?? ''; + } + else{ + return $event->checkedOutTo?->email ?? ''; + } + } + private function webhookSelected(){ + if(Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general'){ + return 'slack'; + } + + return Setting::getSettings()->webhook_selected; + } /** * Register the listeners for the subscriber. @@ -265,4 +345,17 @@ class CheckoutableListener { return Setting::getSettings() && Setting::getSettings()->webhook_endpoint; } + + private function checkoutableShouldSendEmail($event): bool + { + if($event->checkoutable instanceof LicenseSeat){ + return $event->checkoutable->license->checkin_email(); + } + return (method_exists($event->checkoutable, 'checkin_email') && $event->checkoutable->checkin_email()); + } + + private function newMicrosoftTeamsWebhookEnabled(): bool + { + return Setting::getSettings()->webhook_selected === 'microsoft' && Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'); + } } diff --git a/app/Livewire/CustomFieldSetDefaultValuesForModel.php b/app/Livewire/CustomFieldSetDefaultValuesForModel.php index 0ca733eb24..a5f179b1cb 100644 --- a/app/Livewire/CustomFieldSetDefaultValuesForModel.php +++ b/app/Livewire/CustomFieldSetDefaultValuesForModel.php @@ -24,7 +24,15 @@ class CustomFieldSetDefaultValuesForModel extends Component $this->fieldset_id = $this->model?->fieldset_id; $this->add_default_values = ($this->model?->defaultValues->count() > 0); + $this->initializeSelectedValuesArray(); + if (session()->has('errors')) { + $errors = session('errors')->keys(); + $selectedValuesKeys = array_keys($this->selectedValues); + if (count(array_intersect($selectedValuesKeys, $errors)) > 0) { + $this->add_default_values = true; + }; + } $this->populatedSelectedValuesArray(); } @@ -81,6 +89,12 @@ class CustomFieldSetDefaultValuesForModel extends Component { $this->fields->each(function ($field) { $this->selectedValues[$field->db_column] = $this->getSelectedValueForField($field); + + // if the element is a checkbox and the value was just sent to null, make it + // an array since Livewire can't bind to non-array values for checkboxes. + if ($field->element === 'checkbox' && is_null($this->selectedValues[$field->db_column])) { + $this->selectedValues[$field->db_column] = []; + } }); } diff --git a/app/Livewire/Importer.php b/app/Livewire/Importer.php index 3c6f7990ef..75b707b8ad 100644 --- a/app/Livewire/Importer.php +++ b/app/Livewire/Importer.php @@ -73,6 +73,9 @@ class Importer extends Component case 'asset': $results = $this->assets_fields; break; + case 'assetModel': + $results = $this->assetmodels_fields; + break; case 'accessory': $results = $this->accessories_fields; break; @@ -82,6 +85,9 @@ class Importer extends Component case 'component': $results = $this->components_fields; break; + case 'consumable': + $results = $this->consumables_fields; + break; case 'license': $results = $this->licenses_fields; break; @@ -91,10 +97,14 @@ class Importer extends Component case 'location': $results = $this->locations_fields; break; + case 'user': + $results = $this->users_fields; + break; default: $results = []; } asort($results, SORT_FLAG_CASE | SORT_STRING); + if ($type == "asset") { // add Custom Fields after a horizontal line $results['-'] = "———" . trans('admin/custom_fields/general.custom_fields') . "———’"; @@ -107,6 +117,7 @@ class Importer extends Component public function updatingTypeOfImport($type) { + // go through each header, find a matching field to try and map it to. foreach ($this->headerRow as $i => $header) { // do we have something mapped already? @@ -152,13 +163,14 @@ class Importer extends Component { $this->authorize('import'); $this->importTypes = [ - 'asset' => trans('general.assets'), - 'accessory' => trans('general.accessories'), - 'consumable' => trans('general.consumables'), - 'component' => trans('general.components'), - 'license' => trans('general.licenses'), - 'user' => trans('general.users'), - 'location' => trans('general.locations'), + 'accessory' => trans('general.accessories'), + 'asset' => trans('general.assets'), + 'assetModel' => trans('general.asset_models'), + 'component' => trans('general.components'), + 'consumable' => trans('general.consumables'), + 'license' => trans('general.licenses'), + 'location' => trans('general.locations'), + 'user' => trans('general.users'), ]; /** @@ -196,7 +208,6 @@ class Importer extends Component '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'), @@ -332,6 +343,19 @@ class Importer extends Component 'parent_location' => trans('admin/locations/table.parent'), ]; + $this->assetmodels_fields = [ + 'item_name' => trans('general.item_name_var', ['item' => trans('general.asset_model')]), + 'category' => trans('general.category'), + 'manufacturer' => trans('general.manufacturer'), + 'model_number' => trans('general.model_no'), + 'notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]), + 'min_amt' => trans('mail.min_QTY'), + 'fieldset' => trans('admin/models/general.fieldset'), + 'eol' => trans('general.eol'), + 'requestable' => trans('admin/models/general.requestable'), + + ]; + // "real fieldnames" to a list of aliases for that field $this->aliases_fields = [ 'item_name' => @@ -360,6 +384,23 @@ class Importer extends Component 'eol date', 'asset eol date', ], + 'eol' => + [ + 'eol', + 'EOL', + 'eol months', + ], + 'depreciation' => + [ + 'Depreciation', + 'depreciation', + ], + 'requestable' => + [ + 'requestable', + 'Requestable', + ], + 'gravatar' => [ 'gravatar', @@ -504,7 +545,6 @@ class Importer extends Component if (!$this->activeFile) { $this->message = trans('admin/hardware/message.import.file_missing'); $this->message_type = 'danger'; - return; } @@ -519,6 +559,8 @@ class Importer extends Component $this->field_map[] = null; // re-inject the 'nulls' if a file was imported with some 'Do Not Import' settings } } + + $this->file_id = $id; $this->import_errors = null; $this->statusText = null; diff --git a/app/Livewire/SlackSettingsForm.php b/app/Livewire/SlackSettingsForm.php index 45b8b7b41e..f12ef568f0 100644 --- a/app/Livewire/SlackSettingsForm.php +++ b/app/Livewire/SlackSettingsForm.php @@ -4,10 +4,11 @@ namespace App\Livewire; use GuzzleHttp\Client; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Str; use Livewire\Component; use App\Models\Setting; use App\Helpers\Helper; - +use Osama\LaravelTeamsNotification\TeamsNotification; class SlackSettingsForm extends Component { public $webhook_endpoint; @@ -19,6 +20,7 @@ class SlackSettingsForm extends Component public $webhook_placeholder; public $webhook_icon; public $webhook_selected; + public $teams_webhook_deprecated; public array $webhook_text; public Setting $setting; @@ -62,7 +64,7 @@ class SlackSettingsForm extends Component "name" => trans('admin/settings/general.ms_teams'), "icon" => "fa-brands fa-microsoft", "placeholder" => "https://abcd.webhook.office.com/webhookb2/XXXXXXX", - "link" => "https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=dotnet#create-incoming-webhooks-1", + "link" => "https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498", "test" => "msTeamTestWebhook" ), ]; @@ -79,15 +81,17 @@ class SlackSettingsForm extends Component $this->webhook_channel = $this->setting->webhook_channel; $this->webhook_botname = $this->setting->webhook_botname; $this->webhook_options = $this->setting->webhook_selected; - if($this->webhook_selected == 'microsoft' || $this->webhook_selected == 'google'){ + $this->teams_webhook_deprecated = !Str::contains($this->webhook_endpoint, 'workflows'); + if($this->webhook_selected === 'microsoft' || $this->webhook_selected === 'google'){ $this->webhook_channel = '#NA'; } - if($this->setting->webhook_endpoint != null && $this->setting->webhook_channel != null){ $this->isDisabled= ''; } - + if($this->webhook_selected === 'microsoft' && $this->teams_webhook_deprecated) { + session()->flash('warning', 'The selected Microsoft Teams webhook URL will be deprecated Jan 31st, 2025. Please use a workflow URL. Microsofts Documentation on creating a workflow can be found here.'); + } } public function updated($field) { @@ -109,7 +113,11 @@ class SlackSettingsForm extends Component if($this->webhook_selected == 'microsoft' || $this->webhook_selected == 'google'){ $this->webhook_channel = '#NA'; } + } + public function updatedwebhookEndpoint() + { + $this->teams_webhook_deprecated = !Str::contains($this->webhook_endpoint, 'workflows'); } private function isButtonDisabled() { @@ -126,7 +134,9 @@ class SlackSettingsForm extends Component public function render() { $this->isButtonDisabled(); + return view('livewire.slack-settings-form'); + } public function testWebhook(){ @@ -214,7 +224,7 @@ class SlackSettingsForm extends Component try { $response = Http::withHeaders([ - 'content-type' => 'applications/json', + 'content-type' => 'application/json', ])->post($this->webhook_endpoint, $payload)->throw(); @@ -236,20 +246,32 @@ class SlackSettingsForm extends Component } public function msTeamTestWebhook(){ - $payload = - [ - "@type" => "MessageCard", - "@context" => "http://schema.org/extensions", - "summary" => trans('mail.snipe_webhook_summary'), - "title" => trans('mail.snipe_webhook_test'), - "text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]), - ]; + try { - try { - $response = Http::withHeaders([ - 'content-type' => 'applications/json', - ])->post($this->webhook_endpoint, - $payload)->throw(); + if($this->teams_webhook_deprecated){ + //will use the deprecated webhook format + $payload = + [ + "@type" => "MessageCard", + "@context" => "http://schema.org/extensions", + "summary" => trans('mail.snipe_webhook_summary'), + "title" => trans('mail.snipe_webhook_test'), + "text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]), + ]; + $response = Http::withHeaders([ + 'content-type' => 'application/json', + ])->post($this->webhook_endpoint, + $payload)->throw(); + } + else { + $notification = new TeamsNotification($this->webhook_endpoint); + $message = trans('general.webhook_test_msg', ['app' => $this->webhook_name]); + $notification->success()->sendMessage($message); + + $response = Http::withHeaders([ + 'content-type' => 'application/json', + ])->post($this->webhook_endpoint); + } if(($response->getStatusCode() == 302)||($response->getStatusCode() == 301)){ return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint])); diff --git a/app/Mail/CheckinAccessoryMail.php b/app/Mail/CheckinAccessoryMail.php new file mode 100644 index 0000000000..311ee87a25 --- /dev/null +++ b/app/Mail/CheckinAccessoryMail.php @@ -0,0 +1,70 @@ +item = $accessory; + $this->target = $checkedOutTo; + $this->admin = $checkedInby; + $this->note = $note; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.Accessory_Checkin_Notification'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'mail.markdown.checkin-accessory', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckinAssetMail.php b/app/Mail/CheckinAssetMail.php new file mode 100644 index 0000000000..355c2f9f13 --- /dev/null +++ b/app/Mail/CheckinAssetMail.php @@ -0,0 +1,93 @@ +target = $checkedOutTo; + $this->item = $asset; + $this->admin = $checkedInBy; + $this->note = $note; + + $this->settings = Setting::getSettings(); + $this->expected_checkin = ''; + + if ($this->item->expected_checkin) { + $this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date', + false); + } + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.Asset_Checkin_Notification'), + ); + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return Content + */ + public function content(): Content + { + $this->item->load('assetstatus'); + $fields = []; + + // Check if the item has custom fields associated with it + if (($this->item->model) && ($this->item->model->fieldset)) { + $fields = $this->item->model->fieldset->fields; + } + + return new Content( + markdown: 'mail.markdown.checkin-asset', + with: [ + 'item' => $this->item, + 'status' => $this->item->assetstatus?->name, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'fields' => $fields, + 'expected_checkin' => $this->expected_checkin, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckinLicenseMail.php b/app/Mail/CheckinLicenseMail.php new file mode 100644 index 0000000000..6895fb9695 --- /dev/null +++ b/app/Mail/CheckinLicenseMail.php @@ -0,0 +1,71 @@ +target = $checkedOutTo; + $this->item = $licenseSeat; + $this->admin = $checkedInBy; + $this->note = $note; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.License_Checkin_Notification'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'mail.markdown.checkin-license', + with: [ + 'license_seat' => $this->item, + 'license' => $this->item->license, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutAccessoryMail.php b/app/Mail/CheckoutAccessoryMail.php new file mode 100644 index 0000000000..64c02e31ed --- /dev/null +++ b/app/Mail/CheckoutAccessoryMail.php @@ -0,0 +1,82 @@ +item = $accessory; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->checkout_qty = $accessory->checkout_qty; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: (trans('mail.Accessory_Checkout_Notification')), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + Log::debug($this->item->getImageUrl()); + $eula = $this->item->getEula(); + $req_accept = $this->item->requireAcceptance(); + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-accessory', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + 'checkout_qty' => $this->checkout_qty, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutAssetMail.php b/app/Mail/CheckoutAssetMail.php new file mode 100644 index 0000000000..680014dcd1 --- /dev/null +++ b/app/Mail/CheckoutAssetMail.php @@ -0,0 +1,110 @@ +item = $asset; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + + $this->settings = Setting::getSettings(); + + $this->last_checkout = ''; + $this->expected_checkin = ''; + + if ($this->item->last_checkout) { + $this->last_checkout = Helper::getFormattedDateObject($this->item->last_checkout, 'date', + false); + } + + if ($this->item->expected_checkin) { + $this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date', + false); + } + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.Asset_Checkout_Notification'), + ); + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return Content + */ + public function content(): Content + { + $this->item->load('assetstatus'); + $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; + $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; + $fields = []; + + // Check if the item has custom fields associated with it + if (($this->item->model) && ($this->item->model->fieldset)) { + $fields = $this->item->model->fieldset->fields; + } + + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-asset', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'status' => $this->item->assetstatus?->name, + 'note' => $this->note, + 'target' => $this->target, + 'fields' => $fields, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + 'last_checkout' => $this->last_checkout, + 'expected_checkin' => $this->expected_checkin, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutConsumableMail.php b/app/Mail/CheckoutConsumableMail.php new file mode 100644 index 0000000000..6a3d806790 --- /dev/null +++ b/app/Mail/CheckoutConsumableMail.php @@ -0,0 +1,84 @@ +item = $consumable; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->qty = $consumable->checkout_qty; + + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.Confirm_consumable_delivery'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + + $eula = $this->item->getEula(); + $req_accept = $this->item->requireAcceptance(); + + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-consumable', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + 'qty' => $this->qty, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutLicenseMail.php b/app/Mail/CheckoutLicenseMail.php new file mode 100644 index 0000000000..9462c6c332 --- /dev/null +++ b/app/Mail/CheckoutLicenseMail.php @@ -0,0 +1,79 @@ +item = $licenseSeat; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.Confirm_license_delivery'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; + $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; + + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + return new Content( + markdown: 'mail.markdown.checkout-license', + with: [ + 'license_seat' => $this->item, + 'license' => $this->item->license, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/UnacceptedAssetReminderMail.php b/app/Mail/UnacceptedAssetReminderMail.php new file mode 100644 index 0000000000..1436bbc84e --- /dev/null +++ b/app/Mail/UnacceptedAssetReminderMail.php @@ -0,0 +1,67 @@ +count = $count; + $this->target = $checkout_info['acceptance']?->assignedTo; + $this->acceptance = $checkout_info['acceptance']; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(config('mail.from.address'), config('mail.from.name')); + + return new Envelope( + from: $from, + subject: trans('mail.unaccepted_asset_reminder'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + $accept_url = route('account.accept'); + + return new Content( + markdown: 'notifications.markdown.asset-reminder', + with: [ + 'count' => $this->count, + 'assigned_to' => $this->target?->present()->fullName, + 'link' => route('account.accept'), + 'accept_url' => $accept_url, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index 3fc4c5c9c6..fc1bb36ab4 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -62,7 +62,7 @@ class Accessory extends SnipeModel 'category_id' => 'required|integer|exists:categories,id', 'company_id' => 'integer|nullable', 'min_amt' => 'integer|min:0|nullable', - 'purchase_cost' => 'numeric|nullable|gte:0', + 'purchase_cost' => 'numeric|nullable|gte:0|max:9999999999999', 'purchase_date' => 'date_format:Y-m-d|nullable', ]; diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php index 0831352b87..008c5b1146 100755 --- a/app/Models/Actionlog.php +++ b/app/Models/Actionlog.php @@ -55,6 +55,8 @@ class Actionlog extends SnipeModel 'created_by', 'remote_ip', 'user_agent', + 'item_type', + 'target_type', 'action_source' ]; @@ -64,10 +66,10 @@ class Actionlog extends SnipeModel * @var array */ protected $searchableRelations = [ - 'company' => ['name'], - 'adminuser' => ['first_name','last_name','username', 'email'], - 'user' => ['first_name','last_name','username', 'email'], - 'assets' => ['asset_tag','name'], + 'company' => ['name'], + 'adminuser' => ['first_name','last_name','username', 'email'], + 'user' => ['first_name','last_name','username', 'email'], + 'assets' => ['asset_tag','name'], ]; /** diff --git a/app/Models/Asset.php b/app/Models/Asset.php index bd0578fc2e..ce8b870eb2 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -43,16 +43,16 @@ class Asset extends Depreciable /** * Run after the checkout acceptance was declined by the user - * + * * @param User $acceptedBy * @param string $signature - */ + */ public function declinedCheckout(User $declinedBy, $signature) { $this->assigned_to = null; $this->assigned_type = null; - $this->accepted = null; - $this->save(); + $this->accepted = null; + $this->save(); } /** @@ -112,7 +112,7 @@ class Asset extends Depreciable 'rtd_location_id' => ['nullable', 'exists:locations,id'], 'purchase_date' => ['nullable', 'date', 'date_format:Y-m-d'], 'serial' => ['nullable', 'unique_undeleted:assets,serial'], - 'purchase_cost' => ['nullable', 'numeric', 'gte:0'], + 'purchase_cost' => ['nullable', 'numeric', 'gte:0', 'max:9999999999999'], 'supplier_id' => ['nullable', 'exists:suppliers,id'], 'asset_eol_date' => ['nullable', 'date'], 'eol_explicit' => ['nullable', 'boolean'], @@ -368,7 +368,7 @@ class Asset extends Depreciable if ($this->save()) { if (is_int($admin)) { $checkedOutBy = User::findOrFail($admin); - } elseif (get_class($admin) === \App\Models\User::class) { + } elseif ($admin && get_class($admin) === \App\Models\User::class) { $checkedOutBy = $admin; } else { $checkedOutBy = auth()->user(); @@ -1705,7 +1705,7 @@ class Asset extends Depreciable }); }); } - + /** * THIS CLUNKY BIT IS VERY IMPORTANT @@ -1726,7 +1726,7 @@ class Asset extends Depreciable * assets.location would fail, as that field doesn't exist -- plus we're already searching * against those relationships earlier in this method. * - * - snipe + * - snipe * */ diff --git a/app/Models/AssetMaintenance.php b/app/Models/AssetMaintenance.php index f629b03dfb..246220f5c7 100644 --- a/app/Models/AssetMaintenance.php +++ b/app/Models/AssetMaintenance.php @@ -176,7 +176,7 @@ class AssetMaintenance extends Model implements ICompanyableChild */ public function adminuser() { - return $this->belongsTo(\App\Models\User::class, 'user_id') + return $this->belongsTo(\App\Models\User::class, 'created_by') ->withTrashed(); } diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php index 0c8f8e7b3c..02b5df40d1 100755 --- a/app/Models/AssetModel.php +++ b/app/Models/AssetModel.php @@ -68,6 +68,7 @@ class AssetModel extends SnipeModel 'model_number', 'name', 'notes', + 'requestable', ]; use Searchable; @@ -328,4 +329,14 @@ class AssetModel extends SnipeModel { return $query->leftJoin('custom_fieldsets', 'models.fieldset_id', '=', 'custom_fieldsets.id')->orderBy('custom_fieldsets.name', $order); } + + /** + * Query builder scope to order on created_by name + * + */ + public function scopeOrderByCreatedByName($query, $order) + { + return $query->leftJoin('users as admin_sort', 'models.created_by', '=', 'admin_sort.id')->select('models.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order); + } + } diff --git a/app/Models/Company.php b/app/Models/Company.php index 171d559542..8886da77f6 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -116,7 +116,7 @@ final class Company extends SnipeModel if ($current_user->company_id != null) { return $current_user->company_id; } else { - return static::getIdFromInput($unescaped_input); + return null; } } } diff --git a/app/Models/CompanyableTrait.php b/app/Models/CompanyableTrait.php index df67f2be4f..04a620d8e3 100644 --- a/app/Models/CompanyableTrait.php +++ b/app/Models/CompanyableTrait.php @@ -8,9 +8,6 @@ trait CompanyableTrait * This trait is used to scope models to the current company. To use this scope on companyable models, * we use the "use Companyable;" statement at the top of the mode. * - * We CANNOT USE THIS ON USERS, as it causes an infinite loop and prevents users from logging in, since this scope will be - * applied to the currently logged in (or logging in) user in addition to the user model for viewing lists of users. - * * @see \App\Models\Company\Company::scopeCompanyables() * @return void */ diff --git a/app/Models/Component.php b/app/Models/Component.php index 7bba37ac12..fb77bf0824 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -30,14 +30,15 @@ class Component extends SnipeModel * Category validation rules */ public $rules = [ - 'name' => 'required|min:3|max:255', + 'name' => 'required|min:3|max:191', 'qty' => 'required|integer|min:1', 'category_id' => 'required|integer|exists:categories,id', 'supplier_id' => 'nullable|integer|exists:suppliers,id', 'company_id' => 'integer|nullable|exists:companies,id', 'min_amt' => 'integer|min:0|nullable', 'purchase_date' => 'date_format:Y-m-d|nullable', - 'purchase_cost' => 'numeric|nullable|gte:0', + 'purchase_cost' => 'numeric|nullable|gte:0|max:9999999999999', + 'manufacturer_id' => 'integer|exists:manufacturers,id|nullable', ]; /** @@ -60,6 +61,8 @@ class Component extends SnipeModel 'company_id', 'supplier_id', 'location_id', + 'manufacturer_id', + 'model_number', 'name', 'purchase_cost', 'purchase_date', @@ -77,7 +80,15 @@ class Component extends SnipeModel * * @var array */ - protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date', 'notes']; + protected $searchableAttributes = [ + 'name', + 'order_number', + 'serial', + 'purchase_cost', + 'purchase_date', + 'notes', + 'model_number', + ]; /** * The relations and their attributes that should be included when searching the model. @@ -89,6 +100,7 @@ class Component extends SnipeModel 'company' => ['name'], 'location' => ['name'], 'supplier' => ['name'], + 'manufacturer' => ['name'], ]; @@ -183,6 +195,19 @@ class Component extends SnipeModel return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id'); } + + /** + * Establishes the item -> manufacturer relationship + * + * @author [A. Gianotto] [] + * @since [v3.0] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function manufacturer() + { + return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id'); + } + /** * Establishes the component -> action logs relationship * @@ -311,6 +336,19 @@ class Component extends SnipeModel return $query->leftJoin('suppliers', 'components.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order); } + /** + * Query builder scope to order on manufacturer + * + * @param \Illuminate\Database\Query\Builder $query Query builder instance + * @param text $order Order + * + * @return \Illuminate\Database\Query\Builder Modified query builder + */ + public function scopeOrderManufacturer($query, $order) + { + return $query->leftJoin('manufacturers', 'components.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order); + } + public function scopeOrderByCreatedBy($query, $order) { return $query->leftJoin('users as admin_sort', 'components.created_by', '=', 'admin_sort.id')->select('components.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order); diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index eb0acc8016..30161e8429 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -50,7 +50,7 @@ class Consumable extends SnipeModel 'category_id' => 'required|integer', 'company_id' => 'integer|nullable', 'min_amt' => 'integer|min:0|max:99999|nullable', - 'purchase_cost' => 'numeric|nullable|gte:0', + 'purchase_cost' => 'numeric|nullable|gte:0|max:9999999999999', 'purchase_date' => 'date_format:Y-m-d|nullable', ]; diff --git a/app/Models/CustomFieldset.php b/app/Models/CustomFieldset.php index 71be28e8a3..d6bd7a1bef 100644 --- a/app/Models/CustomFieldset.php +++ b/app/Models/CustomFieldset.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Rules\AlphaEncrypted; +use App\Rules\NumericEncrypted; use Gate; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -95,6 +97,19 @@ class CustomFieldset extends Model array_push($rule, $field->attributes['format']); $rules[$field->db_column_name()] = $rule; + + // these are to replace the standard 'numeric' and 'alpha' rules if the custom field is also encrypted. + // the values need to be decrypted first, because encrypted strings are alphanumeric + if ($field->format === 'NUMERIC' && $field->field_encrypted) { + $numericKey = array_search('numeric', $rules[$field->db_column_name()]); + $rules[$field->db_column_name()][$numericKey] = new NumericEncrypted; + } + + if ($field->format === 'ALPHA' && $field->field_encrypted) { + $alphaKey = array_search('alpha', $rules[$field->db_column_name()]); + $rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted; + } + // add not_array to rules for all fields but checkboxes if ($field->element != 'checkbox') { $rules[$field->db_column_name()][] = 'not_array'; diff --git a/app/Models/Import.php b/app/Models/Import.php index 81728c8a5e..d824a3840c 100644 --- a/app/Models/Import.php +++ b/app/Models/Import.php @@ -2,13 +2,28 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Import extends Model { + use HasFactory; + protected $casts = [ 'header_row' => 'array', 'first_row' => 'array', 'field_map' => 'json', ]; + + /** + * Establishes the license -> admin user relationship + * + * @author A. Gianotto + * @since [v2.0] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function adminuser() + { + return $this->belongsTo(\App\Models\User::class, 'created_by'); + } } diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php index 9b56012f7a..9f5fa735e4 100644 --- a/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php +++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php @@ -49,14 +49,7 @@ class LabelWriter_1933081 extends LabelWriter ); $currentX += $barcodeSize + self::BARCODE_MARGIN; $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; - } else { - static::writeText( - $pdf, $record->get('tag'), - $pa->x1, $pa->y2 - self::TAG_SIZE, - 'freesans', 'b', self::TAG_SIZE, 'R', - $usableWidth, self::TAG_SIZE, true, 0 - ); - } + } if ($record->has('title')) { static::writeText( diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php index e1305bd068..117486a8e5 100644 --- a/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php +++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php @@ -49,13 +49,6 @@ class LabelWriter_2112283 extends LabelWriter ); $currentX += $barcodeSize + self::BARCODE_MARGIN; $usableWidth -= $barcodeSize + self::BARCODE_MARGIN; - } else { - static::writeText( - $pdf, $record->get('tag'), - $pa->x1, $pa->y2 - self::TAG_SIZE, - 'freesans', 'b', self::TAG_SIZE, 'R', - $usableWidth, self::TAG_SIZE, true, 0 - ); } if ($record->has('title')) { diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php index d5f0e8d122..ed8074547b 100644 --- a/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php +++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php @@ -50,13 +50,6 @@ class LabelWriter_30252 extends LabelWriter ); $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')) { diff --git a/app/Models/License.php b/app/Models/License.php index 4923072f07..0997c1e57b 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -184,7 +184,7 @@ class License extends Depreciable $logAction->item_type = self::class; $logAction->item_id = $license->id; $logAction->created_by = auth()->id() ?: 1; // We don't have an id while running the importer from CLI. - $logAction->note = "deleted ${change} seats"; + $logAction->note = "deleted {$change} seats"; $logAction->target_id = null; $logAction->logaction('delete seats'); @@ -216,7 +216,7 @@ class License extends Depreciable $logAction->item_type = self::class; $logAction->item_id = $license->id; $logAction->created_by = auth()->id() ?: 1; // Importer. - $logAction->note = "added ${change} seats"; + $logAction->note = "added {$change} seats"; $logAction->target_id = null; $logAction->logaction('add seats'); } @@ -743,4 +743,4 @@ class License extends Depreciable { return $query->leftJoin('users as admin_sort', 'licenses.created_by', '=', 'admin_sort.id')->select('licenses.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order); } -} \ No newline at end of file +} diff --git a/app/Models/Location.php b/app/Models/Location.php index e6c310979b..014db3053e 100755 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -42,7 +42,7 @@ class Location extends SnipeModel ]; /** - * Whether the model should inject it's identifier to the unique + * Whether the model should inject its identifier to the unique * validation rules before attempting validation. If this property * is not set in the model it will default to true. * diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index 249afc00a8..72a7a7f262 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -117,7 +117,6 @@ trait Loggable */ public function logCheckin($target, $note, $action_date = null, $originalValues = []) { - $settings = Setting::getSettings(); $log = new Actionlog; if($target != null){ @@ -171,39 +170,6 @@ trait Loggable $log->logaction('checkin from'); -// $params = [ -// 'target' => $target, -// 'item' => $log->item, -// 'admin' => $log->user, -// 'note' => $note, -// 'target_type' => $log->target_type, -// 'settings' => $settings, -// ]; -// -// -// $checkinClass = null; -// -// if (method_exists($target, 'notify')) { -// try { -// $target->notify(new static::$checkinClass($params)); -// } catch (\Exception $e) { -// Log::debug($e); -// } -// -// } -// -// // Send to the admin, if settings dictate -// $recipient = new \App\Models\Recipients\AdminRecipient(); -// -// if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) { -// try { -// $recipient->notify(new static::$checkinClass($params)); -// } catch (\Exception $e) { -// Log::debug($e); -// } -// -// } - return $log; } diff --git a/app/Models/Manufacturer.php b/app/Models/Manufacturer.php index 6e72b3a2bb..1b31f496d3 100755 --- a/app/Models/Manufacturer.php +++ b/app/Models/Manufacturer.php @@ -78,6 +78,7 @@ class Manufacturer extends SnipeModel && (($this->licenses_count ?? $this->licenses()->count()) === 0) && (($this->consumables_count ?? $this->consumables()->count()) === 0) && (($this->accessories_count ?? $this->accessories()->count()) === 0) + && (($this->components_count ?? $this->components()->count()) === 0) && ($this->deleted_at == ''); } @@ -106,6 +107,10 @@ class Manufacturer extends SnipeModel return $this->hasMany(\App\Models\Consumable::class, 'manufacturer_id'); } + public function components() + { + return $this->hasMany(\App\Models\Component::class, 'manufacturer_id'); + } public function adminuser() { diff --git a/app/Models/Recipients/AdminRecipient.php b/app/Models/Recipients/AdminRecipient.php index 433bd00209..90e39d4ee5 100644 --- a/app/Models/Recipients/AdminRecipient.php +++ b/app/Models/Recipients/AdminRecipient.php @@ -6,9 +6,15 @@ use App\Models\Setting; class AdminRecipient extends Recipient { + + protected $email; public function __construct() { $settings = Setting::getSettings(); $this->email = trim($settings->admin_cc_email); } + + public function getEmail(){ + return $this->email; + } } diff --git a/app/Models/Setting.php b/app/Models/Setting.php index d775be81c5..232285fbdb 100755 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -51,36 +51,7 @@ class Setting extends Model */ protected $rules = [ 'brand' => 'required|min:1|numeric', - 'qr_text' => 'max:31|nullable', - 'alert_email' => 'email_array|nullable', - 'admin_cc_email' => 'email|nullable', - 'default_currency' => 'required', - 'locale' => 'required', - 'labels_per_page' => 'numeric', - 'labels_width' => 'numeric', - 'labels_height' => 'numeric', - 'labels_pmargin_left' => 'numeric|nullable', - 'labels_pmargin_right' => 'numeric|nullable', - 'labels_pmargin_top' => 'numeric|nullable', - 'labels_pmargin_bottom' => 'numeric|nullable', - 'labels_display_bgutter' => 'numeric|nullable', - 'labels_display_sgutter' => 'numeric|nullable', - 'labels_fontsize' => 'numeric|min:5', - 'labels_pagewidth' => 'numeric|nullable', - 'labels_pageheight' => 'numeric|nullable', - 'login_remote_user_enabled' => 'numeric|nullable', - 'login_common_disabled' => 'numeric|nullable', - 'login_remote_user_custom_logout_url' => 'string|nullable', - 'login_remote_user_header_name' => 'string|nullable', 'thumbnail_max_h' => 'numeric|max:500|min:25', - 'pwd_secure_min' => 'numeric|required|min:8', - 'alert_threshold' => 'numeric|nullable', - 'alert_interval' => 'numeric|nullable', - 'audit_warning_days' => 'numeric|nullable', - 'due_checkin_days' => 'numeric|nullable', - 'audit_interval' => 'numeric|nullable', - 'custom_forgot_pass_url' => 'url|nullable', - 'privacy_policy_link' => 'nullable|url', 'google_client_id' => 'nullable|ends_with:apps.googleusercontent.com' ]; @@ -99,6 +70,7 @@ class Setting extends Model protected $casts = [ 'label2_asset_logo' => 'boolean', + 'require_checkinout_notes' => 'boolean', ]; /** diff --git a/app/Notifications/CheckinAccessoryNotification.php b/app/Notifications/CheckinAccessoryNotification.php index 7e033f1870..28e6c054f7 100644 --- a/app/Notifications/CheckinAccessoryNotification.php +++ b/app/Notifications/CheckinAccessoryNotification.php @@ -6,9 +6,11 @@ use App\Models\Accessory; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\GoogleChatChannel; use NotificationChannels\GoogleChat\GoogleChatMessage; @@ -55,22 +57,9 @@ class CheckinAccessoryNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; + $notifyBy[] = SlackWebhookChannel::class; } - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - Log::debug('The target is a user'); - - if ($this->item->checkin_email()) { - $notifyBy[] = 'mail'; - } - } - - Log::debug('checkin_email on this category is '.$this->item->checkin_email()); - return $notifyBy; } @@ -103,18 +92,29 @@ class CheckinAccessoryNotification extends Notification $admin = $this->admin; $item = $this->item; $note = $this->note; + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { + return MicrosoftTeamsMessage::create() + ->to($this->settings->webhook_endpoint) + ->type('success') + ->addStartGroupToSection('activityTitle') + ->title(trans('Accessory_Checkin_Notification')) + ->addStartGroupToSection('activityText') + ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle') + ->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '') + ->fact(trans('mail.Accessory_Checkin_Notification')." by ", $admin->present()->fullName()) + ->fact(trans('admin/consumables/general.remaining'), $item->numRemaining()) + ->fact(trans('mail.notes'), $note ?: ''); + } - return MicrosoftTeamsMessage::create() - ->to($this->settings->webhook_endpoint) - ->type('success') - ->addStartGroupToSection('activityTitle') - ->title(trans('Accessory_Checkin_Notification')) - ->addStartGroupToSection('activityText') - ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle') - ->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '') - ->fact(trans('mail.Accessory_Checkin_Notification')." by ", $admin->present()->fullName()) - ->fact(trans('admin/consumables/general.remaining'), $item->numRemaining()) - ->fact(trans('mail.notes'), $note ?: ''); + $message = trans('mail.Accessory_Checkin_Notification'); + $details = [ + trans('mail.accessory_name') => htmlspecialchars_decode($item->present()->name), + trans('mail.checked_into') => $item->location->name ? $item->location->name : '', + trans('mail.Accessory_Checkin_Notification'). ' by' => $admin->present()->fullName(), + trans('admin/consumables/general.remaining')=> $item->numRemaining(), + trans('mail.notes') => $note ?: '', + ]; + return array($message, $details); } public function toGoogleChat() { @@ -142,24 +142,4 @@ class CheckinAccessoryNotification extends Notification ); } - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - Log::debug('to email called'); - - return (new MailMessage)->markdown('notifications.markdown.checkin-accessory', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - ]) - ->subject(trans('mail.Accessory_Checkin_Notification')); - } } diff --git a/app/Notifications/CheckinAssetNotification.php b/app/Notifications/CheckinAssetNotification.php index 77cd6d9b5a..fa4780c1fd 100644 --- a/app/Notifications/CheckinAssetNotification.php +++ b/app/Notifications/CheckinAssetNotification.php @@ -7,9 +7,11 @@ use App\Models\Asset; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\GoogleChatChannel; use NotificationChannels\GoogleChat\GoogleChatMessage; @@ -50,7 +52,6 @@ class CheckinAssetNotification extends Notification */ public function via() { - $notifyBy = []; if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) { $notifyBy[] = GoogleChatChannel::class; @@ -62,15 +63,7 @@ class CheckinAssetNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { Log::debug('use webhook'); - $notifyBy[] = 'slack'; - } - - /** - * Only send checkin notifications to users if the category - * has the corresponding checkbox checked. - */ - if ($this->item->checkin_email() && $this->target instanceof User && $this->target->email != '') { - $notifyBy[] = 'mail'; + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -106,16 +99,30 @@ class CheckinAssetNotification extends Notification $item = $this->item; $note = $this->note; - return MicrosoftTeamsMessage::create() - ->to($this->settings->webhook_endpoint) - ->type('success') - ->title(trans('mail.Asset_Checkin_Notification')) - ->addStartGroupToSection('activityText') - ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText') - ->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '') - ->fact(trans('mail.Asset_Checkin_Notification')." by ", $admin->present()->fullName()) - ->fact(trans('admin/hardware/form.status'), $item->assetstatus->name) - ->fact(trans('mail.notes'), $note ?: ''); + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { + return MicrosoftTeamsMessage::create() + ->to($this->settings->webhook_endpoint) + ->type('success') + ->title(trans('mail.Asset_Checkin_Notification')) + ->addStartGroupToSection('activityText') + ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText') + ->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '') + ->fact(trans('mail.Asset_Checkin_Notification') . " by ", $admin->present()->fullName()) + ->fact(trans('admin/hardware/form.status'), $item->assetstatus->name) + ->fact(trans('mail.notes'), $note ?: ''); + } + + + $message = trans('mail.Asset_Checkin_Notification'); + $details = [ + trans('mail.asset') => htmlspecialchars_decode($item->present()->name), + trans('mail.checked_into') => $item->location->name ? $item->location->name : '', + trans('mail.Asset_Checkin_Notification')." by " => $admin->present()->fullName(), + trans('admin/hardware/form.status') => $item->assetstatus->name, + trans('mail.notes') => $note ?: '', + ]; + + return array($message, $details); } public function toGoogleChat() { @@ -142,35 +149,5 @@ class CheckinAssetNotification extends Notification ) ) ); - - } - - /** - * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - $fields = []; - - // Check if the item has custom fields associated with it - if (($this->item->model) && ($this->item->model->fieldset)) { - $fields = $this->item->model->fieldset->fields; - } - - $message = (new MailMessage)->markdown('notifications.markdown.checkin-asset', - [ - 'item' => $this->item, - 'status' => $this->item->assetstatus?->name, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - 'fields' => $fields, - 'expected_checkin' => $this->expected_checkin, - ]) - ->subject(trans('mail.Asset_Checkin_Notification')); - - return $message; } } diff --git a/app/Notifications/CheckinLicenseSeatNotification.php b/app/Notifications/CheckinLicenseSeatNotification.php index 289e63a162..1cb8706e67 100644 --- a/app/Notifications/CheckinLicenseSeatNotification.php +++ b/app/Notifications/CheckinLicenseSeatNotification.php @@ -6,9 +6,11 @@ use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\GoogleChatChannel; use NotificationChannels\GoogleChat\GoogleChatMessage; @@ -58,15 +60,7 @@ class CheckinLicenseSeatNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; - } - - /** - * Only send checkin notifications to users if the category - * has the corresponding checkbox checked. - */ - if ($this->item->checkin_email() && $this->target instanceof User && $this->target->email != '') { - $notifyBy[] = 'mail'; + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -109,18 +103,30 @@ class CheckinLicenseSeatNotification extends Notification $admin = $this->admin; $item = $this->item; $note = $this->note; + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { + return MicrosoftTeamsMessage::create() + ->to($this->settings->webhook_endpoint) + ->type('success') + ->addStartGroupToSection('activityTitle') + ->title(trans('mail.License_Checkin_Notification')) + ->addStartGroupToSection('activityText') + ->fact(htmlspecialchars_decode($item->present()->name), '', 'header') + ->fact(trans('mail.License_Checkin_Notification')." by ", $admin->present()->fullName() ?: 'CLI tool') + ->fact(trans('mail.checkedin_from'), $target->present()->fullName()) + ->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count()) + ->fact(trans('mail.notes'), $note ?: ''); + } - return MicrosoftTeamsMessage::create() - ->to($this->settings->webhook_endpoint) - ->type('success') - ->addStartGroupToSection('activityTitle') - ->title(trans('mail.License_Checkin_Notification')) - ->addStartGroupToSection('activityText') - ->fact(htmlspecialchars_decode($item->present()->name), '', 'header') - ->fact(trans('mail.License_Checkin_Notification')." by ", $admin->present()->fullName() ?: 'CLI tool') - ->fact(trans('mail.checkedin_from'), $target->present()->fullName()) - ->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count()) - ->fact(trans('mail.notes'), $note ?: ''); + $message = trans('mail.License_Checkin_Notification'); + $details = [ + trans('mail.checkedin_from')=> $target->present()->fullName(), + trans('mail.license_for') => htmlspecialchars_decode($item->present()->name), + trans('mail.License_Checkin_Notification')." by " => $admin->present()->fullName() ?: 'CLI tool', + trans('admin/consumables/general.remaining') => $item->availCount()->count(), + trans('mail.notes') => $note ?: '', + ]; + + return array($message, $details); } public function toGoogleChat() { @@ -149,23 +155,4 @@ class CheckinLicenseSeatNotification extends Notification ); } - - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - return (new MailMessage)->markdown('notifications.markdown.checkin-license', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - ]) - ->subject(trans('mail.License_Checkin_Notification')); - } } diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 721ba7f6a4..116a5ac29f 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -9,6 +9,7 @@ use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\GoogleChatChannel; use NotificationChannels\GoogleChat\GoogleChatMessage; @@ -120,6 +121,7 @@ class CheckoutAccessoryNotification extends Notification $item = $this->item; $note = $this->note; + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { return MicrosoftTeamsMessage::create() ->to($this->settings->webhook_endpoint) ->type('success') @@ -133,7 +135,19 @@ class CheckoutAccessoryNotification extends Notification ->fact(trans('mail.Accessory_Checkout_Notification') . " by ", $admin->present()->fullName()) ->fact(trans('admin/consumables/general.remaining'), $item->numRemaining()) ->fact(trans('mail.notes'), $note ?: ''); + } + $message = trans('mail.Accessory_Checkout_Notification'); + $details = [ + trans('mail.assigned_to') => $target->present()->name, + trans('mail.accessory_name') => htmlspecialchars_decode($item->present()->name), + trans('general.qty') => $this->checkout_qty, + trans('mail.checkedout_from') => $item->location->name ? $item->location->name : '', + trans('mail.Accessory_Checkout_Notification'). ' by' => $admin->present()->fullName(), + trans('admin/consumables/general.remaining')=> $item->numRemaining(), + trans('mail.notes') => $note ?: '', + ]; + return array($message, $details); } public function toGoogleChat() { diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index b14796fb8c..61499e62f2 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -8,9 +8,10 @@ use App\Models\Setting; use App\Models\User; use Exception; use Illuminate\Bus\Queueable; -use Illuminate\Notifications\Messages\MailMessage; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\Enums\Icon; use NotificationChannels\GoogleChat\Enums\ImageStyle; @@ -21,6 +22,9 @@ use NotificationChannels\GoogleChat\Widgets\KeyValue; use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel; use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage; use Illuminate\Support\Facades\Log; +use Osama\LaravelTeamsNotification\Logging\TeamsLoggingChannel; +use Osama\LaravelTeamsNotification\TeamsNotification; + class CheckoutAssetNotification extends Notification { use Queueable; @@ -32,14 +36,11 @@ class CheckoutAssetNotification extends Notification */ public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { + $this->settings = Setting::getSettings(); $this->item = $asset; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; - $this->acceptance = $acceptance; - - $this->settings = Setting::getSettings(); - $this->last_checkout = ''; $this->expected_checkin = ''; @@ -53,7 +54,6 @@ class CheckoutAssetNotification extends Notification false); } } - /** * Get the notification's delivery channels. * @@ -62,61 +62,34 @@ class CheckoutAssetNotification extends Notification public function via() { $notifyBy = []; - if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) { + + if (Setting::getSettings()->webhook_selected === 'google' && Setting::getSettings()->webhook_endpoint) { $notifyBy[] = GoogleChatChannel::class; } - if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) { + if (Setting::getSettings()->webhook_selected === 'microsoft' && Setting::getSettings()->webhook_endpoint) { $notifyBy[] = MicrosoftTeamsChannel::class; } - if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { + if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general' ) { Log::debug('use webhook'); - $notifyBy[] = 'slack'; - } - - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - - /** - * Send an email if the asset requires acceptance, - * so the user can accept or decline the asset - */ - if ($this->item->requireAcceptance()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if the item has a EULA, since the user should always receive it - */ - if ($this->item->getEula()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if an email should be sent at checkin/checkout - */ - if ($this->item->checkin_email()) { - $notifyBy[1] = 'mail'; - } + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; } - public function toSlack() + public function toSlack() :SlackMessage { $target = $this->target; $admin = $this->admin; $item = $this->item; $note = $this->note; - $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $botname = ($this->settings->webhook_botname) ?: 'Snipe-Bot'; $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ @@ -124,7 +97,7 @@ class CheckoutAssetNotification extends Notification 'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>', ]; - if (($this->expected_checkin) && ($this->expected_checkin != '')) { + if (($this->expected_checkin) && ($this->expected_checkin !== '')) { $fields['Expected Checkin'] = $this->expected_checkin; } @@ -138,6 +111,7 @@ class CheckoutAssetNotification extends Notification ->content($note); }); } + public function toMicrosoftTeams() { $target = $this->target; @@ -145,17 +119,26 @@ class CheckoutAssetNotification extends Notification $item = $this->item; $note = $this->note; - return MicrosoftTeamsMessage::create() - ->to($this->settings->webhook_endpoint) - ->type('success') - ->title(trans('mail.Asset_Checkout_Notification')) - ->addStartGroupToSection('activityText') - ->fact(trans('mail.assigned_to'), $target->present()->name) - ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText') - ->fact(trans('mail.Asset_Checkout_Notification') . " by ", $admin->present()->fullName()) - ->fact(trans('mail.notes'), $note ?: ''); - + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { + return MicrosoftTeamsMessage::create() + ->to($this->settings->webhook_endpoint) + ->type('success') + ->title(trans('mail.Asset_Checkout_Notification')) + ->addStartGroupToSection('activityText') + ->fact(trans('mail.assigned_to'), $target->present()->name) + ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText') + ->fact(trans('mail.Asset_Checkout_Notification') . " by ", $admin->present()->fullName()) + ->fact(trans('mail.notes'), $note ?: ''); + } + $message = trans('mail.Asset_Checkout_Notification'); + $details = [ + trans('mail.assigned_to') => $target->present()->name, + trans('mail.asset') => htmlspecialchars_decode($item->present()->name), + trans('mail.Asset_Checkout_Notification'). ' by' => $admin->present()->fullName(), + trans('mail.notes') => $note ?: '', + ]; + return array($message, $details); } public function toGoogleChat() { @@ -184,42 +167,4 @@ public function toGoogleChat() ); } - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { $this->item->load('assetstatus'); - $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; - $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; - $fields = []; - - // Check if the item has custom fields associated with it - if (($this->item->model) && ($this->item->model->fieldset)) { - $fields = $this->item->model->fieldset->fields; - } - - $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); - - $message = (new MailMessage)->markdown('notifications.markdown.checkout-asset', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'status' => $this->item->assetstatus?->name, - 'note' => $this->note, - 'target' => $this->target, - 'fields' => $fields, - 'eula' => $eula, - 'req_accept' => $req_accept, - 'accept_url' => $accept_url, - 'last_checkout' => $this->last_checkout, - 'expected_checkin' => $this->expected_checkin, - ]) - ->subject(trans('mail.Confirm_asset_delivery')); - - return $message; - } } diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index 6746795f2c..ba7c5646ab 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -6,9 +6,11 @@ use App\Models\Consumable; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\GoogleChatChannel; use NotificationChannels\GoogleChat\GoogleChatMessage; @@ -38,6 +40,7 @@ class CheckoutConsumableNotification extends Notification $this->note = $note; $this->target = $checkedOutTo; $this->acceptance = $acceptance; + $this->qty = $consumable->checkout_qty; $this->settings = Setting::getSettings(); } @@ -61,35 +64,7 @@ class CheckoutConsumableNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; - } - - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - - /** - * Send an email if the asset requires acceptance, - * so the user can accept or decline the asset - */ - if ($this->item->requireAcceptance()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if the item has a EULA, since the user should always receive it - */ - if ($this->item->getEula()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if an email should be sent at checkin/checkout - */ - if ((method_exists($this->item, 'checkin_email')) && ($this->item->checkin_email())) { - $notifyBy[1] = 'mail'; - } + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -126,17 +101,30 @@ class CheckoutConsumableNotification extends Notification $item = $this->item; $note = $this->note; - return MicrosoftTeamsMessage::create() - ->to($this->settings->webhook_endpoint) - ->type('success') - ->addStartGroupToSection('activityTitle') - ->title(trans('mail.Consumable_checkout_notification')) - ->addStartGroupToSection('activityText') - ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle') - ->fact(trans('mail.Consumable_checkout_notification')." by ", $admin->present()->fullName()) - ->fact(trans('mail.assigned_to'), $target->present()->fullName()) - ->fact(trans('admin/consumables/general.remaining'), $item->numRemaining()) - ->fact(trans('mail.notes'), $note ?: ''); + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { + return MicrosoftTeamsMessage::create() + ->to($this->settings->webhook_endpoint) + ->type('success') + ->addStartGroupToSection('activityTitle') + ->title(trans('mail.Consumable_checkout_notification')) + ->addStartGroupToSection('activityText') + ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle') + ->fact(trans('mail.Consumable_checkout_notification')." by ", $admin->present()->fullName()) + ->fact(trans('mail.assigned_to'), $target->present()->fullName()) + ->fact(trans('admin/consumables/general.remaining'), $item->numRemaining()) + ->fact(trans('mail.notes'), $note ?: ''); + } + + $message = trans('mail.Consumable_checkout_notification'); + $details = [ + trans('mail.assigned_to') => $target->present()->fullName(), + trans('mail.item') => htmlspecialchars_decode($item->present()->name), + trans('mail.Consumable_checkout_notification').' by' => $admin->present()->fullName(), + trans('admin/consumables/general.remaining') => $item->numRemaining(), + trans('mail.notes') => $note ?: '', + ]; + + return array($message, $details); } public function toGoogleChat() { @@ -165,30 +153,4 @@ class CheckoutConsumableNotification extends Notification ); } - - /** - * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - Log::debug($this->item->getImageUrl()); - $eula = $this->item->getEula(); - $req_accept = $this->item->requireAcceptance(); - - $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); - - return (new MailMessage)->markdown('notifications.markdown.checkout-consumable', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - 'eula' => $eula, - 'req_accept' => $req_accept, - 'accept_url' => $accept_url, - ]) - ->subject(trans('mail.Confirm_consumable_delivery')); - } } diff --git a/app/Notifications/CheckoutLicenseSeatNotification.php b/app/Notifications/CheckoutLicenseSeatNotification.php index 8e0273c66e..1aed0d2004 100644 --- a/app/Notifications/CheckoutLicenseSeatNotification.php +++ b/app/Notifications/CheckoutLicenseSeatNotification.php @@ -6,9 +6,11 @@ use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\Str; use NotificationChannels\GoogleChat\Card; use NotificationChannels\GoogleChat\GoogleChatChannel; use NotificationChannels\GoogleChat\GoogleChatMessage; @@ -60,35 +62,7 @@ class CheckoutLicenseSeatNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; - } - - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - - /** - * Send an email if the asset requires acceptance, - * so the user can accept or decline the asset - */ - if ($this->item->requireAcceptance()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if the item has a EULA, since the user should always receive it - */ - if ($this->item->getEula()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if an email should be sent at checkin/checkout - */ - if ($this->item->checkin_email()) { - $notifyBy[1] = 'mail'; - } + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -125,17 +99,29 @@ class CheckoutLicenseSeatNotification extends Notification $item = $this->item; $note = $this->note; - return MicrosoftTeamsMessage::create() - ->to($this->settings->webhook_endpoint) - ->type('success') - ->addStartGroupToSection('activityTitle') - ->title(trans('mail.License_Checkout_Notification')) - ->addStartGroupToSection('activityText') - ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle') - ->fact(trans('mail.License_Checkout_Notification')." by ", $admin->present()->fullName()) - ->fact(trans('mail.assigned_to'), $target->present()->fullName()) - ->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count()) - ->fact(trans('mail.notes'), $note ?: ''); + if(!Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows')) { + return MicrosoftTeamsMessage::create() + ->to($this->settings->webhook_endpoint) + ->type('success') + ->addStartGroupToSection('activityTitle') + ->title(trans('mail.License_Checkout_Notification')) + ->addStartGroupToSection('activityText') + ->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle') + ->fact(trans('mail.License_Checkout_Notification')." by ", $admin->present()->fullName()) + ->fact(trans('mail.assigned_to'), $target->present()->fullName()) + ->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count()) + ->fact(trans('mail.notes'), $note ?: ''); + } + + $message = trans('mail.License_Checkout_Notification'); + $details = [ + trans('mail.assigned_to') => $target->present()->fullName(), + trans('mail.license_for') => htmlspecialchars_decode($item->present()->name), + trans('mail.License_Checkout_Notification').' by' => $admin->present()->fullName(), + trans('admin/consumables/general.remaining') => $item->availCount()->count(), + trans('mail.notes') => $note ?: '', + ]; + return array($message, $details); } public function toGoogleChat() { @@ -164,29 +150,4 @@ class CheckoutLicenseSeatNotification extends Notification ); } - - /** - * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; - $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; - - $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); - - return (new MailMessage)->markdown('notifications.markdown.checkout-license', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - 'eula' => $eula, - 'req_accept' => $req_accept, - 'accept_url' => $accept_url, - ]) - ->subject(trans('mail.Confirm_license_delivery')); - } } diff --git a/app/Notifications/UnacceptedAssetReminderNotification.php b/app/Notifications/UnacceptedAssetReminderNotification.php deleted file mode 100644 index e05b007033..0000000000 --- a/app/Notifications/UnacceptedAssetReminderNotification.php +++ /dev/null @@ -1,73 +0,0 @@ -count = $count; - $this->target = $checkout_info['acceptance']->assignedTo; - $this->acceptance = $checkout_info['acceptance']; - - } - - /** - * Get the notification's delivery channels. - * - * @param mixed $notifiable - * @return array - */ - public function via() - { - return ['mail']; - } - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - $accept_url = route('account.accept'); - $message = (new MailMessage)->markdown('notifications.markdown.asset-reminder', - [ - 'count' => $this->count, - 'assigned_to' => $this->target->present()->fullName, - 'link' => route('account.accept'), - 'accept_url' => $accept_url, - ]) - ->subject(trans('mail.unaccepted_asset_reminder')); - - return $message; - } - - /** - * Get the array representation of the notification. - * - * @param mixed $notifiable - * @return array - */ - public function toArray($notifiable) - { - return [ - // - ]; - } -} diff --git a/app/Presenters/ActionlogPresenter.php b/app/Presenters/ActionlogPresenter.php index 37a1adbc28..9251ce6347 100644 --- a/app/Presenters/ActionlogPresenter.php +++ b/app/Presenters/ActionlogPresenter.php @@ -42,27 +42,27 @@ class ActionlogPresenter extends Presenter // User related icons if ($this->itemType() == 'user') { - if ($this->actionType()=='2fa reset') { + if ($this->action_type == '2fa reset') { return 'fa-solid fa-mobile-screen'; } - if ($this->actionType()=='create new') { + if ($this->action_type == 'create new') { return 'fa-solid fa-user-plus'; } - if ($this->actionType()=='merged') { + if ($this->action_type == 'merged') { return 'fa-solid fa-people-arrows'; } - if ($this->actionType()=='delete') { + if ($this->action_type == 'delete') { return 'fa-solid fa-user-minus'; } - if ($this->actionType()=='delete') { + if ($this->action_type == 'delete') { return 'fa-solid fa-user-minus'; } - if ($this->actionType()=='update') { + if ($this->action_type == 'update') { return 'fa-solid fa-user-pen'; } @@ -70,31 +70,31 @@ class ActionlogPresenter extends Presenter } // Everything else - if ($this->actionType()=='create new') { + if ($this->action_type == 'create new') { return 'fa-solid fa-plus'; } - if ($this->actionType()=='delete') { + if ($this->action_type == 'delete') { return 'fa-solid fa-trash'; } - if ($this->actionType()=='update') { + if ($this->action_type == 'update') { return 'fa-solid fa-pen'; } - if ($this->actionType()=='restore') { + if ($this->action_type == 'restore') { return 'fa-solid fa-trash-arrow-up'; } - if ($this->actionType()=='upload') { + if ($this->action_type == 'upload') { return 'fas fa-paperclip'; } - if ($this->actionType()=='checkout') { + if ($this->action_type == 'checkout') { return 'fa-solid fa-rotate-left'; } - if ($this->actionType()=='checkin from') { + if ($this->action_type == 'checkin from') { return 'fa-solid fa-rotate-right'; } diff --git a/app/Presenters/AssetMaintenancesPresenter.php b/app/Presenters/AssetMaintenancesPresenter.php index 6a315ad8e0..ca49c931a4 100644 --- a/app/Presenters/AssetMaintenancesPresenter.php +++ b/app/Presenters/AssetMaintenancesPresenter.php @@ -116,12 +116,6 @@ class AssetMaintenancesPresenter extends Presenter 'sortable' => true, 'title' => trans('admin/asset_maintenances/form.cost'), 'class' => 'text-right', - ], [ - 'field' => 'user_id', - 'searchable' => true, - 'sortable' => true, - 'title' => trans('general.admin'), - 'formatter' => 'usersLinkObjFormatter', ], [ 'field' => 'created_by', 'searchable' => false, diff --git a/app/Presenters/ComponentPresenter.php b/app/Presenters/ComponentPresenter.php index f32bb56d57..39a177592d 100644 --- a/app/Presenters/ComponentPresenter.php +++ b/app/Presenters/ComponentPresenter.php @@ -66,8 +66,20 @@ class ComponentPresenter extends Presenter 'title' => trans('general.supplier'), 'visible' => false, 'formatter' => 'suppliersLinkObjFormatter', - ], - [ + ], [ + 'field' => 'model_number', + 'searchable' => true, + 'sortable' => true, + 'title' => trans('admin/models/table.modelnumber'), + ], [ + 'field' => 'manufacturer', + 'searchable' => true, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.manufacturer'), + 'visible' => false, + 'formatter' => 'manufacturersLinkObjFormatter', + ], [ 'field' => 'qty', 'searchable' => false, 'sortable' => true, diff --git a/app/Presenters/DepreciationReportPresenter.php b/app/Presenters/DepreciationReportPresenter.php index 6905385615..1d4c459614 100644 --- a/app/Presenters/DepreciationReportPresenter.php +++ b/app/Presenters/DepreciationReportPresenter.php @@ -140,7 +140,7 @@ class DepreciationReportPresenter extends Presenter ], [ "field" => "book_value", "searchable" => true, - "sortable" => true, + "sortable" => false, "visible" => true, "title" => trans('admin/hardware/table.book_value'), "footerFormatter" => 'sumFormatter', diff --git a/app/Presenters/ManufacturerPresenter.php b/app/Presenters/ManufacturerPresenter.php index ea29974f34..dfefec2998 100644 --- a/app/Presenters/ManufacturerPresenter.php +++ b/app/Presenters/ManufacturerPresenter.php @@ -124,8 +124,15 @@ class ManufacturerPresenter extends Presenter 'title' => trans('general.accessories'), 'visible' => true, 'class' => 'css-accessory', - ], - [ + ], [ + 'field' => 'components_count', + 'searchable' => false, + 'sortable' => true, + 'switchable' => true, + 'title' => trans('general.components'), + 'visible' => true, + 'class' => 'css-component', + ], [ 'field' => 'created_by', 'searchable' => false, 'sortable' => true, diff --git a/app/Providers/ValidationServiceProvider.php b/app/Providers/ValidationServiceProvider.php index 1f3abca8a6..76ba1b629a 100644 --- a/app/Providers/ValidationServiceProvider.php +++ b/app/Providers/ValidationServiceProvider.php @@ -31,6 +31,7 @@ class ValidationServiceProvider extends ServiceProvider Validator::extend('email_array', function ($attribute, $value, $parameters, $validator) { $value = str_replace(' ', '', $value); $array = explode(',', $value); + $email_to_validate = []; foreach ($array as $email) { //loop over values $email_to_validate['alert_email'][] = $email; @@ -38,7 +39,7 @@ class ValidationServiceProvider extends ServiceProvider $rules = ['alert_email.*'=>'email']; $messages = [ - 'alert_email.*'=>trans('validation.email_array'), + 'alert_email.*' => trans('validation.custom.email_array'), ]; $validator = Validator::make($email_to_validate, $rules, $messages); diff --git a/app/Rules/AlphaEncrypted.php b/app/Rules/AlphaEncrypted.php new file mode 100644 index 0000000000..f4ed1d6c32 --- /dev/null +++ b/app/Rules/AlphaEncrypted.php @@ -0,0 +1,29 @@ + $attributeName])); + } + } catch (\Exception $e) { + report($e); + $fail(trans('general.something_went_wrong')); + } + } +} diff --git a/app/Rules/NumericEncrypted.php b/app/Rules/NumericEncrypted.php new file mode 100644 index 0000000000..f3cb3ba76e --- /dev/null +++ b/app/Rules/NumericEncrypted.php @@ -0,0 +1,31 @@ + $attributeName])); + } + } catch (\Exception $e) { + report($e->getMessage()); + $fail(trans('general.something_went_wrong')); + } + } +} diff --git a/composer.json b/composer.json index 6d89312578..865878280c 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "intervention/image": "^2.5", "javiereguiluz/easyslugger": "^1.0", "laravel-notification-channels/google-chat": "^3.0", - "laravel-notification-channels/microsoft-teams": "^1.1", + "laravel-notification-channels/microsoft-teams": "^1.2", "laravel/framework": "^10.0", "laravel/helpers": "^1.4", "laravel/passport": "^11.0", @@ -55,6 +55,7 @@ "nunomaduro/collision": "^7.0", "okvpn/clock-lts": "^1.0", "onelogin/php-saml": "^3.4", + "osa-eg/laravel-teams-notification": "^2.1", "paragonie/constant_time_encoding": "^2.3", "paragonie/sodium_compat": "^1.19", "phpdocumentor/reflection-docblock": "^5.1", @@ -74,7 +75,6 @@ "ext-exif": "*" }, "require-dev": { - "brianium/paratest": "^7.0", "fakerphp/faker": "^1.16", "larastan/larastan": "^2.9", "mockery/mockery": "^1.4", diff --git a/composer.lock b/composer.lock index 715070df0f..677099e085 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": "3819ab4ef72eb77fabe494c0e746b83b", + "content-hash": "0750e3a427347b2a56a05a8b9b533d48", "packages": [ { "name": "alek13/slack", @@ -137,16 +137,16 @@ }, { "name": "aws/aws-crt-php", - "version": "v1.2.5", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/awslabs/aws-crt-php.git", - "reference": "0ea1f04ec5aa9f049f97e012d1ed63b76834a31b" + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/0ea1f04ec5aa9f049f97e012d1ed63b76834a31b", - "reference": "0ea1f04ec5aa9f049f97e012d1ed63b76834a31b", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", "shasum": "" }, "require": { @@ -185,22 +185,22 @@ ], "support": { "issues": "https://github.com/awslabs/aws-crt-php/issues", - "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.5" + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" }, - "time": "2024-04-19T21:30:56+00:00" + "time": "2024-10-18T22:15:13+00:00" }, { "name": "aws/aws-sdk-php", - "version": "3.314.6", + "version": "3.326.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "d04da330b0201edab71edd33a03b8d5ad6e4a313" + "reference": "5420284de9aad84e375fa8012cefd834bebfd623" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d04da330b0201edab71edd33a03b8d5ad6e4a313", - "reference": "d04da330b0201edab71edd33a03b8d5ad6e4a313", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5420284de9aad84e375fa8012cefd834bebfd623", + "reference": "5420284de9aad84e375fa8012cefd834bebfd623", "shasum": "" }, "require": { @@ -253,7 +253,10 @@ ], "psr-4": { "Aws\\": "src/" - } + }, + "exclude-from-classmap": [ + "src/data/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -280,9 +283,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.314.6" + "source": "https://github.com/aws/aws-sdk-php/tree/3.326.0" }, - "time": "2024-06-20T18:09:51+00:00" + "time": "2024-11-13T19:07:44+00:00" }, { "name": "bacon/bacon-qr-code", @@ -340,23 +343,23 @@ }, { "name": "barryvdh/laravel-debugbar", - "version": "v3.13.5", + "version": "v3.14.6", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "92d86be45ee54edff735e46856f64f14b6a8bb07" + "reference": "14e4517bd49130d6119228107eb21ae47ae120ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/92d86be45ee54edff735e46856f64f14b6a8bb07", - "reference": "92d86be45ee54edff735e46856f64f14b6a8bb07", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/14e4517bd49130d6119228107eb21ae47ae120ab", + "reference": "14e4517bd49130d6119228107eb21ae47ae120ab", "shasum": "" }, "require": { "illuminate/routing": "^9|^10|^11", "illuminate/session": "^9|^10|^11", "illuminate/support": "^9|^10|^11", - "maximebf/debugbar": "~1.22.0", + "maximebf/debugbar": "~1.23.0", "php": "^8.0", "symfony/finder": "^6|^7" }, @@ -369,7 +372,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.13-dev" + "dev-master": "3.14-dev" }, "laravel": { "providers": [ @@ -408,7 +411,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.13.5" + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.14.6" }, "funding": [ { @@ -420,7 +423,7 @@ "type": "github" } ], - "time": "2024-04-12T11:20:37+00:00" + "time": "2024-10-18T13:15:12+00:00" }, { "name": "barryvdh/laravel-dompdf", @@ -630,23 +633,23 @@ }, { "name": "dasprid/enum", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", "shasum": "" }, "require": { "php": ">=7.1 <9.0" }, "require-dev": { - "phpunit/phpunit": "^7 | ^8 | ^9", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", "squizlabs/php_codesniffer": "*" }, "type": "library", @@ -674,9 +677,9 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" }, - "time": "2023-08-25T16:18:39+00:00" + "time": "2024-08-09T14:30:48+00:00" }, { "name": "defuse/php-encryption", @@ -747,16 +750,16 @@ }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -816,9 +819,9 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "doctrine/cache", @@ -921,16 +924,16 @@ }, { "name": "doctrine/dbal", - "version": "3.8.6", + "version": "3.9.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1" + "reference": "61446f07fcb522414d6cfd8b1c3e5f9e18c579ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/b7411825cf7efb7e51f9791dea19d86e43b399a1", - "reference": "b7411825cf7efb7e51f9791dea19d86e43b399a1", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/61446f07fcb522414d6cfd8b1c3e5f9e18c579ba", + "reference": "61446f07fcb522414d6cfd8b1c3e5f9e18c579ba", "shasum": "" }, "require": { @@ -946,12 +949,12 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.11.5", + "phpstan/phpstan": "1.12.6", "phpstan/phpstan-strict-rules": "^1.6", - "phpunit/phpunit": "9.6.19", + "phpunit/phpunit": "9.6.20", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.10.1", + "squizlabs/php_codesniffer": "3.10.2", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" @@ -1014,7 +1017,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.6" + "source": "https://github.com/doctrine/dbal/tree/3.9.3" }, "funding": [ { @@ -1030,7 +1033,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T10:38:17+00:00" + "time": "2024-10-10T17:56:43+00:00" }, { "name": "doctrine/deprecations", @@ -1472,16 +1475,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.3", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" + "reference": "8c784d071debd117328803d86b2097615b457500" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", "shasum": "" }, "require": { @@ -1494,10 +1497,14 @@ "require-dev": { "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, "autoload": { "psr-4": { "Cron\\": "src/Cron/" @@ -1521,7 +1528,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" }, "funding": [ { @@ -1529,7 +1536,7 @@ "type": "github" } ], - "time": "2023-08-10T19:36:49+00:00" + "time": "2024-10-09T13:47:03+00:00" }, { "name": "eduardokum/laravel-mail-auto-embed", @@ -1765,26 +1772,26 @@ }, { "name": "filp/whoops", - "version": "2.15.4", + "version": "2.16.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", + "php": "^7.1 || ^8.0", "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -1824,7 +1831,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "source": "https://github.com/filp/whoops/tree/2.16.0" }, "funding": [ { @@ -1832,7 +1839,7 @@ "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2024-09-25T12:00:00+00:00" }, { "name": "firebase/php-jwt", @@ -1970,24 +1977,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.1.2", + "version": "v1.1.3", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2" + "phpoption/phpoption": "^1.9.3" }, "require-dev": { - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "autoload": { @@ -2016,7 +2023,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" }, "funding": [ { @@ -2028,26 +2035,26 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:16:48+00:00" + "time": "2024-07-20T21:45:45+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.9.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -2058,9 +2065,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -2138,7 +2145,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -2154,20 +2161,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2024-07-24T11:22:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -2175,7 +2182,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", "extra": { @@ -2221,7 +2228,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -2237,20 +2244,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { @@ -2265,8 +2272,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -2337,7 +2344,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -2353,7 +2360,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "guzzlehttp/uri-template", @@ -2675,16 +2682,16 @@ }, { "name": "laravel/framework", - "version": "v10.48.14", + "version": "v10.48.23", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "27cb4736bb7e60a5311ec73160068dfbcf98336b" + "reference": "625269ca4881d2b50eded2045cb930960a181d98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/27cb4736bb7e60a5311ec73160068dfbcf98336b", - "reference": "27cb4736bb7e60a5311ec73160068dfbcf98336b", + "url": "https://api.github.com/repos/laravel/framework/zipball/625269ca4881d2b50eded2045cb930960a181d98", + "reference": "625269ca4881d2b50eded2045cb930960a181d98", "shasum": "" }, "require": { @@ -2878,7 +2885,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-06-21T10:06:42+00:00" + "time": "2024-11-12T15:39:10+00:00" }, { "name": "laravel/helpers", @@ -3017,16 +3024,16 @@ }, { "name": "laravel/prompts", - "version": "v0.1.24", + "version": "v0.1.25", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "409b0b4305273472f3754826e68f4edbd0150149" + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/409b0b4305273472f3754826e68f4edbd0150149", - "reference": "409b0b4305273472f3754826e68f4edbd0150149", + "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", "shasum": "" }, "require": { @@ -3069,32 +3076,33 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.24" + "source": "https://github.com/laravel/prompts/tree/v0.1.25" }, - "time": "2024-06-17T13:58:22+00:00" + "time": "2024-08-12T22:06:33+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.3", + "version": "v1.3.6", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" + "reference": "f865a58ea3a0107c336b7045104c75243fa59d96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f865a58ea3a0107c336b7045104c75243fa59d96", + "reference": "f865a58ea3a0107c336b7045104c75243fa59d96", "shasum": "" }, "require": { "php": "^7.3|^8.0" }, "require-dev": { - "nesbot/carbon": "^2.61", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.61|^3.0", "pestphp/pest": "^1.21.3", "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11" + "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" }, "type": "library", "extra": { @@ -3131,7 +3139,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-11-08T14:08:06+00:00" + "time": "2024-11-11T17:06:04+00:00" }, { "name": "laravel/slack-notification-channel", @@ -3196,16 +3204,16 @@ }, { "name": "laravel/socialite", - "version": "v5.15.0", + "version": "v5.16.0", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "c8234bfb286a8210df8d62f94562c71bfda4a446" + "reference": "40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/c8234bfb286a8210df8d62f94562c71bfda4a446", - "reference": "c8234bfb286a8210df8d62f94562c71bfda4a446", + "url": "https://api.github.com/repos/laravel/socialite/zipball/40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf", + "reference": "40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf", "shasum": "" }, "require": { @@ -3264,20 +3272,20 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2024-06-11T13:33:20+00:00" + "time": "2024-09-03T09:46:57+00:00" }, { "name": "laravel/tinker", - "version": "v2.9.0", + "version": "v2.10.0", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe" + "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/502e0fe3f0415d06d5db1f83a472f0f3b754bafe", - "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "url": "https://api.github.com/repos/laravel/tinker/zipball/ba4d51eb56de7711b3a37d63aa0643e99a339ae5", + "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5", "shasum": "" }, "require": { @@ -3328,9 +3336,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.9.0" + "source": "https://github.com/laravel/tinker/tree/v2.10.0" }, - "time": "2024-01-04T16:10:04+00:00" + "time": "2024-09-23T13:32:56+00:00" }, { "name": "laravel/ui", @@ -3543,16 +3551,16 @@ }, { "name": "league/commonmark", - "version": "2.4.2", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" + "reference": "d150f911e0079e90ae3c106734c93137c184f932" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d150f911e0079e90ae3c106734c93137c184f932", + "reference": "d150f911e0079e90ae3c106734c93137c184f932", "shasum": "" }, "require": { @@ -3565,8 +3573,8 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.3", - "commonmark/commonmark.js": "0.30.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", "erusev/parsedown": "^1.0", @@ -3577,8 +3585,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 || ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0" }, @@ -3588,7 +3597,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "2.7-dev" } }, "autoload": { @@ -3645,7 +3654,7 @@ "type": "tidelift" } ], - "time": "2024-02-02T11:59:32+00:00" + "time": "2024-12-07T15:34:16+00:00" }, { "name": "league/config", @@ -3731,16 +3740,16 @@ }, { "name": "league/csv", - "version": "9.16.0", + "version": "9.18.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "998280c6c34bd67d8125fdc8b45bae28d761b440" + "reference": "b02d010e4055ae992247f6ffd1e7b103ef2a0790" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/998280c6c34bd67d8125fdc8b45bae28d761b440", - "reference": "998280c6c34bd67d8125fdc8b45bae28d761b440", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/b02d010e4055ae992247f6ffd1e7b103ef2a0790", + "reference": "b02d010e4055ae992247f6ffd1e7b103ef2a0790", "shasum": "" }, "require": { @@ -3748,17 +3757,16 @@ "php": "^8.1.2" }, "require-dev": { - "doctrine/collections": "^2.2.2", "ext-dom": "*", "ext-xdebug": "*", - "friendsofphp/php-cs-fixer": "^3.57.1", - "phpbench/phpbench": "^1.2.15", - "phpstan/phpstan": "^1.11.1", - "phpstan/phpstan-deprecation-rules": "^1.2.0", + "friendsofphp/php-cs-fixer": "^3.64.0", + "phpbench/phpbench": "^1.3.1", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-deprecation-rules": "^1.2.1", "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.0", - "phpunit/phpunit": "^10.5.16 || ^11.1.3", - "symfony/var-dumper": "^6.4.6 || ^7.0.7" + "phpstan/phpstan-strict-rules": "^1.6.1", + "phpunit/phpunit": "^10.5.16 || ^11.4.1", + "symfony/var-dumper": "^6.4.8 || ^7.1.5" }, "suggest": { "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", @@ -3776,7 +3784,7 @@ "src/functions_include.php" ], "psr-4": { - "League\\Csv\\": "src" + "League\\Csv\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3815,7 +3823,7 @@ "type": "github" } ], - "time": "2024-05-24T11:04:54+00:00" + "time": "2024-10-18T08:14:48+00:00" }, { "name": "league/event", @@ -3873,16 +3881,16 @@ }, { "name": "league/flysystem", - "version": "3.28.0", + "version": "3.29.1", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c" + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c", - "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", "shasum": "" }, "require": { @@ -3950,22 +3958,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.28.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" }, - "time": "2024-05-22T10:09:12+00:00" + "time": "2024-10-08T08:58:34+00:00" }, { "name": "league/flysystem-aws-s3-v3", - "version": "3.28.0", + "version": "3.29.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "22071ef1604bc776f5ff2468ac27a752514665c8" + "reference": "c6ff6d4606e48249b63f269eba7fabdb584e76a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/22071ef1604bc776f5ff2468ac27a752514665c8", - "reference": "22071ef1604bc776f5ff2468ac27a752514665c8", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/c6ff6d4606e48249b63f269eba7fabdb584e76a9", + "reference": "c6ff6d4606e48249b63f269eba7fabdb584e76a9", "shasum": "" }, "require": { @@ -4005,22 +4013,22 @@ "storage" ], "support": { - "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.28.0" + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.29.0" }, - "time": "2024-05-06T20:05:52+00:00" + "time": "2024-08-17T13:10:48+00:00" }, { "name": "league/flysystem-local", - "version": "3.28.0", + "version": "3.29.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40" + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/13f22ea8be526ea58c2ddff9e158ef7c296e4f40", - "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", "shasum": "" }, "require": { @@ -4054,22 +4062,22 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.28.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" }, - "time": "2024-05-06T20:05:52+00:00" + "time": "2024-08-09T21:24:39+00:00" }, { "name": "league/mime-type-detection", - "version": "1.15.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301" + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", - "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", "shasum": "" }, "require": { @@ -4100,7 +4108,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.15.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" }, "funding": [ { @@ -4112,7 +4120,7 @@ "type": "tidelift" } ], - "time": "2024-01-28T23:22:08+00:00" + "time": "2024-09-21T08:32:55+00:00" }, { "name": "league/oauth1-client", @@ -4454,16 +4462,16 @@ }, { "name": "livewire/livewire", - "version": "v3.5.1", + "version": "v3.5.12", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "da044261bb5c5449397f18fda3409f14acf47c0a" + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/da044261bb5c5449397f18fda3409f14acf47c0a", - "reference": "da044261bb5c5449397f18fda3409f14acf47c0a", + "url": "https://api.github.com/repos/livewire/livewire/zipball/3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", "shasum": "" }, "require": { @@ -4471,6 +4479,7 @@ "illuminate/routing": "^10.0|^11.0", "illuminate/support": "^10.0|^11.0", "illuminate/validation": "^10.0|^11.0", + "laravel/prompts": "^0.1.24|^0.2|^0.3", "league/mime-type-detection": "^1.9", "php": "^8.1", "symfony/console": "^6.0|^7.0", @@ -4479,7 +4488,6 @@ "require-dev": { "calebporzio/sushi": "^2.1", "laravel/framework": "^10.15.0|^11.0", - "laravel/prompts": "^0.1.6", "mockery/mockery": "^1.3.1", "orchestra/testbench": "^8.21.0|^9.0", "orchestra/testbench-dusk": "^8.24|^9.1", @@ -4518,7 +4526,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.5.1" + "source": "https://github.com/livewire/livewire/tree/v3.5.12" }, "funding": [ { @@ -4526,7 +4534,7 @@ "type": "github" } ], - "time": "2024-06-18T11:10:42+00:00" + "time": "2024-10-15T19:35:06+00:00" }, { "name": "masterminds/html5", @@ -4597,16 +4605,16 @@ }, { "name": "maximebf/debugbar", - "version": "v1.22.3", + "version": "v1.23.3", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "7aa9a27a0b1158ed5ad4e7175e8d3aee9a818b96" + "reference": "687400043d77943ef95e8417cb44e1673ee57844" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/7aa9a27a0b1158ed5ad4e7175e8d3aee9a818b96", - "reference": "7aa9a27a0b1158ed5ad4e7175e8d3aee9a818b96", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/687400043d77943ef95e8417cb44e1673ee57844", + "reference": "687400043d77943ef95e8417cb44e1673ee57844", "shasum": "" }, "require": { @@ -4628,7 +4636,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.22-dev" + "dev-master": "1.23-dev" } }, "autoload": { @@ -4659,22 +4667,22 @@ ], "support": { "issues": "https://github.com/maximebf/php-debugbar/issues", - "source": "https://github.com/maximebf/php-debugbar/tree/v1.22.3" + "source": "https://github.com/maximebf/php-debugbar/tree/v1.23.3" }, - "time": "2024-04-03T19:39:26+00:00" + "time": "2024-10-29T12:24:25+00:00" }, { "name": "monolog/monolog", - "version": "3.6.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654" + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4b18b21a5527a3d5ffdac2fd35d3ab25a9597654", - "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/32e515fdc02cdafbe4593e30a9350d486b125b67", + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67", "shasum": "" }, "require": { @@ -4694,12 +4702,14 @@ "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^10.5.17", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", "predis/predis": "^1.1 || ^2", - "ruflin/elastica": "^7", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -4750,7 +4760,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.6.0" + "source": "https://github.com/Seldaek/monolog/tree/3.8.0" }, "funding": [ { @@ -4762,20 +4772,20 @@ "type": "tidelift" } ], - "time": "2024-04-12T21:02:21+00:00" + "time": "2024-11-12T13:57:08+00:00" }, { "name": "mtdowling/jmespath.php", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b" + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", "shasum": "" }, "require": { @@ -4792,7 +4802,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { @@ -4826,9 +4836,9 @@ ], "support": { "issues": "https://github.com/jmespath/jmespath.php/issues", - "source": "https://github.com/jmespath/jmespath.php/tree/2.7.0" + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" }, - "time": "2023-08-25T10:54:48+00:00" + "time": "2024-09-04T18:46:31+00:00" }, { "name": "neitanod/forceutf8", @@ -4980,24 +4990,24 @@ }, { "name": "nette/schema", - "version": "v1.3.0", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188" + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", - "reference": "a6d3a6d1f545f01ef38e60f375d1cf1f4de98188", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.3" + "php": "8.1 - 8.4" }, "require-dev": { - "nette/tester": "^2.4", + "nette/tester": "^2.5.2", "phpstan/phpstan-nette": "^1.0", "tracy/tracy": "^2.8" }, @@ -5036,26 +5046,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.0" + "source": "https://github.com/nette/schema/tree/v1.3.2" }, - "time": "2023-12-11T11:54:22+00:00" + "time": "2024-10-06T23:10:23+00:00" }, { "name": "nette/utils", - "version": "v4.0.4", + "version": "v4.0.5", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", - "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", "shasum": "" }, "require": { - "php": ">=8.0 <8.4" + "php": "8.0 - 8.4" }, "conflict": { "nette/finder": "<3", @@ -5122,22 +5132,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.4" + "source": "https://github.com/nette/utils/tree/v4.0.5" }, - "time": "2024-01-17T16:50:36+00:00" + "time": "2024-08-07T15:39:19+00:00" }, { "name": "nikic/php-parser", - "version": "v4.19.1", + "version": "v4.19.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", "shasum": "" }, "require": { @@ -5146,7 +5156,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -5178,46 +5188,46 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" }, - "time": "2024-03-17T08:10:35+00:00" + "time": "2024-09-29T15:01:53+00:00" }, { "name": "nunomaduro/collision", - "version": "v7.10.0", + "version": "v7.11.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "49ec67fa7b002712da8526678abd651c09f375b2" + "reference": "994ea93df5d4132f69d3f1bd74730509df6e8a05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", - "reference": "49ec67fa7b002712da8526678abd651c09f375b2", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/994ea93df5d4132f69d3f1bd74730509df6e8a05", + "reference": "994ea93df5d4132f69d3f1bd74730509df6e8a05", "shasum": "" }, "require": { - "filp/whoops": "^2.15.3", + "filp/whoops": "^2.16.0", "nunomaduro/termwind": "^1.15.1", "php": "^8.1.0", - "symfony/console": "^6.3.4" + "symfony/console": "^6.4.12" }, "conflict": { "laravel/framework": ">=11.0.0" }, "require-dev": { - "brianium/paratest": "^7.3.0", - "laravel/framework": "^10.28.0", - "laravel/pint": "^1.13.3", - "laravel/sail": "^1.25.0", - "laravel/sanctum": "^3.3.1", - "laravel/tinker": "^2.8.2", - "nunomaduro/larastan": "^2.6.4", - "orchestra/testbench-core": "^8.13.0", - "pestphp/pest": "^2.23.2", - "phpunit/phpunit": "^10.4.1", - "sebastian/environment": "^6.0.1", - "spatie/laravel-ignition": "^2.3.1" + "brianium/paratest": "^7.3.1", + "laravel/framework": "^10.48.22", + "laravel/pint": "^1.18.1", + "laravel/sail": "^1.36.0", + "laravel/sanctum": "^3.3.3", + "laravel/tinker": "^2.10.0", + "nunomaduro/larastan": "^2.9.8", + "orchestra/testbench-core": "^8.28.3", + "pestphp/pest": "^2.35.1", + "phpunit/phpunit": "^10.5.36", + "sebastian/environment": "^6.1.0", + "spatie/laravel-ignition": "^2.8.0" }, "type": "library", "extra": { @@ -5276,37 +5286,36 @@ "type": "patreon" } ], - "time": "2023-10-11T15:45:01+00:00" + "time": "2024-10-15T15:12:40+00:00" }, { "name": "nunomaduro/termwind", - "version": "v1.15.1", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" + "reference": "dcf1ec3dfa36137b7ce41d43866644a7ab8fc257" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", - "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dcf1ec3dfa36137b7ce41d43866644a7ab8fc257", + "reference": "dcf1ec3dfa36137b7ce41d43866644a7ab8fc257", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "^8.0", - "symfony/console": "^5.3.0|^6.0.0" + "php": "^8.1", + "symfony/console": "^6.4.12" }, "require-dev": { - "ergebnis/phpstan-rules": "^1.0.", - "illuminate/console": "^8.0|^9.0", - "illuminate/support": "^8.0|^9.0", - "laravel/pint": "^1.0.0", - "pestphp/pest": "^1.21.0", - "pestphp/pest-plugin-mock": "^1.0", - "phpstan/phpstan": "^1.4.6", - "phpstan/phpstan-strict-rules": "^1.1.0", - "symfony/var-dumper": "^5.2.7|^6.0.0", + "illuminate/console": "^10.48.22", + "illuminate/support": "^10.48.22", + "laravel/pint": "^1.18.1", + "pestphp/pest": "^2", + "pestphp/pest-plugin-mock": "2.0.0", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^6.4.11", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -5346,7 +5355,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" + "source": "https://github.com/nunomaduro/termwind/tree/v1.16.0" }, "funding": [ { @@ -5362,20 +5371,20 @@ "type": "github" } ], - "time": "2023-02-08T01:06:31+00:00" + "time": "2024-10-15T15:27:12+00:00" }, { "name": "nyholm/psr7", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e" + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/aa5fc277a4f5508013d571341ade0c3886d4d00e", - "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", "shasum": "" }, "require": { @@ -5428,7 +5437,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.8.1" + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" }, "funding": [ { @@ -5440,7 +5449,7 @@ "type": "github" } ], - "time": "2023-11-13T09:31:12+00:00" + "time": "2024-09-09T07:06:30+00:00" }, { "name": "okvpn/clock-lts", @@ -5565,6 +5574,71 @@ ], "time": "2024-05-30T15:14:26+00:00" }, + { + "name": "osa-eg/laravel-teams-notification", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/osa-eg/laravel-teams-notification.git", + "reference": "76173689930aca92b5174a3b102e705279192c0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/osa-eg/laravel-teams-notification/zipball/76173689930aca92b5174a3b102e705279192c0f", + "reference": "76173689930aca92b5174a3b102e705279192c0f", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": ">=6.5", + "illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0", + "monolog/monolog": ">=1.0", + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Osama\\LaravelTeamsNotification\\LaravelTeamsNotificationServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Osama\\LaravelTeamsNotification\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Osama Saad", + "email": "osaad96eg@gmail.com" + } + ], + "description": "A Laravel package to send notifications to Microsoft Teams", + "keywords": [ + "Teams", + "adaptive-card", + "laravel", + "logging", + "microsoft-teams-workflow", + "notification", + "teams-connector", + "teams-webhock", + "teams-workflow", + "teams_logging" + ], + "support": { + "issues": "https://github.com/osa-eg/laravel-teams-notification/issues", + "source": "https://github.com/osa-eg/laravel-teams-notification/tree/v2.1.2" + }, + "time": "2024-09-23T05:24:48+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v2.7.0", @@ -5913,16 +5987,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", "shasum": "" }, "require": { @@ -5931,17 +6005,17 @@ "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.5", + "mockery/mockery": "~1.3.5 || ~1.6.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -5971,29 +6045,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.0" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-11-12T11:25:25+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", @@ -6029,22 +6103,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-11-09T15:12:26+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", "shasum": "" }, "require": { @@ -6052,13 +6126,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "1.9-dev" @@ -6094,7 +6168,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" }, "funding": [ { @@ -6106,20 +6180,20 @@ "type": "tidelift" } ], - "time": "2023-11-12T21:59:55+00:00" + "time": "2024-07-20T21:41:07+00:00" }, { "name": "phpseclib/phpseclib", - "version": "3.0.38", + "version": "3.0.42", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "b18b8788e51156c4dd97b7f220a31149a0052067" + "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/b18b8788e51156c4dd97b7f220a31149a0052067", - "reference": "b18b8788e51156c4dd97b7f220a31149a0052067", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db92f1b1987b12b13f248fe76c3a52cadb67bb98", + "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98", "shasum": "" }, "require": { @@ -6200,7 +6274,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.38" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.42" }, "funding": [ { @@ -6216,7 +6290,7 @@ "type": "tidelift" } ], - "time": "2024-06-17T10:11:32+00:00" + "time": "2024-09-16T03:06:04+00:00" }, { "name": "phpspec/prophecy", @@ -6289,16 +6363,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "1.33.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", "shasum": "" }, "require": { @@ -6330,30 +6404,30 @@ "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.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-10-13T11:25:22+00:00" }, { "name": "pragmarx/google2fa", - "version": "v8.0.1", + "version": "v8.0.3", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0", + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", "php": "^7.1|^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.18", + "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^7.5.15|^8.5|^9.0" }, "type": "library", @@ -6382,9 +6456,9 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" }, - "time": "2022-06-13T21:57:56+00:00" + "time": "2024-09-05T11:56:40+00:00" }, { "name": "pragmarx/google2fa-laravel", @@ -6885,16 +6959,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -6929,9 +7003,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", @@ -7467,24 +7541,24 @@ }, { "name": "sabberworm/php-css-parser", - "version": "v8.5.1", + "version": "v8.7.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152" + "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152", - "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/f414ff953002a9b18e3a116f5e462c56f21237cf", + "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf", "shasum": "" }, "require": { "ext-iconv": "*", - "php": ">=5.6.20" + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27" + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.40" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -7526,22 +7600,22 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.7.0" }, - "time": "2024-02-15T16:41:13+00:00" + "time": "2024-10-27T17:38:32+00:00" }, { "name": "sebastian/comparator", - "version": "5.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { @@ -7552,7 +7626,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -7597,7 +7671,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { @@ -7605,7 +7679,7 @@ "type": "github" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { "name": "sebastian/diff", @@ -7817,16 +7891,16 @@ }, { "name": "spatie/backtrace", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23" + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23", - "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9", + "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9", "shasum": "" }, "require": { @@ -7864,7 +7938,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.6.1" + "source": "https://github.com/spatie/backtrace/tree/1.6.2" }, "funding": [ { @@ -7876,20 +7950,20 @@ "type": "other" } ], - "time": "2024-04-24T13:22:11+00:00" + "time": "2024-07-22T08:21:24+00:00" }, { "name": "spatie/db-dumper", - "version": "3.6.0", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/spatie/db-dumper.git", - "reference": "faca5056830bccea04eadf07e8074669cb9e905e" + "reference": "22553ab8c34a9bb70645cb9bc2d9f236f3135705" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/db-dumper/zipball/faca5056830bccea04eadf07e8074669cb9e905e", - "reference": "faca5056830bccea04eadf07e8074669cb9e905e", + "url": "https://api.github.com/repos/spatie/db-dumper/zipball/22553ab8c34a9bb70645cb9bc2d9f236f3135705", + "reference": "22553ab8c34a9bb70645cb9bc2d9f236f3135705", "shasum": "" }, "require": { @@ -7927,7 +8001,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/db-dumper/tree/3.6.0" + "source": "https://github.com/spatie/db-dumper/tree/3.7.0" }, "funding": [ { @@ -7939,20 +8013,20 @@ "type": "github" } ], - "time": "2024-04-24T14:54:13+00:00" + "time": "2024-09-23T08:58:35+00:00" }, { "name": "spatie/error-solutions", - "version": "1.0.1", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/spatie/error-solutions.git", - "reference": "d60d4d2ef4b7701c86134ded959667cac6215233" + "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/error-solutions/zipball/d60d4d2ef4b7701c86134ded959667cac6215233", - "reference": "d60d4d2ef4b7701c86134ded959667cac6215233", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/ae7393122eda72eed7cc4f176d1e96ea444f2d67", + "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67", "shasum": "" }, "require": { @@ -8005,7 +8079,7 @@ ], "support": { "issues": "https://github.com/spatie/error-solutions/issues", - "source": "https://github.com/spatie/error-solutions/tree/1.0.1" + "source": "https://github.com/spatie/error-solutions/tree/1.1.1" }, "funding": [ { @@ -8013,20 +8087,20 @@ "type": "github" } ], - "time": "2024-06-21T10:09:00+00:00" + "time": "2024-07-25T11:06:04+00:00" }, { "name": "spatie/flare-client-php", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "097040ff51e660e0f6fc863684ac4b02c93fa234" + "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/097040ff51e660e0f6fc863684ac4b02c93fa234", - "reference": "097040ff51e660e0f6fc863684ac4b02c93fa234", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", + "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", "shasum": "" }, "require": { @@ -8044,7 +8118,7 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "spatie/phpunit-snapshot-assertions": "^4.0|^5.0" + "spatie/pest-plugin-snapshots": "^1.0|^2.0" }, "type": "library", "extra": { @@ -8074,7 +8148,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.7.0" + "source": "https://github.com/spatie/flare-client-php/tree/1.8.0" }, "funding": [ { @@ -8082,7 +8156,7 @@ "type": "github" } ], - "time": "2024-06-12T14:39:14+00:00" + "time": "2024-08-01T08:27:26+00:00" }, { "name": "spatie/ignition", @@ -8169,16 +8243,16 @@ }, { "name": "spatie/laravel-backup", - "version": "8.8.1", + "version": "8.8.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-backup.git", - "reference": "a9c2d2f726f4c60c2dc5d7c0c8380f72492638c2" + "reference": "5b672713283703a74c629ccd67b1d77eb57e24b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/a9c2d2f726f4c60c2dc5d7c0c8380f72492638c2", - "reference": "a9c2d2f726f4c60c2dc5d7c0c8380f72492638c2", + "url": "https://api.github.com/repos/spatie/laravel-backup/zipball/5b672713283703a74c629ccd67b1d77eb57e24b9", + "reference": "5b672713283703a74c629ccd67b1d77eb57e24b9", "shasum": "" }, "require": { @@ -8252,7 +8326,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-backup/issues", - "source": "https://github.com/spatie/laravel-backup/tree/8.8.1" + "source": "https://github.com/spatie/laravel-backup/tree/8.8.2" }, "funding": [ { @@ -8264,7 +8338,7 @@ "type": "other" } ], - "time": "2024-06-04T11:31:33+00:00" + "time": "2024-08-07T11:07:52+00:00" }, { "name": "spatie/laravel-ignition", @@ -8359,16 +8433,16 @@ }, { "name": "spatie/laravel-package-tools", - "version": "1.16.4", + "version": "1.16.5", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53" + "reference": "c7413972cf22ffdff97b68499c22baa04eddb6a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53", - "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/c7413972cf22ffdff97b68499c22baa04eddb6a2", + "reference": "c7413972cf22ffdff97b68499c22baa04eddb6a2", "shasum": "" }, "require": { @@ -8407,7 +8481,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.4" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.5" }, "funding": [ { @@ -8415,7 +8489,7 @@ "type": "github" } ], - "time": "2024-03-20T07:29:11+00:00" + "time": "2024-08-27T18:56:10+00:00" }, { "name": "spatie/laravel-signal-aware-command", @@ -8554,16 +8628,16 @@ }, { "name": "symfony/console", - "version": "v6.4.10", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "504974cbe43d05f83b201d6498c206f16fc0cdbc" + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/504974cbe43d05f83b201d6498c206f16fc0cdbc", - "reference": "504974cbe43d05f83b201d6498c206f16fc0cdbc", + "url": "https://api.github.com/repos/symfony/console/zipball/f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", "shasum": "" }, "require": { @@ -8628,7 +8702,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.10" + "source": "https://github.com/symfony/console/tree/v6.4.15" }, "funding": [ { @@ -8644,7 +8718,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:30:32+00:00" + "time": "2024-11-06T14:19:14+00:00" }, { "name": "symfony/css-selector", @@ -8781,16 +8855,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.4.8", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "ef836152bf13472dc5fb5b08b0c0c4cfeddc0fcc" + "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/ef836152bf13472dc5fb5b08b0c0c4cfeddc0fcc", - "reference": "ef836152bf13472dc5fb5b08b0c0c4cfeddc0fcc", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/9e024324511eeb00983ee76b9aedc3e6ecd993d9", + "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9", "shasum": "" }, "require": { @@ -8836,7 +8910,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.8" + "source": "https://github.com/symfony/error-handler/tree/v6.4.14" }, "funding": [ { @@ -8852,20 +8926,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-11-05T15:34:40+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b" + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8d7507f02b06e06815e56bb39aa0128e3806208b", - "reference": "8d7507f02b06e06815e56bb39aa0128e3806208b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { @@ -8916,7 +8990,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.8" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "funding": [ { @@ -8932,7 +9006,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -9012,16 +9086,16 @@ }, { "name": "symfony/finder", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" + "reference": "daea9eca0b08d0ed1dc9ab702a46128fd1be4958" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", - "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", + "url": "https://api.github.com/repos/symfony/finder/zipball/daea9eca0b08d0ed1dc9ab702a46128fd1be4958", + "reference": "daea9eca0b08d0ed1dc9ab702a46128fd1be4958", "shasum": "" }, "require": { @@ -9056,7 +9130,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.8" + "source": "https://github.com/symfony/finder/tree/v6.4.13" }, "funding": [ { @@ -9072,20 +9146,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-10-01T08:30:56+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.4.8", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "27de8cc95e11db7a50b027e71caaab9024545947" + "reference": "9b3165eb2f04aeaa1a5a2cfef73e63fe3b22dff6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/27de8cc95e11db7a50b027e71caaab9024545947", - "reference": "27de8cc95e11db7a50b027e71caaab9024545947", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9b3165eb2f04aeaa1a5a2cfef73e63fe3b22dff6", + "reference": "9b3165eb2f04aeaa1a5a2cfef73e63fe3b22dff6", "shasum": "" }, "require": { @@ -9095,12 +9169,12 @@ "symfony/polyfill-php83": "^1.27" }, "conflict": { - "symfony/cache": "<6.3" + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, "require-dev": { "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.3|^7.0", + "symfony/cache": "^6.4.12|^7.1.5", "symfony/dependency-injection": "^5.4|^6.0|^7.0", "symfony/expression-language": "^5.4|^6.0|^7.0", "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", @@ -9133,7 +9207,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.15" }, "funding": [ { @@ -9149,20 +9223,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-11-08T16:09:24+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.8", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6c519aa3f32adcfd1d1f18d923f6b227d9acf3c1" + "reference": "b002a5b3947653c5aee3adac2a024ea615fd3ff5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6c519aa3f32adcfd1d1f18d923f6b227d9acf3c1", - "reference": "6c519aa3f32adcfd1d1f18d923f6b227d9acf3c1", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b002a5b3947653c5aee3adac2a024ea615fd3ff5", + "reference": "b002a5b3947653c5aee3adac2a024ea615fd3ff5", "shasum": "" }, "require": { @@ -9247,7 +9321,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.8" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.15" }, "funding": [ { @@ -9263,20 +9337,20 @@ "type": "tidelift" } ], - "time": "2024-06-02T16:06:25+00:00" + "time": "2024-11-13T13:57:37+00:00" }, { "name": "symfony/mailer", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "76326421d44c07f7824b19487cfbf87870b37efc" + "reference": "c2f7e0d8d7ac8fe25faccf5d8cac462805db2663" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/76326421d44c07f7824b19487cfbf87870b37efc", - "reference": "76326421d44c07f7824b19487cfbf87870b37efc", + "url": "https://api.github.com/repos/symfony/mailer/zipball/c2f7e0d8d7ac8fe25faccf5d8cac462805db2663", + "reference": "c2f7e0d8d7ac8fe25faccf5d8cac462805db2663", "shasum": "" }, "require": { @@ -9327,7 +9401,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.4.8" + "source": "https://github.com/symfony/mailer/tree/v6.4.13" }, "funding": [ { @@ -9343,20 +9417,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/mime", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "618597ab8b78ac86d1c75a9d0b35540cda074f33" + "reference": "1de1cf14d99b12c7ebbb850491ec6ae3ed468855" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/618597ab8b78ac86d1c75a9d0b35540cda074f33", - "reference": "618597ab8b78ac86d1c75a9d0b35540cda074f33", + "url": "https://api.github.com/repos/symfony/mime/zipball/1de1cf14d99b12c7ebbb850491ec6ae3ed468855", + "reference": "1de1cf14d99b12c7ebbb850491ec6ae3ed468855", "shasum": "" }, "require": { @@ -9370,7 +9444,7 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.3.2" + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", @@ -9380,7 +9454,7 @@ "symfony/process": "^5.4|^6.4|^7.0", "symfony/property-access": "^5.4|^6.0|^7.0", "symfony/property-info": "^5.4|^6.0|^7.0", - "symfony/serializer": "^6.3.2|^7.0" + "symfony/serializer": "^6.4.3|^7.0.3" }, "type": "library", "autoload": { @@ -9412,7 +9486,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.4.8" + "source": "https://github.com/symfony/mime/tree/v6.4.13" }, "funding": [ { @@ -9428,24 +9502,24 @@ "type": "tidelift" } ], - "time": "2024-06-01T07:50:16+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -9491,7 +9565,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -9507,24 +9581,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -9569,7 +9643,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -9585,26 +9659,25 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -9653,7 +9726,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" }, "funding": [ { @@ -9669,24 +9742,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -9734,7 +9807,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -9750,24 +9823,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -9814,7 +9887,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -9830,97 +9903,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" - }, - { - "name": "symfony/polyfill-php72", - "version": "v1.30.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "10112722600777e02d2745716b70c5db4ca70442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", - "reference": "10112722600777e02d2745716b70c5db4ca70442", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -9967,7 +9967,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -9983,24 +9983,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9" + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", - "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -10043,7 +10043,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" }, "funding": [ { @@ -10059,24 +10059,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:35:24+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9" + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-uuid": "*" @@ -10122,7 +10122,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" }, "funding": [ { @@ -10138,20 +10138,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v6.4.8", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5" + "reference": "3cb242f059c14ae08591c5c4087d1fe443564392" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8d92dd79149f29e89ee0f480254db595f6a6a2c5", - "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5", + "url": "https://api.github.com/repos/symfony/process/zipball/3cb242f059c14ae08591c5c4087d1fe443564392", + "reference": "3cb242f059c14ae08591c5c4087d1fe443564392", "shasum": "" }, "require": { @@ -10183,7 +10183,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.8" + "source": "https://github.com/symfony/process/tree/v6.4.15" }, "funding": [ { @@ -10199,7 +10199,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-11-06T14:19:14+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -10292,16 +10292,16 @@ }, { "name": "symfony/routing", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58" + "reference": "640a74250d13f9c30d5ca045b6aaaabcc8215278" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", - "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", + "url": "https://api.github.com/repos/symfony/routing/zipball/640a74250d13f9c30d5ca045b6aaaabcc8215278", + "reference": "640a74250d13f9c30d5ca045b6aaaabcc8215278", "shasum": "" }, "require": { @@ -10355,7 +10355,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.8" + "source": "https://github.com/symfony/routing/tree/v6.4.13" }, "funding": [ { @@ -10371,7 +10371,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-10-01T08:30:56+00:00" }, { "name": "symfony/service-contracts", @@ -10458,16 +10458,16 @@ }, { "name": "symfony/string", - "version": "v6.4.10", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ccf9b30251719567bfd46494138327522b9a9446" + "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ccf9b30251719567bfd46494138327522b9a9446", - "reference": "ccf9b30251719567bfd46494138327522b9a9446", + "url": "https://api.github.com/repos/symfony/string/zipball/73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", + "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", "shasum": "" }, "require": { @@ -10524,7 +10524,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.10" + "source": "https://github.com/symfony/string/tree/v6.4.15" }, "funding": [ { @@ -10540,20 +10540,20 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:21:14+00:00" + "time": "2024-11-13T13:31:12+00:00" }, { "name": "symfony/translation", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a" + "reference": "bee9bfabfa8b4045a66bf82520e492cddbaffa66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/a002933b13989fc4bd0b58e04bf7eec5210e438a", - "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a", + "url": "https://api.github.com/repos/symfony/translation/zipball/bee9bfabfa8b4045a66bf82520e492cddbaffa66", + "reference": "bee9bfabfa8b4045a66bf82520e492cddbaffa66", "shasum": "" }, "require": { @@ -10619,7 +10619,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.8" + "source": "https://github.com/symfony/translation/tree/v6.4.13" }, "funding": [ { @@ -10635,7 +10635,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-27T18:14:25+00:00" }, { "name": "symfony/translation-contracts", @@ -10717,16 +10717,16 @@ }, { "name": "symfony/uid", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "35904eca37a84bb764c560cbfcac9f0ac2bcdbdf" + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/35904eca37a84bb764c560cbfcac9f0ac2bcdbdf", - "reference": "35904eca37a84bb764c560cbfcac9f0ac2bcdbdf", + "url": "https://api.github.com/repos/symfony/uid/zipball/18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007", "shasum": "" }, "require": { @@ -10771,7 +10771,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.4.8" + "source": "https://github.com/symfony/uid/tree/v6.4.13" }, "funding": [ { @@ -10787,20 +10787,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.8", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "ad23ca4312395f0a8a8633c831ef4c4ee542ed25" + "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ad23ca4312395f0a8a8633c831ef4c4ee542ed25", - "reference": "ad23ca4312395f0a8a8633c831ef4c4ee542ed25", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", + "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", "shasum": "" }, "require": { @@ -10856,7 +10856,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.8" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.15" }, "funding": [ { @@ -10872,7 +10872,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-11-08T15:28:48+00:00" }, { "name": "tecnickcom/tc-lib-barcode", @@ -11045,16 +11045,16 @@ }, { "name": "tecnickcom/tcpdf", - "version": "6.7.5", + "version": "6.7.7", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36" + "reference": "cfbc0028cc23f057f2baf9e73bdc238153c22086" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/cfbc0028cc23f057f2baf9e73bdc238153c22086", + "reference": "cfbc0028cc23f057f2baf9e73bdc238153c22086", "shasum": "" }, "require": { @@ -11105,7 +11105,7 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.7.5" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.7.7" }, "funding": [ { @@ -11113,7 +11113,7 @@ "type": "custom" } ], - "time": "2024-04-20T17:25:10+00:00" + "time": "2024-10-26T12:15:02+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -11427,23 +11427,23 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.2", + "graham-campbell/result-type": "^1.1.3", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2", + "phpoption/phpoption": "^1.9.3", "symfony/polyfill-ctype": "^1.24", "symfony/polyfill-mbstring": "^1.24", "symfony/polyfill-php80": "^1.24" @@ -11460,7 +11460,7 @@ "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "5.6-dev" @@ -11495,7 +11495,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" }, "funding": [ { @@ -11507,7 +11507,7 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:43:29+00:00" + "time": "2024-07-20T21:52:34+00:00" }, { "name": "voku/portable-ascii", @@ -11857,101 +11857,6 @@ ], "time": "2024-04-13T18:00:56+00:00" }, - { - "name": "brianium/paratest", - "version": "v7.3.1", - "source": { - "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.7", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0", - "phpunit/phpunit": "^10.4.2", - "sebastian/environment": "^6.0.1", - "symfony/console": "^6.3.4 || ^7.0.0", - "symfony/process": "^6.3.4 || ^7.0.0" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "ext-pcov": "*", - "ext-posix": "*", - "infection/infection": "^0.27.6", - "phpstan/phpstan": "^1.10.40", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.7.2", - "symfony/filesystem": "^6.3.1 || ^7.0.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/v7.3.1" - }, - "funding": [ - { - "url": "https://github.com/sponsors/Slamdunk", - "type": "github" - }, - { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" - } - ], - "time": "2023-10-31T09:24:17+00:00" - }, { "name": "clue/ndjson-react", "version": "v1.3.0", @@ -12018,24 +11923,24 @@ }, { "name": "cmgmyr/phploc", - "version": "8.0.3", + "version": "8.0.4", "source": { "type": "git", "url": "https://github.com/cmgmyr/phploc.git", - "reference": "e61d4729df46c5920ab61973bfa3f70f81a70b5f" + "reference": "b0c4ec71f40ef84c9893e1a7212a72e1098b90f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cmgmyr/phploc/zipball/e61d4729df46c5920ab61973bfa3f70f81a70b5f", - "reference": "e61d4729df46c5920ab61973bfa3f70f81a70b5f", + "url": "https://api.github.com/repos/cmgmyr/phploc/zipball/b0c4ec71f40ef84c9893e1a7212a72e1098b90f7", + "reference": "b0c4ec71f40ef84c9893e1a7212a72e1098b90f7", "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" + "phpunit/php-file-iterator": "^3.0|^4.0|^5.0", + "sebastian/cli-parser": "^1.0|^2.0|^3.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", @@ -12071,7 +11976,7 @@ "homepage": "https://github.com/cmgmyr/phploc", "support": { "issues": "https://github.com/cmgmyr/phploc/issues", - "source": "https://github.com/cmgmyr/phploc/tree/8.0.3" + "source": "https://github.com/cmgmyr/phploc/tree/8.0.4" }, "funding": [ { @@ -12079,34 +11984,42 @@ "type": "github" } ], - "time": "2023-08-05T16:49:39+00:00" + "time": "2024-10-31T19:26:53+00:00" }, { "name": "composer/pcre", - "version": "3.1.4", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "04229f163664973f68f38f6f73d917799168ef24" + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24", - "reference": "04229f163664973f68f38f6f73d917799168ef24", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -12134,7 +12047,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.4" + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { @@ -12150,28 +12063,28 @@ "type": "tidelift" } ], - "time": "2024-05-27T13:40:54+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -12215,7 +12128,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -12231,7 +12144,7 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "composer/xdebug-handler", @@ -12463,16 +12376,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + "reference": "a136842a532bac9ecd8a1c723852b09915d7db50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/a136842a532bac9ecd8a1c723852b09915d7db50", + "reference": "a136842a532bac9ecd8a1c723852b09915d7db50", "shasum": "" }, "require": { @@ -12520,9 +12433,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.0" }, - "time": "2024-01-02T13:46:09+00:00" + "time": "2024-11-07T15:11:20+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -12571,16 +12484,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "v1.5.2", + "version": "v1.5.3", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", "shasum": "" }, "require": { @@ -12621,22 +12534,22 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" }, - "time": "2022-03-02T22:36:06+00:00" + "time": "2024-04-30T00:40:11+00:00" }, { "name": "fidry/cpu-core-counter", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + "reference": "8520451a140d3f46ac33042715115e290cf5785f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", "shasum": "" }, "require": { @@ -12676,7 +12589,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" }, "funding": [ { @@ -12684,20 +12597,20 @@ "type": "github" } ], - "time": "2024-02-07T09:43:46+00:00" + "time": "2024-08-06T10:04:20+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.59.3", + "version": "v3.64.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "30ba9ecc2b0e5205e578fe29973c15653d9bfd29" + "reference": "58dd9c931c785a79739310aef5178928305ffa67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/30ba9ecc2b0e5205e578fe29973c15653d9bfd29", - "reference": "30ba9ecc2b0e5205e578fe29973c15653d9bfd29", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/58dd9c931c785a79739310aef5178928305ffa67", + "reference": "58dd9c931c785a79739310aef5178928305ffa67", "shasum": "" }, "require": { @@ -12779,7 +12692,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.59.3" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.64.0" }, "funding": [ { @@ -12787,7 +12700,7 @@ "type": "github" } ], - "time": "2024-06-16T14:17:03+00:00" + "time": "2024-08-30T23:09:38+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -12840,81 +12753,22 @@ }, "time": "2020-07-09T08:09:16+00:00" }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.6", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", - "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.6" - }, - "time": "2024-03-08T09:58:59+00:00" - }, { "name": "justinrainbow/json-schema", - "version": "v5.2.13", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", - "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", + "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", @@ -12925,11 +12779,6 @@ "bin/validate-json" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, "autoload": { "psr-4": { "JsonSchema\\": "src/JsonSchema/" @@ -12965,42 +12814,45 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/v5.2.13" + "source": "https://github.com/jsonrainbow/json-schema/tree/5.3.0" }, - "time": "2023-09-26T02:20:38+00:00" + "time": "2024-07-06T21:00:26+00:00" }, { "name": "larastan/larastan", - "version": "v2.9.7", + "version": "v2.9.11", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "5c805f636095cc2e0b659e3954775cf8f1dad1bb" + "reference": "54eccd35d1732b9ee4392c25aec606a6a9c521e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/5c805f636095cc2e0b659e3954775cf8f1dad1bb", - "reference": "5c805f636095cc2e0b659e3954775cf8f1dad1bb", + "url": "https://api.github.com/repos/larastan/larastan/zipball/54eccd35d1732b9ee4392c25aec606a6a9c521e7", + "reference": "54eccd35d1732b9ee4392c25aec606a6a9c521e7", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/container": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/contracts": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/database": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/http": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/pipeline": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/support": "^9.52.16 || ^10.28.0 || ^11.0", + "illuminate/console": "^9.52.16 || ^10.28.0 || ^11.16", + "illuminate/container": "^9.52.16 || ^10.28.0 || ^11.16", + "illuminate/contracts": "^9.52.16 || ^10.28.0 || ^11.16", + "illuminate/database": "^9.52.16 || ^10.28.0 || ^11.16", + "illuminate/http": "^9.52.16 || ^10.28.0 || ^11.16", + "illuminate/pipeline": "^9.52.16 || ^10.28.0 || ^11.16", + "illuminate/support": "^9.52.16 || ^10.28.0 || ^11.16", "php": "^8.0.2", "phpmyadmin/sql-parser": "^5.9.0", - "phpstan/phpstan": "^1.11.1" + "phpstan/phpstan": "^1.12.5" }, "require-dev": { "doctrine/coding-standard": "^12.0", + "laravel/framework": "^9.52.16 || ^10.28.0 || ^11.16", + "mockery/mockery": "^1.5.1", "nikic/php-parser": "^4.19.1", "orchestra/canvas": "^7.11.1 || ^8.11.0 || ^9.0.2", - "orchestra/testbench": "^7.33.0 || ^8.13.0 || ^9.0.3", + "orchestra/testbench-core": "^7.33.0 || ^8.13.0 || ^9.0.9", + "phpstan/phpstan-deprecation-rules": "^1.2", "phpunit/phpunit": "^9.6.13 || ^10.5.16" }, "suggest": { @@ -13049,7 +12901,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v2.9.7" + "source": "https://github.com/larastan/larastan/tree/v2.9.11" }, "funding": [ { @@ -13069,20 +12921,20 @@ "type": "patreon" } ], - "time": "2024-05-27T18:33:26+00:00" + "time": "2024-11-11T23:11:00+00:00" }, { "name": "league/container", - "version": "4.2.2", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/thephpleague/container.git", - "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88" + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/ff346319ca1ff0e78277dc2311a42107cc1aab88", - "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88", + "url": "https://api.github.com/repos/thephpleague/container/zipball/7ea728b013b9a156c409c6f0fc3624071b742dec", + "reference": "7ea728b013b9a156c409c6f0fc3624071b742dec", "shasum": "" }, "require": { @@ -13143,7 +12995,7 @@ ], "support": { "issues": "https://github.com/thephpleague/container/issues", - "source": "https://github.com/thephpleague/container/tree/4.2.2" + "source": "https://github.com/thephpleague/container/tree/4.2.4" }, "funding": [ { @@ -13151,7 +13003,7 @@ "type": "github" } ], - "time": "2024-03-13T13:12:53+00:00" + "time": "2024-11-10T12:42:13+00:00" }, { "name": "mockery/mockery", @@ -13238,16 +13090,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -13286,7 +13138,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -13294,20 +13146,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "netresearch/jsonmapper", - "version": "v4.4.1", + "version": "v4.5.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", - "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", "shasum": "" }, "require": { @@ -13343,22 +13195,22 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" }, - "time": "2024-01-31T06:18:54+00:00" + "time": "2024-09-08T10:13:13+00:00" }, { "name": "nunomaduro/phpinsights", - "version": "v2.11.0", + "version": "v2.12.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/phpinsights.git", - "reference": "f476219759a61aad988641476259465c77203383" + "reference": "5c12a8d626712de6db5e6d2db52b1eb4e9596650" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/phpinsights/zipball/f476219759a61aad988641476259465c77203383", - "reference": "f476219759a61aad988641476259465c77203383", + "url": "https://api.github.com/repos/nunomaduro/phpinsights/zipball/5c12a8d626712de6db5e6d2db52b1eb4e9596650", + "reference": "5c12a8d626712de6db5e6d2db52b1eb4e9596650", "shasum": "" }, "require": { @@ -13375,7 +13227,7 @@ "php-parallel-lint/php-parallel-lint": "^1.3.2", "psr/container": "^1.0|^2.0.2", "psr/simple-cache": "^1.0|^2.0|^3.0", - "sebastian/diff": "^4.0|^5.0.3", + "sebastian/diff": "^4.0|^5.0.3|^6.0", "slevomat/coding-standard": "^8.14.1", "squizlabs/php_codesniffer": "^3.7.2", "symfony/cache": "^5.4|^6.0|^7.0", @@ -13435,7 +13287,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/phpinsights/issues", - "source": "https://github.com/nunomaduro/phpinsights/tree/v2.11.0" + "source": "https://github.com/nunomaduro/phpinsights/tree/v2.12.0" }, "funding": [ { @@ -13451,7 +13303,7 @@ "type": "github" } ], - "time": "2023-11-30T10:54:50+00:00" + "time": "2024-11-11T14:42:55+00:00" }, { "name": "phar-io/manifest", @@ -13841,16 +13693,16 @@ }, { "name": "phpmyadmin/sql-parser", - "version": "5.9.0", + "version": "5.10.1", "source": { "type": "git", "url": "https://github.com/phpmyadmin/sql-parser.git", - "reference": "011fa18a4e55591fac6545a821921dd1d61c6984" + "reference": "b14fd66496a22d8dd7f7e2791edd9e8674422f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/011fa18a4e55591fac6545a821921dd1d61c6984", - "reference": "011fa18a4e55591fac6545a821921dd1d61c6984", + "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/b14fd66496a22d8dd7f7e2791edd9e8674422f17", + "reference": "b14fd66496a22d8dd7f7e2791edd9e8674422f17", "shasum": "" }, "require": { @@ -13868,8 +13720,7 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.9.12", "phpstan/phpstan-phpunit": "^1.3.3", - "phpunit/php-code-coverage": "*", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^8.5 || ^9.6", "psalm/plugin-phpunit": "^0.16.1", "vimeo/psalm": "^4.11", "zumba/json-serializer": "~3.0.2" @@ -13925,20 +13776,20 @@ "type": "other" } ], - "time": "2024-01-20T20:34:02+00:00" + "time": "2024-11-10T04:10:31+00:00" }, { "name": "phpstan/phpstan", - "version": "1.11.5", + "version": "1.12.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "490f0ae1c92b082f154681d7849aee776a7c1443" + "reference": "fc463b5d0fe906dcf19689be692c65c50406a071" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/490f0ae1c92b082f154681d7849aee776a7c1443", - "reference": "490f0ae1c92b082f154681d7849aee776a7c1443", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071", + "reference": "fc463b5d0fe906dcf19689be692c65c50406a071", "shasum": "" }, "require": { @@ -13983,36 +13834,36 @@ "type": "github" } ], - "time": "2024-06-17T15:10:54+00:00" + "time": "2024-11-11T15:37:09+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.15", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -14024,7 +13875,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -14053,7 +13904,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -14061,7 +13912,7 @@ "type": "github" } ], - "time": "2024-06-29T08:25:15+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -14308,16 +14159,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.29", + "version": "10.5.38", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8e9e80872b4e8064401788ee8a32d40b4455318f" + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e9e80872b4e8064401788ee8a32d40b4455318f", - "reference": "8e9e80872b4e8064401788ee8a32d40b4455318f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", "shasum": "" }, "require": { @@ -14331,14 +14182,14 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.15", + "phpunit/php-code-coverage": "^10.1.16", "phpunit/php-file-iterator": "^4.1.0", "phpunit/php-invoker": "^4.0.0", "phpunit/php-text-template": "^3.0.1", "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.1", + "sebastian/comparator": "^5.0.3", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", "sebastian/exporter": "^5.1.2", @@ -14389,7 +14240,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.29" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38" }, "funding": [ { @@ -14405,7 +14256,7 @@ "type": "tidelift" } ], - "time": "2024-07-30T11:08:00+00:00" + "time": "2024-10-28T13:06:21+00:00" }, { "name": "react/cache", @@ -14781,31 +14632,31 @@ }, { "name": "react/socket", - "version": "v1.15.0", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038" + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/216d3aec0b87f04a40ca04f481e6af01bdd1d038", - "reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "php": ">=5.3.0", - "react/dns": "^1.11", + "react/dns": "^1.13", "react/event-loop": "^1.2", - "react/promise": "^3 || ^2.6 || ^1.2.1", - "react/stream": "^1.2" + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4 || ^3 || ^2", + "react/async": "^4.3 || ^3.3 || ^2", "react/promise-stream": "^1.4", - "react/promise-timer": "^1.10" + "react/promise-timer": "^1.11" }, "type": "library", "autoload": { @@ -14849,7 +14700,7 @@ ], "support": { "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.15.0" + "source": "https://github.com/reactphp/socket/tree/v1.16.0" }, "funding": [ { @@ -14857,7 +14708,7 @@ "type": "open_collective" } ], - "time": "2023-12-15T11:02:10+00:00" + "time": "2024-07-26T10:38:09+00:00" }, { "name": "react/stream", @@ -15703,16 +15554,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.1", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877" + "reference": "70c08f8d20c0eb4fe56f26644dd94dae76a7f450" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/70c08f8d20c0eb4fe56f26644dd94dae76a7f450", + "reference": "70c08f8d20c0eb4fe56f26644dd94dae76a7f450", "shasum": "" }, "require": { @@ -15779,20 +15630,20 @@ "type": "open_collective" } ], - "time": "2024-05-22T21:24:41+00:00" + "time": "2024-11-12T09:53:29+00:00" }, { "name": "symfony/cache", - "version": "v6.4.8", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "287142df5579ce223c485b3872df3efae8390984" + "reference": "36fb8aa88833708e9f29014b6f15fac051a8b613" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/287142df5579ce223c485b3872df3efae8390984", - "reference": "287142df5579ce223c485b3872df3efae8390984", + "url": "https://api.github.com/repos/symfony/cache/zipball/36fb8aa88833708e9f29014b6f15fac051a8b613", + "reference": "36fb8aa88833708e9f29014b6f15fac051a8b613", "shasum": "" }, "require": { @@ -15859,7 +15710,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.8" + "source": "https://github.com/symfony/cache/tree/v6.4.14" }, "funding": [ { @@ -15875,7 +15726,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-11-05T15:34:40+00:00" }, { "name": "symfony/cache-contracts", @@ -16029,16 +15880,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d37529150e7081c51b3c5d5718c55a04a9503f3", - "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { @@ -16075,7 +15926,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.8" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -16091,20 +15942,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.8", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "61faba993e620fc22d4f0ab3b6bcf8fbb0d44b05" + "reference": "cb4073c905cd12b8496d24ac428a9228c1750670" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/61faba993e620fc22d4f0ab3b6bcf8fbb0d44b05", - "reference": "61faba993e620fc22d4f0ab3b6bcf8fbb0d44b05", + "url": "https://api.github.com/repos/symfony/http-client/zipball/cb4073c905cd12b8496d24ac428a9228c1750670", + "reference": "cb4073c905cd12b8496d24ac428a9228c1750670", "shasum": "" }, "require": { @@ -16168,7 +16019,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.8" + "source": "https://github.com/symfony/http-client/tree/v6.4.15" }, "funding": [ { @@ -16184,7 +16035,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-11-13T13:40:18+00:00" }, { "name": "symfony/http-client-contracts", @@ -16266,16 +16117,16 @@ }, { "name": "symfony/options-resolver", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "22ab9e9101ab18de37839074f8a1197f55590c1b" + "reference": "0a62a9f2504a8dd27083f89d21894ceb01cc59db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/22ab9e9101ab18de37839074f8a1197f55590c1b", - "reference": "22ab9e9101ab18de37839074f8a1197f55590c1b", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0a62a9f2504a8dd27083f89d21894ceb01cc59db", + "reference": "0a62a9f2504a8dd27083f89d21894ceb01cc59db", "shasum": "" }, "require": { @@ -16313,7 +16164,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.4.8" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.13" }, "funding": [ { @@ -16329,24 +16180,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", - "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -16389,7 +16240,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -16405,20 +16256,20 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "63e069eb616049632cde9674c46957819454b8aa" + "reference": "2cae0a6f8d04937d02f6d19806251e2104d54f92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/63e069eb616049632cde9674c46957819454b8aa", - "reference": "63e069eb616049632cde9674c46957819454b8aa", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2cae0a6f8d04937d02f6d19806251e2104d54f92", + "reference": "2cae0a6f8d04937d02f6d19806251e2104d54f92", "shasum": "" }, "require": { @@ -16451,7 +16302,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.4.8" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.13" }, "funding": [ { @@ -16467,20 +16318,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "792ca836f99b340f2e9ca9497c7953948c49a504" + "reference": "0f605f72a363f8743001038a176eeb2a11223b51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/792ca836f99b340f2e9ca9497c7953948c49a504", - "reference": "792ca836f99b340f2e9ca9497c7953948c49a504", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f605f72a363f8743001038a176eeb2a11223b51", + "reference": "0f605f72a363f8743001038a176eeb2a11223b51", "shasum": "" }, "require": { @@ -16528,7 +16379,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.8" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.13" }, "funding": [ { @@ -16544,7 +16395,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "theseer/tokenizer", @@ -16598,16 +16449,16 @@ }, { "name": "vimeo/psalm", - "version": "5.25.0", + "version": "5.26.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505" + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", - "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", "shasum": "" }, "require": { @@ -16628,7 +16479,7 @@ "felixfbecker/language-server-protocol": "^1.5.2", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.16", + "nikic/php-parser": "^4.17", "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", "sebastian/diff": "^4.0 || ^5.0 || ^6.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", @@ -16704,7 +16555,7 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm" }, - "time": "2024-06-16T15:08:35+00:00" + "time": "2024-09-08T18:53:08+00:00" } ], "aliases": [], @@ -16722,6 +16573,6 @@ "ext-mbstring": "*", "ext-pdo": "*" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/config/app.php b/config/app.php index bc74b4dd05..39898ff437 100755 --- a/config/app.php +++ b/config/app.php @@ -280,7 +280,6 @@ return [ Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, -// Illuminate\Translation\TranslationServiceProvider::class, //replaced on next line App\Providers\SnipeTranslationServiceProvider::class, //we REPLACE the default Laravel translator with our own Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, @@ -373,7 +372,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) + 'StorageHelper' => App\Helpers\StorageHelper::class, 'Icon' => App\Helpers\IconHelper::class, 'Socialite' => Laravel\Socialite\Facades\Socialite::class, diff --git a/config/version.php b/config/version.php index a080340b56..83815d8cab 100644 --- a/config/version.php +++ b/config/version.php @@ -1,10 +1,10 @@ 'v7.0.13-pre', - 'full_app_version' => 'v7.0.13-pre - build 15360-g4ab478bb9', - 'build_version' => '15360', + 'app_version' => 'v7.1.15', + 'full_app_version' => 'v7.1.15 - build 16052-g25bfd3e84', + 'build_version' => '16052', 'prerelease_version' => '', - 'hash_version' => 'g4ab478bb9', - 'full_hash' => 'v7.0.13-pre-111-g4ab478bb9', - 'branch' => 'develop', + 'hash_version' => 'g25bfd3e84', + 'full_hash' => 'v7.1.15-105-g25bfd3e84', + 'branch' => 'master', ); \ No newline at end of file diff --git a/database/factories/AccessoryFactory.php b/database/factories/AccessoryFactory.php index ea68e2b57f..6442472d5f 100644 --- a/database/factories/AccessoryFactory.php +++ b/database/factories/AccessoryFactory.php @@ -3,7 +3,6 @@ namespace Database\Factories; use App\Models\Accessory; -use App\Models\AccessoryCheckout; use App\Models\Category; use App\Models\Location; use App\Models\Manufacturer; @@ -156,4 +155,19 @@ class AccessoryFactory extends Factory ]); }); } + + public function checkedOutToUsers(array $users) + { + return $this->afterCreating(function (Accessory $accessory) use ($users) { + foreach ($users as $user) { + $accessory->checkouts()->create([ + 'accessory_id' => $accessory->id, + 'created_at' => Carbon::now(), + 'user_id' => 1, + 'assigned_to' => $user->id, + 'assigned_type' => User::class, + ]); + } + }); + } } diff --git a/database/factories/ComponentFactory.php b/database/factories/ComponentFactory.php index 51942fc694..786e780895 100644 --- a/database/factories/ComponentFactory.php +++ b/database/factories/ComponentFactory.php @@ -7,6 +7,7 @@ use App\Models\Asset; use App\Models\Category; use App\Models\Company; use App\Models\Component; +use App\Models\Manufacturer; use App\Models\Consumable; use App\Models\Location; use App\Models\User; @@ -30,6 +31,7 @@ class ComponentFactory extends Factory */ public function definition() { + return [ 'name' => $this->faker->text(20), 'category_id' => Category::factory(), @@ -42,12 +44,14 @@ class ComponentFactory extends Factory 'min_amt' => $this->faker->numberBetween($min = 1, $max = 2), 'company_id' => Company::factory(), 'supplier_id' => Supplier::factory(), + 'model_number' => $this->faker->numberBetween(1000000, 50000000), ]; } public function ramCrucial4() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial 4GB DDR3L-1600 SODIMM', 'category_id' => function () { @@ -55,6 +59,7 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, 'location_id' => Location::factory(), ]; }); @@ -62,7 +67,8 @@ class ComponentFactory extends Factory public function ramCrucial8() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial 8GB DDR3L-1600 SODIMM Memory for Mac', 'category_id' => function () { @@ -70,13 +76,15 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, ]; }); } public function ssdCrucial120() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial BX300 120GB SATA Internal SSD', 'category_id' => function () { @@ -84,13 +92,15 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, ]; }); } public function ssdCrucial240() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial BX300 240GB SATA Internal SSD', 'category_id' => function () { @@ -98,6 +108,7 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, ]; }); } diff --git a/database/factories/ImportFactory.php b/database/factories/ImportFactory.php new file mode 100644 index 0000000000..11fdbe17a9 --- /dev/null +++ b/database/factories/ImportFactory.php @@ -0,0 +1,168 @@ + + */ +class ImportFactory extends Factory +{ + /** + * @inheritdoc + */ + protected $model = Import::class; + + /** + * @inheritdoc + */ + public function definition() + { + return [ + 'name' => $this->faker->company, + 'file_path' => Str::random().'.csv', + 'filesize' => $this->faker->randomDigitNotNull(), + 'field_map' => null, + ]; + } + + /** + * Create an accessory import type. + * + * @return static + */ + public function accessory() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\AccessoriesImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Accessories"; + $attributes['import_type'] = 'accessory'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + + /** + * Create an asset import type. + * + * @return static + */ + public function asset() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\AssetsImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Assets"; + $attributes['import_type'] = 'asset'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + + /** + * Create a component import type. + * + * @return static + */ + public function component() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\ComponentsImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Components"; + $attributes['import_type'] = 'component'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + + /** + * Create a consumable import type. + * + * @return static + */ + public function consumable() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\ConsumablesImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Consumables"; + $attributes['import_type'] = 'consumable'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + + /** + * Create a license import type. + * + * @return static + */ + public function license() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\LicensesImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Licenses"; + $attributes['import_type'] = 'license'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + + /** + * Create a users import type. + * + * @return static + */ + public function users() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\UsersImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Employees"; + $attributes['import_type'] = 'user'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + + + + /** + * Create an asset model import type. + * + * @return static + */ + public function assetmodel() + { + return $this->state(function (array $attributes) { + $fileBuilder = Importing\AssetModelsImportFileBuilder::new(); + + $attributes['name'] = "{$attributes['name']} Asset Model"; + $attributes['import_type'] = 'assetModel'; + $attributes['header_row'] = $fileBuilder->toCsv()[0]; + $attributes['first_row'] = $fileBuilder->firstRow(); + + return $attributes; + }); + } + +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 1b469941b4..4b752b736f 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -7,6 +7,9 @@ use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use \Auth; +/** + * @extends Factory + */ class UserFactory extends Factory { /** 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 index 59c9728e2e..ddce33e903 100644 --- 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 @@ -26,7 +26,7 @@ class ChangeWebhookSettingsVariableType extends Migration public function down() { Schema::table('settings', function (Blueprint $table) { - $table->varchar('webhook_endpoint')->change(); + $table->string('webhook_endpoint')->change(); }); } diff --git a/database/migrations/2024_08_01_201721_add_required_notes_setting.php b/database/migrations/2024_08_01_201721_add_required_notes_setting.php new file mode 100644 index 0000000000..38fdd38c11 --- /dev/null +++ b/database/migrations/2024_08_01_201721_add_required_notes_setting.php @@ -0,0 +1,30 @@ +boolean('require_checkinout_notes')->nullable()->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('settings', function (Blueprint $table) { + if (Schema::hasColumn('settings', 'require_checkinout_notes')) { + $table->dropColumn('require_checkinout_notes'); + } + }); + } +}; diff --git a/database/migrations/2024_09_17_204302_change_user_id_to_created_by.php b/database/migrations/2024_09_17_204302_change_user_id_to_created_by.php index a57406ce10..3bef5948b6 100644 --- a/database/migrations/2024_09_17_204302_change_user_id_to_created_by.php +++ b/database/migrations/2024_09_17_204302_change_user_id_to_created_by.php @@ -42,7 +42,7 @@ return new class extends Migration } foreach ($this->existing_table_list() as $table) { - if (Schema::hasColumn($table, 'user_id')) { + if (Schema::hasColumn($table, 'created_by')) { Schema::table($table, function (Blueprint $table) { $table->renameColumn('created_by', 'user_id'); }); diff --git a/database/migrations/2024_10_23_162301_add_manufacturer_id_model_number_to_consumables.php b/database/migrations/2024_10_23_162301_add_manufacturer_id_model_number_to_consumables.php new file mode 100644 index 0000000000..0180ac0edd --- /dev/null +++ b/database/migrations/2024_10_23_162301_add_manufacturer_id_model_number_to_consumables.php @@ -0,0 +1,30 @@ +integer('manufacturer_id')->after('purchase_cost')->nullable()->default(null); + $table->string('model_number')->after('purchase_cost')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('components', function (Blueprint $table) { + $table->dropColumn('manufacturer_id'); + $table->dropColumn('model_number'); + }); + } +}; diff --git a/database/migrations/2024_11_06_211457_add_manager_indexes_to_location_and_user.php b/database/migrations/2024_11_06_211457_add_manager_indexes_to_location_and_user.php new file mode 100644 index 0000000000..d1a151c126 --- /dev/null +++ b/database/migrations/2024_11_06_211457_add_manager_indexes_to_location_and_user.php @@ -0,0 +1,34 @@ +index('manager_id'); + }); + Schema::table('users', function (Blueprint $table) { + $table->index('manager_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('locations', function (Blueprint $table) { + $table->dropIndex(['manager_id']); + }); + Schema::table('users', function (Blueprint $table) { + $table->dropIndex(['manager_id']); + }); + } +}; diff --git a/database/migrations/2024_11_07_113631_improve_manager_indexes_on_users_and_locations.php b/database/migrations/2024_11_07_113631_improve_manager_indexes_on_users_and_locations.php new file mode 100644 index 0000000000..cc2963fecc --- /dev/null +++ b/database/migrations/2024_11_07_113631_improve_manager_indexes_on_users_and_locations.php @@ -0,0 +1,39 @@ +dropIndex(['manager_id']); + $table->index(['manager_id','deleted_at']); + }); + Schema::table('users', function (Blueprint $table) { + $table->dropIndex(['manager_id']); + $table->index(['manager_id','deleted_at']); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('locations', function (Blueprint $table) { + $table->dropIndex(['manager_id','deleted_at']); + $table->index(['manager_id']); + }); + Schema::table('users', function (Blueprint $table) { + $table->dropIndex(['manager_id','deleted_at']); + $table->index(['manager_id']); + }); + } +}; diff --git a/database/seeders/LicenseSeeder.php b/database/seeders/LicenseSeeder.php index bc19727f7e..94e1e05cc1 100644 --- a/database/seeders/LicenseSeeder.php +++ b/database/seeders/LicenseSeeder.php @@ -8,6 +8,7 @@ use App\Models\LicenseSeat; use App\Models\Supplier; use App\Models\User; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\Log; class LicenseSeeder extends Seeder { @@ -20,7 +21,20 @@ class LicenseSeeder extends Seeder $this->call(CategorySeeder::class); } - $categoryIds = Category::all()->pluck('id'); + $categories = Category::where('category_type', 'license')->get(); + + $graphicsSoftwareCategory = $categories->first(fn($category) => $category->name === 'Graphics Software'); + $officeSoftwareCategory = $categories->first(fn($category) => $category->name === 'Office Software'); + + if (!$graphicsSoftwareCategory) { + Log::info('Graphics Software category not created. Using random category for seeding.'); + $graphicsSoftwareCategory = Category::inRandomOrder()->first(); + } + + if (!$officeSoftwareCategory) { + Log::info('Office Software category not created. Using random category for seeding.'); + $officeSoftwareCategory = Category::inRandomOrder()->first(); + } if (! Supplier::count()) { $this->call(SupplierSeeder::class); @@ -31,25 +45,25 @@ class LicenseSeeder extends Seeder $admin = User::where('permissions->superuser', '1')->first() ?? User::factory()->firstAdmin()->create(); License::factory()->count(1)->photoshop()->create([ - 'category_id' => $categoryIds->random(), + 'category_id' => $graphicsSoftwareCategory->id, 'supplier_id' => $supplierIds->random(), 'created_by' => $admin->id, ]); License::factory()->count(1)->acrobat()->create([ - 'category_id' => $categoryIds->random(), + 'category_id' => $officeSoftwareCategory->id, 'supplier_id' => $supplierIds->random(), 'created_by' => $admin->id, ]); License::factory()->count(1)->indesign()->create([ - 'category_id' => $categoryIds->random(), + 'category_id' => $graphicsSoftwareCategory->id, 'supplier_id' => $supplierIds->random(), 'created_by' => $admin->id, ]); License::factory()->count(1)->office()->create([ - 'category_id' => $categoryIds->random(), + 'category_id' => $officeSoftwareCategory->id, 'supplier_id' => $supplierIds->random(), 'created_by' => $admin->id, ]); diff --git a/package-lock.json b/package-lock.json index 5a98e56c6c..1cf415e096 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "bootstrap-colorpicker": "^2.5.3", "bootstrap-datepicker": "^1.10.0", "bootstrap-less": "^3.3.8", - "bootstrap-table": "1.23.2", + "bootstrap-table": "1.23.5", "canvas-confetti": "^1.9.3", "chart.js": "^2.9.4", "clipboard": "^2.0.11", @@ -23,10 +23,10 @@ "ekko-lightbox": "^5.1.1", "imagemin": "^8.0.1", "jquery-slimscroll": "^1.3.8", - "jquery-ui": "^1.14.0", + "jquery-ui": "^1.14.1", "jquery-validation": "^1.21.0", "jquery.iframe-transport": "^1.0.0", - "jspdf-autotable": "^3.8.3", + "jspdf-autotable": "^3.8.4", "less": "^4.2.0", "less-loader": "^6.0", "list.js": "^1.5.0", @@ -37,7 +37,7 @@ "signature_pad": "^4.2.0", "tableexport.jquery.plugin": "1.30.0", "tether": "^1.4.0", - "webpack": "^5.94.0" + "webpack": "^5.95.0" }, "devDependencies": { "all-contributors-cli": "^6.26.1", @@ -2105,10 +2105,28 @@ "@types/node": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/express": { "version": "4.17.21", @@ -2467,9 +2485,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "bin": { "acorn": "bin/acorn" }, @@ -2484,14 +2502,6 @@ "acorn": "^8" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-node": { "version": "1.8.2", "license": "Apache-2.0", @@ -3678,9 +3688,9 @@ "license": "MIT" }, "node_modules/bootstrap-table": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.2.tgz", - "integrity": "sha512-1IFiWFZzbKlleXgYEHdwHkX6rxlQMEx2N1tA8rJK/j08pI+NjIGnxFeXUL26yQLQ0U135eis/BX3OV1+anY25g==", + "version": "1.23.5", + "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.5.tgz", + "integrity": "sha512-9WByoSpJvA73gi2YYIlX6IWR74oZtBmSixul/Th8FTBtBd/kZRpbKESGTjhA3BA3AYTnfyY8Iy1KeRWPlV2GWQ==", "peerDependencies": { "jquery": "3" } @@ -3940,7 +3950,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "funding": [ { "type": "opencollective", @@ -3955,12 +3967,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4067,7 +4078,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001616", + "version": "1.0.30001677", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", + "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", "funding": [ { "type": "opencollective", @@ -4081,8 +4094,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/canvas-confetti": { "version": "1.9.3", @@ -5254,8 +5266,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.756", - "license": "ISC" + "version": "1.5.52", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", + "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==" }, "node_modules/elliptic": { "version": "6.5.5", @@ -5388,8 +5401,9 @@ "license": "MIT" }, "node_modules/escalade": { - "version": "3.1.2", - "license": "MIT", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -7052,9 +7066,9 @@ "license": "BSD-2-Clause" }, "node_modules/jquery-ui": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.0.tgz", - "integrity": "sha512-mPfYKBoRCf0MzaT2cyW5i3IuZ7PfTITaasO5OFLAQxrHuI+ZxruPa+4/K1OMNT8oElLWGtIxc9aRbyw20BKr8g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.1.tgz", + "integrity": "sha512-DhzsYH8VeIvOaxwi+B/2BCsFFT5EGjShdzOcm5DssWjtcpGWIMsn66rJciDA6jBruzNiLf1q0KvwMoX1uGNvnQ==", "dependencies": { "jquery": ">=1.12.0 <5.0.0" } @@ -7167,9 +7181,9 @@ } }, "node_modules/jspdf-autotable": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.8.3.tgz", - "integrity": "sha512-PQFdljBt+ijm6ZWXYxhZ54A/awV63UKcipYoA2+YGsz0BXXiXTIL/FIg+V30j7wPdSdzClfbB3qKX9UeuFylPQ==", + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.8.4.tgz", + "integrity": "sha512-rSffGoBsJYX83iTRv8Ft7FhqfgEL2nLpGAIiqruEQQ3e4r0qdLFbPUB7N9HAle0I3XgpisvyW751VHCqKUVOgQ==", "peerDependencies": { "jspdf": "^2.5.1" } @@ -8097,8 +8111,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "license": "MIT" + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -8549,8 +8564,9 @@ "optional": true }, "node_modules/picocolors": { - "version": "1.0.0", - "license": "ISC" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -10703,7 +10719,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.15", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -10718,10 +10736,9 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -10866,17 +10883,17 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dependencies": { - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", diff --git a/package.json b/package.json index 2b3ec19b63..2bc8762489 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "bootstrap-colorpicker": "^2.5.3", "bootstrap-datepicker": "^1.10.0", "bootstrap-less": "^3.3.8", - "bootstrap-table": "1.23.2", + "bootstrap-table": "1.23.5", "canvas-confetti": "^1.9.3", "chart.js": "^2.9.4", "clipboard": "^2.0.11", @@ -43,10 +43,10 @@ "ekko-lightbox": "^5.1.1", "imagemin": "^8.0.1", "jquery-slimscroll": "^1.3.8", - "jquery-ui": "^1.14.0", + "jquery-ui": "^1.14.1", "jquery-validation": "^1.21.0", "jquery.iframe-transport": "^1.0.0", - "jspdf-autotable": "^3.8.3", + "jspdf-autotable": "^3.8.4", "less": "^4.2.0", "less-loader": "^6.0", "list.js": "^1.5.0", @@ -57,6 +57,6 @@ "signature_pad": "^4.2.0", "tableexport.jquery.plugin": "1.30.0", "tether": "^1.4.0", - "webpack": "^5.94.0" + "webpack": "^5.95.0" } } diff --git a/phpunit.xml b/phpunit.xml index c21353eefb..30a78b7a63 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -21,6 +21,7 @@ + diff --git a/public/css/build/AdminLTE.css b/public/css/build/AdminLTE.css index 46885f5f02..ca7aa7a66c 100644 Binary files a/public/css/build/AdminLTE.css and b/public/css/build/AdminLTE.css differ diff --git a/public/css/build/app.css b/public/css/build/app.css index e724190f48..f54fa9d704 100644 Binary files a/public/css/build/app.css and b/public/css/build/app.css differ diff --git a/public/css/build/overrides.css b/public/css/build/overrides.css index 586a3f2a37..ba2cd38fcc 100644 Binary files a/public/css/build/overrides.css and b/public/css/build/overrides.css differ diff --git a/public/css/dist/all.css b/public/css/dist/all.css index 9518eff1cf..08440c410d 100644 Binary files a/public/css/dist/all.css and b/public/css/dist/all.css differ diff --git a/public/css/dist/bootstrap-table.css b/public/css/dist/bootstrap-table.css index 0b8274e974..89c11da6f8 100644 Binary files a/public/css/dist/bootstrap-table.css and b/public/css/dist/bootstrap-table.css differ diff --git a/public/css/dist/signature-pad.min.css b/public/css/dist/signature-pad.min.css index 218b9c2365..7065929572 100644 Binary files a/public/css/dist/signature-pad.min.css and b/public/css/dist/signature-pad.min.css differ diff --git a/public/css/dist/skins/_all-skins.css b/public/css/dist/skins/_all-skins.css index c4d2130f99..dd53f5635c 100644 Binary files a/public/css/dist/skins/_all-skins.css and b/public/css/dist/skins/_all-skins.css differ diff --git a/public/css/dist/skins/_all-skins.min.css b/public/css/dist/skins/_all-skins.min.css index c4d2130f99..9427f05bb0 100644 Binary files a/public/css/dist/skins/_all-skins.min.css and b/public/css/dist/skins/_all-skins.min.css differ diff --git a/public/css/dist/skins/skin-black-dark.css b/public/css/dist/skins/skin-black-dark.css index 89528e8ac4..c8f18f6979 100644 Binary files a/public/css/dist/skins/skin-black-dark.css and b/public/css/dist/skins/skin-black-dark.css differ diff --git a/public/css/dist/skins/skin-black-dark.min.css b/public/css/dist/skins/skin-black-dark.min.css index 89528e8ac4..bb68e3148a 100644 Binary files a/public/css/dist/skins/skin-black-dark.min.css and b/public/css/dist/skins/skin-black-dark.min.css differ diff --git a/public/css/dist/skins/skin-black.css b/public/css/dist/skins/skin-black.css index ab4d845e2a..2d3d0edc3b 100644 Binary files a/public/css/dist/skins/skin-black.css and b/public/css/dist/skins/skin-black.css differ diff --git a/public/css/dist/skins/skin-black.min.css b/public/css/dist/skins/skin-black.min.css index ab4d845e2a..5a5decc906 100644 Binary files a/public/css/dist/skins/skin-black.min.css and b/public/css/dist/skins/skin-black.min.css differ diff --git a/public/css/dist/skins/skin-blue-dark.css b/public/css/dist/skins/skin-blue-dark.css index bfac907779..aad99fcc92 100644 Binary files a/public/css/dist/skins/skin-blue-dark.css and b/public/css/dist/skins/skin-blue-dark.css differ diff --git a/public/css/dist/skins/skin-blue-dark.min.css b/public/css/dist/skins/skin-blue-dark.min.css index bfac907779..7923bbc585 100644 Binary files a/public/css/dist/skins/skin-blue-dark.min.css and b/public/css/dist/skins/skin-blue-dark.min.css differ diff --git a/public/css/dist/skins/skin-blue.css b/public/css/dist/skins/skin-blue.css index cac9000174..cdc62766ec 100644 Binary files a/public/css/dist/skins/skin-blue.css and b/public/css/dist/skins/skin-blue.css differ diff --git a/public/css/dist/skins/skin-blue.min.css b/public/css/dist/skins/skin-blue.min.css index cac9000174..09b0bba755 100644 Binary files a/public/css/dist/skins/skin-blue.min.css and b/public/css/dist/skins/skin-blue.min.css differ diff --git a/public/css/dist/skins/skin-contrast.css b/public/css/dist/skins/skin-contrast.css index 50dfc577e2..8f5c99c013 100644 Binary files a/public/css/dist/skins/skin-contrast.css and b/public/css/dist/skins/skin-contrast.css differ diff --git a/public/css/dist/skins/skin-contrast.min.css b/public/css/dist/skins/skin-contrast.min.css index 50dfc577e2..25ebe1f277 100644 Binary files a/public/css/dist/skins/skin-contrast.min.css and b/public/css/dist/skins/skin-contrast.min.css differ diff --git a/public/css/dist/skins/skin-green-dark.css b/public/css/dist/skins/skin-green-dark.css index 275fa58ade..6adec7c623 100644 Binary files a/public/css/dist/skins/skin-green-dark.css and b/public/css/dist/skins/skin-green-dark.css differ diff --git a/public/css/dist/skins/skin-green-dark.min.css b/public/css/dist/skins/skin-green-dark.min.css index 275fa58ade..70e85ed032 100644 Binary files a/public/css/dist/skins/skin-green-dark.min.css and b/public/css/dist/skins/skin-green-dark.min.css differ diff --git a/public/css/dist/skins/skin-green.css b/public/css/dist/skins/skin-green.css index fe0b851609..cd113d5e54 100644 Binary files a/public/css/dist/skins/skin-green.css and b/public/css/dist/skins/skin-green.css differ diff --git a/public/css/dist/skins/skin-green.min.css b/public/css/dist/skins/skin-green.min.css index fe0b851609..620d48e3f4 100644 Binary files a/public/css/dist/skins/skin-green.min.css and b/public/css/dist/skins/skin-green.min.css differ diff --git a/public/css/dist/skins/skin-orange-dark.css b/public/css/dist/skins/skin-orange-dark.css index 816c529ef2..4fe3148e7b 100644 Binary files a/public/css/dist/skins/skin-orange-dark.css and b/public/css/dist/skins/skin-orange-dark.css differ diff --git a/public/css/dist/skins/skin-orange-dark.min.css b/public/css/dist/skins/skin-orange-dark.min.css index 816c529ef2..ccebd4458e 100644 Binary files a/public/css/dist/skins/skin-orange-dark.min.css and b/public/css/dist/skins/skin-orange-dark.min.css differ diff --git a/public/css/dist/skins/skin-orange.css b/public/css/dist/skins/skin-orange.css index b26415b6a3..411d4994ac 100644 Binary files a/public/css/dist/skins/skin-orange.css and b/public/css/dist/skins/skin-orange.css differ diff --git a/public/css/dist/skins/skin-orange.min.css b/public/css/dist/skins/skin-orange.min.css index b26415b6a3..af01a70cff 100644 Binary files a/public/css/dist/skins/skin-orange.min.css and b/public/css/dist/skins/skin-orange.min.css differ diff --git a/public/css/dist/skins/skin-purple-dark.css b/public/css/dist/skins/skin-purple-dark.css index 88fe4ee35b..201e4709b1 100644 Binary files a/public/css/dist/skins/skin-purple-dark.css and b/public/css/dist/skins/skin-purple-dark.css differ diff --git a/public/css/dist/skins/skin-purple-dark.min.css b/public/css/dist/skins/skin-purple-dark.min.css index 88fe4ee35b..1b90bca8e3 100644 Binary files a/public/css/dist/skins/skin-purple-dark.min.css and b/public/css/dist/skins/skin-purple-dark.min.css differ diff --git a/public/css/dist/skins/skin-purple.css b/public/css/dist/skins/skin-purple.css index d4f67fee9b..344cbfce10 100644 Binary files a/public/css/dist/skins/skin-purple.css and b/public/css/dist/skins/skin-purple.css differ diff --git a/public/css/dist/skins/skin-purple.min.css b/public/css/dist/skins/skin-purple.min.css index d4f67fee9b..8a89eae1a6 100644 Binary files a/public/css/dist/skins/skin-purple.min.css and b/public/css/dist/skins/skin-purple.min.css differ diff --git a/public/css/dist/skins/skin-red-dark.css b/public/css/dist/skins/skin-red-dark.css index db532575b2..092c24bf11 100644 Binary files a/public/css/dist/skins/skin-red-dark.css and b/public/css/dist/skins/skin-red-dark.css differ diff --git a/public/css/dist/skins/skin-red-dark.min.css b/public/css/dist/skins/skin-red-dark.min.css index db532575b2..5601ce5055 100644 Binary files a/public/css/dist/skins/skin-red-dark.min.css and b/public/css/dist/skins/skin-red-dark.min.css differ diff --git a/public/css/dist/skins/skin-red.css b/public/css/dist/skins/skin-red.css index 0dc3658056..c6167ad739 100644 Binary files a/public/css/dist/skins/skin-red.css and b/public/css/dist/skins/skin-red.css differ diff --git a/public/css/dist/skins/skin-red.min.css b/public/css/dist/skins/skin-red.min.css index 0dc3658056..4f73db5918 100644 Binary files a/public/css/dist/skins/skin-red.min.css and b/public/css/dist/skins/skin-red.min.css differ diff --git a/public/css/dist/skins/skin-yellow-dark.css b/public/css/dist/skins/skin-yellow-dark.css index fb692ce058..115637bf1d 100644 Binary files a/public/css/dist/skins/skin-yellow-dark.css and b/public/css/dist/skins/skin-yellow-dark.css differ diff --git a/public/css/dist/skins/skin-yellow-dark.min.css b/public/css/dist/skins/skin-yellow-dark.min.css index fb692ce058..6000adc7b5 100644 Binary files a/public/css/dist/skins/skin-yellow-dark.min.css and b/public/css/dist/skins/skin-yellow-dark.min.css differ diff --git a/public/css/dist/skins/skin-yellow.css b/public/css/dist/skins/skin-yellow.css index 8aef4cb90e..5424b7f75d 100644 Binary files a/public/css/dist/skins/skin-yellow.css and b/public/css/dist/skins/skin-yellow.css differ diff --git a/public/css/dist/skins/skin-yellow.min.css b/public/css/dist/skins/skin-yellow.min.css index 8aef4cb90e..f64159f334 100644 Binary files a/public/css/dist/skins/skin-yellow.min.css and b/public/css/dist/skins/skin-yellow.min.css differ diff --git a/public/js/build/app.js b/public/js/build/app.js index 3c80510129..642de2534e 100644 Binary files a/public/js/build/app.js and b/public/js/build/app.js differ diff --git a/public/js/build/app.js.LICENSE.txt b/public/js/build/app.js.LICENSE.txt index cb3342c839..5b8cf0d08b 100644 Binary files a/public/js/build/app.js.LICENSE.txt and b/public/js/build/app.js.LICENSE.txt differ diff --git a/public/js/build/vendor.js b/public/js/build/vendor.js index 6f934b73d0..39600c0d68 100644 Binary files a/public/js/build/vendor.js and b/public/js/build/vendor.js differ diff --git a/public/js/dist/all.js b/public/js/dist/all.js index f6c3b6fa22..d0ceba6d97 100644 Binary files a/public/js/dist/all.js and b/public/js/dist/all.js differ diff --git a/public/js/dist/bootstrap-table-en-US.min.js b/public/js/dist/bootstrap-table-en-US.min.js index 186dd4aea7..4361492573 100644 Binary files a/public/js/dist/bootstrap-table-en-US.min.js and b/public/js/dist/bootstrap-table-en-US.min.js differ diff --git a/public/js/dist/bootstrap-table-locale-all.min.js b/public/js/dist/bootstrap-table-locale-all.min.js index b7fd59a5a4..760f77ba81 100644 Binary files a/public/js/dist/bootstrap-table-locale-all.min.js and b/public/js/dist/bootstrap-table-locale-all.min.js differ diff --git a/public/js/dist/bootstrap-table.js b/public/js/dist/bootstrap-table.js index 76f9864aab..79aca2ea19 100644 Binary files a/public/js/dist/bootstrap-table.js and b/public/js/dist/bootstrap-table.js differ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 80fa987f64..3bdb29df3d 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,25 +1,25 @@ { - "/js/build/app.js": "/js/build/app.js?id=5e9ac5c1a7e089f056fb1dba566193a6", - "/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f0b08873a06bb54daeee176a9459f4a9", - "/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=f4397c717b99fce41a633ca6edd5d1f4", - "/css/build/overrides.css": "/css/build/overrides.css?id=c9a07471f306075f2d5d5726119712dc", - "/css/build/app.css": "/css/build/app.css?id=6a514869e025840d60113c282b97d25f", - "/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9", - "/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690", - "/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=393aaa7b368b0670fc42434c8cca7dc7", - "/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898", - "/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=ad39859637dafa781288630f9d6d6523", - "/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=6fe68325d5356197672c27bc77cedcb4", - "/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=f8b26018a1533b9db864247daaf06daa", - "/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=6f0563e726c2fe4fab4026daaa5bfdf2", - "/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=4fa7aa3ba499c8f4e390eb8549da3f74", - "/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397", - "/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=553ee68741b5a392037abcf04da80adc", - "/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da", - "/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374", - "/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899", - "/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb", - "/css/dist/all.css": "/css/dist/all.css?id=8b0098987597c40f9e27a48c5dd2af9d", + "/js/build/app.js": "/js/build/app.js?id=5572f3bd32a6131651ab3022edd76941", + "/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=d34ae2483cbe2c77478c45f4006eba55", + "/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=b1c78591f51b52beab05b52f407ad6e6", + "/css/build/overrides.css": "/css/build/overrides.css?id=b1146d30456ed95c3d4a9b60ddc58313", + "/css/build/app.css": "/css/build/app.css?id=e5f692af9dd2c217455ad87e520ef32a", + "/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c", + "/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f", + "/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=53edc92eb2d272744bc7404ec259930e", + "/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb", + "/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=cdbb4f543fb448f03617c7ed9d2cbec3", + "/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5", + "/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=cfa51820f16533fd62b3c3d720e368ec", + "/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005", + "/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=98bd6927e46418c642fdd33fe56f4f26", + "/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f", + "/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=16dc04eb54142bd3c32c2768d365d988", + "/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460", + "/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091", + "/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=18787b3f00a3be7be38ee4e26cbd2a07", + "/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b", + "/css/dist/all.css": "/css/dist/all.css?id=fa28de72acfa72bfdd7ad4206a65d4eb", "/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7", "/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7", "/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde", @@ -90,26 +90,26 @@ "/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=541cafc702f56f57de95f3d1f792f428", "/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=51ade19e1b10d7a0031b18568a2b01d5", "/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=1cc408d68a27c3757b4460bbc542433e", - "/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=467938d6a524df8e62c4fb8ae5e7f3f1", - "/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=d4ef3db8dc9f809258218c187de5ee2a", - "/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=f4397c717b99fce41a633ca6edd5d1f4", - "/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=f0b08873a06bb54daeee176a9459f4a9", - "/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb", - "/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=0640e45bad692dcf62873c6e85904899", - "/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374", - "/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=da6c7997d9de2f8329142399f0ce50da", - "/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=553ee68741b5a392037abcf04da80adc", - "/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397", - "/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=4fa7aa3ba499c8f4e390eb8549da3f74", - "/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=6f0563e726c2fe4fab4026daaa5bfdf2", - "/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=f8b26018a1533b9db864247daaf06daa", - "/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=6fe68325d5356197672c27bc77cedcb4", - "/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=ad39859637dafa781288630f9d6d6523", - "/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898", - "/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=393aaa7b368b0670fc42434c8cca7dc7", - "/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690", - "/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=393d720a0f9aba560094fbc8d3b0c0f0", - "/js/build/vendor.js": "/js/build/vendor.js?id=5269eb5a6beb74f03387c78938cf17b2", - "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=6660df122e24940d42d03c06775fec7b", - "/js/dist/all.js": "/js/dist/all.js?id=e0a4b1a80b09333a460973137f39eab4" + "/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=c5445e15be5ce91a9ffef05e08ad6898", + "/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=0f6e85ae692d03a3b11cab445ff263ab", + "/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=b1c78591f51b52beab05b52f407ad6e6", + "/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=d34ae2483cbe2c77478c45f4006eba55", + "/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b", + "/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=18787b3f00a3be7be38ee4e26cbd2a07", + "/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091", + "/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=f0fbbb0ac729ea092578fb05ca615460", + "/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=16dc04eb54142bd3c32c2768d365d988", + "/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f", + "/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=98bd6927e46418c642fdd33fe56f4f26", + "/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=268041e902b019730c23ee3875838005", + "/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=cfa51820f16533fd62b3c3d720e368ec", + "/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5", + "/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=cdbb4f543fb448f03617c7ed9d2cbec3", + "/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb", + "/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=53edc92eb2d272744bc7404ec259930e", + "/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f", + "/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=c384582a6ba08903af353be861ffe74e", + "/js/build/vendor.js": "/js/build/vendor.js?id=89dffa552c6e3abe3a2aac6c9c7b466b", + "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=b4c3069f1a292527a96c058b77b28d69", + "/js/dist/all.js": "/js/dist/all.js?id=074c4b862b013dbffef2b075b6aae802" } diff --git a/public/vendor/livewire/livewire.esm.js b/public/vendor/livewire/livewire.esm.js index 0f6daa54ec..2d7ef4791c 100644 --- a/public/vendor/livewire/livewire.esm.js +++ b/public/vendor/livewire/livewire.esm.js @@ -1432,10 +1432,10 @@ var require_module_cjs = __commonJS({ }); } function cleanupElement(el) { - if (el._x_cleanups) { - while (el._x_cleanups.length) - el._x_cleanups.pop()(); - } + var _a, _b; + (_a = el._x_effects) == null ? void 0 : _a.forEach(dequeueJob); + while ((_b = el._x_cleanups) == null ? void 0 : _b.length) + el._x_cleanups.pop()(); } var observer = new MutationObserver(onMutate); var currentlyObserving = false; @@ -1673,27 +1673,23 @@ var require_module_cjs = __commonJS({ magics[name] = callback; } function injectMagics(obj, el) { + let memoizedUtilities = getUtilities(el); Object.entries(magics).forEach(([name, callback]) => { - let memoizedUtilities = null; - function getUtilities() { - if (memoizedUtilities) { - return memoizedUtilities; - } else { - let [utilities, cleanup] = getElementBoundUtilities(el); - memoizedUtilities = { interceptor, ...utilities }; - onElRemoved(el, cleanup); - return memoizedUtilities; - } - } Object.defineProperty(obj, `$${name}`, { get() { - return callback(el, getUtilities()); + return callback(el, memoizedUtilities); }, enumerable: false }); }); return obj; } + function getUtilities(el) { + let [utilities, cleanup] = getElementBoundUtilities(el); + let utils = { interceptor, ...utilities }; + onElRemoved(el, cleanup); + return utils; + } function tryCatch(el, expression, callback, ...args) { try { return callback(...args); @@ -2067,8 +2063,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } function destroyTree(root, walker = walk) { walker(root, (el) => { - cleanupAttributes(el); cleanupElement(el); + cleanupAttributes(el); }); } function warnAboutMissingPlugins() { @@ -2561,7 +2557,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } } function bindInputValue(el, value) { - if (el.type === "radio") { + if (isRadio(el)) { if (el.attributes.value === void 0) { el.value = value; } @@ -2572,7 +2568,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); el.checked = checkedAttrLooseCompare(el.value, value); } } - } else if (el.type === "checkbox") { + } else if (isCheckbox(el)) { if (Number.isInteger(value)) { el.value = value; } else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) { @@ -2648,34 +2644,37 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } return rawValue ? Boolean(rawValue) : null; } + var booleanAttributes = /* @__PURE__ */ new Set([ + "allowfullscreen", + "async", + "autofocus", + "autoplay", + "checked", + "controls", + "default", + "defer", + "disabled", + "formnovalidate", + "inert", + "ismap", + "itemscope", + "loop", + "multiple", + "muted", + "nomodule", + "novalidate", + "open", + "playsinline", + "readonly", + "required", + "reversed", + "selected", + "shadowrootclonable", + "shadowrootdelegatesfocus", + "shadowrootserializable" + ]); function isBooleanAttr(attrName) { - const booleanAttributes = [ - "disabled", - "checked", - "required", - "readonly", - "open", - "selected", - "autofocus", - "itemscope", - "multiple", - "novalidate", - "allowfullscreen", - "allowpaymentrequest", - "formnovalidate", - "autoplay", - "controls", - "loop", - "muted", - "playsinline", - "default", - "ismap", - "reversed", - "async", - "defer", - "nomodule" - ]; - return booleanAttributes.includes(attrName); + return booleanAttributes.has(attrName); } function attributeShouldntBePreservedIfFalsy(name) { return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name); @@ -2708,6 +2707,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } return attr; } + function isCheckbox(el) { + return el.type === "checkbox" || el.localName === "ui-checkbox" || el.localName === "ui-switch"; + } + function isRadio(el) { + return el.type === "radio" || el.localName === "ui-radio"; + } function debounce2(func, wait) { var timeout; return function() { @@ -2776,10 +2781,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); return stores[name]; } stores[name] = value; + initInterceptors(stores[name]); if (typeof value === "object" && value !== null && value.hasOwnProperty("init") && typeof value.init === "function") { stores[name].init(); } - initInterceptors(stores[name]); } function getStores() { return stores; @@ -2861,7 +2866,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); get raw() { return raw; }, - version: "3.14.1", + version: "3.14.3", flushAndStopDeferringMutations, dontAutoEvaluateFunctions, disableEffectScheduling, @@ -3070,7 +3075,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); placeInDom(el._x_teleport, target2, modifiers); }); }; - cleanup(() => clone2.remove()); + cleanup(() => mutateDom(() => { + clone2.remove(); + destroyTree(clone2); + })); }); var teleportContainerDuringClone = document.createElement("div"); function getTarget(expression) { @@ -3294,7 +3302,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); setValue(getInputValue(el, modifiers, e, getValue())); }); if (modifiers.includes("fill")) { - if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) { + if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) { setValue(getInputValue(el, modifiers, { target: el }, getValue())); } } @@ -3334,7 +3342,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); return mutateDom(() => { if (event instanceof CustomEvent && event.detail !== void 0) return event.detail !== null && event.detail !== void 0 ? event.detail : event.target.value; - else if (el.type === "checkbox") { + else if (isCheckbox(el)) { if (Array.isArray(currentValue)) { let newValue = null; if (modifiers.includes("number")) { @@ -3365,7 +3373,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); } else { let newValue; - if (el.type === "radio") { + if (isRadio(el)) { if (event.target.checked) { newValue = event.target.value; } else { @@ -3558,7 +3566,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); el._x_lookup = {}; effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey)); cleanup(() => { - Object.values(el._x_lookup).forEach((el2) => el2.remove()); + Object.values(el._x_lookup).forEach((el2) => mutateDom(() => { + destroyTree(el2); + el2.remove(); + })); delete el._x_prevKeys; delete el._x_lookup; }); @@ -3627,11 +3638,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } for (let i = 0; i < removes.length; i++) { let key = removes[i]; - if (!!lookup[key]._x_effects) { - lookup[key]._x_effects.forEach(dequeueJob); - } - lookup[key].remove(); - lookup[key] = null; + if (!(key in lookup)) + continue; + mutateDom(() => { + destroyTree(lookup[key]); + lookup[key].remove(); + }); delete lookup[key]; } for (let i = 0; i < moves.length; i++) { @@ -3752,12 +3764,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); el._x_currentIfEl = clone2; el._x_undoIf = () => { - walk(clone2, (node) => { - if (!!node._x_effects) { - node._x_effects.forEach(dequeueJob); - } + mutateDom(() => { + destroyTree(clone2); + clone2.remove(); }); - clone2.remove(); delete el._x_currentIfEl; }; return clone2; @@ -3812,9 +3822,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } }); -// ../alpine/packages/collapse/dist/module.cjs.js +// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js var require_module_cjs2 = __commonJS({ - "../alpine/packages/collapse/dist/module.cjs.js"(exports, module) { + "../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js"(exports, module) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; @@ -3887,7 +3897,7 @@ var require_module_cjs2 = __commonJS({ start: { height: current + "px" }, end: { height: full + "px" } }, () => el._x_isShown = true, () => { - if (Math.abs(el.getBoundingClientRect().height - full) < 1) { + if (el.getBoundingClientRect().height == full) { el.style.overflow = null; } }); @@ -3933,9 +3943,9 @@ var require_module_cjs2 = __commonJS({ } }); -// ../alpine/packages/focus/dist/module.cjs.js +// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js var require_module_cjs3 = __commonJS({ - "../alpine/packages/focus/dist/module.cjs.js"(exports, module) { + "../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js"(exports, module) { var __create2 = Object.create; var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; @@ -4935,9 +4945,9 @@ var require_module_cjs3 = __commonJS({ } }); -// ../alpine/packages/persist/dist/module.cjs.js +// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js var require_module_cjs4 = __commonJS({ - "../alpine/packages/persist/dist/module.cjs.js"(exports, module) { + "../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js"(exports, module) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; @@ -5024,9 +5034,9 @@ var require_module_cjs4 = __commonJS({ } }); -// ../alpine/packages/intersect/dist/module.cjs.js +// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js var require_module_cjs5 = __commonJS({ - "../alpine/packages/intersect/dist/module.cjs.js"(exports, module) { + "../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js"(exports, module) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; @@ -5106,8 +5116,80 @@ var require_module_cjs5 = __commonJS({ } }); -// ../alpine/packages/anchor/dist/module.cjs.js +// node_modules/@alpinejs/resize/dist/module.cjs.js var require_module_cjs6 = __commonJS({ + "node_modules/@alpinejs/resize/dist/module.cjs.js"(exports, module) { + var __defProp2 = Object.defineProperty; + var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; + var __getOwnPropNames2 = Object.getOwnPropertyNames; + var __hasOwnProp2 = Object.prototype.hasOwnProperty; + var __export = (target, all2) => { + for (var name in all2) + __defProp2(target, name, { get: all2[name], enumerable: true }); + }; + var __copyProps2 = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames2(from)) + if (!__hasOwnProp2.call(to, key) && key !== except) + __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable }); + } + return to; + }; + var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod); + var module_exports = {}; + __export(module_exports, { + default: () => module_default, + resize: () => src_default + }); + module.exports = __toCommonJS(module_exports); + function src_default(Alpine19) { + Alpine19.directive("resize", Alpine19.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => { + let evaluator = evaluateLater(expression); + let evaluate = (width, height) => { + evaluator(() => { + }, { scope: { "$width": width, "$height": height } }); + }; + let off = modifiers.includes("document") ? onDocumentResize(evaluate) : onElResize(el, evaluate); + cleanup(() => off()); + })); + } + function onElResize(el, callback) { + let observer = new ResizeObserver((entries) => { + let [width, height] = dimensions(entries); + callback(width, height); + }); + observer.observe(el); + return () => observer.disconnect(); + } + var documentResizeObserver; + var documentResizeObserverCallbacks = /* @__PURE__ */ new Set(); + function onDocumentResize(callback) { + documentResizeObserverCallbacks.add(callback); + if (!documentResizeObserver) { + documentResizeObserver = new ResizeObserver((entries) => { + let [width, height] = dimensions(entries); + documentResizeObserverCallbacks.forEach((i) => i(width, height)); + }); + documentResizeObserver.observe(document.documentElement); + } + return () => { + documentResizeObserverCallbacks.delete(callback); + }; + } + function dimensions(entries) { + let width, height; + for (let entry of entries) { + width = entry.borderBoxSize[0].inlineSize; + height = entry.borderBoxSize[0].blockSize; + } + return [width, height]; + } + var module_default = src_default; + } +}); + +// ../alpine/packages/anchor/dist/module.cjs.js +var require_module_cjs7 = __commonJS({ "../alpine/packages/anchor/dist/module.cjs.js"(exports, module) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; @@ -6645,7 +6727,7 @@ var require_nprogress = __commonJS({ }); // ../alpine/packages/morph/dist/module.cjs.js -var require_module_cjs7 = __commonJS({ +var require_module_cjs8 = __commonJS({ "../alpine/packages/morph/dist/module.cjs.js"(exports, module) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; @@ -7006,9 +7088,9 @@ var require_module_cjs7 = __commonJS({ } }); -// ../alpine/packages/mask/dist/module.cjs.js -var require_module_cjs8 = __commonJS({ - "../alpine/packages/mask/dist/module.cjs.js"(exports, module) { +// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js +var require_module_cjs9 = __commonJS({ + "../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js"(exports, module) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; @@ -8509,7 +8591,8 @@ var import_collapse = __toESM(require_module_cjs2()); var import_focus = __toESM(require_module_cjs3()); var import_persist2 = __toESM(require_module_cjs4()); var import_intersect = __toESM(require_module_cjs5()); -var import_anchor = __toESM(require_module_cjs6()); +var import_resize = __toESM(require_module_cjs6()); +var import_anchor = __toESM(require_module_cjs7()); // js/plugins/navigate/history.js var Snapshot = class { @@ -8660,7 +8743,7 @@ function extractDestinationFromLink(linkEl) { return createUrlObjectFromString(linkEl.getAttribute("href")); } function createUrlObjectFromString(urlString) { - return new URL(urlString, document.baseURI); + return urlString !== null && new URL(urlString, document.baseURI); } function getUriStringFromUrlObject(urlObject) { return urlObject.pathname + urlObject.search + urlObject.hash; @@ -8782,8 +8865,10 @@ function restoreScrollPositionOrScrollToTop() { } }; queueMicrotask(() => { - scroll(document.body); - document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll); + queueMicrotask(() => { + scroll(document.body); + document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll); + }); }); } @@ -8932,6 +9017,44 @@ function injectStyles() { document.head.appendChild(style); } +// js/plugins/navigate/popover.js +function packUpPersistedPopovers(persistedEl) { + persistedEl.querySelectorAll(":popover-open").forEach((el) => { + el.setAttribute("data-navigate-popover-open", ""); + let animations = el.getAnimations(); + el._pausedAnimations = animations.map((animation) => ({ + keyframes: animation.effect.getKeyframes(), + options: { + duration: animation.effect.getTiming().duration, + easing: animation.effect.getTiming().easing, + fill: animation.effect.getTiming().fill, + iterations: animation.effect.getTiming().iterations + }, + currentTime: animation.currentTime, + playState: animation.playState + })); + animations.forEach((i) => i.pause()); + }); +} +function unPackPersistedPopovers(persistedEl) { + persistedEl.querySelectorAll("[data-navigate-popover-open]").forEach((el) => { + el.removeAttribute("data-navigate-popover-open"); + queueMicrotask(() => { + if (!el.isConnected) + return; + el.showPopover(); + el.getAnimations().forEach((i) => i.finish()); + if (el._pausedAnimations) { + el._pausedAnimations.forEach(({ keyframes, options, currentTime, now, playState }) => { + let animation = el.animate(keyframes, options); + animation.currentTime = currentTime; + }); + delete el._pausedAnimations; + } + }); + }); +} + // js/plugins/navigate/page.js var oldBodyScriptTagHashes = []; var attributesExemptFromScriptTagHashing = [ @@ -9070,7 +9193,7 @@ var autofocus = false; function navigate_default(Alpine19) { Alpine19.navigate = (url) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: false, cached: false @@ -9087,17 +9210,21 @@ function navigate_default(Alpine19) { let shouldPrefetchOnHover = modifiers.includes("hover"); shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => { let destination = extractDestinationFromLink(el); + if (!destination) + return; prefetchHtml(destination, (html, finalDestination) => { storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination); }); }); whenThisLinkIsPressed(el, (whenItIsReleased) => { let destination = extractDestinationFromLink(el); + if (!destination) + return; prefetchHtml(destination, (html, finalDestination) => { storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination); }); whenItIsReleased(() => { - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: false, cached: false @@ -9111,7 +9238,7 @@ function navigate_default(Alpine19) { function navigateTo(destination, shouldPushToHistoryState = true) { showProgressBar && showAndStartProgressBar(); fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => { - fireEventForOtherLibariesToHookInto("alpine:navigating"); + fireEventForOtherLibrariesToHookInto("alpine:navigating"); restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway(); showProgressBar && finishAndHideProgressBar(); cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement(); @@ -9119,6 +9246,7 @@ function navigate_default(Alpine19) { preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => { enablePersist && storePersistantElementsForLater((persistedEl) => { packUpPersistedTeleports(persistedEl); + packUpPersistedPopovers(persistedEl); }); if (shouldPushToHistoryState) { updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination); @@ -9129,6 +9257,7 @@ function navigate_default(Alpine19) { removeAnyLeftOverStaleTeleportTargets(document.body); enablePersist && putPersistantElementsBack((persistedEl, newStub) => { unPackPersistedTeleports(persistedEl); + unPackPersistedPopovers(persistedEl); }); restoreScrollPositionOrScrollToTop(); afterNewScriptsAreDoneLoading(() => { @@ -9137,7 +9266,7 @@ function navigate_default(Alpine19) { autofocus && autofocusElementsWithTheAutofocusAttribute(); }); nowInitializeAlpineOnTheNewPage(Alpine19); - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); }); }); @@ -9147,7 +9276,7 @@ function navigate_default(Alpine19) { whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => { ifThePageBeingVisitedHasntBeenCached((url) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: true, cached: false @@ -9159,7 +9288,7 @@ function navigate_default(Alpine19) { }); }, (html, url, currentPageUrl, currentPageKey) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: true, cached: true @@ -9167,29 +9296,31 @@ function navigate_default(Alpine19) { if (prevented) return; storeScrollInformationInHtmlBeforeNavigatingAway(); - fireEventForOtherLibariesToHookInto("alpine:navigating"); + fireEventForOtherLibrariesToHookInto("alpine:navigating"); updateCurrentPageHtmlInSnapshotCacheForLaterBackButtonClicks(currentPageUrl, currentPageKey); preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => { enablePersist && storePersistantElementsForLater((persistedEl) => { packUpPersistedTeleports(persistedEl); + packUpPersistedPopovers(persistedEl); }); swapCurrentPageWithNewHtml(html, () => { removeAnyLeftOverStaleProgressBars(); removeAnyLeftOverStaleTeleportTargets(document.body); enablePersist && putPersistantElementsBack((persistedEl, newStub) => { unPackPersistedTeleports(persistedEl); + unPackPersistedPopovers(persistedEl); }); restoreScrollPositionOrScrollToTop(); andAfterAllThis(() => { autofocus && autofocusElementsWithTheAutofocusAttribute(); nowInitializeAlpineOnTheNewPage(Alpine19); - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); }); }); }); setTimeout(() => { - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); } function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) { @@ -9206,7 +9337,7 @@ function preventAlpineFromPickingUpDomChanges(Alpine19, callback) { }); }); } -function fireEventForOtherLibariesToHookInto(name, detail) { +function fireEventForOtherLibrariesToHookInto(name, detail) { let event = new CustomEvent(name, { cancelable: true, bubbles: true, @@ -9441,8 +9572,8 @@ function fromQueryString(search) { } // js/lifecycle.js -var import_morph = __toESM(require_module_cjs7()); -var import_mask = __toESM(require_module_cjs8()); +var import_morph = __toESM(require_module_cjs8()); +var import_mask = __toESM(require_module_cjs9()); var import_alpinejs5 = __toESM(require_module_cjs()); function start() { setTimeout(() => ensureLivewireScriptIsntMisplaced()); @@ -9451,6 +9582,7 @@ function start() { import_alpinejs5.default.plugin(import_morph.default); import_alpinejs5.default.plugin(history2); import_alpinejs5.default.plugin(import_intersect.default); + import_alpinejs5.default.plugin(import_resize.default); import_alpinejs5.default.plugin(import_collapse.default); import_alpinejs5.default.plugin(import_anchor.default); import_alpinejs5.default.plugin(import_focus.default); @@ -9721,6 +9853,7 @@ function morph2(component, el, html) { }, lookahead: false }); + trigger("morphed", { el, component }); } function isntElement(el) { return typeof el.hasAttribute !== "function"; @@ -10790,3 +10923,4 @@ focus-trap/dist/focus-trap.js: * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE *) */ +//# sourceMappingURL=livewire.esm.js.map diff --git a/public/vendor/livewire/livewire.esm.js.map b/public/vendor/livewire/livewire.esm.js.map new file mode 100644 index 0000000000..c4af6b08ae --- /dev/null +++ b/public/vendor/livewire/livewire.esm.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../alpine/packages/alpinejs/dist/module.cjs.js", "../../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js", "../../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js", "../../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js", "../../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js", "../node_modules/@alpinejs/resize/dist/module.cjs.js", "../../alpine/packages/anchor/dist/module.cjs.js", "../node_modules/nprogress/nprogress.js", "../../alpine/packages/morph/dist/module.cjs.js", "../../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js", "../js/utils.js", "../js/features/supportFileUploads.js", "../js/features/supportEntangle.js", "../js/hooks.js", "../js/request/modal.js", "../js/request/pool.js", "../js/request/commit.js", "../js/request/bus.js", "../js/request/index.js", "../js/$wire.js", "../js/component.js", "../js/store.js", "../js/events.js", "../js/directives.js", "../js/lifecycle.js", "../js/plugins/navigate/history.js", "../js/plugins/navigate/links.js", "../js/plugins/navigate/fetch.js", "../js/plugins/navigate/prefetch.js", "../js/plugins/navigate/teleport.js", "../js/plugins/navigate/scroll.js", "../js/plugins/navigate/persist.js", "../js/plugins/navigate/bar.js", "../js/plugins/navigate/popover.js", "../js/plugins/navigate/page.js", "../js/plugins/navigate/index.js", "../js/plugins/history/index.js", "../js/index.js", "../js/features/supportListeners.js", "../js/features/supportScriptsAndAssets.js", "../js/features/supportJsEvaluation.js", "../js/morph.js", "../js/features/supportMorphDom.js", "../js/features/supportDispatches.js", "../js/features/supportDisablingFormsDuringRequest.js", "../js/features/supportPropsAndModelables.js", "../js/features/supportFileDownloads.js", "../js/features/supportLazyLoading.js", "../js/features/supportQueryString.js", "../js/features/supportLaravelEcho.js", "../js/features/supportIsolating.js", "../js/features/supportNavigate.js", "../js/features/supportRedirects.js", "../js/directives/wire-transition.js", "../js/debounce.js", "../js/directives/wire-wildcard.js", "../js/directives/wire-navigate.js", "../js/directives/wire-confirm.js", "../js/directives/shared.js", "../js/directives/wire-offline.js", "../js/directives/wire-loading.js", "../js/directives/wire-stream.js", "../js/directives/wire-replace.js", "../js/directives/wire-ignore.js", "../js/directives/wire-dirty.js", "../js/directives/wire-model.js", "../js/directives/wire-init.js", "../js/directives/wire-poll.js"], + "sourcesContent": ["var __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __commonJS = (cb, mod) => function __require() {\n return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;\n};\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// node_modules/@vue/shared/dist/shared.cjs.js\nvar require_shared_cjs = __commonJS({\n \"node_modules/@vue/shared/dist/shared.cjs.js\"(exports) {\n \"use strict\";\n Object.defineProperty(exports, \"__esModule\", { value: true });\n function makeMap(str, expectsLowerCase) {\n const map = /* @__PURE__ */ Object.create(null);\n const list = str.split(\",\");\n for (let i = 0; i < list.length; i++) {\n map[list[i]] = true;\n }\n return expectsLowerCase ? (val) => !!map[val.toLowerCase()] : (val) => !!map[val];\n }\n var PatchFlagNames = {\n [\n 1\n /* TEXT */\n ]: `TEXT`,\n [\n 2\n /* CLASS */\n ]: `CLASS`,\n [\n 4\n /* STYLE */\n ]: `STYLE`,\n [\n 8\n /* PROPS */\n ]: `PROPS`,\n [\n 16\n /* FULL_PROPS */\n ]: `FULL_PROPS`,\n [\n 32\n /* HYDRATE_EVENTS */\n ]: `HYDRATE_EVENTS`,\n [\n 64\n /* STABLE_FRAGMENT */\n ]: `STABLE_FRAGMENT`,\n [\n 128\n /* KEYED_FRAGMENT */\n ]: `KEYED_FRAGMENT`,\n [\n 256\n /* UNKEYED_FRAGMENT */\n ]: `UNKEYED_FRAGMENT`,\n [\n 512\n /* NEED_PATCH */\n ]: `NEED_PATCH`,\n [\n 1024\n /* DYNAMIC_SLOTS */\n ]: `DYNAMIC_SLOTS`,\n [\n 2048\n /* DEV_ROOT_FRAGMENT */\n ]: `DEV_ROOT_FRAGMENT`,\n [\n -1\n /* HOISTED */\n ]: `HOISTED`,\n [\n -2\n /* BAIL */\n ]: `BAIL`\n };\n var slotFlagsText = {\n [\n 1\n /* STABLE */\n ]: \"STABLE\",\n [\n 2\n /* DYNAMIC */\n ]: \"DYNAMIC\",\n [\n 3\n /* FORWARDED */\n ]: \"FORWARDED\"\n };\n var GLOBALS_WHITE_LISTED = \"Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt\";\n var isGloballyWhitelisted = /* @__PURE__ */ makeMap(GLOBALS_WHITE_LISTED);\n var range = 2;\n function generateCodeFrame(source, start2 = 0, end = source.length) {\n let lines = source.split(/(\\r?\\n)/);\n const newlineSequences = lines.filter((_, idx) => idx % 2 === 1);\n lines = lines.filter((_, idx) => idx % 2 === 0);\n let count = 0;\n const res = [];\n for (let i = 0; i < lines.length; i++) {\n count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0);\n if (count >= start2) {\n for (let j = i - range; j <= i + range || end > count; j++) {\n if (j < 0 || j >= lines.length)\n continue;\n const line = j + 1;\n res.push(`${line}${\" \".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`);\n const lineLength = lines[j].length;\n const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0;\n if (j === i) {\n const pad = start2 - (count - (lineLength + newLineSeqLength));\n const length = Math.max(1, end > count ? lineLength - pad : end - start2);\n res.push(` | ` + \" \".repeat(pad) + \"^\".repeat(length));\n } else if (j > i) {\n if (end > count) {\n const length = Math.max(Math.min(end - count, lineLength), 1);\n res.push(` | ` + \"^\".repeat(length));\n }\n count += lineLength + newLineSeqLength;\n }\n }\n break;\n }\n }\n return res.join(\"\\n\");\n }\n var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`;\n var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs);\n var isBooleanAttr2 = /* @__PURE__ */ makeMap(specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`);\n var unsafeAttrCharRE = /[>/=\"'\\u0009\\u000a\\u000c\\u0020]/;\n var attrValidationCache = {};\n function isSSRSafeAttrName(name) {\n if (attrValidationCache.hasOwnProperty(name)) {\n return attrValidationCache[name];\n }\n const isUnsafe = unsafeAttrCharRE.test(name);\n if (isUnsafe) {\n console.error(`unsafe attribute name: ${name}`);\n }\n return attrValidationCache[name] = !isUnsafe;\n }\n var propsToAttrMap = {\n acceptCharset: \"accept-charset\",\n className: \"class\",\n htmlFor: \"for\",\n httpEquiv: \"http-equiv\"\n };\n var isNoUnitNumericStyleProp = /* @__PURE__ */ makeMap(`animation-iteration-count,border-image-outset,border-image-slice,border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,grid-row,grid-row-end,grid-row-span,grid-row-start,grid-column,grid-column-end,grid-column-span,grid-column-start,font-weight,line-clamp,line-height,opacity,order,orphans,tab-size,widows,z-index,zoom,fill-opacity,flood-opacity,stop-opacity,stroke-dasharray,stroke-dashoffset,stroke-miterlimit,stroke-opacity,stroke-width`);\n var isKnownAttr = /* @__PURE__ */ makeMap(`accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`);\n function normalizeStyle(value) {\n if (isArray(value)) {\n const res = {};\n for (let i = 0; i < value.length; i++) {\n const item = value[i];\n const normalized = normalizeStyle(isString(item) ? parseStringStyle(item) : item);\n if (normalized) {\n for (const key in normalized) {\n res[key] = normalized[key];\n }\n }\n }\n return res;\n } else if (isObject(value)) {\n return value;\n }\n }\n var listDelimiterRE = /;(?![^(]*\\))/g;\n var propertyDelimiterRE = /:(.+)/;\n function parseStringStyle(cssText) {\n const ret = {};\n cssText.split(listDelimiterRE).forEach((item) => {\n if (item) {\n const tmp = item.split(propertyDelimiterRE);\n tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());\n }\n });\n return ret;\n }\n function stringifyStyle(styles) {\n let ret = \"\";\n if (!styles) {\n return ret;\n }\n for (const key in styles) {\n const value = styles[key];\n const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key);\n if (isString(value) || typeof value === \"number\" && isNoUnitNumericStyleProp(normalizedKey)) {\n ret += `${normalizedKey}:${value};`;\n }\n }\n return ret;\n }\n function normalizeClass(value) {\n let res = \"\";\n if (isString(value)) {\n res = value;\n } else if (isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n const normalized = normalizeClass(value[i]);\n if (normalized) {\n res += normalized + \" \";\n }\n }\n } else if (isObject(value)) {\n for (const name in value) {\n if (value[name]) {\n res += name + \" \";\n }\n }\n }\n return res.trim();\n }\n var HTML_TAGS = \"html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot\";\n var SVG_TAGS = \"svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view\";\n var VOID_TAGS = \"area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr\";\n var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS);\n var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS);\n var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS);\n var escapeRE = /[\"'&<>]/;\n function escapeHtml(string) {\n const str = \"\" + string;\n const match = escapeRE.exec(str);\n if (!match) {\n return str;\n }\n let html = \"\";\n let escaped;\n let index;\n let lastIndex = 0;\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34:\n escaped = \""\";\n break;\n case 38:\n escaped = \"&\";\n break;\n case 39:\n escaped = \"'\";\n break;\n case 60:\n escaped = \"<\";\n break;\n case 62:\n escaped = \">\";\n break;\n default:\n continue;\n }\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n lastIndex = index + 1;\n html += escaped;\n }\n return lastIndex !== index ? html + str.substring(lastIndex, index) : html;\n }\n var commentStripRE = /^-?>||--!>| looseEqual(item, val));\n }\n var toDisplayString = (val) => {\n return val == null ? \"\" : isObject(val) ? JSON.stringify(val, replacer, 2) : String(val);\n };\n var replacer = (_key, val) => {\n if (isMap(val)) {\n return {\n [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val2]) => {\n entries[`${key} =>`] = val2;\n return entries;\n }, {})\n };\n } else if (isSet(val)) {\n return {\n [`Set(${val.size})`]: [...val.values()]\n };\n } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) {\n return String(val);\n }\n return val;\n };\n var babelParserDefaultPlugins = [\n \"bigInt\",\n \"optionalChaining\",\n \"nullishCoalescingOperator\"\n ];\n var EMPTY_OBJ = Object.freeze({});\n var EMPTY_ARR = Object.freeze([]);\n var NOOP = () => {\n };\n var NO = () => false;\n var onRE = /^on[^a-z]/;\n var isOn = (key) => onRE.test(key);\n var isModelListener = (key) => key.startsWith(\"onUpdate:\");\n var extend = Object.assign;\n var remove = (arr, el) => {\n const i = arr.indexOf(el);\n if (i > -1) {\n arr.splice(i, 1);\n }\n };\n var hasOwnProperty = Object.prototype.hasOwnProperty;\n var hasOwn = (val, key) => hasOwnProperty.call(val, key);\n var isArray = Array.isArray;\n var isMap = (val) => toTypeString(val) === \"[object Map]\";\n var isSet = (val) => toTypeString(val) === \"[object Set]\";\n var isDate = (val) => val instanceof Date;\n var isFunction = (val) => typeof val === \"function\";\n var isString = (val) => typeof val === \"string\";\n var isSymbol = (val) => typeof val === \"symbol\";\n var isObject = (val) => val !== null && typeof val === \"object\";\n var isPromise = (val) => {\n return isObject(val) && isFunction(val.then) && isFunction(val.catch);\n };\n var objectToString = Object.prototype.toString;\n var toTypeString = (value) => objectToString.call(value);\n var toRawType = (value) => {\n return toTypeString(value).slice(8, -1);\n };\n var isPlainObject = (val) => toTypeString(val) === \"[object Object]\";\n var isIntegerKey = (key) => isString(key) && key !== \"NaN\" && key[0] !== \"-\" && \"\" + parseInt(key, 10) === key;\n var isReservedProp = /* @__PURE__ */ makeMap(\n // the leading comma is intentional so empty string \"\" is also included\n \",key,ref,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted\"\n );\n var cacheStringFunction = (fn) => {\n const cache = /* @__PURE__ */ Object.create(null);\n return (str) => {\n const hit = cache[str];\n return hit || (cache[str] = fn(str));\n };\n };\n var camelizeRE = /-(\\w)/g;\n var camelize = cacheStringFunction((str) => {\n return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : \"\");\n });\n var hyphenateRE = /\\B([A-Z])/g;\n var hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, \"-$1\").toLowerCase());\n var capitalize = cacheStringFunction((str) => str.charAt(0).toUpperCase() + str.slice(1));\n var toHandlerKey = cacheStringFunction((str) => str ? `on${capitalize(str)}` : ``);\n var hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue);\n var invokeArrayFns = (fns, arg) => {\n for (let i = 0; i < fns.length; i++) {\n fns[i](arg);\n }\n };\n var def = (obj, key, value) => {\n Object.defineProperty(obj, key, {\n configurable: true,\n enumerable: false,\n value\n });\n };\n var toNumber = (val) => {\n const n = parseFloat(val);\n return isNaN(n) ? val : n;\n };\n var _globalThis;\n var getGlobalThis = () => {\n return _globalThis || (_globalThis = typeof globalThis !== \"undefined\" ? globalThis : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : typeof global !== \"undefined\" ? global : {});\n };\n exports.EMPTY_ARR = EMPTY_ARR;\n exports.EMPTY_OBJ = EMPTY_OBJ;\n exports.NO = NO;\n exports.NOOP = NOOP;\n exports.PatchFlagNames = PatchFlagNames;\n exports.babelParserDefaultPlugins = babelParserDefaultPlugins;\n exports.camelize = camelize;\n exports.capitalize = capitalize;\n exports.def = def;\n exports.escapeHtml = escapeHtml;\n exports.escapeHtmlComment = escapeHtmlComment;\n exports.extend = extend;\n exports.generateCodeFrame = generateCodeFrame;\n exports.getGlobalThis = getGlobalThis;\n exports.hasChanged = hasChanged;\n exports.hasOwn = hasOwn;\n exports.hyphenate = hyphenate;\n exports.invokeArrayFns = invokeArrayFns;\n exports.isArray = isArray;\n exports.isBooleanAttr = isBooleanAttr2;\n exports.isDate = isDate;\n exports.isFunction = isFunction;\n exports.isGloballyWhitelisted = isGloballyWhitelisted;\n exports.isHTMLTag = isHTMLTag;\n exports.isIntegerKey = isIntegerKey;\n exports.isKnownAttr = isKnownAttr;\n exports.isMap = isMap;\n exports.isModelListener = isModelListener;\n exports.isNoUnitNumericStyleProp = isNoUnitNumericStyleProp;\n exports.isObject = isObject;\n exports.isOn = isOn;\n exports.isPlainObject = isPlainObject;\n exports.isPromise = isPromise;\n exports.isReservedProp = isReservedProp;\n exports.isSSRSafeAttrName = isSSRSafeAttrName;\n exports.isSVGTag = isSVGTag;\n exports.isSet = isSet;\n exports.isSpecialBooleanAttr = isSpecialBooleanAttr;\n exports.isString = isString;\n exports.isSymbol = isSymbol;\n exports.isVoidTag = isVoidTag;\n exports.looseEqual = looseEqual;\n exports.looseIndexOf = looseIndexOf;\n exports.makeMap = makeMap;\n exports.normalizeClass = normalizeClass;\n exports.normalizeStyle = normalizeStyle;\n exports.objectToString = objectToString;\n exports.parseStringStyle = parseStringStyle;\n exports.propsToAttrMap = propsToAttrMap;\n exports.remove = remove;\n exports.slotFlagsText = slotFlagsText;\n exports.stringifyStyle = stringifyStyle;\n exports.toDisplayString = toDisplayString;\n exports.toHandlerKey = toHandlerKey;\n exports.toNumber = toNumber;\n exports.toRawType = toRawType;\n exports.toTypeString = toTypeString;\n }\n});\n\n// node_modules/@vue/shared/index.js\nvar require_shared = __commonJS({\n \"node_modules/@vue/shared/index.js\"(exports, module2) {\n \"use strict\";\n if (false) {\n module2.exports = null;\n } else {\n module2.exports = require_shared_cjs();\n }\n }\n});\n\n// node_modules/@vue/reactivity/dist/reactivity.cjs.js\nvar require_reactivity_cjs = __commonJS({\n \"node_modules/@vue/reactivity/dist/reactivity.cjs.js\"(exports) {\n \"use strict\";\n Object.defineProperty(exports, \"__esModule\", { value: true });\n var shared = require_shared();\n var targetMap = /* @__PURE__ */ new WeakMap();\n var effectStack = [];\n var activeEffect;\n var ITERATE_KEY = Symbol(\"iterate\");\n var MAP_KEY_ITERATE_KEY = Symbol(\"Map key iterate\");\n function isEffect(fn) {\n return fn && fn._isEffect === true;\n }\n function effect3(fn, options = shared.EMPTY_OBJ) {\n if (isEffect(fn)) {\n fn = fn.raw;\n }\n const effect4 = createReactiveEffect(fn, options);\n if (!options.lazy) {\n effect4();\n }\n return effect4;\n }\n function stop2(effect4) {\n if (effect4.active) {\n cleanup(effect4);\n if (effect4.options.onStop) {\n effect4.options.onStop();\n }\n effect4.active = false;\n }\n }\n var uid = 0;\n function createReactiveEffect(fn, options) {\n const effect4 = function reactiveEffect() {\n if (!effect4.active) {\n return fn();\n }\n if (!effectStack.includes(effect4)) {\n cleanup(effect4);\n try {\n enableTracking();\n effectStack.push(effect4);\n activeEffect = effect4;\n return fn();\n } finally {\n effectStack.pop();\n resetTracking();\n activeEffect = effectStack[effectStack.length - 1];\n }\n }\n };\n effect4.id = uid++;\n effect4.allowRecurse = !!options.allowRecurse;\n effect4._isEffect = true;\n effect4.active = true;\n effect4.raw = fn;\n effect4.deps = [];\n effect4.options = options;\n return effect4;\n }\n function cleanup(effect4) {\n const { deps } = effect4;\n if (deps.length) {\n for (let i = 0; i < deps.length; i++) {\n deps[i].delete(effect4);\n }\n deps.length = 0;\n }\n }\n var shouldTrack = true;\n var trackStack = [];\n function pauseTracking() {\n trackStack.push(shouldTrack);\n shouldTrack = false;\n }\n function enableTracking() {\n trackStack.push(shouldTrack);\n shouldTrack = true;\n }\n function resetTracking() {\n const last = trackStack.pop();\n shouldTrack = last === void 0 ? true : last;\n }\n function track(target, type, key) {\n if (!shouldTrack || activeEffect === void 0) {\n return;\n }\n let depsMap = targetMap.get(target);\n if (!depsMap) {\n targetMap.set(target, depsMap = /* @__PURE__ */ new Map());\n }\n let dep = depsMap.get(key);\n if (!dep) {\n depsMap.set(key, dep = /* @__PURE__ */ new Set());\n }\n if (!dep.has(activeEffect)) {\n dep.add(activeEffect);\n activeEffect.deps.push(dep);\n if (activeEffect.options.onTrack) {\n activeEffect.options.onTrack({\n effect: activeEffect,\n target,\n type,\n key\n });\n }\n }\n }\n function trigger(target, type, key, newValue, oldValue, oldTarget) {\n const depsMap = targetMap.get(target);\n if (!depsMap) {\n return;\n }\n const effects = /* @__PURE__ */ new Set();\n const add2 = (effectsToAdd) => {\n if (effectsToAdd) {\n effectsToAdd.forEach((effect4) => {\n if (effect4 !== activeEffect || effect4.allowRecurse) {\n effects.add(effect4);\n }\n });\n }\n };\n if (type === \"clear\") {\n depsMap.forEach(add2);\n } else if (key === \"length\" && shared.isArray(target)) {\n depsMap.forEach((dep, key2) => {\n if (key2 === \"length\" || key2 >= newValue) {\n add2(dep);\n }\n });\n } else {\n if (key !== void 0) {\n add2(depsMap.get(key));\n }\n switch (type) {\n case \"add\":\n if (!shared.isArray(target)) {\n add2(depsMap.get(ITERATE_KEY));\n if (shared.isMap(target)) {\n add2(depsMap.get(MAP_KEY_ITERATE_KEY));\n }\n } else if (shared.isIntegerKey(key)) {\n add2(depsMap.get(\"length\"));\n }\n break;\n case \"delete\":\n if (!shared.isArray(target)) {\n add2(depsMap.get(ITERATE_KEY));\n if (shared.isMap(target)) {\n add2(depsMap.get(MAP_KEY_ITERATE_KEY));\n }\n }\n break;\n case \"set\":\n if (shared.isMap(target)) {\n add2(depsMap.get(ITERATE_KEY));\n }\n break;\n }\n }\n const run = (effect4) => {\n if (effect4.options.onTrigger) {\n effect4.options.onTrigger({\n effect: effect4,\n target,\n key,\n type,\n newValue,\n oldValue,\n oldTarget\n });\n }\n if (effect4.options.scheduler) {\n effect4.options.scheduler(effect4);\n } else {\n effect4();\n }\n };\n effects.forEach(run);\n }\n var isNonTrackableKeys = /* @__PURE__ */ shared.makeMap(`__proto__,__v_isRef,__isVue`);\n var builtInSymbols = new Set(Object.getOwnPropertyNames(Symbol).map((key) => Symbol[key]).filter(shared.isSymbol));\n var get2 = /* @__PURE__ */ createGetter();\n var shallowGet = /* @__PURE__ */ createGetter(false, true);\n var readonlyGet = /* @__PURE__ */ createGetter(true);\n var shallowReadonlyGet = /* @__PURE__ */ createGetter(true, true);\n var arrayInstrumentations = /* @__PURE__ */ createArrayInstrumentations();\n function createArrayInstrumentations() {\n const instrumentations = {};\n [\"includes\", \"indexOf\", \"lastIndexOf\"].forEach((key) => {\n instrumentations[key] = function(...args) {\n const arr = toRaw2(this);\n for (let i = 0, l = this.length; i < l; i++) {\n track(arr, \"get\", i + \"\");\n }\n const res = arr[key](...args);\n if (res === -1 || res === false) {\n return arr[key](...args.map(toRaw2));\n } else {\n return res;\n }\n };\n });\n [\"push\", \"pop\", \"shift\", \"unshift\", \"splice\"].forEach((key) => {\n instrumentations[key] = function(...args) {\n pauseTracking();\n const res = toRaw2(this)[key].apply(this, args);\n resetTracking();\n return res;\n };\n });\n return instrumentations;\n }\n function createGetter(isReadonly2 = false, shallow = false) {\n return function get3(target, key, receiver) {\n if (key === \"__v_isReactive\") {\n return !isReadonly2;\n } else if (key === \"__v_isReadonly\") {\n return isReadonly2;\n } else if (key === \"__v_raw\" && receiver === (isReadonly2 ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target)) {\n return target;\n }\n const targetIsArray = shared.isArray(target);\n if (!isReadonly2 && targetIsArray && shared.hasOwn(arrayInstrumentations, key)) {\n return Reflect.get(arrayInstrumentations, key, receiver);\n }\n const res = Reflect.get(target, key, receiver);\n if (shared.isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {\n return res;\n }\n if (!isReadonly2) {\n track(target, \"get\", key);\n }\n if (shallow) {\n return res;\n }\n if (isRef(res)) {\n const shouldUnwrap = !targetIsArray || !shared.isIntegerKey(key);\n return shouldUnwrap ? res.value : res;\n }\n if (shared.isObject(res)) {\n return isReadonly2 ? readonly(res) : reactive3(res);\n }\n return res;\n };\n }\n var set2 = /* @__PURE__ */ createSetter();\n var shallowSet = /* @__PURE__ */ createSetter(true);\n function createSetter(shallow = false) {\n return function set3(target, key, value, receiver) {\n let oldValue = target[key];\n if (!shallow) {\n value = toRaw2(value);\n oldValue = toRaw2(oldValue);\n if (!shared.isArray(target) && isRef(oldValue) && !isRef(value)) {\n oldValue.value = value;\n return true;\n }\n }\n const hadKey = shared.isArray(target) && shared.isIntegerKey(key) ? Number(key) < target.length : shared.hasOwn(target, key);\n const result = Reflect.set(target, key, value, receiver);\n if (target === toRaw2(receiver)) {\n if (!hadKey) {\n trigger(target, \"add\", key, value);\n } else if (shared.hasChanged(value, oldValue)) {\n trigger(target, \"set\", key, value, oldValue);\n }\n }\n return result;\n };\n }\n function deleteProperty(target, key) {\n const hadKey = shared.hasOwn(target, key);\n const oldValue = target[key];\n const result = Reflect.deleteProperty(target, key);\n if (result && hadKey) {\n trigger(target, \"delete\", key, void 0, oldValue);\n }\n return result;\n }\n function has(target, key) {\n const result = Reflect.has(target, key);\n if (!shared.isSymbol(key) || !builtInSymbols.has(key)) {\n track(target, \"has\", key);\n }\n return result;\n }\n function ownKeys(target) {\n track(target, \"iterate\", shared.isArray(target) ? \"length\" : ITERATE_KEY);\n return Reflect.ownKeys(target);\n }\n var mutableHandlers = {\n get: get2,\n set: set2,\n deleteProperty,\n has,\n ownKeys\n };\n var readonlyHandlers = {\n get: readonlyGet,\n set(target, key) {\n {\n console.warn(`Set operation on key \"${String(key)}\" failed: target is readonly.`, target);\n }\n return true;\n },\n deleteProperty(target, key) {\n {\n console.warn(`Delete operation on key \"${String(key)}\" failed: target is readonly.`, target);\n }\n return true;\n }\n };\n var shallowReactiveHandlers = /* @__PURE__ */ shared.extend({}, mutableHandlers, {\n get: shallowGet,\n set: shallowSet\n });\n var shallowReadonlyHandlers = /* @__PURE__ */ shared.extend({}, readonlyHandlers, {\n get: shallowReadonlyGet\n });\n var toReactive = (value) => shared.isObject(value) ? reactive3(value) : value;\n var toReadonly = (value) => shared.isObject(value) ? readonly(value) : value;\n var toShallow = (value) => value;\n var getProto = (v) => Reflect.getPrototypeOf(v);\n function get$1(target, key, isReadonly2 = false, isShallow = false) {\n target = target[\n \"__v_raw\"\n /* RAW */\n ];\n const rawTarget = toRaw2(target);\n const rawKey = toRaw2(key);\n if (key !== rawKey) {\n !isReadonly2 && track(rawTarget, \"get\", key);\n }\n !isReadonly2 && track(rawTarget, \"get\", rawKey);\n const { has: has2 } = getProto(rawTarget);\n const wrap = isShallow ? toShallow : isReadonly2 ? toReadonly : toReactive;\n if (has2.call(rawTarget, key)) {\n return wrap(target.get(key));\n } else if (has2.call(rawTarget, rawKey)) {\n return wrap(target.get(rawKey));\n } else if (target !== rawTarget) {\n target.get(key);\n }\n }\n function has$1(key, isReadonly2 = false) {\n const target = this[\n \"__v_raw\"\n /* RAW */\n ];\n const rawTarget = toRaw2(target);\n const rawKey = toRaw2(key);\n if (key !== rawKey) {\n !isReadonly2 && track(rawTarget, \"has\", key);\n }\n !isReadonly2 && track(rawTarget, \"has\", rawKey);\n return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey);\n }\n function size(target, isReadonly2 = false) {\n target = target[\n \"__v_raw\"\n /* RAW */\n ];\n !isReadonly2 && track(toRaw2(target), \"iterate\", ITERATE_KEY);\n return Reflect.get(target, \"size\", target);\n }\n function add(value) {\n value = toRaw2(value);\n const target = toRaw2(this);\n const proto = getProto(target);\n const hadKey = proto.has.call(target, value);\n if (!hadKey) {\n target.add(value);\n trigger(target, \"add\", value, value);\n }\n return this;\n }\n function set$1(key, value) {\n value = toRaw2(value);\n const target = toRaw2(this);\n const { has: has2, get: get3 } = getProto(target);\n let hadKey = has2.call(target, key);\n if (!hadKey) {\n key = toRaw2(key);\n hadKey = has2.call(target, key);\n } else {\n checkIdentityKeys(target, has2, key);\n }\n const oldValue = get3.call(target, key);\n target.set(key, value);\n if (!hadKey) {\n trigger(target, \"add\", key, value);\n } else if (shared.hasChanged(value, oldValue)) {\n trigger(target, \"set\", key, value, oldValue);\n }\n return this;\n }\n function deleteEntry(key) {\n const target = toRaw2(this);\n const { has: has2, get: get3 } = getProto(target);\n let hadKey = has2.call(target, key);\n if (!hadKey) {\n key = toRaw2(key);\n hadKey = has2.call(target, key);\n } else {\n checkIdentityKeys(target, has2, key);\n }\n const oldValue = get3 ? get3.call(target, key) : void 0;\n const result = target.delete(key);\n if (hadKey) {\n trigger(target, \"delete\", key, void 0, oldValue);\n }\n return result;\n }\n function clear() {\n const target = toRaw2(this);\n const hadItems = target.size !== 0;\n const oldTarget = shared.isMap(target) ? new Map(target) : new Set(target);\n const result = target.clear();\n if (hadItems) {\n trigger(target, \"clear\", void 0, void 0, oldTarget);\n }\n return result;\n }\n function createForEach(isReadonly2, isShallow) {\n return function forEach(callback, thisArg) {\n const observed = this;\n const target = observed[\n \"__v_raw\"\n /* RAW */\n ];\n const rawTarget = toRaw2(target);\n const wrap = isShallow ? toShallow : isReadonly2 ? toReadonly : toReactive;\n !isReadonly2 && track(rawTarget, \"iterate\", ITERATE_KEY);\n return target.forEach((value, key) => {\n return callback.call(thisArg, wrap(value), wrap(key), observed);\n });\n };\n }\n function createIterableMethod(method, isReadonly2, isShallow) {\n return function(...args) {\n const target = this[\n \"__v_raw\"\n /* RAW */\n ];\n const rawTarget = toRaw2(target);\n const targetIsMap = shared.isMap(rawTarget);\n const isPair = method === \"entries\" || method === Symbol.iterator && targetIsMap;\n const isKeyOnly = method === \"keys\" && targetIsMap;\n const innerIterator = target[method](...args);\n const wrap = isShallow ? toShallow : isReadonly2 ? toReadonly : toReactive;\n !isReadonly2 && track(rawTarget, \"iterate\", isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY);\n return {\n // iterator protocol\n next() {\n const { value, done } = innerIterator.next();\n return done ? { value, done } : {\n value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),\n done\n };\n },\n // iterable protocol\n [Symbol.iterator]() {\n return this;\n }\n };\n };\n }\n function createReadonlyMethod(type) {\n return function(...args) {\n {\n const key = args[0] ? `on key \"${args[0]}\" ` : ``;\n console.warn(`${shared.capitalize(type)} operation ${key}failed: target is readonly.`, toRaw2(this));\n }\n return type === \"delete\" ? false : this;\n };\n }\n function createInstrumentations() {\n const mutableInstrumentations2 = {\n get(key) {\n return get$1(this, key);\n },\n get size() {\n return size(this);\n },\n has: has$1,\n add,\n set: set$1,\n delete: deleteEntry,\n clear,\n forEach: createForEach(false, false)\n };\n const shallowInstrumentations2 = {\n get(key) {\n return get$1(this, key, false, true);\n },\n get size() {\n return size(this);\n },\n has: has$1,\n add,\n set: set$1,\n delete: deleteEntry,\n clear,\n forEach: createForEach(false, true)\n };\n const readonlyInstrumentations2 = {\n get(key) {\n return get$1(this, key, true);\n },\n get size() {\n return size(this, true);\n },\n has(key) {\n return has$1.call(this, key, true);\n },\n add: createReadonlyMethod(\n \"add\"\n /* ADD */\n ),\n set: createReadonlyMethod(\n \"set\"\n /* SET */\n ),\n delete: createReadonlyMethod(\n \"delete\"\n /* DELETE */\n ),\n clear: createReadonlyMethod(\n \"clear\"\n /* CLEAR */\n ),\n forEach: createForEach(true, false)\n };\n const shallowReadonlyInstrumentations2 = {\n get(key) {\n return get$1(this, key, true, true);\n },\n get size() {\n return size(this, true);\n },\n has(key) {\n return has$1.call(this, key, true);\n },\n add: createReadonlyMethod(\n \"add\"\n /* ADD */\n ),\n set: createReadonlyMethod(\n \"set\"\n /* SET */\n ),\n delete: createReadonlyMethod(\n \"delete\"\n /* DELETE */\n ),\n clear: createReadonlyMethod(\n \"clear\"\n /* CLEAR */\n ),\n forEach: createForEach(true, true)\n };\n const iteratorMethods = [\"keys\", \"values\", \"entries\", Symbol.iterator];\n iteratorMethods.forEach((method) => {\n mutableInstrumentations2[method] = createIterableMethod(method, false, false);\n readonlyInstrumentations2[method] = createIterableMethod(method, true, false);\n shallowInstrumentations2[method] = createIterableMethod(method, false, true);\n shallowReadonlyInstrumentations2[method] = createIterableMethod(method, true, true);\n });\n return [\n mutableInstrumentations2,\n readonlyInstrumentations2,\n shallowInstrumentations2,\n shallowReadonlyInstrumentations2\n ];\n }\n var [mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations] = /* @__PURE__ */ createInstrumentations();\n function createInstrumentationGetter(isReadonly2, shallow) {\n const instrumentations = shallow ? isReadonly2 ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly2 ? readonlyInstrumentations : mutableInstrumentations;\n return (target, key, receiver) => {\n if (key === \"__v_isReactive\") {\n return !isReadonly2;\n } else if (key === \"__v_isReadonly\") {\n return isReadonly2;\n } else if (key === \"__v_raw\") {\n return target;\n }\n return Reflect.get(shared.hasOwn(instrumentations, key) && key in target ? instrumentations : target, key, receiver);\n };\n }\n var mutableCollectionHandlers = {\n get: /* @__PURE__ */ createInstrumentationGetter(false, false)\n };\n var shallowCollectionHandlers = {\n get: /* @__PURE__ */ createInstrumentationGetter(false, true)\n };\n var readonlyCollectionHandlers = {\n get: /* @__PURE__ */ createInstrumentationGetter(true, false)\n };\n var shallowReadonlyCollectionHandlers = {\n get: /* @__PURE__ */ createInstrumentationGetter(true, true)\n };\n function checkIdentityKeys(target, has2, key) {\n const rawKey = toRaw2(key);\n if (rawKey !== key && has2.call(target, rawKey)) {\n const type = shared.toRawType(target);\n console.warn(`Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.`);\n }\n }\n var reactiveMap = /* @__PURE__ */ new WeakMap();\n var shallowReactiveMap = /* @__PURE__ */ new WeakMap();\n var readonlyMap = /* @__PURE__ */ new WeakMap();\n var shallowReadonlyMap = /* @__PURE__ */ new WeakMap();\n function targetTypeMap(rawType) {\n switch (rawType) {\n case \"Object\":\n case \"Array\":\n return 1;\n case \"Map\":\n case \"Set\":\n case \"WeakMap\":\n case \"WeakSet\":\n return 2;\n default:\n return 0;\n }\n }\n function getTargetType(value) {\n return value[\n \"__v_skip\"\n /* SKIP */\n ] || !Object.isExtensible(value) ? 0 : targetTypeMap(shared.toRawType(value));\n }\n function reactive3(target) {\n if (target && target[\n \"__v_isReadonly\"\n /* IS_READONLY */\n ]) {\n return target;\n }\n return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);\n }\n function shallowReactive(target) {\n return createReactiveObject(target, false, shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap);\n }\n function readonly(target) {\n return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap);\n }\n function shallowReadonly(target) {\n return createReactiveObject(target, true, shallowReadonlyHandlers, shallowReadonlyCollectionHandlers, shallowReadonlyMap);\n }\n function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {\n if (!shared.isObject(target)) {\n {\n console.warn(`value cannot be made reactive: ${String(target)}`);\n }\n return target;\n }\n if (target[\n \"__v_raw\"\n /* RAW */\n ] && !(isReadonly2 && target[\n \"__v_isReactive\"\n /* IS_REACTIVE */\n ])) {\n return target;\n }\n const existingProxy = proxyMap.get(target);\n if (existingProxy) {\n return existingProxy;\n }\n const targetType = getTargetType(target);\n if (targetType === 0) {\n return target;\n }\n const proxy = new Proxy(target, targetType === 2 ? collectionHandlers : baseHandlers);\n proxyMap.set(target, proxy);\n return proxy;\n }\n function isReactive2(value) {\n if (isReadonly(value)) {\n return isReactive2(value[\n \"__v_raw\"\n /* RAW */\n ]);\n }\n return !!(value && value[\n \"__v_isReactive\"\n /* IS_REACTIVE */\n ]);\n }\n function isReadonly(value) {\n return !!(value && value[\n \"__v_isReadonly\"\n /* IS_READONLY */\n ]);\n }\n function isProxy(value) {\n return isReactive2(value) || isReadonly(value);\n }\n function toRaw2(observed) {\n return observed && toRaw2(observed[\n \"__v_raw\"\n /* RAW */\n ]) || observed;\n }\n function markRaw(value) {\n shared.def(value, \"__v_skip\", true);\n return value;\n }\n var convert = (val) => shared.isObject(val) ? reactive3(val) : val;\n function isRef(r) {\n return Boolean(r && r.__v_isRef === true);\n }\n function ref(value) {\n return createRef(value);\n }\n function shallowRef(value) {\n return createRef(value, true);\n }\n var RefImpl = class {\n constructor(value, _shallow = false) {\n this._shallow = _shallow;\n this.__v_isRef = true;\n this._rawValue = _shallow ? value : toRaw2(value);\n this._value = _shallow ? value : convert(value);\n }\n get value() {\n track(toRaw2(this), \"get\", \"value\");\n return this._value;\n }\n set value(newVal) {\n newVal = this._shallow ? newVal : toRaw2(newVal);\n if (shared.hasChanged(newVal, this._rawValue)) {\n this._rawValue = newVal;\n this._value = this._shallow ? newVal : convert(newVal);\n trigger(toRaw2(this), \"set\", \"value\", newVal);\n }\n }\n };\n function createRef(rawValue, shallow = false) {\n if (isRef(rawValue)) {\n return rawValue;\n }\n return new RefImpl(rawValue, shallow);\n }\n function triggerRef(ref2) {\n trigger(toRaw2(ref2), \"set\", \"value\", ref2.value);\n }\n function unref(ref2) {\n return isRef(ref2) ? ref2.value : ref2;\n }\n var shallowUnwrapHandlers = {\n get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),\n set: (target, key, value, receiver) => {\n const oldValue = target[key];\n if (isRef(oldValue) && !isRef(value)) {\n oldValue.value = value;\n return true;\n } else {\n return Reflect.set(target, key, value, receiver);\n }\n }\n };\n function proxyRefs(objectWithRefs) {\n return isReactive2(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers);\n }\n var CustomRefImpl = class {\n constructor(factory) {\n this.__v_isRef = true;\n const { get: get3, set: set3 } = factory(() => track(this, \"get\", \"value\"), () => trigger(this, \"set\", \"value\"));\n this._get = get3;\n this._set = set3;\n }\n get value() {\n return this._get();\n }\n set value(newVal) {\n this._set(newVal);\n }\n };\n function customRef(factory) {\n return new CustomRefImpl(factory);\n }\n function toRefs(object) {\n if (!isProxy(object)) {\n console.warn(`toRefs() expects a reactive object but received a plain one.`);\n }\n const ret = shared.isArray(object) ? new Array(object.length) : {};\n for (const key in object) {\n ret[key] = toRef(object, key);\n }\n return ret;\n }\n var ObjectRefImpl = class {\n constructor(_object, _key) {\n this._object = _object;\n this._key = _key;\n this.__v_isRef = true;\n }\n get value() {\n return this._object[this._key];\n }\n set value(newVal) {\n this._object[this._key] = newVal;\n }\n };\n function toRef(object, key) {\n return isRef(object[key]) ? object[key] : new ObjectRefImpl(object, key);\n }\n var ComputedRefImpl = class {\n constructor(getter, _setter, isReadonly2) {\n this._setter = _setter;\n this._dirty = true;\n this.__v_isRef = true;\n this.effect = effect3(getter, {\n lazy: true,\n scheduler: () => {\n if (!this._dirty) {\n this._dirty = true;\n trigger(toRaw2(this), \"set\", \"value\");\n }\n }\n });\n this[\n \"__v_isReadonly\"\n /* IS_READONLY */\n ] = isReadonly2;\n }\n get value() {\n const self2 = toRaw2(this);\n if (self2._dirty) {\n self2._value = this.effect();\n self2._dirty = false;\n }\n track(self2, \"get\", \"value\");\n return self2._value;\n }\n set value(newValue) {\n this._setter(newValue);\n }\n };\n function computed(getterOrOptions) {\n let getter;\n let setter;\n if (shared.isFunction(getterOrOptions)) {\n getter = getterOrOptions;\n setter = () => {\n console.warn(\"Write operation failed: computed value is readonly\");\n };\n } else {\n getter = getterOrOptions.get;\n setter = getterOrOptions.set;\n }\n return new ComputedRefImpl(getter, setter, shared.isFunction(getterOrOptions) || !getterOrOptions.set);\n }\n exports.ITERATE_KEY = ITERATE_KEY;\n exports.computed = computed;\n exports.customRef = customRef;\n exports.effect = effect3;\n exports.enableTracking = enableTracking;\n exports.isProxy = isProxy;\n exports.isReactive = isReactive2;\n exports.isReadonly = isReadonly;\n exports.isRef = isRef;\n exports.markRaw = markRaw;\n exports.pauseTracking = pauseTracking;\n exports.proxyRefs = proxyRefs;\n exports.reactive = reactive3;\n exports.readonly = readonly;\n exports.ref = ref;\n exports.resetTracking = resetTracking;\n exports.shallowReactive = shallowReactive;\n exports.shallowReadonly = shallowReadonly;\n exports.shallowRef = shallowRef;\n exports.stop = stop2;\n exports.toRaw = toRaw2;\n exports.toRef = toRef;\n exports.toRefs = toRefs;\n exports.track = track;\n exports.trigger = trigger;\n exports.triggerRef = triggerRef;\n exports.unref = unref;\n }\n});\n\n// node_modules/@vue/reactivity/index.js\nvar require_reactivity = __commonJS({\n \"node_modules/@vue/reactivity/index.js\"(exports, module2) {\n \"use strict\";\n if (false) {\n module2.exports = null;\n } else {\n module2.exports = require_reactivity_cjs();\n }\n }\n});\n\n// packages/alpinejs/builds/module.js\nvar module_exports = {};\n__export(module_exports, {\n Alpine: () => src_default,\n default: () => module_default\n});\nmodule.exports = __toCommonJS(module_exports);\n\n// packages/alpinejs/src/scheduler.js\nvar flushPending = false;\nvar flushing = false;\nvar queue = [];\nvar lastFlushedIndex = -1;\nfunction scheduler(callback) {\n queueJob(callback);\n}\nfunction queueJob(job) {\n if (!queue.includes(job))\n queue.push(job);\n queueFlush();\n}\nfunction dequeueJob(job) {\n let index = queue.indexOf(job);\n if (index !== -1 && index > lastFlushedIndex)\n queue.splice(index, 1);\n}\nfunction queueFlush() {\n if (!flushing && !flushPending) {\n flushPending = true;\n queueMicrotask(flushJobs);\n }\n}\nfunction flushJobs() {\n flushPending = false;\n flushing = true;\n for (let i = 0; i < queue.length; i++) {\n queue[i]();\n lastFlushedIndex = i;\n }\n queue.length = 0;\n lastFlushedIndex = -1;\n flushing = false;\n}\n\n// packages/alpinejs/src/reactivity.js\nvar reactive;\nvar effect;\nvar release;\nvar raw;\nvar shouldSchedule = true;\nfunction disableEffectScheduling(callback) {\n shouldSchedule = false;\n callback();\n shouldSchedule = true;\n}\nfunction setReactivityEngine(engine) {\n reactive = engine.reactive;\n release = engine.release;\n effect = (callback) => engine.effect(callback, { scheduler: (task) => {\n if (shouldSchedule) {\n scheduler(task);\n } else {\n task();\n }\n } });\n raw = engine.raw;\n}\nfunction overrideEffect(override) {\n effect = override;\n}\nfunction elementBoundEffect(el) {\n let cleanup = () => {\n };\n let wrappedEffect = (callback) => {\n let effectReference = effect(callback);\n if (!el._x_effects) {\n el._x_effects = /* @__PURE__ */ new Set();\n el._x_runEffects = () => {\n el._x_effects.forEach((i) => i());\n };\n }\n el._x_effects.add(effectReference);\n cleanup = () => {\n if (effectReference === void 0)\n return;\n el._x_effects.delete(effectReference);\n release(effectReference);\n };\n return effectReference;\n };\n return [wrappedEffect, () => {\n cleanup();\n }];\n}\nfunction watch(getter, callback) {\n let firstTime = true;\n let oldValue;\n let effectReference = effect(() => {\n let value = getter();\n JSON.stringify(value);\n if (!firstTime) {\n queueMicrotask(() => {\n callback(value, oldValue);\n oldValue = value;\n });\n } else {\n oldValue = value;\n }\n firstTime = false;\n });\n return () => release(effectReference);\n}\n\n// packages/alpinejs/src/mutation.js\nvar onAttributeAddeds = [];\nvar onElRemoveds = [];\nvar onElAddeds = [];\nfunction onElAdded(callback) {\n onElAddeds.push(callback);\n}\nfunction onElRemoved(el, callback) {\n if (typeof callback === \"function\") {\n if (!el._x_cleanups)\n el._x_cleanups = [];\n el._x_cleanups.push(callback);\n } else {\n callback = el;\n onElRemoveds.push(callback);\n }\n}\nfunction onAttributesAdded(callback) {\n onAttributeAddeds.push(callback);\n}\nfunction onAttributeRemoved(el, name, callback) {\n if (!el._x_attributeCleanups)\n el._x_attributeCleanups = {};\n if (!el._x_attributeCleanups[name])\n el._x_attributeCleanups[name] = [];\n el._x_attributeCleanups[name].push(callback);\n}\nfunction cleanupAttributes(el, names) {\n if (!el._x_attributeCleanups)\n return;\n Object.entries(el._x_attributeCleanups).forEach(([name, value]) => {\n if (names === void 0 || names.includes(name)) {\n value.forEach((i) => i());\n delete el._x_attributeCleanups[name];\n }\n });\n}\nfunction cleanupElement(el) {\n var _a, _b;\n (_a = el._x_effects) == null ? void 0 : _a.forEach(dequeueJob);\n while ((_b = el._x_cleanups) == null ? void 0 : _b.length)\n el._x_cleanups.pop()();\n}\nvar observer = new MutationObserver(onMutate);\nvar currentlyObserving = false;\nfunction startObservingMutations() {\n observer.observe(document, { subtree: true, childList: true, attributes: true, attributeOldValue: true });\n currentlyObserving = true;\n}\nfunction stopObservingMutations() {\n flushObserver();\n observer.disconnect();\n currentlyObserving = false;\n}\nvar queuedMutations = [];\nfunction flushObserver() {\n let records = observer.takeRecords();\n queuedMutations.push(() => records.length > 0 && onMutate(records));\n let queueLengthWhenTriggered = queuedMutations.length;\n queueMicrotask(() => {\n if (queuedMutations.length === queueLengthWhenTriggered) {\n while (queuedMutations.length > 0)\n queuedMutations.shift()();\n }\n });\n}\nfunction mutateDom(callback) {\n if (!currentlyObserving)\n return callback();\n stopObservingMutations();\n let result = callback();\n startObservingMutations();\n return result;\n}\nvar isCollecting = false;\nvar deferredMutations = [];\nfunction deferMutations() {\n isCollecting = true;\n}\nfunction flushAndStopDeferringMutations() {\n isCollecting = false;\n onMutate(deferredMutations);\n deferredMutations = [];\n}\nfunction onMutate(mutations) {\n if (isCollecting) {\n deferredMutations = deferredMutations.concat(mutations);\n return;\n }\n let addedNodes = /* @__PURE__ */ new Set();\n let removedNodes = /* @__PURE__ */ new Set();\n let addedAttributes = /* @__PURE__ */ new Map();\n let removedAttributes = /* @__PURE__ */ new Map();\n for (let i = 0; i < mutations.length; i++) {\n if (mutations[i].target._x_ignoreMutationObserver)\n continue;\n if (mutations[i].type === \"childList\") {\n mutations[i].addedNodes.forEach((node) => node.nodeType === 1 && addedNodes.add(node));\n mutations[i].removedNodes.forEach((node) => node.nodeType === 1 && removedNodes.add(node));\n }\n if (mutations[i].type === \"attributes\") {\n let el = mutations[i].target;\n let name = mutations[i].attributeName;\n let oldValue = mutations[i].oldValue;\n let add = () => {\n if (!addedAttributes.has(el))\n addedAttributes.set(el, []);\n addedAttributes.get(el).push({ name, value: el.getAttribute(name) });\n };\n let remove = () => {\n if (!removedAttributes.has(el))\n removedAttributes.set(el, []);\n removedAttributes.get(el).push(name);\n };\n if (el.hasAttribute(name) && oldValue === null) {\n add();\n } else if (el.hasAttribute(name)) {\n remove();\n add();\n } else {\n remove();\n }\n }\n }\n removedAttributes.forEach((attrs, el) => {\n cleanupAttributes(el, attrs);\n });\n addedAttributes.forEach((attrs, el) => {\n onAttributeAddeds.forEach((i) => i(el, attrs));\n });\n for (let node of removedNodes) {\n if (addedNodes.has(node))\n continue;\n onElRemoveds.forEach((i) => i(node));\n }\n addedNodes.forEach((node) => {\n node._x_ignoreSelf = true;\n node._x_ignore = true;\n });\n for (let node of addedNodes) {\n if (removedNodes.has(node))\n continue;\n if (!node.isConnected)\n continue;\n delete node._x_ignoreSelf;\n delete node._x_ignore;\n onElAddeds.forEach((i) => i(node));\n node._x_ignore = true;\n node._x_ignoreSelf = true;\n }\n addedNodes.forEach((node) => {\n delete node._x_ignoreSelf;\n delete node._x_ignore;\n });\n addedNodes = null;\n removedNodes = null;\n addedAttributes = null;\n removedAttributes = null;\n}\n\n// packages/alpinejs/src/scope.js\nfunction scope(node) {\n return mergeProxies(closestDataStack(node));\n}\nfunction addScopeToNode(node, data2, referenceNode) {\n node._x_dataStack = [data2, ...closestDataStack(referenceNode || node)];\n return () => {\n node._x_dataStack = node._x_dataStack.filter((i) => i !== data2);\n };\n}\nfunction closestDataStack(node) {\n if (node._x_dataStack)\n return node._x_dataStack;\n if (typeof ShadowRoot === \"function\" && node instanceof ShadowRoot) {\n return closestDataStack(node.host);\n }\n if (!node.parentNode) {\n return [];\n }\n return closestDataStack(node.parentNode);\n}\nfunction mergeProxies(objects) {\n return new Proxy({ objects }, mergeProxyTrap);\n}\nvar mergeProxyTrap = {\n ownKeys({ objects }) {\n return Array.from(\n new Set(objects.flatMap((i) => Object.keys(i)))\n );\n },\n has({ objects }, name) {\n if (name == Symbol.unscopables)\n return false;\n return objects.some(\n (obj) => Object.prototype.hasOwnProperty.call(obj, name) || Reflect.has(obj, name)\n );\n },\n get({ objects }, name, thisProxy) {\n if (name == \"toJSON\")\n return collapseProxies;\n return Reflect.get(\n objects.find(\n (obj) => Reflect.has(obj, name)\n ) || {},\n name,\n thisProxy\n );\n },\n set({ objects }, name, value, thisProxy) {\n const target = objects.find(\n (obj) => Object.prototype.hasOwnProperty.call(obj, name)\n ) || objects[objects.length - 1];\n const descriptor = Object.getOwnPropertyDescriptor(target, name);\n if ((descriptor == null ? void 0 : descriptor.set) && (descriptor == null ? void 0 : descriptor.get))\n return descriptor.set.call(thisProxy, value) || true;\n return Reflect.set(target, name, value);\n }\n};\nfunction collapseProxies() {\n let keys = Reflect.ownKeys(this);\n return keys.reduce((acc, key) => {\n acc[key] = Reflect.get(this, key);\n return acc;\n }, {});\n}\n\n// packages/alpinejs/src/interceptor.js\nfunction initInterceptors(data2) {\n let isObject = (val) => typeof val === \"object\" && !Array.isArray(val) && val !== null;\n let recurse = (obj, basePath = \"\") => {\n Object.entries(Object.getOwnPropertyDescriptors(obj)).forEach(([key, { value, enumerable }]) => {\n if (enumerable === false || value === void 0)\n return;\n if (typeof value === \"object\" && value !== null && value.__v_skip)\n return;\n let path = basePath === \"\" ? key : `${basePath}.${key}`;\n if (typeof value === \"object\" && value !== null && value._x_interceptor) {\n obj[key] = value.initialize(data2, path, key);\n } else {\n if (isObject(value) && value !== obj && !(value instanceof Element)) {\n recurse(value, path);\n }\n }\n });\n };\n return recurse(data2);\n}\nfunction interceptor(callback, mutateObj = () => {\n}) {\n let obj = {\n initialValue: void 0,\n _x_interceptor: true,\n initialize(data2, path, key) {\n return callback(this.initialValue, () => get(data2, path), (value) => set(data2, path, value), path, key);\n }\n };\n mutateObj(obj);\n return (initialValue) => {\n if (typeof initialValue === \"object\" && initialValue !== null && initialValue._x_interceptor) {\n let initialize = obj.initialize.bind(obj);\n obj.initialize = (data2, path, key) => {\n let innerValue = initialValue.initialize(data2, path, key);\n obj.initialValue = innerValue;\n return initialize(data2, path, key);\n };\n } else {\n obj.initialValue = initialValue;\n }\n return obj;\n };\n}\nfunction get(obj, path) {\n return path.split(\".\").reduce((carry, segment) => carry[segment], obj);\n}\nfunction set(obj, path, value) {\n if (typeof path === \"string\")\n path = path.split(\".\");\n if (path.length === 1)\n obj[path[0]] = value;\n else if (path.length === 0)\n throw error;\n else {\n if (obj[path[0]])\n return set(obj[path[0]], path.slice(1), value);\n else {\n obj[path[0]] = {};\n return set(obj[path[0]], path.slice(1), value);\n }\n }\n}\n\n// packages/alpinejs/src/magics.js\nvar magics = {};\nfunction magic(name, callback) {\n magics[name] = callback;\n}\nfunction injectMagics(obj, el) {\n let memoizedUtilities = getUtilities(el);\n Object.entries(magics).forEach(([name, callback]) => {\n Object.defineProperty(obj, `$${name}`, {\n get() {\n return callback(el, memoizedUtilities);\n },\n enumerable: false\n });\n });\n return obj;\n}\nfunction getUtilities(el) {\n let [utilities, cleanup] = getElementBoundUtilities(el);\n let utils = { interceptor, ...utilities };\n onElRemoved(el, cleanup);\n return utils;\n}\n\n// packages/alpinejs/src/utils/error.js\nfunction tryCatch(el, expression, callback, ...args) {\n try {\n return callback(...args);\n } catch (e) {\n handleError(e, el, expression);\n }\n}\nfunction handleError(error2, el, expression = void 0) {\n error2 = Object.assign(\n error2 != null ? error2 : { message: \"No error message given.\" },\n { el, expression }\n );\n console.warn(`Alpine Expression Error: ${error2.message}\n\n${expression ? 'Expression: \"' + expression + '\"\\n\\n' : \"\"}`, el);\n setTimeout(() => {\n throw error2;\n }, 0);\n}\n\n// packages/alpinejs/src/evaluator.js\nvar shouldAutoEvaluateFunctions = true;\nfunction dontAutoEvaluateFunctions(callback) {\n let cache = shouldAutoEvaluateFunctions;\n shouldAutoEvaluateFunctions = false;\n let result = callback();\n shouldAutoEvaluateFunctions = cache;\n return result;\n}\nfunction evaluate(el, expression, extras = {}) {\n let result;\n evaluateLater(el, expression)((value) => result = value, extras);\n return result;\n}\nfunction evaluateLater(...args) {\n return theEvaluatorFunction(...args);\n}\nvar theEvaluatorFunction = normalEvaluator;\nfunction setEvaluator(newEvaluator) {\n theEvaluatorFunction = newEvaluator;\n}\nfunction normalEvaluator(el, expression) {\n let overriddenMagics = {};\n injectMagics(overriddenMagics, el);\n let dataStack = [overriddenMagics, ...closestDataStack(el)];\n let evaluator = typeof expression === \"function\" ? generateEvaluatorFromFunction(dataStack, expression) : generateEvaluatorFromString(dataStack, expression, el);\n return tryCatch.bind(null, el, expression, evaluator);\n}\nfunction generateEvaluatorFromFunction(dataStack, func) {\n return (receiver = () => {\n }, { scope: scope2 = {}, params = [] } = {}) => {\n let result = func.apply(mergeProxies([scope2, ...dataStack]), params);\n runIfTypeOfFunction(receiver, result);\n };\n}\nvar evaluatorMemo = {};\nfunction generateFunctionFromString(expression, el) {\n if (evaluatorMemo[expression]) {\n return evaluatorMemo[expression];\n }\n let AsyncFunction = Object.getPrototypeOf(async function() {\n }).constructor;\n let rightSideSafeExpression = /^[\\n\\s]*if.*\\(.*\\)/.test(expression.trim()) || /^(let|const)\\s/.test(expression.trim()) ? `(async()=>{ ${expression} })()` : expression;\n const safeAsyncFunction = () => {\n try {\n let func2 = new AsyncFunction(\n [\"__self\", \"scope\"],\n `with (scope) { __self.result = ${rightSideSafeExpression} }; __self.finished = true; return __self.result;`\n );\n Object.defineProperty(func2, \"name\", {\n value: `[Alpine] ${expression}`\n });\n return func2;\n } catch (error2) {\n handleError(error2, el, expression);\n return Promise.resolve();\n }\n };\n let func = safeAsyncFunction();\n evaluatorMemo[expression] = func;\n return func;\n}\nfunction generateEvaluatorFromString(dataStack, expression, el) {\n let func = generateFunctionFromString(expression, el);\n return (receiver = () => {\n }, { scope: scope2 = {}, params = [] } = {}) => {\n func.result = void 0;\n func.finished = false;\n let completeScope = mergeProxies([scope2, ...dataStack]);\n if (typeof func === \"function\") {\n let promise = func(func, completeScope).catch((error2) => handleError(error2, el, expression));\n if (func.finished) {\n runIfTypeOfFunction(receiver, func.result, completeScope, params, el);\n func.result = void 0;\n } else {\n promise.then((result) => {\n runIfTypeOfFunction(receiver, result, completeScope, params, el);\n }).catch((error2) => handleError(error2, el, expression)).finally(() => func.result = void 0);\n }\n }\n };\n}\nfunction runIfTypeOfFunction(receiver, value, scope2, params, el) {\n if (shouldAutoEvaluateFunctions && typeof value === \"function\") {\n let result = value.apply(scope2, params);\n if (result instanceof Promise) {\n result.then((i) => runIfTypeOfFunction(receiver, i, scope2, params)).catch((error2) => handleError(error2, el, value));\n } else {\n receiver(result);\n }\n } else if (typeof value === \"object\" && value instanceof Promise) {\n value.then((i) => receiver(i));\n } else {\n receiver(value);\n }\n}\n\n// packages/alpinejs/src/directives.js\nvar prefixAsString = \"x-\";\nfunction prefix(subject = \"\") {\n return prefixAsString + subject;\n}\nfunction setPrefix(newPrefix) {\n prefixAsString = newPrefix;\n}\nvar directiveHandlers = {};\nfunction directive(name, callback) {\n directiveHandlers[name] = callback;\n return {\n before(directive2) {\n if (!directiveHandlers[directive2]) {\n console.warn(String.raw`Cannot find directive \\`${directive2}\\`. \\`${name}\\` will use the default order of execution`);\n return;\n }\n const pos = directiveOrder.indexOf(directive2);\n directiveOrder.splice(pos >= 0 ? pos : directiveOrder.indexOf(\"DEFAULT\"), 0, name);\n }\n };\n}\nfunction directiveExists(name) {\n return Object.keys(directiveHandlers).includes(name);\n}\nfunction directives(el, attributes, originalAttributeOverride) {\n attributes = Array.from(attributes);\n if (el._x_virtualDirectives) {\n let vAttributes = Object.entries(el._x_virtualDirectives).map(([name, value]) => ({ name, value }));\n let staticAttributes = attributesOnly(vAttributes);\n vAttributes = vAttributes.map((attribute) => {\n if (staticAttributes.find((attr) => attr.name === attribute.name)) {\n return {\n name: `x-bind:${attribute.name}`,\n value: `\"${attribute.value}\"`\n };\n }\n return attribute;\n });\n attributes = attributes.concat(vAttributes);\n }\n let transformedAttributeMap = {};\n let directives2 = attributes.map(toTransformedAttributes((newName, oldName) => transformedAttributeMap[newName] = oldName)).filter(outNonAlpineAttributes).map(toParsedDirectives(transformedAttributeMap, originalAttributeOverride)).sort(byPriority);\n return directives2.map((directive2) => {\n return getDirectiveHandler(el, directive2);\n });\n}\nfunction attributesOnly(attributes) {\n return Array.from(attributes).map(toTransformedAttributes()).filter((attr) => !outNonAlpineAttributes(attr));\n}\nvar isDeferringHandlers = false;\nvar directiveHandlerStacks = /* @__PURE__ */ new Map();\nvar currentHandlerStackKey = Symbol();\nfunction deferHandlingDirectives(callback) {\n isDeferringHandlers = true;\n let key = Symbol();\n currentHandlerStackKey = key;\n directiveHandlerStacks.set(key, []);\n let flushHandlers = () => {\n while (directiveHandlerStacks.get(key).length)\n directiveHandlerStacks.get(key).shift()();\n directiveHandlerStacks.delete(key);\n };\n let stopDeferring = () => {\n isDeferringHandlers = false;\n flushHandlers();\n };\n callback(flushHandlers);\n stopDeferring();\n}\nfunction getElementBoundUtilities(el) {\n let cleanups = [];\n let cleanup = (callback) => cleanups.push(callback);\n let [effect3, cleanupEffect] = elementBoundEffect(el);\n cleanups.push(cleanupEffect);\n let utilities = {\n Alpine: alpine_default,\n effect: effect3,\n cleanup,\n evaluateLater: evaluateLater.bind(evaluateLater, el),\n evaluate: evaluate.bind(evaluate, el)\n };\n let doCleanup = () => cleanups.forEach((i) => i());\n return [utilities, doCleanup];\n}\nfunction getDirectiveHandler(el, directive2) {\n let noop = () => {\n };\n let handler4 = directiveHandlers[directive2.type] || noop;\n let [utilities, cleanup] = getElementBoundUtilities(el);\n onAttributeRemoved(el, directive2.original, cleanup);\n let fullHandler = () => {\n if (el._x_ignore || el._x_ignoreSelf)\n return;\n handler4.inline && handler4.inline(el, directive2, utilities);\n handler4 = handler4.bind(handler4, el, directive2, utilities);\n isDeferringHandlers ? directiveHandlerStacks.get(currentHandlerStackKey).push(handler4) : handler4();\n };\n fullHandler.runCleanups = cleanup;\n return fullHandler;\n}\nvar startingWith = (subject, replacement) => ({ name, value }) => {\n if (name.startsWith(subject))\n name = name.replace(subject, replacement);\n return { name, value };\n};\nvar into = (i) => i;\nfunction toTransformedAttributes(callback = () => {\n}) {\n return ({ name, value }) => {\n let { name: newName, value: newValue } = attributeTransformers.reduce((carry, transform) => {\n return transform(carry);\n }, { name, value });\n if (newName !== name)\n callback(newName, name);\n return { name: newName, value: newValue };\n };\n}\nvar attributeTransformers = [];\nfunction mapAttributes(callback) {\n attributeTransformers.push(callback);\n}\nfunction outNonAlpineAttributes({ name }) {\n return alpineAttributeRegex().test(name);\n}\nvar alpineAttributeRegex = () => new RegExp(`^${prefixAsString}([^:^.]+)\\\\b`);\nfunction toParsedDirectives(transformedAttributeMap, originalAttributeOverride) {\n return ({ name, value }) => {\n let typeMatch = name.match(alpineAttributeRegex());\n let valueMatch = name.match(/:([a-zA-Z0-9\\-_:]+)/);\n let modifiers = name.match(/\\.[^.\\]]+(?=[^\\]]*$)/g) || [];\n let original = originalAttributeOverride || transformedAttributeMap[name] || name;\n return {\n type: typeMatch ? typeMatch[1] : null,\n value: valueMatch ? valueMatch[1] : null,\n modifiers: modifiers.map((i) => i.replace(\".\", \"\")),\n expression: value,\n original\n };\n };\n}\nvar DEFAULT = \"DEFAULT\";\nvar directiveOrder = [\n \"ignore\",\n \"ref\",\n \"data\",\n \"id\",\n \"anchor\",\n \"bind\",\n \"init\",\n \"for\",\n \"model\",\n \"modelable\",\n \"transition\",\n \"show\",\n \"if\",\n DEFAULT,\n \"teleport\"\n];\nfunction byPriority(a, b) {\n let typeA = directiveOrder.indexOf(a.type) === -1 ? DEFAULT : a.type;\n let typeB = directiveOrder.indexOf(b.type) === -1 ? DEFAULT : b.type;\n return directiveOrder.indexOf(typeA) - directiveOrder.indexOf(typeB);\n}\n\n// packages/alpinejs/src/utils/dispatch.js\nfunction dispatch(el, name, detail = {}) {\n el.dispatchEvent(\n new CustomEvent(name, {\n detail,\n bubbles: true,\n // Allows events to pass the shadow DOM barrier.\n composed: true,\n cancelable: true\n })\n );\n}\n\n// packages/alpinejs/src/utils/walk.js\nfunction walk(el, callback) {\n if (typeof ShadowRoot === \"function\" && el instanceof ShadowRoot) {\n Array.from(el.children).forEach((el2) => walk(el2, callback));\n return;\n }\n let skip = false;\n callback(el, () => skip = true);\n if (skip)\n return;\n let node = el.firstElementChild;\n while (node) {\n walk(node, callback, false);\n node = node.nextElementSibling;\n }\n}\n\n// packages/alpinejs/src/utils/warn.js\nfunction warn(message, ...args) {\n console.warn(`Alpine Warning: ${message}`, ...args);\n}\n\n// packages/alpinejs/src/lifecycle.js\nvar started = false;\nfunction start() {\n if (started)\n warn(\"Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems.\");\n started = true;\n if (!document.body)\n warn(\"Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` -@stop \ No newline at end of file +@stop diff --git a/resources/views/account/view-assets.blade.php b/resources/views/account/view-assets.blade.php index 96b95484a6..b03069d45b 100755 --- a/resources/views/account/view-assets.blade.php +++ b/resources/views/account/view-assets.blade.php @@ -103,20 +103,23 @@ @can('self.profile') @endcan @can('self.api') @@ -124,7 +127,8 @@ @@ -134,10 +138,16 @@ @if (!empty($user->email))
{{ csrf_field() }} - +
@else - + @endif @@ -370,13 +380,8 @@
@if ($user->id)
-
-

{{ trans('admin/users/general.assets_user', array('name' => $user->first_name)) }}

-
@endif - -
@@ -411,16 +416,24 @@ {{ trans('admin/hardware/table.asset_tag') }} - {{ trans('general.name') }} + + {{ trans('general.name') }} + {{ trans('admin/hardware/table.asset_model') }} + + {{ trans('general.model_no') }} + {{ trans('admin/hardware/table.serial') }} {{ trans('admin/hardware/form.default_location') }} + + {{ trans('general.location') }} + @can('self.view_purchase_cost') @@ -469,9 +482,10 @@ {{ $asset->name }} - @if ($asset->physical=='1') {{ $asset->model->name }} - @endif + + + {{ $asset->model->model_number }} {{ $asset->serial }} @@ -479,6 +493,9 @@ {{ ($asset->defaultLoc) ? $asset->defaultLoc->name : '' }} + + {{ ($asset->location) ? $asset->location->name : '' }} + @can('self.view_purchase_cost') {!! Helper::formatCurrencyOutput($asset->purchase_cost) !!} @@ -512,7 +529,6 @@
-
diff --git a/resources/views/asset_maintenances/edit.blade.php b/resources/views/asset_maintenances/edit.blade.php index c3aa8e1483..4dd390ddfa 100644 --- a/resources/views/asset_maintenances/edit.blade.php +++ b/resources/views/asset_maintenances/edit.blade.php @@ -41,28 +41,32 @@
- @include ('partials.forms.edit.asset-select', ['translated_name' => trans('admin/hardware/table.asset_tag'), 'fieldname' => 'asset_id', 'required' => 'true']) - @include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id', 'required' => 'true']) - @include ('partials.forms.edit.maintenance_type')
-
- +
+ {!! $errors->first('title', '') !!}
+ + @include ('partials.forms.edit.asset-select', ['translated_name' => trans('general.asset'), 'fieldname' => 'asset_id', 'required' => 'true']) + @include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id', 'required' => 'true']) + @include ('partials.forms.edit.maintenance_type') + + +
-
+
- +
{!! $errors->first('start_date', '') !!} @@ -75,10 +79,12 @@
-
+
- - + + + +
{!! $errors->first('completion_date', '') !!}
diff --git a/resources/views/blade/filestable.blade.php b/resources/views/blade/filestable.blade.php new file mode 100644 index 0000000000..e198e339da --- /dev/null +++ b/resources/views/blade/filestable.blade.php @@ -0,0 +1,142 @@ + +@props([ + 'filepath', + 'object', + 'showfile_routename', + 'deletefile_routename', +]) + + +
+ + + + + + + + + + + + + + + + + + @foreach ($object->uploads as $file) + + + + + + + + + + + + + + + + @endforeach + + +
+ {{trans('general.id')}} + + {{trans('general.file_type')}} + + {{ trans('general.image') }} + + {{ trans('general.file_name') }} + + {{ trans('general.filesize') }} + + {{ trans('general.notes') }} + + {{ trans('general.download') }} + + {{ trans('general.created_at') }} + + {{ trans('general.created_by') }} + + {{ trans('table.actions') }} +
+ {{ $file->id }} + + @if (Storage::exists($filepath.$file->filename)) + {{ pathinfo($filepath.$file->filename, PATHINFO_EXTENSION) }} + + @endif + + + @if (($file->filename) && (Storage::exists($filepath.$file->filename))) + @if (Helper::checkUploadIsImage($file->get_src(str_plural(strtolower(class_basename(get_class($object))))))) + + + + @else + {{ trans('general.preview_not_available') }} + @endif + @else + + {{ trans('general.file_not_found') }} + @endif + + + {{ $file->filename }} + + {{ (Storage::exists($filepath.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size($filepath.$file->filename)) : '' }} + + @if ($file->note) + {{ $file->note }} + @endif + + @if ($file->filename) + @if (Storage::exists($filepath.$file->filename)) + + + + {{ trans('general.download') }} + + + + + + + @endif + @endif + + {{ $file->created_at }} + + {{ ($file->adminuser) ? $file->adminuser->present()->getFullNameAttribute() : '' }} + + + + {{ trans('general.delete') }} + +
+
+ \ No newline at end of file diff --git a/resources/views/components/checkout.blade.php b/resources/views/components/checkout.blade.php index 39890a1938..7b2b313ba6 100644 --- a/resources/views/components/checkout.blade.php +++ b/resources/views/components/checkout.blade.php @@ -33,7 +33,7 @@ {{ trans('general.qty') }}
- +
@if ($errors->first('assigned_qty'))
diff --git a/resources/views/components/edit.blade.php b/resources/views/components/edit.blade.php index ce77b5130d..206418f894 100644 --- a/resources/views/components/edit.blade.php +++ b/resources/views/components/edit.blade.php @@ -20,6 +20,8 @@ @include ('partials.forms.edit.quantity') @include ('partials.forms.edit.minimum_quantity') @include ('partials.forms.edit.serial', ['fieldname' => 'serial']) +@include ('partials.forms.edit.manufacturer-select', ['translated_name' => trans('general.manufacturer'), 'fieldname' => 'manufacturer_id']) +@include ('partials.forms.edit.model_number') @include ('partials.forms.edit.company-select', ['translated_name' => trans('general.company'), 'fieldname' => 'company_id']) @include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id']) @include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id']) diff --git a/resources/views/components/view.blade.php b/resources/views/components/view.blade.php index 287b0b23a8..ba3a6d6e24 100644 --- a/resources/views/components/view.blade.php +++ b/resources/views/components/view.blade.php @@ -140,96 +140,14 @@ @can('components.files', $component)
- -
- - - - - - - - - - - - - - - @if ($component->uploads->count() > 0) - @foreach ($component->uploads as $file) - - - - - - - - - - - - @endforeach - @else - - - - @endif - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- - {{ Helper::filetype_icon($file->filename) }} - - - @if ($file->filename) - @if ( Helper::checkUploadIsImage($file->get_src('components'))) - - @endif - @endif - - {{ $file->filename }} - - {{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/components/'.$file->filename) ? Storage::size('private_uploads/components/'.$file->filename) : '') }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if ($file->filename) - - - {{ trans('general.download') }} - - - - - - - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - -
{{ trans('general.no_results') }}
+
+
+ +
@endcan @@ -243,7 +161,7 @@
@if ($component->image!='') diff --git a/resources/views/consumables/checkout.blade.php b/resources/views/consumables/checkout.blade.php index bb3023290f..8e73bdeedf 100644 --- a/resources/views/consumables/checkout.blade.php +++ b/resources/views/consumables/checkout.blade.php @@ -91,7 +91,7 @@
- +
{!! $errors->first('qty', '
') !!} diff --git a/resources/views/consumables/view.blade.php b/resources/views/consumables/view.blade.php index 88ea19195f..ac95e6391b 100644 --- a/resources/views/consumables/view.blade.php +++ b/resources/views/consumables/view.blade.php @@ -89,7 +89,7 @@ @if ($consumable->image!='') @endif @@ -428,102 +428,18 @@
+
+
+ -
-
- - - - - - - - - - - - - - - - @if ($consumable->uploads->count() > 0) - @foreach ($consumable->uploads as $file) - - - - - - - - - - - - @endforeach - @else - - - - @endif - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- - {{ Helper::filetype_icon($file->filename) }} - - - @if ($file->filename) - @if ( Helper::checkUploadIsImage($file->get_src('consumables'))) - - @endif - @endif - - {{ $file->filename }} - - {{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/consumables/'.$file->filename) ? Storage::size('private_uploads/consumables/'.$file->filename) : '') }} - - @if ($file->note) - {!! nl2br(Helper::parseEscapedMarkedownInline($file->note)) !!} - @endif - - @if ($file->filename) - - - {{ trans('general.download') }} - - - - - - @endif - {{ $file->created_at }} - - - {{ trans('general.delete') }} - - -
{{ trans('general.no_results') }}
-
-
+
+
diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 39a5d8b751..9eb2eb5386 100755 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -34,7 +34,7 @@

{{ number_format(\App\Models\Asset::AssetsForShow()->count()) }}

-

{{ strtolower(trans('general.assets')) }}

+

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

diff --git a/resources/views/modals/partials/categories-select.blade.php b/resources/views/modals/partials/categories-select.blade.php new file mode 100644 index 0000000000..116a0808e9 --- /dev/null +++ b/resources/views/modals/partials/categories-select.blade.php @@ -0,0 +1,11 @@ + +@php + $required = $required ?? ''; +@endphp +
+
+
+ +
+
+ \ No newline at end of file diff --git a/resources/views/modals/partials/fieldset-select.blade.php b/resources/views/modals/partials/fieldset-select.blade.php new file mode 100644 index 0000000000..3f4efc24b6 --- /dev/null +++ b/resources/views/modals/partials/fieldset-select.blade.php @@ -0,0 +1,6 @@ + +
+
+
{{ Form::select('fieldset_id', Helper::customFieldsetList(),old('fieldset_id'), array('class'=>'select2', 'id'=>'modal-fieldset_id', 'style'=>'width:100%;')) }}
+
+ \ No newline at end of file diff --git a/resources/views/modals/partials/footer.blade.php b/resources/views/modals/partials/footer.blade.php new file mode 100644 index 0000000000..354d189b9e --- /dev/null +++ b/resources/views/modals/partials/footer.blade.php @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/resources/views/modals/partials/manufacturer-select.blade.php b/resources/views/modals/partials/manufacturer-select.blade.php new file mode 100644 index 0000000000..810292067e --- /dev/null +++ b/resources/views/modals/partials/manufacturer-select.blade.php @@ -0,0 +1,8 @@ + +
+
+
+ +
+
+ \ No newline at end of file diff --git a/resources/views/modals/partials/model-number.blade.php b/resources/views/modals/partials/model-number.blade.php new file mode 100644 index 0000000000..2691f30d48 --- /dev/null +++ b/resources/views/modals/partials/model-number.blade.php @@ -0,0 +1,6 @@ + +
+
+
+
+ \ No newline at end of file diff --git a/resources/views/modals/partials/name.blade.php b/resources/views/modals/partials/name.blade.php new file mode 100644 index 0000000000..6a64ed525f --- /dev/null +++ b/resources/views/modals/partials/name.blade.php @@ -0,0 +1,10 @@ + +@php + $required = $required ?? ''; +@endphp +
+
+
+
+ \ No newline at end of file diff --git a/resources/views/modals/statuslabel.blade.php b/resources/views/modals/statuslabel.blade.php index 7e07854197..e7076f1bbb 100644 --- a/resources/views/modals/statuslabel.blade.php +++ b/resources/views/modals/statuslabel.blade.php @@ -10,21 +10,18 @@
-
-
+ @include('partials.forms.edit.name', ['required' => 'true', 'item' => new \App\Models\Statuslabel(),'translated_name' => trans('admin/statuslabels/table.name') ])
-
-
diff --git a/resources/views/modals/supplier.blade.php b/resources/views/modals/supplier.blade.php index 0dd10595bf..806b57c1de 100644 --- a/resources/views/modals/supplier.blade.php +++ b/resources/views/modals/supplier.blade.php @@ -10,15 +10,12 @@
-
-
+ @include('partials.forms.edit.name', [ 'item' => new \App\Models\Supplier(), 'translated_name' => trans('admin/suppliers/table.name')])
-
diff --git a/resources/views/modals/user.blade.php b/resources/views/modals/user.blade.php index e65fcca5b0..5672119fa1 100644 --- a/resources/views/modals/user.blade.php +++ b/resources/views/modals/user.blade.php @@ -25,11 +25,10 @@
- -
diff --git a/resources/views/models/custom_fields_form.blade.php b/resources/views/models/custom_fields_form.blade.php index 8d414f337d..4949fae71c 100644 --- a/resources/views/models/custom_fields_form.blade.php +++ b/resources/views/models/custom_fields_form.blade.php @@ -2,20 +2,22 @@ @foreach($model->fieldset->fields AS $field)
-
+
@if ($field->element!='text') - + @if ($field->element=='listbox') + {{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(), - old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }} + old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class' => 'format select2 form-control', ($field->pivot->required=='1' ? ' required' : '') ]) }} @elseif ($field->element=='textarea') - + + @elseif ($field->element=='checkbox') - + @foreach ($field->formatFieldValuesAsArray() as $key => $value)
@if ($field->field_encrypted) diff --git a/resources/views/models/edit.blade.php b/resources/views/models/edit.blade.php index 713e517c72..9c72107ef8 100755 --- a/resources/views/models/edit.blade.php +++ b/resources/views/models/edit.blade.php @@ -36,7 +36,7 @@ -@livewire('custom-field-set-default-values-for-model',["model_id" => $item->id ?? $model_id ?? null ]) +@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 d38b63264e..f26b9babd4 100755 --- a/resources/views/models/view.blade.php +++ b/resources/views/models/view.blade.php @@ -107,99 +107,11 @@
- @if ($model->uploads->count() > 0) - - - - - - - - - - - - - - - - @foreach ($model->uploads as $file) - - - - - - - - - - - @endforeach - -
{{trans('general.file_type')}}{{ trans('general.image') }}{{ trans('general.file_name') }}{{ trans('general.filesize') }}{{ trans('general.notes') }}{{ trans('general.download') }}{{ trans('general.created_at') }}{{ trans('table.actions') }}
- @if ((Storage::exists('private_uploads/assetmodels/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('assetmodels')))) - - - - @endif - - @if (Storage::exists('private_uploads/assetmodels/'.$file->filename)) - {{ $file->filename }} - @else - {{ $file->filename }} - @endif - - {{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/assetmodels/'.$file->filename)) : '' }} - - @if ($file->note) - {{ $file->note }} - @endif - - @if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename))) - - - - - - - - - @endif - - @if ($file->created_at) - {{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }} - @endif - - @can('update', \App\Models\AssetModel::class) - - @endcan -
- - @else - -
- - {{ trans('general.no_results') }} -
- @endif +
diff --git a/resources/views/notifications/FirstAdmin.blade.php b/resources/views/notifications/FirstAdmin.blade.php index b6d8e89db2..83a5c8bf04 100644 --- a/resources/views/notifications/FirstAdmin.blade.php +++ b/resources/views/notifications/FirstAdmin.blade.php @@ -1,8 +1,8 @@ @component('mail::message') {{ trans('mail.hello') }} {{ $first_name }} {{$last_name}}, -{{ trans('mail.login') }} {{ $username }}
-{{ trans('mail.password') }} {{ $password }} +{{ trans('mail.login') }}: {{ $username }}
+{{ trans('mail.password') }}: {{ $password }} @component('mail::button', ['url' => $url]) Go To {{$snipeSettings->site_name}} diff --git a/resources/views/notifications/Welcome.blade.php b/resources/views/notifications/Welcome.blade.php index 82dcd3e155..e6e72ad2cd 100644 --- a/resources/views/notifications/Welcome.blade.php +++ b/resources/views/notifications/Welcome.blade.php @@ -3,8 +3,8 @@ {{ trans('mail.admin_has_created', ['web' => $snipeSettings->site_name]) }} -{{ trans('mail.login') }} {{ $username }}
-{{ trans('mail.password') }} {{ $password }} +{{ trans('mail.login') }}: {{ $username }}
+{{ trans('mail.password') }}: {{ $password }} @component('mail::button', ['url' => $url]) Go To {{$snipeSettings->site_name}} diff --git a/resources/views/notifications/markdown/asset-acceptance.blade.php b/resources/views/notifications/markdown/asset-acceptance.blade.php index c5c2a2fa27..65446f8a30 100644 --- a/resources/views/notifications/markdown/asset-acceptance.blade.php +++ b/resources/views/notifications/markdown/asset-acceptance.blade.php @@ -22,6 +22,9 @@ @if ((isset($item_tag)) && ($item_tag!='')) | **{{ trans('mail.asset_tag') }}** | {{ $item_tag }} | @endif +@if (isset($item->model->category)) +| **{{ trans('general.category') }}** | {{ $item->model->category->name }} | +@endif @if ((isset($item_model)) && ($item_model!='')) | **{{ trans('mail.asset_name') }}** | {{ $item_model }} | @endif diff --git a/resources/views/notifications/markdown/asset-requested.blade.php b/resources/views/notifications/markdown/asset-requested.blade.php index 56ceefb4a7..a634993f99 100644 --- a/resources/views/notifications/markdown/asset-requested.blade.php +++ b/resources/views/notifications/markdown/asset-requested.blade.php @@ -18,6 +18,9 @@ @if ((isset($item->asset_tag)) && ($item->asset_tag!='')) | **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} | @endif +@if (isset($item->model->category)) +| **{{ trans('general.category') }}** | {{ $item->model->category->name }} | +@endif @if ((isset($item->name)) && ($item->name!='')) | **{{ trans('mail.asset_name') }}** | {{ $item->name }} | @endif diff --git a/resources/views/notifications/markdown/user-inventory.blade.php b/resources/views/notifications/markdown/user-inventory.blade.php index f1b329ec8d..6c062b4a0a 100644 --- a/resources/views/notifications/markdown/user-inventory.blade.php +++ b/resources/views/notifications/markdown/user-inventory.blade.php @@ -9,12 +9,15 @@ ## {{ $assets->count() }} {{ trans('general.assets') }} - + + + @foreach($assets as $asset) + @if (($snipeSettings->show_images_in_email =='1') && $asset->getImageUrl()) - diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index fc342897a7..223f9992cf 100755 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -86,28 +86,16 @@
-
- -
- - {!! $errors->first('first_name', '') !!} -
-
+ @include('partials.forms.edit.name-first') -
- -
- - {!! $errors->first('last_name', '') !!} -
-
+ @include('partials.forms.edit.name-last')
-
+
@if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone')) id)) ? ' disabled' : '') }} > @@ -150,7 +139,7 @@ -
+
@if ($user->ldap_import!='1' || str_contains(Route::currentRouteName(), 'clone') ) id)) ? ' required' : '' }} onfocus="this.removeAttribute('readonly');" {{ ((config('app.lock_passwords') && ($user->id)) ? ' disabled' : '') }}> @else @@ -183,7 +173,7 @@ -
+
id) ? ' required' : '' }} onfocus="this.removeAttribute('readonly');" {{ ((config('app.lock_passwords') && ($user->id)) ? ' disabled' : '') }} > @@ -237,11 +228,10 @@
-
-
+
id)) ? ' disabled' : '') }} autocomplete="off" readonly + {{ (Helper::checkIfRequired($user, 'email')) ? ' required' : '' }} onfocus="this.removeAttribute('readonly');"> @if (config('app.lock_passwords') && ($user->id))

{{ trans('admin/users/table.lock_passwords') }}

diff --git a/resources/views/users/print.blade.php b/resources/views/users/print.blade.php index a1fa3373ff..40575e7efc 100644 --- a/resources/views/users/print.blade.php +++ b/resources/views/users/print.blade.php @@ -2,7 +2,7 @@ - @if (count($users) === 1) + @if ((isset($users) && count($users) === 1)) {{ trans('general.assigned_to', ['name' => $users[0]->present()->fullName()]) }} - {{ date('Y-m-d H:i', time()) }} @else {{ trans('admin/users/general.print_assigned') }} - {{ date('Y-m-d H:i', time()) }} @@ -36,11 +36,7 @@ @page { size: A4; } - - #start_of_user_section { - break-before: page; - } - + .print-logo { max-height: 40px; } @@ -51,13 +47,6 @@ } - @@ -139,7 +128,7 @@
@foreach ($show_user->assets as $asset) @php - if ($asset->model->category->getEula()) $eulas[] = $asset->model->category->getEula() + if (($asset->model->category) && ($asset->model->category->getEula())) $eulas[] = $asset->model->category->getEula() @endphp @@ -236,7 +225,7 @@ @foreach ($show_user->licenses as $license) @php - if ($license->category->getEula()) $eulas[] = $license->category->getEula() + if (($license->category) && ($license->category->getEula())) $eulas[] = $license->category->getEula() @endphp @@ -294,7 +283,7 @@ @foreach ($show_user->accessories as $accessory) @if ($accessory) @php - if ($accessory->category->getEula()) $eulas[] = $accessory->category->getEula() + if (($accessory->category) && ($accessory->category->getEula())) $eulas[] = $accessory->category->getEula() @endphp @@ -357,7 +346,7 @@ @foreach ($show_user->consumables as $consumable) @if ($consumable) @php - if ($consumable->category->getEula()) $eulas[] = $consumable->category->getEula() + if (($consumable->category) && ($consumable->category->getEula())) $eulas[] = $consumable->category->getEula() @endphp @@ -384,8 +373,11 @@
{{ trans('mail.name') }} {{ trans('mail.asset_tag') }}{{ trans('admin/hardware/table.serial') }}
{{ trans('mail.name') }} {{ trans('mail.asset_tag') }}{{ trans('admin/hardware/table.serial') }}{{ trans('general.category') }}
{{ $asset->present()->name }} {{ $asset->asset_tag }} {{ $asset->serial }} {{ $asset->model->category->name }} Asset diff --git a/resources/views/partials/asset-bulk-actions.blade.php b/resources/views/partials/asset-bulk-actions.blade.php index b597ad647f..992fb52bba 100644 --- a/resources/views/partials/asset-bulk-actions.blade.php +++ b/resources/views/partials/asset-bulk-actions.blade.php @@ -16,17 +16,20 @@ diff --git a/resources/views/partials/forms/edit/accessory-select.blade.php b/resources/views/partials/forms/edit/accessory-select.blade.php index 12be9bf7b7..d34b6b2fe0 100644 --- a/resources/views/partials/forms/edit/accessory-select.blade.php +++ b/resources/views/partials/forms/edit/accessory-select.blade.php @@ -1,8 +1,8 @@
{{ Form::label($fieldname, $translated_name, array('class' => 'col-md-3 control-label')) }} -
- @if ((!isset($unselect)) && ($accessory_id = old($fieldname, (isset($accessory) ? $accessory->id : (isset($item) ? $item->{$fieldname} : '')))))
- {{ Form::label('city', trans('general.city'), array('class' => 'col-md-3 control-label', 'maxlength'=>'191')) }} + {{ Form::label('city', trans('general.city'), array('class' => 'col-md-3 control-label')) }}
- {{Form::text('city', old('city', $item->city), array('class' => 'form-control', 'aria-label'=>'city')) }} + {{Form::text('city', old('city', $item->city), array('class' => 'form-control', 'aria-label'=>'city', 'maxlength'=>'191')) }} {!! $errors->first('city', '') !!}
- {{ Form::label('state', trans('general.state'), array('class' => 'col-md-3 control-label', 'maxlength'=>'191')) }} + {{ Form::label('state', trans('general.state'), array('class' => 'col-md-3 control-label')) }}
- {{Form::text('state', old('state', $item->state), array('class' => 'form-control', 'aria-label'=>'state')) }} + {{Form::text('state', old('state', $item->state), array('class' => 'form-control', 'aria-label'=>'state', 'maxlength'=>'191')) }} {!! $errors->first('state', '') !!}
diff --git a/resources/views/partials/forms/edit/asset-select.blade.php b/resources/views/partials/forms/edit/asset-select.blade.php index 86dba8206f..9867a8e552 100644 --- a/resources/views/partials/forms/edit/asset-select.blade.php +++ b/resources/views/partials/forms/edit/asset-select.blade.php @@ -1,8 +1,9 @@ -
+
{{ Form::label($fieldname, $translated_name, array('class' => 'col-md-3 control-label')) }} -
- @if ((!isset($unselect)) && ($asset_id = old($fieldname, (isset($asset) ? $asset->id : (isset($item) ? $item->{$fieldname} : ''))))) + @else + @if(isset($asset_ids)) + @foreach($asset_ids as $asset_id) + + @endforeach + @endif @endif @endif diff --git a/resources/views/partials/forms/edit/category-select.blade.php b/resources/views/partials/forms/edit/category-select.blade.php index bfa8e3b793..f610f68470 100644 --- a/resources/views/partials/forms/edit/category-select.blade.php +++ b/resources/views/partials/forms/edit/category-select.blade.php @@ -3,7 +3,7 @@ {{ Form::label($fieldname, $translated_name, array('class' => 'col-md-3 control-label')) }} -
+
@if ($company_id = old($fieldname, (isset($item)) ? $item->{$fieldname} : ''))
{{ trans('general.file_name') }} + {{ trans('general.type') }} diff --git a/resources/views/settings/alerts.blade.php b/resources/views/settings/alerts.blade.php index edbf1be839..ddca5c8158 100644 --- a/resources/views/settings/alerts.blade.php +++ b/resources/views/settings/alerts.blade.php @@ -21,9 +21,10 @@ - {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }} +
+ - {{csrf_field()}} + {{ csrf_field() }}
@@ -68,12 +69,10 @@ {{ Form::label('alert_email', trans('admin/settings/general.alert_email')) }}
- {{ Form::text('alert_email', old('alert_email', $setting->alert_email), array('class' => 'form-control','placeholder' => 'admin@yourcompany.com')) }} + {!! $errors->first('alert_email', '
') !!} -

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

-
@@ -84,7 +83,7 @@ {{ Form::label('admin_cc_email', trans('admin/settings/general.admin_cc_email')) }}
- {{ Form::text('admin_cc_email', old('admin_cc_email', $setting->admin_cc_email), array('class' => 'form-control','placeholder' => 'admin@yourcompany.com')) }} + {!! $errors->first('admin_cc_email', '
') !!}

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

@@ -122,7 +121,7 @@ {{ Form::label('audit_interval', trans('admin/settings/general.audit_interval')) }}
- {{ Form::text('audit_interval', old('audit_interval', $setting->audit_interval), array('class' => 'form-control','placeholder' => '12', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} + {{ Form::text('audit_interval', old('audit_interval', $setting->audit_interval), array('class' => 'form-control','placeholder' => '12', 'maxlength'=>'3')) }} {{ trans('general.months') }}
@@ -137,7 +136,7 @@ {{ Form::label('audit_warning_days', trans('admin/settings/general.audit_warning_days')) }}
- {{ Form::text('audit_warning_days', old('audit_warning_days', $setting->audit_warning_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} + {{ Form::text('audit_warning_days', old('audit_warning_days', $setting->audit_warning_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3')) }} {{ trans('general.days') }}
@@ -152,12 +151,8 @@ {{ Form::label('due_checkin_days', trans('admin/settings/general.due_checkin_days')) }}
- {{ Form::text('due_checkin_days', old('due_checkin_days', $setting->due_checkin_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} + {{ Form::text('due_checkin_days', old('due_checkin_days', $setting->due_checkin_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3')) }} {{ trans('general.days') }} - - - -
{!! $errors->first('due_checkin_days', '') !!} diff --git a/resources/views/settings/general.blade.php b/resources/views/settings/general.blade.php index c800b26ac9..1de5d4035d 100644 --- a/resources/views/settings/general.blade.php +++ b/resources/views/settings/general.blade.php @@ -18,7 +18,7 @@ - {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }} + {{csrf_field()}} @@ -215,6 +215,23 @@
+ +
+
+ +
+
+ +

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

+
+
+ +
diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php index f00f1f6e5c..40a0178649 100755 --- a/resources/views/settings/index.blade.php +++ b/resources/views/settings/index.blade.php @@ -341,7 +341,7 @@

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

-
+
diff --git a/resources/views/settings/ldap.blade.php b/resources/views/settings/ldap.blade.php index 0c9faf8c2e..5b483f48a2 100644 --- a/resources/views/settings/ldap.blade.php +++ b/resources/views/settings/ldap.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') - Update LDAP/AD Settings + {{ trans('admin/settings/general.ldap_ad') }} @parent @stop @@ -42,8 +42,7 @@ @endif - - {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form']) }} + {{csrf_field()}} @@ -62,7 +61,7 @@

{{ trans('admin/settings/general.ldap_ad') }} -

+
@@ -76,11 +75,15 @@
+ @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -93,13 +96,21 @@
- {!! $errors->first('is_ad', '') !!} + @error('is_ad') + + + {{ $message }} + + @enderror @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -111,14 +122,23 @@

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

- {!! $errors->first('ldap_pw_sync_help', '') !!} + @error('ldap_pw_sync') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

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

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

- {!! $errors->first('ad_domain', '') !!} + @error('ad_domain') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
- {{-- NOTICE - this was a feature for AdLdap2-based LDAP syncing, and is already handled in 'classic' LDAP, so we now hide the checkbox (but haven't deleted the field) -
-
- {{ Form::label('ad_append_domain', trans('admin/settings/general.ad_append_domain_label')) }} -
-
- {{ Form::checkbox('ad_append_domain', '1', 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') }}

- {!! $errors->first('ad_append_domain', ':message') !!} - @if (config('app.lock_passwords')===true) -

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

- @endif -
-
--}} -
{{ Form::label('ldap_client_tls_key', trans('admin/settings/general.ldap_client_tls_key')) }}
- {{ Form::textarea('ldap_client_tls_key', 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', '') !!} + {{ Form::textarea('ldap_client_tls_key', 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-----"]) }} + @error('ldap_client_tls_key') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -176,11 +197,20 @@ {{ Form::label('ldap_client_tls_cert', trans('admin/settings/general.ldap_client_tls_cert')) }}
- {{ Form::textarea('ldap_client_tls_cert', 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]) }} + {{ Form::textarea('ldap_client_tls_cert', 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-----"]) }}

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

- {!! $errors->first('ldap_client_tls_cert', '') !!} + @error('ldap_client_tls_cert') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -191,11 +221,21 @@ {{ Form::label('ldap_server', trans('admin/settings/general.ldap_server')) }}
- {{ Form::text('ldap_server', old('ldap_server', $setting->ldap_server), ['class' => 'form-control','placeholder' => trans('general.example') .'ldap://ldap.example.com', $setting->demoMode]) }} + {{ Form::text('ldap_server', old('ldap_server', $setting->ldap_server), ['class' => 'form-control','placeholder' => trans('general.example') .'ldap://ldap.example.com']) }} + @error('ldap_server') + + + {{ $message }} + + @enderror +

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

- {!! $errors->first('ldap_server', '') !!} + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -207,12 +247,21 @@
- {!! $errors->first('ldap_tls', '') !!} + @error('ldap_tls') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -224,13 +273,24 @@
- {!! $errors->first('ldap_server_cert_ignore', '') !!} -

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

+ @error('ldap_server_cert_ignore') + + + {{ $message }} + + @enderror + +

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

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

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

+

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

@endif
@@ -241,10 +301,19 @@ {{ Form::label('ldap_uname', trans('admin/settings/general.ldap_uname')) }}
- {{ Form::text('ldap_uname', old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','autocomplete' => 'off', 'placeholder' => trans('general.example') .'binduser@example.com', $setting->demoMode]) }} - {!! $errors->first('ldap_uname', '') !!} + {{ Form::text('ldap_uname', old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','autocomplete' => 'off', 'placeholder' => trans('general.example') .'binduser@example.com']) }} + @error('ldap_uname') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -255,10 +324,19 @@ {{ Form::label('ldap_pword', trans('admin/settings/general.ldap_pword')) }}
- {{ Form::password('ldap_pword', ['class' => 'form-control', 'autocomplete' => 'off', 'onfocus' => "this.removeAttribute('readonly');", $setting->demoMode, ' readonly']) }} - {!! $errors->first('ldap_pword', '') !!} + {{ Form::password('ldap_pword', ['class' => 'form-control', 'autocomplete' => 'off', 'onfocus' => "this.removeAttribute('readonly');", ' readonly']) }} + @error('ldap_pword') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -269,10 +347,19 @@ {{ Form::label('ldap_basedn', trans('admin/settings/general.ldap_basedn')) }}
- {{ Form::text('ldap_basedn', 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', '') !!} + {{ Form::text('ldap_basedn', old('ldap_basedn', $setting->ldap_basedn), ['class' => 'form-control', 'placeholder' => trans('general.example') .'cn=users/authorized,dc=example,dc=com']) }} + @error('ldap_basedn') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -283,10 +370,19 @@ {{ Form::label('ldap_filter', trans('admin/settings/general.ldap_filter')) }}
- {{ Form::text('ldap_filter', old('ldap_filter', $setting->ldap_filter), ['class' => 'form-control','placeholder' => trans('general.example') .'&(cn=*)', $setting->demoMode]) }} - {!! $errors->first('ldap_filter', '') !!} + + @error('ldap_filter') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -297,10 +393,19 @@ {{ Form::label('ldap_username_field', trans('admin/settings/general.ldap_username_field')) }}
- {{ Form::text('ldap_username_field', old('ldap_username_field', $setting->ldap_username_field), ['class' => 'form-control','placeholder' => trans('general.example') .'samaccountname', $setting->demoMode]) }} - {!! $errors->first('ldap_username_field', '') !!} + + @error('ldap_username_field') + + + {!! $message !!} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -311,10 +416,19 @@ {{ Form::label('ldap_lname_field', trans('admin/settings/general.ldap_lname_field')) }}
- {{ Form::text('ldap_lname_field', old('ldap_lname_field', $setting->ldap_lname_field), ['class' => 'form-control','placeholder' => trans('general.example') .'sn', $setting->demoMode]) }} - {!! $errors->first('ldap_lname_field', '') !!} + + @error('ldap_lname_field') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -325,10 +439,19 @@ {{ Form::label('ldap_fname_field', trans('admin/settings/general.ldap_fname_field')) }}
- {{ Form::text('ldap_fname_field', old('ldap_fname_field', $setting->ldap_fname_field), ['class' => 'form-control', 'placeholder' => trans('general.example') .'givenname', $setting->demoMode]) }} - {!! $errors->first('ldap_fname_field', '') !!} + + @error('ldap_fname_field') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -336,13 +459,23 @@
- {{ Form::label('ldap_auth_filter_query', trans('admin/settings/general.ldap_auth_filter_query')) }} +
- {{ Form::text('ldap_auth_filter_query', 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', '') !!} + + + @error('ldap_auth_filter_query') + + + {!! $message !!} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -364,7 +497,6 @@ @endforeach - {{ trans('admin/users/general.group_memberships_helpblock') }} @else
@@ -383,7 +515,7 @@
@endif @else -

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

+

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

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

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

- {!! $errors->first('ldap_active_flag', '') !!} + @error('ldap_active_flag') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -412,10 +552,19 @@ {{ Form::label('ldap_emp_num', trans('admin/settings/general.ldap_emp_num')) }}
- {{ Form::text('ldap_emp_num', old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => trans('general.example') .'employeenumber/employeeid', $setting->demoMode]) }} - {!! $errors->first('ldap_emp_num', '') !!} + {{ Form::text('ldap_emp_num', old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => trans('general.example') .'employeenumber/employeeid']) }} + @error('ldap_emp_num') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -425,10 +574,20 @@ {{ Form::label('ldap_dept', trans('admin/settings/general.ldap_dept')) }}
- {{ Form::text('ldap_dept', old('ldap_dept', $setting->ldap_dept), ['class' => 'form-control','placeholder' => trans('general.example') .'department', $setting->demoMode]) }} - {!! $errors->first('ldap_dept', '') !!} + {{ Form::text('ldap_dept', old('ldap_dept', $setting->ldap_dept), ['class' => 'form-control','placeholder' => trans('general.example') .'department']) }} + + @error('ldap_dept') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -438,10 +597,19 @@ {{ Form::label('ldap_dept', trans('admin/settings/general.ldap_manager')) }}
- {{ Form::text('ldap_manager', old('ldap_manager', $setting->ldap_manager), ['class' => 'form-control','placeholder' => trans('general.example') .'manager', $setting->demoMode]) }} - {!! $errors->first('ldap_manager', '') !!} + {{ Form::text('ldap_manager', old('ldap_manager', $setting->ldap_manager), ['class' => 'form-control','placeholder' => trans('general.example') .'manager']) }} + @error('ldap_manager') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -452,10 +620,19 @@ {{ Form::label('ldap_email', trans('admin/settings/general.ldap_email')) }}
- {{ Form::text('ldap_email', old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => trans('general.example') .'mail', $setting->demoMode]) }} - {!! $errors->first('ldap_email', '') !!} + {{ Form::text('ldap_email', old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => trans('general.example') .'mail']) }} + @error('ldap_email') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -466,10 +643,19 @@ {{ Form::label('ldap_phone', trans('admin/settings/general.ldap_phone')) }}
- {{ Form::text('ldap_phone', old('ldap_phone', $setting->ldap_phone_field), ['class' => 'form-control','placeholder' => trans('general.example') .'telephonenumber', $setting->demoMode]) }} - {!! $errors->first('ldap_phone', '') !!} + {{ Form::text('ldap_phone', old('ldap_phone', $setting->ldap_phone_field), ['class' => 'form-control','placeholder' => trans('general.example') .'telephonenumber']) }} + @error('ldap_phone') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -480,10 +666,19 @@ {{ Form::label('ldap_jobtitle', trans('admin/settings/general.ldap_jobtitle')) }}
- {{ Form::text('ldap_jobtitle', old('ldap_jobtitle', $setting->ldap_jobtitle), ['class' => 'form-control','placeholder' => trans('general.example') .'title', $setting->demoMode]) }} - {!! $errors->first('ldap_jobtitle', '') !!} + {{ Form::text('ldap_jobtitle', old('ldap_jobtitle', $setting->ldap_jobtitle), ['class' => 'form-control','placeholder' => trans('general.example') .'title']) }} + @error('ldap_jobtitle') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -494,10 +689,19 @@ {{ Form::label('ldap_country', trans('admin/settings/general.ldap_country')) }}
- {{ Form::text('ldap_country', old('ldap_country', $setting->ldap_country), ['class' => 'form-control','placeholder' => trans('general.example') .'c', $setting->demoMode]) }} - {!! $errors->first('ldap_country', '') !!} + {{ Form::text('ldap_country', old('ldap_country', $setting->ldap_country), ['class' => 'form-control','placeholder' => trans('general.example') .'c']) }} + @error('ldap_country') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -507,11 +711,20 @@ {{ Form::label('ldap_location', trans('admin/settings/general.ldap_location')) }}
- {{ Form::text('ldap_location', old('ldap_location', $setting->ldap_location), ['class' => 'form-control','placeholder' => trans('general.example') .'physicaldeliveryofficename', $setting->demoMode]) }} + {{ Form::text('ldap_location', old('ldap_location', $setting->ldap_location), ['class' => 'form-control','placeholder' => trans('general.example') .'physicaldeliveryofficename']) }}

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

- {!! $errors->first('ldap_location', '') !!} + @error('ldap_location') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -523,7 +736,7 @@ {{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}

@@ -532,7 +745,10 @@

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

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

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

+

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

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

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

- {!! $errors->first('custom_forgot_pass_url', '') !!} + @error('custom_forgot_pass_url') + + + {{ $message }} + + @enderror + @if (config('app.lock_passwords')===true) -

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

+

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

@endif
@@ -607,9 +832,10 @@ {{Form::close()}} -@stop +@endsection @push('js') + diff --git a/resources/views/settings/security.blade.php b/resources/views/settings/security.blade.php index 1b04f7d58a..a51c86fccb 100644 --- a/resources/views/settings/security.blade.php +++ b/resources/views/settings/security.blade.php @@ -16,9 +16,10 @@ - {{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'off', 'class' => 'form-horizontal', 'role' => 'form' ]) }} + + - {{csrf_field()}} + {{ csrf_field() }}
diff --git a/resources/views/setup/user.blade.php b/resources/views/setup/user.blade.php index 20bc861b85..b35060b01f 100644 --- a/resources/views/setup/user.blade.php +++ b/resources/views/setup/user.blade.php @@ -21,7 +21,7 @@ - {{ Form::text('site_name', old('site_name'), array('class' => 'form-control','placeholder' => 'Snipe-IT Asset Management')) }} + {{ Form::text('site_name', old('site_name'), array('class' => 'form-control','placeholder' => 'Snipe-IT Asset Management', 'required' => true)) }} {!! $errors->first('site_name', '') !!}
@@ -30,7 +30,7 @@
-
+
@@ -39,7 +39,7 @@
-
+
{{ Form::label('default_currency', trans('admin/settings/general.default_currency')) }} {{ Form::text('default_currency', old('default_currency'), array('class' => 'form-control','placeholder' => 'USD', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} @@ -70,14 +70,14 @@
-
+
{{ Form::label('auto_increment_prefix', trans('admin/settings/general.auto_increment_prefix')) }} {{ Form::text('auto_increment_prefix', old('auto_increment_prefix'), array('class' => 'form-control')) }} {!! $errors->first('auto_increment_prefix', '') !!}
-
+
{{ Form::label('zerofill_count', trans('admin/settings/general.zerofill_count')) }} {{ Form::text('zerofill_count', old('zerofill_count', 5), array('class' => 'form-control')) }} @@ -90,14 +90,14 @@
{{ Form::label('email_domain', trans('general.email_domain')) }} - {{ Form::text('email_domain', old('email_domain'), array('class' => 'form-control','placeholder' => 'example.com')) }} + {{ Form::text('email_domain', old('email_domain'), array('class' => 'form-control','placeholder' => 'example.com','required' => true)) }} {{ trans('general.email_domain_help') }} {!! $errors->first('email_domain', '') !!}
-
+
{{ Form::label('email_format', trans('general.email_format')) }} {!! Form::username_format('email_format', old('email_format', 'filastname'), 'select2') !!} {!! $errors->first('email_format', '') !!} @@ -107,32 +107,32 @@
-
- {{ Form::label('first_name', trans('general.first_name')) }} - {{ Form::text('first_name', old('first_name'), array('class' => 'form-control','placeholder' => 'Jane')) }} +
+ {{ Form::label('first_name', trans('general.first_name'), 'required') }} + {{ Form::text('first_name', old('first_name'), array('class' => 'form-control','placeholder' => 'Jane', 'required' => true)) }} {!! $errors->first('first_name', '') !!}
{{ Form::label('last_name', trans('general.last_name')) }} - {{ Form::text('last_name', old('last_name'), array('class' => 'form-control','placeholder' => 'Smith')) }} + {{ Form::text('last_name', old('last_name'), array('class' => 'form-control','placeholder' => 'Smith', 'required' => true)) }} {!! $errors->first('last_name', '') !!}
-
+
{{ Form::label('email', trans('admin/users/table.email')) }} - {{ Form::email('email', config('mail.from.address'), array('class' => 'form-control','placeholder' => 'you@example.com')) }} + {{ Form::email('email', config('mail.from.address'), array('class' => 'form-control','placeholder' => 'you@example.com', 'required' => true)) }} {!! $errors->first('email', '') !!}
{{ Form::label('username', trans('admin/users/table.username')) }} - {{ Form::text('username', old('username'), array('class' => 'form-control','placeholder' => 'jsmith')) }} + {{ Form::text('username', old('username'), array('class' => 'form-control','placeholder' => 'jsmith', 'required' => true)) }} {!! $errors->first('username', '') !!}
@@ -141,14 +141,14 @@
{{ Form::label('password', trans('admin/users/table.password')) }} - {{ Form::password('password', array('class' => 'form-control')) }} + {{ Form::password('password', array('class' => 'form-control','required' => true)) }} {!! $errors->first('password', '') !!}
{{ Form::label('password_confirmation', trans('admin/users/table.password_confirm')) }} - {{ Form::password('password_confirmation', array('class' => 'form-control')) }} + {{ Form::password('password_confirmation', array('class' => 'form-control','required' => true)) }} {!! $errors->first('password_confirmation', '') !!}
diff --git a/resources/views/users/confirm-bulk-delete.blade.php b/resources/views/users/confirm-bulk-delete.blade.php index 74fbaf8307..8e4ba33071 100644 --- a/resources/views/users/confirm-bulk-delete.blade.php +++ b/resources/views/users/confirm-bulk-delete.blade.php @@ -109,7 +109,7 @@
- {{ Form::select('status_id', $statuslabel_list , old('status_id'), array('class'=>'select2', 'style'=>'width:250px')) }} + {{ Form::select('status_id', $statuslabel_list , old('status_id'), array('class'=>'select2', 'style'=>'width:250px', 'required' => true)) }}
{{ $counter }}
{{ $lcounter }}
{{ $acounter }}
{{ $ccounter }}
@endif + @php + if (!empty($eulas)) $eulas = array_unique($eulas); + @endphp {{-- This may have been render at the top of the page if we're rendering more than one user... --}} - @if (count($users) === 1) + @if (count($users) === 1 && !empty($eulas))