Compare commits

..

1 Commits

Author SHA1 Message Date
snipe
0f4548e112 Use anoynynous blade component for errors
Signed-off-by: snipe <snipe@snipe.net>
2024-08-29 23:22:53 +01:00
2779 changed files with 47512 additions and 82516 deletions

View File

@@ -3217,78 +3217,6 @@
"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"
]
},
{
"login": "AnouarTouati",
"name": "Anouar Touati",
"avatar_url": "https://avatars.githubusercontent.com/u/41107778?v=4",
"profile": "https://github.com/AnouarTouati",
"contributions": [
"code"
]
},
{
"login": "aHVzY2g",
"name": "aHVzY2g",
"avatar_url": "https://avatars.githubusercontent.com/u/25596663?v=4",
"profile": "https://github.com/aHVzY2g",
"contributions": [
"code"
]
},
{
"login": "brlin-tw",
"name": "林博仁 Buo-ren Lin",
"avatar_url": "https://avatars.githubusercontent.com/u/13408130?v=4",
"profile": "https://brlin.me",
"contributions": [
"code"
]
},
{
"login": "addex12",
"name": "Adugna Gizaw",
"avatar_url": "https://avatars.githubusercontent.com/u/18550946?v=4",
"profile": "https://orbalia.pythonanywhere.com/",
"contributions": [
"translation"
]
},
{
"login": "jostrander",
"name": "Jesse Ostrander",
"avatar_url": "https://avatars.githubusercontent.com/u/760989?v=4",
"profile": "https://github.com/jostrander",
"contributions": [
"code"
]
}
]
}

View File

@@ -11,12 +11,12 @@ MYSQL_ROOT_PASSWORD=changeme1234
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=develop
APP_DEBUG=true
APP_DEBUG=false
# please regenerate the APP_KEY value by calling `docker-compose run --rm snipeit bash` and then `php artisan key:generate --show` and then copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en-US
APP_LOCALE=en
MAX_RESULTS=500
# --------------------------------------------
@@ -158,7 +158,7 @@ RESET_PASSWORD_LINK_EXPIRES=900
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=single
LOG_CHANNEL=stderr
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC

View File

@@ -1,7 +1,7 @@
# --------------------------------------------
# REQUIRED: DOCKER SPECIFIC SETTINGS
# --------------------------------------------
APP_VERSION=
APP_VERSION=v6.4.1
APP_PORT=8000
# --------------------------------------------
@@ -9,7 +9,7 @@ APP_PORT=8000
# --------------------------------------------
APP_ENV=production
APP_DEBUG=false
# Please regenerate the APP_KEY value by calling `docker compose run --rm app php artisan key:generate --show`. Copy paste the value here
# Please regenerate the APP_KEY value by calling `docker compose run --rm snipeit php artisan key:generate --show`. Copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - TZ identifier
@@ -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.16.0.0/12
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.0.0.0/8
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false

View File

@@ -80,12 +80,6 @@ MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
ALLOW_BACKUP_DELETE=false
ALLOW_DATA_PURGE=false
ALL_BACKUP_KEEP_DAYS=7
DAILY_BACKUP_KEEP_DAYS=16
WEEKLY_BACKUP_KEEP_WEEKS=8
MONTHLY_BACKUP_KEEP_MONTHS=4
YEARLY_BACKUP_KEEP_YEARS=2
BACKUP_PURGE_OLDEST_AT_MEGS=5000
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS

38
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,38 @@
#### 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

129
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,129 @@
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.

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

View File

@@ -0,0 +1,25 @@
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.

40
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,40 @@
# 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

1
.github/travis-memory.ini vendored Normal file
View File

@@ -0,0 +1 @@
memory_limit= 2048M

7
.github/weekly-digest.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
# Configuration for weekly-digest - https://github.com/apps/weekly-digest
publishDay: sun
canPublishIssues: true
canPublishPullRequests: true
canPublishContributors: true
canPublishStargazers: true
canPublishCommits: true

View File

@@ -14,8 +14,7 @@ jobs:
- uses: actions/stale@v9
with:
debug-only: true
ascending: true
operations-per-run: 1000 # just while we're debugging
operations-per-run: 100 # just while we're debugging
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60
days-before-close: 7

View File

@@ -25,9 +25,9 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
name: PHP ${{ matrix.php-version }}
@@ -76,4 +76,4 @@ jobs:
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
run: php artisan test
run: php artisan test --parallel

View File

@@ -21,10 +21,9 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
name: PHP ${{ matrix.php-version }}
@@ -75,4 +74,4 @@ jobs:
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
DB_USERNAME: snipeit
DB_PASSWORD: password
run: php artisan test
run: php artisan test --parallel

View File

@@ -15,7 +15,7 @@ jobs:
fail-fast: false
matrix:
php-version:
- "8.3"
- "8.1.1"
name: PHP ${{ matrix.php-version }}
@@ -43,9 +43,6 @@ 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
@@ -60,5 +57,5 @@ jobs:
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite
run: php artisan test
DB_CONNECTION: sqlite_testing
run: php artisan test --parallel

1
.gitignore vendored
View File

@@ -47,7 +47,6 @@ storage/private_uploads/users/*
tests/_data/scenarios
tests/_output/*
tests/_support/_generated/*
tests/coverage/*
/npm-debug.log
/storage/oauth-private.key
/storage/oauth-public.key

View File

@@ -4,7 +4,7 @@
"DOC3": "Please don't rely on these versions for planning upgrades unless you've fetched the most recent version",
"DOC4": "You should really just ignore it and run upgrade.php. Really",
"php_min_version": "8.1.0",
"php_max_major_minor": "8.4",
"php_max_wontwork": "8.5.0",
"current_snipeit_version": "8.0"
"php_max_major_minor": "8.3",
"php_max_wontwork": "8.4.0",
"current_snipeit_version": "7.0"
}

View File

@@ -52,8 +52,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") |
| [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") | [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") |
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -1,4 +1,4 @@
FROM ubuntu:24.04
FROM ubuntu:22.04
LABEL maintainer="Brady Wetherington <bwetherington@grokability.com>"
# No need to add `apt-get clean` here, reference:
@@ -14,16 +14,16 @@ RUN export DEBIAN_FRONTEND=noninteractive; \
apt-utils \
apache2 \
apache2-bin \
libapache2-mod-php8.3 \
php8.3-curl \
php8.3-ldap \
php8.3-mysql \
php8.3-gd \
php8.3-xml \
php8.3-mbstring \
php8.3-zip \
php8.3-bcmath \
php8.3-redis \
libapache2-mod-php8.1 \
php8.1-curl \
php8.1-ldap \
php8.1-mysql \
php8.1-gd \
php8.1-xml \
php8.1-mbstring \
php8.1-zip \
php8.1-bcmath \
php8.1-redis \
php-memcached \
patch \
curl \
@@ -40,7 +40,8 @@ autoconf \
libc-dev \
libldap-common \
pkg-config \
php8.3-dev \
libmcrypt-dev \
php8.1-dev \
ca-certificates \
unzip \
dnsutils \
@@ -50,13 +51,18 @@ dnsutils \
RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar
RUN php go-pear.phar
RUN pecl install mcrypt
RUN bash -c "echo extension=/usr/lib/php/20210902/mcrypt.so > /etc/php/8.1/mods-available/mcrypt.ini"
RUN phpenmod mcrypt
RUN phpenmod gd
RUN phpenmod bcmath
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.3/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.3/cli/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.1/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/8.1/cli/php.ini
RUN useradd -m --uid 10000 --gid 50 docker
RUN useradd -m --uid 1000 --gid 50 docker
RUN echo export APACHE_RUN_USER=docker >> /etc/apache2/envvars
RUN echo export APACHE_RUN_GROUP=staff >> /etc/apache2/envvars

View File

@@ -73,7 +73,7 @@ RUN mkdir -p /var/www/.composer && chown apache /var/www/.composer
# Install dependencies
USER apache
RUN COMPOSER_CACHE_DIR=/dev/null composer install --working-dir=/var/www/html
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html
USER root

View File

@@ -1,34 +1,19 @@
![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/) [![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/) [![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)
[![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
This is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.
It is built on [Laravel 11](http://laravel.com).
It is built on [Laravel 10](http://laravel.com).
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
> [!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
@@ -37,6 +22,8 @@ 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.
<!-- [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) -->
-----
### User's Manual
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
@@ -48,21 +35,20 @@ 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.
@@ -94,37 +80,21 @@ Since the release of the JSON REST API, several third-party developers have been
- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by [@ReticentRobot](https://github.com/ReticentRobot) - Windows agent for Snipe-IT.
- [Gate Pass Generator](https://github.com/cha7uraAE/snipe-it-gate-pass-system) by [@cha7uraAE](https://github.com/cha7uraAE) - A Streamlit application for generating gate passes based on hardware data from a Snipe-IT API.
We also have a handful of [Google Apps scripts](https://github.com/grokability/google-apps-scripts-for-snipe-it) to help with various tasks.
-----
### Join the Community!
- **[Join our Discord](https://discord.gg/yZFtShAcKk)!** Its 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.)
-----
### 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.**
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
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.
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).
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
[Here is a list](CONTRIBUTORS.md) of the wonderful people that have contributed to the 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.
-----
### Security
> [!IMPORTANT]
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**

View File

@@ -10,12 +10,10 @@ however there are times when library dependencies and/or PHP/MySQL dependencies
make it impossible to backport security fixes on older versions.
| Version | Supported |
|---------| ------------------ |
| 7.x | :white_check_mark: |
| 6.x | :x: |
| 5.1.x | :x: |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability

308
app/Console/Commands/LdapSync.php Normal file → Executable file
View File

@@ -2,7 +2,6 @@
namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\Department;
use App\Models\Group;
use Illuminate\Console\Command;
@@ -54,22 +53,18 @@ class LdapSync extends Command
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
$ldap_map = [
"username" => Setting::getSettings()->ldap_username_field,
"last_name" => Setting::getSettings()->ldap_lname_field,
"first_name" => Setting::getSettings()->ldap_fname_field,
"active_flag" => Setting::getSettings()->ldap_active_flag,
"emp_num" => Setting::getSettings()->ldap_emp_num,
"email" => Setting::getSettings()->ldap_email,
"phone" => Setting::getSettings()->ldap_phone_field,
"jobtitle" => Setting::getSettings()->ldap_jobtitle,
"country" => Setting::getSettings()->ldap_country,
"location" => Setting::getSettings()->ldap_location,
"dept" => Setting::getSettings()->ldap_dept,
"manager" => Setting::getSettings()->ldap_manager,
];
$ldap_result_username = Setting::getSettings()->ldap_username_field;
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag;
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
$ldap_result_email = Setting::getSettings()->ldap_email;
$ldap_result_phone = Setting::getSettings()->ldap_phone_field;
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_location = Setting::getSettings()->ldap_location;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
$ldap_result_manager = Setting::getSettings()->ldap_manager;
$ldap_default_group = Setting::getSettings()->ldap_default_group;
$search_base = Setting::getSettings()->ldap_base_dn;
@@ -112,21 +107,14 @@ class LdapSync extends Command
}
/**
* If a filter has been specified, use that, otherwise default to null
* If a filter has been specified, use that
*/
if ($this->option('filter') != '') {
$filter = $this->option('filter');
$results = Ldap::findLdapUsers($search_base, -1, $this->option('filter'));
} else {
$filter = null;
$results = Ldap::findLdapUsers($search_base);
}
/**
* We only need to request the LDAP attributes that we process
*/
$attributes = array_values(array_filter($ldap_map));
$results = Ldap::findLdapUsers($search_base, -1, $filter, $attributes);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = ['error' => true, 'error_message' => $e->getMessage(), 'summary' => []];
@@ -138,24 +126,23 @@ class LdapSync extends Command
}
/* Determine which location to assign users to by default. */
$default_location = null;
$location = null; // TODO - this would be better called "$default_location", which is more explicit about its purpose
if ($this->option('location') != '') {
if ($default_location = Location::where('name', '=', $this->option('location'))->first()) {
if ($location = Location::where('name', '=', $this->option('location'))->first()) {
Log::debug('Location name ' . $this->option('location') . ' passed');
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
Log::debug('Importing to ' . $location->name . ' (' . $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 ($default_location = Location::where('id', '=', $location_id)->first()) {
if ($location = Location::where('id', '=', $location_id)->first()) {
Log::debug('Location ID ' . $location_id . ' passed');
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
}
}
}
if (!isset($default_location)) {
if (! isset($location)) {
Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
@@ -196,17 +183,17 @@ class LdapSync extends Command
}
$usernames = [];
for ($i = 0; $i < $location_users['count']; $i++) {
if (array_key_exists($ldap_map["username"], $location_users[$i])) {
if (array_key_exists($ldap_result_username, $location_users[$i])) {
$location_users[$i]['ldap_location_override'] = true;
$location_users[$i]['location_id'] = $ldap_loc['id'];
$usernames[] = $location_users[$i][$ldap_map["username"]][0];
$usernames[] = $location_users[$i][$ldap_result_username][0];
}
}
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if ((is_array($generic_entry)) && (array_key_exists($ldap_map["username"], $generic_entry))) {
if (in_array($generic_entry[$ldap_map["username"]][0], $usernames)) {
if ((is_array($generic_entry)) && (array_key_exists($ldap_result_username, $generic_entry))) {
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
unset($results[$key]);
}
}
@@ -231,78 +218,77 @@ 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] ?? '';
$location = $default_location; //initially, set '$location' to the default_location (which may just be `null`)
$item = [];
$item['username'] = $results[$i][$ldap_result_username][0] ?? '';
$item['employee_number'] = $results[$i][$ldap_result_emp_num][0] ?? '';
$item['lastname'] = $results[$i][$ldap_result_last_name][0] ?? '';
$item['firstname'] = $results[$i][$ldap_result_first_name][0] ?? '';
$item['email'] = $results[$i][$ldap_result_email][0] ?? '';
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
$item['location_id'] = $results[$i]['location_id'] ?? '';
$item['telephone'] = $results[$i][$ldap_result_phone][0] ?? '';
$item['jobtitle'] = $results[$i][$ldap_result_jobtitle][0] ?? '';
$item['country'] = $results[$i][$ldap_result_country][0] ?? '';
$item['department'] = $results[$i][$ldap_result_dept][0] ?? '';
$item['manager'] = $results[$i][$ldap_result_manager][0] ?? '';
$item['location'] = $results[$i][$ldap_result_location][0] ?? '';
// 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'],
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
if ($ldap_result_location && $item['location']) {
$location = Location::firstOrCreate([
'name' => $item['location'],
]);
}
$department = Department::firstOrCreate([
'name' => $item['department'],
]);
}
$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){
if($ldap_result_username != null){
$user->username = $item['username'];
}
if($ldap_map["last_name"] != null){
if($ldap_result_last_name != null){
$user->last_name = $item['lastname'];
}
if($ldap_map["first_name"] != null){
if($ldap_result_first_name != null){
$user->first_name = $item['firstname'];
}
if($ldap_map["emp_num"] != null){
if($ldap_result_emp_num != null){
$user->employee_num = e($item['employee_number']);
}
if($ldap_map["email"] != null){
if($ldap_result_email != null){
$user->email = $item['email'];
}
if($ldap_map["phone"] != null){
if($ldap_result_phone != null){
$user->phone = $item['telephone'];
}
if($ldap_map["jobtitle"] != null){
if($ldap_result_jobtitle != null){
$user->jobtitle = $item['jobtitle'];
}
if($ldap_map["country"] != null){
if($ldap_result_country != null){
$user->country = $item['country'];
}
if($ldap_map["dept"] != null){
if($ldap_result_dept != null){
$user->department_id = $department->id;
}
if($ldap_map["location"] != null){
$user->location_id = $location?->id;
if($ldap_result_location != null){
$user->location_id = $location ? $location->id : null;
}
if($ldap_map["manager"] != null){
if($ldap_result_manager != null){
if($item['manager'] != null) {
// Check Cache first
if (isset($manager_cache[$item['manager']])) {
@@ -319,70 +305,63 @@ class LdapSync extends Command
$ldap_manager = [
"count" => 1,
0 => [
$ldap_map["username"] => [$item['manager']]
$ldap_result_username => [$item['manager']]
]
];
}
$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 User from Manager username.
$ldap_manager = User::where('username', $ldapManagerUsername)->first();
// 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_result_username][0];
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.');
// 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 ($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'
}
$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_result_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_result_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
@@ -395,55 +374,44 @@ 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; //THIS is the magic line, this should do it.
}
}
// 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 = '';
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
if ($user->wasChanged('location_id')) {
foreach ($user->assets as $asset) {
$asset->location_id = $user->location_id;
// TODO: somehow add note? "Asset Location Changed because of thing"
$asset->save();
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;
}
}
$location = null;
$user->ldap_import = 1;
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .= $err[0];
$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';
}
$item['note'] = $errors;
$item['status'] = 'error';
}
array_push($summary, $item);
array_push($summary, $item);
}
if ($this->option('summary')) {

View File

@@ -6,7 +6,6 @@ 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'));
@@ -30,11 +29,6 @@ class ObjectImportCommand extends Command
*/
protected $description = 'Import Items from CSV';
/**
* The progress indicator instance.
*/
protected ProgressIndicator $progressIndicator;
/**
* Create a new command instance.
*
@@ -45,6 +39,8 @@ class ObjectImportCommand extends Command
parent::__construct();
}
private $bar;
/**
* Execute the console command.
*
@@ -52,14 +48,12 @@ 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'])
->setCreatedBy($this->option('user_id'))
->setUserId($this->option('user_id'))
->setUpdating($this->option('update'))
->setShouldNotify($this->option('send-welcome'))
->setUsernameFormat($this->option('username_format'));
@@ -67,25 +61,46 @@ 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->progressIndicator->start('======= Importing Items from '.$filename.' =========');
$this->comment('======= Importing Items from '.$filename.' =========');
$importer->import();
$this->progressIndicator->finish('Import finished.');
$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('');
}
public function errorCallback($item, $field, $error)
public function errorCallback($item, $field, $errorString)
{
$this->output->write("\x0D\x1B[2K");
$this->warn('Error: Item: '.$item->name.' failed validation: '.json_encode($error));
$this->errors[$item->name][$field] = $errorString;
}
public function progress($importedItemsCount)
public function progress($count)
{
$this->progressIndicator->advance();
if (! $this->bar) {
$this->bar = $this->output->createProgressBar($count);
}
static $index = 0;
$index++;
if ($index < $count) {
$this->bar->advance();
} else {
$this->bar->finish();
}
}
// 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.

View File

@@ -0,0 +1,157 @@
<?php
namespace App\Console\Commands;
use App\LegacyEncrypter\McryptEncrypter;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class RecryptFromMcrypt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:legacy-recrypt
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command allows upgrading users to de-encrypt their deprecated mcrypt encrypted fields and re-encrypt them using the current OpenSSL encryption.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Check and see if they have a legacy app key listed in their .env
// If not, we can try to use the current APP_KEY if looks like it's old
$legacy_key = env('LEGACY_APP_KEY');
$key_parts = explode(':', $legacy_key);
$legacy_cipher = env('LEGACY_CIPHER', 'rijndael-256');
$errors = [];
if (! $legacy_key) {
$this->error('ERROR: You do not have a LEGACY_APP_KEY set in your .env file. Please locate your old APP_KEY and ADD a line to your .env file like: LEGACY_APP_KEY=YOUR_OLD_APP_KEY');
return false;
}
// Do some basic legacy app key length checks
if (strlen($legacy_key) == 32) {
$legacy_length_check = true;
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1]) == 44)) {
$legacy_key = base64_decode($key_parts[1], true);
$legacy_length_check = true;
} else {
$legacy_length_check = false;
}
// Check that the app key is 32 characters
if ($legacy_length_check === true) {
$this->comment('INFO: Your LEGACY_APP_KEY looks correct. Okay to continue.');
} else {
$this->error('ERROR: Your LEGACY_APP_KEY is not the correct length (32 characters or base64 followed by 44 characters for later versions). Please locate your old APP_KEY and use that as your LEGACY_APP_KEY in your .env file to continue.');
return false;
}
$this->error('================================!!!! WARNING !!!!================================');
$this->error('================================!!!! WARNING !!!!================================');
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
$force = ($this->option('force')) ? true : false;
if ($force || ($this->confirm('Are you SURE you wish to continue?'))) {
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
try {
Storage::disk('local')->put($backup_file, 'APP_KEY: '.config('app.key'));
Storage::disk('local')->append($backup_file, 'LEGACY_APP_KEY: '.$legacy_key);
} catch (\Exception $e) {
$this->info('WARNING: Could not backup app keys');
}
if ($legacy_cipher) {
$mcrypter = new McryptEncrypter($legacy_key, $legacy_cipher);
} else {
$mcrypter = new McryptEncrypter($legacy_key);
}
$settings = Setting::getSettings();
if ($settings->ldap_pword == '') {
$this->comment('INFO: No LDAP password found. Skipping... ');
} else {
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
$settings->ldap_pword = Crypt::encrypt($decrypted_ldap_pword);
$settings->save();
}
/** @var CustomField[] $custom_fields */
$custom_fields = CustomField::where('field_encrypted', '=', 1)->get();
$this->comment('INFO: Retrieving encrypted custom fields...');
$query = Asset::withTrashed();
foreach ($custom_fields as $custom_field) {
$this->comment('FIELD TO RECRYPT: '.$custom_field->name.' ('.$custom_field->db_column.')');
$query->orWhereNotNull($custom_field->db_column);
}
// Get all assets with a value in any of the fields that were encrypted
/** @var Asset[] $assets */
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
$columnName = $encrypted_field->db_column;
// Make sure the value isn't null
if ($asset->{$columnName} != '') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
$asset->{$columnName} = Crypt::encrypt($decrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
}
}
$asset->save();
$bar->advance();
}
$bar->finish();
if (count($errors) > 0) {
$this->comment("\n\n");
$this->error("The decrypter encountered some errors: \n");
foreach ($errors as $error) {
$this->error($error);
}
}
}
}
}

View File

@@ -50,12 +50,12 @@ class ResetDemoSettings extends Command
$settings->alert_email = 'service@snipe-it.io';
$settings->login_note = 'Use `admin` / `password` to login to the demo.';
$settings->header_color = null;
$settings->label2_2d_type = 'QRCODE';
$settings->barcode_type = 'QRCODE';
$settings->default_currency = 'USD';
$settings->brand = 2;
$settings->ldap_enabled = 0;
$settings->full_multiple_companies_support = 0;
$settings->label2_1d_type = 'C128';
$settings->alt_barcode = 'C128';
$settings->skin = '';
$settings->email_domain = 'snipeitapp.com';
$settings->email_format = 'filastname';
@@ -65,7 +65,7 @@ class ResetDemoSettings extends Command
$settings->thumbnail_max_h = '30';
$settings->locale = 'en-US';
$settings->version_footer = 'on';
$settings->support_footer = 'on';
$settings->support_footer = null;
$settings->saml_enabled = '0';
$settings->saml_sp_x509cert = null;
$settings->saml_idp_metadata = null;

View File

@@ -51,8 +51,6 @@ class SQLStreamer {
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
"<^/\*!40101 SET NAMES '?[a-zA-Z0-9_-]+'? \*/;$>" => false, //using weird delimiters (<,>) for readability. allow quoted or unquoted charsets
"<^/\*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' \*/;$>" => false, //same, now handle zero-values
];
foreach($allowed_statements as $statement => $statechange) {
@@ -372,7 +370,7 @@ class RestoreFromBackup extends Command
if ($this->option('sanitize-guess-prefix')) {
$prefix = SQLStreamer::guess_prefix($sql_contents);
$this->line($prefix);
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
}
// If we're doing --sql-stdout-only, handle that now so we don't have to open pipes to mysql and all of that silliness

View File

@@ -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\Mail;
use Illuminate\Support\Facades\Notification;
class SendAcceptanceReminder extends Command
{
@@ -50,7 +50,7 @@ class SendAcceptanceReminder extends Command
$query->where('accepted_at', null)
->where('declined_at', null);
})
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.adminuser'])
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.admin'])
->get();
$count = 0;
@@ -65,29 +65,42 @@ 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 ($locale && $email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale));
} elseif ($email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count)));
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++;
}
}
$count++;
}
if (!empty($no_mail_address)) {
foreach($no_mail_address as $user) {
return $user.' has no email.';
}
}
$this->info($count.' users notified.');
return 0;
}
}

View File

@@ -2,13 +2,13 @@
namespace App\Console\Commands;
use App\Mail\ExpiringAssetsMail;
use App\Mail\ExpiringLicenseMail;
use App\Models\Asset;
use App\Models\License;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\ExpiringAssetsNotification;
use App\Notifications\ExpiringLicenseNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendExpirationAlerts extends Command
{
@@ -47,22 +47,22 @@ class SendExpirationAlerts extends Command
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))
->map(fn($item) => trim($item)) // Trim each email
->all();
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new AlertRecipient($item);
});
// Expiring Assets
$assets = Asset::getExpiringWarrantee($threshold);
if ($assets->count() > 0) {
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count' => $assets->count(), 'threshold' => $threshold]));
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $threshold));
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
}
// Expiring licenses
$licenses = License::getExpiringLicenses($threshold);
if ($licenses->count() > 0) {
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
Mail::to($recipients)->send(new ExpiringLicenseMail($licenses, $threshold));
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
}
} else {
if ($settings->alert_email == '') {

View File

@@ -122,27 +122,6 @@ class Handler extends ExceptionHandler
}
// This is traaaaash but it handles models that are not found while using route model binding :(
// The only alternative is to set that at *each* route, which is crazypants
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
$model_name = last(explode('\\', $e->getModel()));
$route = str_plural(strtolower(last(explode('\\', $e->getModel())))).'.index';
// Sigh.
if ($route == 'assets.index') {
$route = 'hardware.index';
} elseif ($route == 'reporttemplates.index') {
$route = 'reports/custom';
} elseif ($route == 'assetmodels.index') {
$route = 'models.index';
} elseif ($route == 'predefinedkits.index') {
$route = 'kits.index';
}
return redirect()
->route($route)
->withError(trans('general.generic_model_not_found', ['model' => $model_name]));
}
if ($this->isHttpException($e) && (isset($statusCode)) && ($statusCode == '404' )) {

View File

@@ -16,7 +16,6 @@ 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;
@@ -709,28 +708,6 @@ 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. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
];
// 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
@@ -1146,7 +1123,6 @@ 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',
@@ -1159,7 +1135,7 @@ class Helper
//Text
'txt' => 'far fa-file-alt',
'rtf' => 'far fa-file-alt',
'xml' => 'fas fa-code',
'xml' => 'far fa-file-alt',
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
@@ -1172,7 +1148,41 @@ 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 <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
public static function generateEncyrptedPassword(): string
{
return bcrypt(self::generateUnencryptedPassword());
}
/**
* Get a random unencrypted password.
@@ -1520,11 +1530,11 @@ class Helper
if ($redirect_option == 'target') {
switch ($checkout_to_type) {
case 'user':
return route('users.show', $request->assigned_user);
return route('users.show', ['user' => $request->assigned_user]);
case 'location':
return route('locations.show', $request->assigned_location);
return route('locations.show', ['location' => $request->assigned_location]);
case 'asset':
return route('hardware.show', $request->assigned_asset);
return route('hardware.show', ['hardware' => $request->assigned_asset]);
}
}
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));

View File

@@ -59,26 +59,19 @@ class IconHelper
return 'fas fa-cog';
case 'angle-left':
return 'fas fa-angle-left';
case 'angle-right':
return 'fas fa-angle-right';
case 'warning':
return 'fas fa-exclamation-triangle';
case 'kits':
return 'fas fa-object-group';
case 'assets':
case 'asset':
return 'fas fa-barcode';
case 'accessories':
case 'accessory':
return 'far fa-keyboard';
case 'components':
case 'component':
return 'far fa-hdd';
case 'consumables':
case 'consumable':
return 'fas fa-tint';
case 'licenses':
case 'license':
return 'far fa-save';
case 'requestable':
return 'fas fa-laptop';
@@ -148,8 +141,6 @@ class IconHelper
return 'fas fa-lock';
case 'locations':
return 'fas fa-map-marker-alt';
case 'location':
return 'fas fa-map-marker-alt';
case 'superadmin':
return 'fas fa-crown';
case 'print':
@@ -182,15 +173,7 @@ class IconHelper
return 'fas fa-crosshairs';
case 'oauth':
return 'fas fa-user-secret';
case 'employee_num' :
return 'fa-regular fa-id-card';
case 'department' :
return 'fa-solid fa-building-user';
case 'home' :
return 'fa-solid fa-house';
case 'note':
case 'notes':
return 'fas fa-sticky-note';
}
}
}

View File

@@ -7,7 +7,6 @@ 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
@@ -26,64 +25,4 @@ 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 <A. Gianotto> [<snipe@snipe.net]>
* @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);
}
}

View File

@@ -31,7 +31,7 @@ class AccessoriesController extends Controller
public function index() : View
{
$this->authorize('index', Accessory::class);
return view('accessories.index');
return view('accessories/index');
}
/**
@@ -73,7 +73,7 @@ class AccessoriesController extends Controller
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost');
$accessory->qty = request('qty');
$accessory->created_by = auth()->id();
$accessory->user_id = auth()->id();
$accessory->supplier_id = request('supplier_id');
$accessory->notes = request('notes');
@@ -95,10 +95,16 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
*/
public function edit(Accessory $accessory) : View | RedirectResponse
public function edit($accessoryId = null) : View | RedirectResponse
{
$this->authorize('update', Accessory::class);
return view('accessories.edit')->with('item', $accessory)->with('category_type', 'accessory');
if ($item = Accessory::find($accessoryId)) {
$this->authorize($item);
return view('accessories/edit', compact('item'))->with('category_type', 'accessory');
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
/**
@@ -108,12 +114,19 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @since [v6.0]
*/
public function getClone(Accessory $accessory) : View | RedirectResponse
public function getClone($accessoryId = null) : View | RedirectResponse
{
$this->authorize('create', Accessory::class);
$accessory = clone $accessory;
// Check if the asset exists
if (is_null($accessory_to_clone = Accessory::find($accessoryId))) {
// Redirect to the asset management page
return redirect()->route('accessories.index')
->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryId]));
}
$accessory = clone $accessory_to_clone;
$accessory->id = null;
$accessory->location_id = null;
@@ -129,9 +142,9 @@ class AccessoriesController extends Controller
* @param ImageUploadRequest $request
* @param int $accessoryId
*/
public function update(ImageUploadRequest $request, Accessory $accessory) : RedirectResponse
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
{
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessory->id)) {
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
$this->authorize($accessory);
@@ -218,10 +231,14 @@ class AccessoriesController extends Controller
* @see AccessoriesController::getDataView() method that generates the JSON response
* @since [v1.0]
*/
public function show(Accessory $accessory) : View | RedirectResponse
public function show($accessoryID = null) : View | RedirectResponse
{
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessory->id);
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
$this->authorize('view', $accessory);
return view('accessories.view', compact('accessory'));
if (isset($accessory->id)) {
return view('accessories/view', compact('accessory'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryID]));
}
}

View File

@@ -51,15 +51,15 @@ class AccessoriesFilesController extends Controller
}
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
return redirect()->route('accessories.show', $accessory->id)->with('success', trans('general.file_upload_success'));
}
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('error', trans('general.no_files_uploaded'));
return redirect()->route('accessories.show', $accessory->id)->with('error', trans('general.no_files_uploaded'));
}
// Prepare the error message
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
return redirect()->route('accessories.index')
->with('error', trans('general.file_does_not_exist'));
}
/**
@@ -72,27 +72,30 @@ class AccessoriesFilesController extends Controller
*/
public function destroy($accessoryId = null, $fileId = null) : RedirectResponse
{
if ($accessory = Accessory::find($accessoryId)) {
$accessory = Accessory::find($accessoryId);
// the asset is valid
if (isset($accessory->id)) {
$this->authorize('update', $accessory);
$log = Actionlog::find($fileId);
if ($log = Actionlog::find($fileId)) {
if (Storage::exists('private_uploads/accessories/'.$log->filename)) {
try {
Storage::delete('private_uploads/accessories/' . $log->filename);
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
} catch (\Exception $e) {
Log::debug($e);
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist'));
}
// Remove the file if one exists
if (Storage::exists('accessories/'.$log->filename)) {
try {
Storage::delete('accessories/'.$log->filename);
} catch (\Exception $e) {
Log::debug($e);
}
}
return redirect()->route('accessories.show', ['accessory' => $accessory])->withFragment('files')->with('error', trans('general.log_record_not_found'));
$log->delete();
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
// Redirect to the licence management page
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist'));
}
/**
@@ -103,30 +106,50 @@ class AccessoriesFilesController extends Controller
* @param int $accessoryId
* @param int $fileId
*/
public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
public function show($accessoryId = null, $fileId = null, $download = true) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
{
Log::debug('Private filesystem is: '.config('filesystems.default'));
$accessory = Accessory::find($accessoryId);
// the accessory is valid
if ($accessory = Accessory::find($accessoryId)) {
if (isset($accessory->id)) {
$this->authorize('view', $accessory);
$this->authorize('accessories.files', $accessory);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
$file = 'private_uploads/accessories/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found'));
}
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'));
}
return redirect()->route('accessories.show', ['accessory' => $accessory])->withFragment('files')->with('error', trans('general.log_record_not_found'));
$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);
}
}
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
}
}

View File

@@ -75,23 +75,20 @@ class AccessoryCheckoutController extends Controller
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
$accessory_checkout = new AccessoryCheckout([
AccessoryCheckout::create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
}
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
// Set this as user since we only allow checkout to user for this item type
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
$request->request->add(['assigned_to' => $target->id]);
$request->request->add(['assigned_user' => $target->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);

View File

@@ -237,11 +237,7 @@ class AcceptanceController extends Controller
}
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
try {
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
} catch (\Exception $e) {
Log::warning($e);
}
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
event(new CheckoutAccepted($acceptance));
$return_msg = trans('admin/users/message.accepted');
@@ -338,5 +334,4 @@ class AcceptanceController extends Controller
return redirect()->to('account/accept')->with('success', $return_msg);
}
}

View File

@@ -37,19 +37,10 @@ class ActionlogController extends Controller
}
}
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
public function getStoredEula($filename) : Response | BinaryFileResponse
{
$this->authorize('view', \App\Models\Asset::class);
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
$file = config('app.private_uploads').'/eula-pdfs/'.$filename;
return response()->download($file);
}
}

View File

@@ -13,7 +13,6 @@ use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\Company;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
@@ -57,9 +56,8 @@ class AccessoriesController extends Controller
];
$accessories = Accessory::select('accessories.*')
->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier', 'adminuser')
->withCount('checkouts as checkouts_count');
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier')
->withCount('checkouts as checkouts_count');
if ($request->filled('search')) {
$accessories = $accessories->TextSearch($request->input('search'));
@@ -113,9 +111,6 @@ class AccessoriesController extends Controller
case 'supplier':
$accessories = $accessories->OrderSupplier($order);
break;
case 'created_by':
$accessories = $accessories->OrderByCreatedByName($order);
break;
default:
$accessories = $accessories->orderBy($column_sort, $order);
break;
@@ -138,6 +133,7 @@ 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);
@@ -185,33 +181,42 @@ class AccessoriesController extends Controller
/**
* Get the list of checkouts for a specific accessory
* Display the specified resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return | array
* @return \Illuminate\Http\Response
*/
public function checkedout(Request $request, $id)
public function checkedout($id, Request $request)
{
$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);
// Total count of all checkouts for this asset
$accessory_checkouts = $accessory->checkouts();
$accessory_checkouts = $accessory->checkouts;
$total = $accessory_checkouts->count();
// Check for search text in the request
if ($request->filled('search')) {
$accessory_checkouts = $accessory_checkouts->TextSearch($request->input('search'));
if ($total < $offset) {
$offset = 0;
}
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_checkouts, $total);
if ($request->filled('search')) {
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
->get();
$total = $accessory_checkouts->count();
}
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
}
@@ -222,7 +227,7 @@ class AccessoriesController extends Controller
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
* @param int $id
* @return \Illuminate\Http\JsonResponse
* @return \Illuminate\Http\Response
*/
public function update(ImageUploadRequest $request, $id)
{
@@ -244,7 +249,7 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\JsonResponse
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
@@ -279,17 +284,14 @@ class AccessoriesController extends Controller
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
$accessory_checkout = new AccessoryCheckout([
AccessoryCheckout::create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
}
// Set this value to be able to pass the qty through to the event
@@ -319,7 +321,7 @@ class AccessoriesController extends Controller
$accessory = Accessory::find($accessory_checkout->accessory_id);
$this->authorize('checkin', $accessory);
$accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
// Was the accessory updated?
if ($accessory_checkout->delete()) {
@@ -327,6 +329,14 @@ 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')));
}

View File

@@ -34,7 +34,7 @@ class AssetMaintenancesController extends Controller
$this->authorize('view', Asset::class);
$maintenances = AssetMaintenance::select('asset_maintenances.*')
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'adminuser');
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'admin');
if ($request->filled('search')) {
$maintenances = $maintenances->TextSearch($request->input('search'));
@@ -48,10 +48,6 @@ class AssetMaintenancesController extends Controller
$maintenances->where('asset_maintenances.supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('created_by')) {
$maintenances->where('asset_maintenances.created_by', '=', $request->input('created_by'));
}
if ($request->filled('asset_maintenance_type')) {
$maintenances->where('asset_maintenance_type', '=', $request->input('asset_maintenance_type'));
}
@@ -73,7 +69,7 @@ class AssetMaintenancesController extends Controller
'asset_tag',
'asset_name',
'serial',
'created_by',
'user_id',
'supplier',
'is_warranty',
'status_label',
@@ -83,8 +79,8 @@ class AssetMaintenancesController extends Controller
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
switch ($sort) {
case 'created_by':
$maintenances = $maintenances->OrderByCreatedBy($order);
case 'user_id':
$maintenances = $maintenances->OrderAdmin($order);
break;
case 'supplier':
$maintenances = $maintenances->OrderBySupplier($order);
@@ -122,13 +118,13 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function store(Request $request) : JsonResponse | array
public function store(Request $request) : JsonResponse
{
$this->authorize('update', Asset::class);
// create a new model instance
$maintenance = new AssetMaintenance();
$maintenance->fill($request->all());
$maintenance->created_by = auth()->id();
$maintenance->user_id = Auth::id();
// Was the asset maintenance created?
if ($maintenance->save()) {
@@ -149,7 +145,7 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v4.0]
*/
public function update(Request $request, $id) : JsonResponse | array
public function update(Request $request, $id) : JsonResponse
{
$this->authorize('update', Asset::class);
@@ -186,13 +182,16 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v4.0]
*/
public function destroy($assetMaintenanceId) : JsonResponse | array
public function destroy($assetMaintenanceId) : JsonResponse
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot delete a maintenance for that asset'));
}
$assetMaintenance->delete();
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
@@ -208,7 +207,7 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v4.0]
*/
public function show($assetMaintenanceId) : JsonResponse | array
public function show($assetMaintenanceId) : JsonResponse
{
$this->authorize('view', Asset::class);
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);

View File

@@ -9,7 +9,6 @@ use App\Http\Controllers\Controller;
use App\Models\AssetModel;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\AssetModelsTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -69,15 +68,37 @@ class AssetModelFilesController extends Controller
/**
* List the files for an asset.
*
* @param int $assetmodel
* @param int $assetModelId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function list($assetmodel_id) : JsonResponse | array
public function list($assetModelId = null) : JsonResponse
{
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
$this->authorize('view', $assetmodel);
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('view', $assetModel);
// Check that there are some uploads on this asset that can be listed
if ($assetModel->uploads->count() > 0) {
$files = array();
foreach ($assetModel->uploads as $upload) {
array_push($files, $upload);
}
// Give the list of files back to the user
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/models/message.upload.success')));
}
// There are no files.
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/models/message.upload.success')));
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error')), 500);
}
/**

View File

@@ -48,29 +48,26 @@ class AssetModelsController extends Controller
'assets_count',
'category',
'fieldset',
'deleted_at',
'updated_at',
];
$assetmodels = AssetModel::select([
'models.id',
'models.image',
'models.name',
'models.model_number',
'models.min_amt',
'models.eol',
'models.created_by',
'models.requestable',
'model_number',
'min_amt',
'eol',
'requestable',
'models.notes',
'models.created_at',
'models.category_id',
'models.manufacturer_id',
'models.depreciation_id',
'models.fieldset_id',
'category_id',
'manufacturer_id',
'depreciation_id',
'fieldset_id',
'models.deleted_at',
'models.updated_at',
])
->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues', 'adminuser')
->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues')
->withCount('assets as assets_count');
if ($request->input('status')=='deleted') {
@@ -96,7 +93,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 ($request->input('sort')) {
switch ($sort) {
case 'manufacturer':
$assetmodels->OrderManufacturer($order);
break;
@@ -106,9 +103,6 @@ class AssetModelsController extends Controller
case 'fieldset':
$assetmodels->OrderFieldset($order);
break;
case 'created_by':
$assetmodels->OrderByCreatedByName($order);
break;
default:
$assetmodels->orderBy($sort, $order);
break;

View File

@@ -6,7 +6,6 @@ use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Http\Traits\MigratesLegacyAssetLocations;
use App\Models\AccessoryCheckout;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use Illuminate\Database\Eloquent\Builder;
@@ -27,13 +26,13 @@ use App\Models\License;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
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;
/**
@@ -57,11 +56,6 @@ class AssetsController extends Controller
public function index(Request $request, $action = null, $upcoming_status = null) : JsonResponse | array
{
// This handles the legacy audit endpoints :(
if ($action == 'audit') {
$action = 'audits';
}
$filter_non_deprecable_assets = false;
/**
@@ -127,20 +121,8 @@ class AssetsController extends Controller
}
$assets = Asset::select('assets.*')
->with(
'model',
'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',
'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) {
@@ -172,8 +154,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':
switch ($upcoming_status) {
case 'due':
$assets->DueForAudit($settings);
@@ -200,7 +182,7 @@ class AssetsController extends Controller
break;
}
break;
}
}
/**
* End handling due and overdue audits and checkin dates
@@ -278,6 +260,7 @@ class AssetsController extends Controller
$join->on('status_alias.id', '=', 'assets.status_id');
});
}
}
@@ -388,32 +371,8 @@ class AssetsController extends Controller
case 'assigned_to':
$assets->OrderAssigned($order);
break;
case 'created_by':
$assets->OrderByCreatedByName($order);
break;
default:
$numeric_sort = false;
// Search through the custom fields array to see if we're sorting on a custom field
if (array_search($column_sort, $all_custom_fields->pluck('db_column')->toArray()) !== false) {
// Check to see if this is a numeric field type
foreach ($all_custom_fields as $field) {
if (($field->db_column == $sort_override) && ($field->format == 'NUMERIC')) {
$numeric_sort = true;
break;
}
}
// This may not work for all databases, but it works for MySQL
if ($numeric_sort) {
$assets->orderByRaw(DB::getTablePrefix() . 'assets.' . $sort_override . ' * 1 ' . $order);
} else {
$assets->orderBy($sort_override, $order);
}
} else {
$assets->orderBy($column_sort, $order);
}
$assets->orderBy($column_sort, $order);
break;
}
@@ -452,7 +411,7 @@ class AssetsController extends Controller
* @since [v4.2.1]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
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');
@@ -474,10 +433,12 @@ 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);
}
/**
@@ -488,7 +449,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');
@@ -499,11 +460,12 @@ class AssetsController extends Controller
}
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);
}
/**
@@ -514,20 +476,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);
@@ -535,7 +497,7 @@ class AssetsController extends Controller
$licenses = $asset->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
}
/**
@@ -545,7 +507,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([
@@ -556,7 +518,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();
@@ -578,12 +540,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;
@@ -606,12 +568,12 @@ class AssetsController extends Controller
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
$asset->fill($request->validated());
$asset->created_by = auth()->id();
$asset->user_id = 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'));
}
@@ -631,9 +593,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
@@ -651,7 +613,7 @@ class AssetsController extends Controller
}
}
if ($field->element == 'checkbox') {
if (is_array($field_val)) {
if(is_array($field_val)) {
$field_val = implode(',', $field_val);
}
}
@@ -710,9 +672,9 @@ 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'));
}
@@ -720,58 +682,54 @@ class AssetsController extends Controller
$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);
// 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 ($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;
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;
}
$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')));
// Below is the *correct* return since it uses the transformer, but we have to use the old, flat return for now until we can update Jamf2Snipe and Kanji2Snipe
// 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')));
// Below is the *correct* return since it uses the transformer, but we have to use the old, flat return for now until we can update Jamf2Snipe and Kanji2Snipe
/// 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);
@@ -785,23 +743,16 @@ 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);
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]);
}
DB::table('assets')
->where('id', $asset->id)
->update(['assigned_to' => null]);
$asset->delete();
@@ -820,7 +771,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)) {
@@ -839,6 +790,7 @@ class AssetsController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
@@ -848,7 +800,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);
@@ -863,13 +815,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);
@@ -886,12 +838,14 @@ 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'));
@@ -905,7 +859,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'));
@@ -919,15 +873,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')));
}
@@ -938,7 +892,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);
@@ -946,7 +900,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')));
@@ -969,7 +923,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');
}
}
@@ -978,7 +932,7 @@ class AssetsController extends Controller
$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'))) {
@@ -996,8 +950,7 @@ class AssetsController extends Controller
[Asset::class],
function (Builder $query) use ($asset) {
$query->where('id', $asset->id);
}
)
})
->get()
->map(function ($acceptance) {
$acceptance->delete();
@@ -1007,13 +960,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')));
}
/**
@@ -1022,7 +975,7 @@ class AssetsController extends Controller
* @author [A. Janes] [<ajanes@adagiohealth.org>]
* @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'))) {
@@ -1035,8 +988,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'));
}
@@ -1047,7 +1000,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);
@@ -1058,8 +1011,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);
}
@@ -1107,25 +1060,28 @@ 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);
}
@@ -1136,7 +1092,7 @@ class AssetsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function requestable(Request $request): JsonResponse | array
public function requestable(Request $request) : JsonResponse | array
{
$this->authorize('viewRequestable', Asset::class);
@@ -1157,18 +1113,8 @@ 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');
@@ -1217,115 +1163,4 @@ class AssetsController extends Controller
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
public function assignedAssets(Request $request, Asset $asset) : JsonResponse | array
{
return [];
// to do
}
public function assignedAccessories(Request $request, Asset $asset) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$accessory_checkouts = AccessoryCheckout::AssetsAssigned()
->where('assigned_to', $asset->id)
->with('adminuser')
->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $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);
}
}
}

View File

@@ -39,12 +39,10 @@ class CategoriesController extends Controller
'components_count',
'licenses_count',
'image',
'notes',
];
$categories = Category::select([
'id',
'created_by',
'created_at',
'updated_at',
'name', 'category_type',
@@ -52,11 +50,8 @@ class CategoriesController extends Controller
'eula_text',
'require_acceptance',
'checkin_email',
'image',
'notes',
])
->with('adminuser')
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count');
'image'
])->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count');
/*
@@ -96,33 +91,13 @@ class CategoriesController extends Controller
$categories->where('checkin_email', '=', $request->input('checkin_email'));
}
if ($request->filled('created_by')) {
$categories->where('created_by', '=', $request->input('created_by'));
}
if ($request->filled('created_at')) {
$categories->where('created_at', '=', $request->input('created_at'));
}
if ($request->filled('updated_at')) {
$categories->where('updated_at', '=', $request->input('updated_at'));
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $categories->count()) ? $categories->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets_count';
switch ($sort_override) {
case 'created_by':
$categories = $categories->OrderByCreatedBy($order);
break;
default:
$categories = $categories->orderBy($column_sort, $order);
break;
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
$categories->orderBy($sort, $order);
$total = $categories->count();
$categories = $categories->skip($offset)->take($limit)->get();

View File

@@ -38,7 +38,6 @@ class CompaniesController extends Controller
'accessories_count',
'consumables_count',
'components_count',
'notes',
];
$companies = Company::withCount(['assets as assets_count' => function ($query) {
@@ -57,29 +56,17 @@ class CompaniesController extends Controller
$companies->where('email', '=', $request->input('email'));
}
if ($request->filled('created_by')) {
$companies->where('created_by', '=', $request->input('created_by'));
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort_override) {
case 'created_by':
$companies = $companies->OrderByCreatedBy($order);
break;
default:
$companies = $companies->orderBy($column_sort, $order);
break;
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$companies->orderBy($sort, $order);
$total = $companies->count();
$companies = $companies->skip($offset)->take($limit)->get();
return (new CompaniesTransformer)->transformCompanies($companies, $total);

View File

@@ -38,7 +38,6 @@ class ComponentsController extends Controller
'name',
'min_amt',
'order_number',
'model_number',
'serial',
'purchase_date',
'purchase_cost',
@@ -48,8 +47,7 @@ class ComponentsController extends Controller
];
$components = Component::select('components.*')
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer', 'uncontrainedAssets')
->withSum('uncontrainedAssets', 'components_assets.assigned_qty');
->with('company', 'location', 'category', 'assets', 'supplier');
if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search'));
@@ -71,14 +69,6 @@ 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'));
}
@@ -108,12 +98,6 @@ 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;
default:
$components = $components->orderBy($column_sort, $order);
break;
@@ -198,11 +182,6 @@ class ComponentsController extends Controller
$this->authorize('delete', Component::class);
$component = Component::findOrFail($id);
$this->authorize('delete', $component);
if ($component->numCheckedOut() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.delete.error_qty')));
}
$component->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.delete.success')));
@@ -291,7 +270,7 @@ class ComponentsController extends Controller
'component_id' => $component->id,
'created_at' => Carbon::now(),
'assigned_qty' => $request->get('assigned_qty', 1),
'created_by' => auth()->id(),
'user_id' => auth()->id(),
'asset_id' => $request->get('assigned_to'),
'note' => $request->get('note'),
]);
@@ -315,7 +294,9 @@ 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')));
}
@@ -323,14 +304,18 @@ class ComponentsController extends Controller
$max_to_checkin = $component_assets->assigned_qty;
$validator = Validator::make($request->all(), [
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
]);
if ($max_to_checkin > 1) {
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));
@@ -340,22 +325,27 @@ class ComponentsController extends Controller
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'));
}
}

View File

@@ -86,15 +86,9 @@ class ConsumablesController extends Controller
case 'company':
$consumables = $consumables->OrderCompany($order);
break;
case 'remaining':
$consumables = $consumables->OrderRemaining($order);
break;
case 'supplier':
$consumables = $consumables->OrderSupplier($order);
break;
case 'created_by':
$consumables = $consumables->OrderByCreatedBy($order);
break;
default:
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
// These must match a column on the consumables table directly.
@@ -213,7 +207,7 @@ class ConsumablesController extends Controller
$consumable = Consumable::with(['consumableAssignments'=> function ($query) {
$query->orderBy($query->getModel()->getTable().'.created_at', 'DESC');
},
'consumableAssignments.adminuser'=> function ($query) {
'consumableAssignments.admin'=> function ($query) {
},
'consumableAssignments.user'=> function ($query) {
},
@@ -231,8 +225,7 @@ class ConsumablesController extends Controller
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
'note' => ($consumable_assignment->note) ? e($consumable_assignment->note) : null,
'admin' => ($consumable_assignment->adminuser) ? $consumable_assignment->adminuser->present()->nameUrl() : null, // legacy, so we don't change the shape of the response
'created_by' => ($consumable_assignment->adminuser) ? $consumable_assignment->adminuser->present()->nameUrl() : null,
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : null,
];
}
@@ -258,8 +251,6 @@ 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')));
@@ -270,12 +261,6 @@ 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'))) {
@@ -286,17 +271,14 @@ class ConsumablesController extends Controller
// Update the consumable data
$consumable->assigned_to = $request->input('assigned_to');
for ($i = 0; $i < $consumable->checkout_qty; $i++) {
$consumable->users()->attach($consumable->id,
$consumable->users()->attach($consumable->id,
[
'consumable_id' => $consumable->id,
'created_by' => $user->id,
'user_id' => $user->id,
'assigned_to' => $request->input('assigned_to'),
'note' => $request->input('note'),
]
);
}
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));

View File

@@ -23,7 +23,7 @@ class DepartmentsController extends Controller
public function index(Request $request) : JsonResponse | array
{
$this->authorize('view', Department::class);
$allowed_columns = ['id', 'name', 'image', 'users_count', 'notes'];
$allowed_columns = ['id', 'name', 'image', 'users_count'];
$departments = Department::select(
'departments.id',
@@ -35,8 +35,7 @@ class DepartmentsController extends Controller
'departments.manager_id',
'departments.created_at',
'departments.updated_at',
'departments.image',
'departments.notes',
'departments.image'
)->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
if ($request->filled('search')) {
@@ -73,9 +72,6 @@ class DepartmentsController extends Controller
case 'manager':
$departments->OrderManager($order);
break;
case 'company':
$departments->OrderCompany($order);
break;
default:
$departments->orderBy($sort, $order);
break;
@@ -101,7 +97,7 @@ class DepartmentsController extends Controller
$department->fill($request->all());
$department = $request->handleImages($department);
$department->created_by = auth()->id();
$department->user_id = auth()->id();
$department->manager_id = ($request->filled('manager_id') ? $request->input('manager_id') : null);
if ($department->save()) {

View File

@@ -32,8 +32,7 @@ class DepreciationsController extends Controller
'licenses_count',
];
$depreciations = Depreciation::select('id','name','months','depreciation_min','depreciation_type','created_at','updated_at', 'created_by')
->with('adminuser')
$depreciations = Depreciation::select('id','name','months','depreciation_min','depreciation_type','user_id','created_at','updated_at')
->withCount('assets as assets_count')
->withCount('models as models_count')
->withCount('licenses as licenses_count');
@@ -45,18 +44,10 @@ class DepreciationsController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort_override) {
case 'created_by':
$depreciations = $depreciations->OrderByCreatedBy($order);
break;
default:
$depreciations = $depreciations->orderBy($column_sort, $order);
break;
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$depreciations->orderBy($sort, $order);
$total = $depreciations->count();
$depreciations = $depreciations->skip($offset)->take($limit)->get();

View File

@@ -23,8 +23,9 @@ class GroupsController extends Controller
$this->authorize('superadmin');
$this->authorize('view', Group::class);
$allowed_columns = ['id', 'name', 'created_at', 'users_count'];
$groups = Group::select('id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by')->with('adminuser')->withCount('users as users_count');
$groups = Group::select('id', 'name', 'permissions', 'created_at', 'updated_at', 'created_by')->with('admin')->withCount('users as users_count');
if ($request->filled('search')) {
$groups = $groups->TextSearch($request->input('search'));
@@ -34,29 +35,13 @@ class GroupsController extends Controller
$groups->where('name', '=', $request->input('name'));
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $groups->count()) ? $groups->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
switch ($request->input('sort')) {
case 'created_by':
$groups = $groups->OrderByCreatedBy($order);
break;
default:
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
// These must match a column on the consumables table directly.
$allowed_columns = [
'id',
'name',
'created_at',
'users_count',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$groups = $groups->orderBy($sort, $order);
break;
}
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$groups->orderBy($sort, $order);
$total = $groups->count();
$groups = $groups->skip($offset)->take($limit)->get();
@@ -81,7 +66,6 @@ class GroupsController extends Controller
$group->name = $request->input('name');
$group->created_by = auth()->id();
$group->notes = $request->input('notes');
$group->permissions = json_encode($request->input('permissions', $groupPermissions));
if ($group->save()) {
@@ -119,7 +103,6 @@ class GroupsController extends Controller
$group = Group::findOrFail($id);
$group->name = $request->input('name');
$group->notes = $request->input('notes');
$group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here
if ($group->save()) {

View File

@@ -9,14 +9,12 @@ use App\Http\Transformers\ImportsTransformer;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Import;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Database\Eloquent\JsonEncodingException;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Storage;
use League\Csv\Reader;
use Onnov\DetectEncoding\EncodingDetector;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\JsonResponse;
@@ -30,7 +28,8 @@ class ImportController extends Controller
public function index() : JsonResponse | array
{
$this->authorize('import');
$imports = Import::with('adminuser')->latest()->get();
$imports = Import::latest()->get();
return (new ImportsTransformer)->transformImports($imports);
}
@@ -47,8 +46,6 @@ class ImportController extends Controller
$path = config('app.private_uploads').'/imports';
$results = [];
$import = new Import;
$detector = new EncodingDetector();
foreach ($files as $file) {
if (! in_array($file->getMimeType(), [
'application/vnd.ms-excel',
@@ -59,6 +56,7 @@ class ImportController extends Controller
'text/comma-separated-values',
'text/tsv', ])) {
$results['error'] = 'File type must be CSV. Uploaded file is '.$file->getMimeType();
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 422);
}
@@ -66,25 +64,7 @@ class ImportController extends Controller
if (! ini_get('auto_detect_line_endings')) {
ini_set('auto_detect_line_endings', '1');
}
$file_contents = $file->getContent(); //TODO - this *does* load the whole file in RAM, but we need that to be able to 'iconv' it?
$encoding = $detector->getEncoding($file_contents);
$reader = null;
if (strcasecmp($encoding, 'UTF-8') != 0) {
$transliterated = iconv($encoding, 'UTF-8', $file_contents);
if ($transliterated !== false) {
$tmpname = tempnam(sys_get_temp_dir(), '');
$tmpresults = file_put_contents($tmpname, $transliterated);
if ($tmpresults !== false) {
$transliterated = null; //save on memory?
$newfile = new UploadedFile($tmpname, $file->getClientOriginalName(), null, null, true); //WARNING: this is enabling 'test mode' - which is gross, but otherwise the file won't be treated as 'uploaded'
if ($newfile->isValid()) {
$file = $newfile;
}
}
}
}
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
$file_contents = null; //try to save on memory, I guess?
try {
$import->header_row = $reader->fetchOne(0);
@@ -153,7 +133,7 @@ class ImportController extends Controller
}
$import->filesize = filesize($path.'/'.$file_name);
$import->created_by = auth()->id();
$import->save();
$results[] = $import;
}
@@ -197,9 +177,6 @@ class ImportController extends Controller
case 'asset':
$redirectTo = 'hardware.index';
break;
case 'assetModel':
$redirectTo = 'models.index';
break;
case 'accessory':
$redirectTo = 'accessories.index';
break;

View File

@@ -107,7 +107,7 @@ class LicenseSeatsController extends Controller
// attempt to update the license seat
$licenseSeat->fill($request->all());
$licenseSeat->created_by = auth()->id();
$licenseSeat->user_id = auth()->id();
// check if this update is a checkin operation
// 1. are relevant fields touched at all?

View File

@@ -70,8 +70,8 @@ class LicensesController extends Controller
$licenses->where('depreciation_id', '=', $request->input('depreciation_id'));
}
if ($request->filled('created_by')) {
$licenses->where('created_by', '=', $request->input('created_by'));
if ($request->filled('user_id')) {
$licenses->where('user_id', '=', $request->input('user_id'));
}
if (($request->filled('maintained')) && ($request->input('maintained')=='true')) {
@@ -117,7 +117,7 @@ class LicensesController extends Controller
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
break;
case 'created_by':
$licenses = $licenses->OrderByCreatedBy($order);
$licenses = $licenses->OrderCreatedBy($order);
break;
default:
$allowed_columns =
@@ -182,7 +182,7 @@ class LicensesController extends Controller
public function show($id) : JsonResponse | array
{
$this->authorize('view', License::class);
$license = License::withCount('freeSeats as free_seats_count')->findOrFail($id);
$license = License::withCount('freeSeats')->findOrFail($id);
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
return (new LicensesTransformer)->transformLicense($license);
@@ -220,6 +220,7 @@ class LicensesController extends Controller
*/
public function destroy($id) : JsonResponse
{
//
$license = License::findOrFail($id);
$this->authorize('delete', $license);

View File

@@ -3,20 +3,17 @@
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Controllers\Controller;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Asset;
use App\Models\Location;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Http\JsonResponse;
class LocationsController extends Controller
{
@@ -31,29 +28,26 @@ class LocationsController extends Controller
{
$this->authorize('view', Location::class);
$allowed_columns = [
'accessories_count',
'id',
'name',
'address',
'address2',
'assets_count',
'assets_count',
'assigned_accessories_count',
'assigned_assets_count',
'assigned_assets_count',
'city',
'country',
'created_at',
'currency',
'id',
'image',
'ldap_ou',
'manager_id',
'name',
'rtd_assets_count',
'state',
'updated_at',
'users_count',
'country',
'zip',
'notes',
'created_at',
'updated_at',
'manager_id',
'image',
'assigned_assets_count',
'users_count',
'assets_count',
'assigned_assets_count',
'assets_count',
'rtd_assets_count',
'currency',
'ldap_ou',
];
$locations = Location::with('parent', 'manager', 'children')->select([
@@ -74,12 +68,8 @@ class LocationsController extends Controller
'locations.image',
'locations.ldap_ou',
'locations.currency',
'locations.notes',
])
->withCount('assignedAssets as assigned_assets_count')
])->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('assignedAccessories as assigned_accessories_count')
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count');
@@ -192,7 +182,6 @@ class LocationsController extends Controller
'locations.updated_at',
'locations.image',
'locations.currency',
'locations.notes',
])
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
@@ -235,17 +224,7 @@ class LocationsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
public function assets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
$assets = Asset::where('location_id', '=', $location->id)->with('model', 'model.category', 'assetstatus', 'location', 'company', 'defaultLoc');
$assets = $assets->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
public function assignedAssets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
@@ -254,20 +233,6 @@ class LocationsController extends Controller
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
public function assignedAccessories(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Accessory::class);
$this->authorize('view', $location);
$accessory_checkouts = AccessoryCheckout::LocationAssigned()->where('assigned_to', $location->id)->with('adminuser')->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new LocationsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
/**
* Remove the specified resource from storage.
*

View File

@@ -25,45 +25,11 @@ class ManufacturersController extends Controller
public function index(Request $request) : JsonResponse | array
{
$this->authorize('view', Manufacturer::class);
$allowed_columns = [
'id',
'name',
'url',
'support_url',
'support_email',
'warranty_lookup_url',
'support_phone',
'created_at',
'updated_at',
'image',
'assets_count',
'consumables_count',
'components_count',
'licenses_count',
'notes',
];
$allowed_columns = ['id', 'name', 'url', 'support_url', 'support_email', 'warranty_lookup_url', 'support_phone', 'created_at', 'updated_at', 'image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count'];
$manufacturers = Manufacturer::select([
'id',
'name',
'url',
'support_url',
'warranty_lookup_url',
'support_email',
'support_phone',
'created_by',
'created_at',
'updated_at',
'image',
'deleted_at',
'notes',
])
->with('adminuser')
->withCount('assets as assets_count')
->withCount('licenses as licenses_count')
->withCount('consumables as consumables_count')
->withCount('accessories as accessories_count')
->withCount('components as components_count');
$manufacturers = Manufacturer::select(
['id', 'name', 'url', 'support_url', 'warranty_lookup_url', 'support_email', 'support_phone', 'created_at', 'updated_at', 'image', 'deleted_at']
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('consumables as consumables_count')->withCount('accessories as accessories_count');
if ($request->input('deleted') == 'true') {
$manufacturers->onlyTrashed();
@@ -100,18 +66,10 @@ class ManufacturersController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort_override) {
case 'created_by':
$manufacturers = $manufacturers->OrderByCreatedBy($order);
break;
default:
$manufacturers = $manufacturers->orderBy($column_sort, $order);
break;
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$manufacturers->orderBy($sort, $order);
$total = $manufacturers->count();
$manufacturers = $manufacturers->skip($offset)->take($limit)->get();
@@ -223,7 +181,7 @@ class ManufacturersController extends Controller
$logaction->item_type = Manufacturer::class;
$logaction->item_id = $manufacturer->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->created_by = auth()->id();
$logaction->user_id = auth()->id();
$logaction->logaction('restore');
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/manufacturers/message.restore.success')), 200);

View File

@@ -23,8 +23,9 @@ class PredefinedKitsController extends Controller
public function index(Request $request) : JsonResponse | array
{
$this->authorize('view', PredefinedKit::class);
$allowed_columns = ['id', 'name'];
$kits = PredefinedKit::query()->with('adminuser');
$kits = PredefinedKit::query();
if ($request->filled('search')) {
$kits = $kits->TextSearch($request->input('search'));
@@ -35,25 +36,8 @@ class PredefinedKitsController extends Controller
$limit = app('api_limit_value');
$order = $request->input('order') === 'desc' ? 'desc' : 'asc';
switch ($request->input('sort')) {
case 'created_by':
$kits = $kits->OrderByCreatedBy($order);
break;
default:
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
// These must match a column on the consumables table directly.
$allowed_columns = [
'id',
'name',
'created_at',
'updated_at',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$kits = $kits->orderBy($sort, $order);
break;
}
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'name';
$kits->orderBy($sort, $order);
$total = $kits->count();
$kits = $kits->skip($offset)->take($limit)->get();

View File

@@ -20,7 +20,7 @@ class ReportsController extends Controller
{
$this->authorize('reports.view');
$actionlogs = Actionlog::with('item', 'user', 'adminuser', 'target', 'location');
$actionlogs = Actionlog::with('item', 'user', 'admin', 'target', 'location');
if ($request->filled('search')) {
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
@@ -45,40 +45,36 @@ class ReportsController extends Controller
}
if ($request->filled('action_type')) {
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'));
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc');
}
if ($request->filled('created_by')) {
$actionlogs = $actionlogs->where('created_by', '=', $request->input('created_by'));
if ($request->filled('user_id')) {
$actionlogs = $actionlogs->where('user_id', '=', $request->input('user_id'));
}
if ($request->filled('action_source')) {
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'));
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc');
}
if ($request->filled('remote_ip')) {
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'));
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'))->orderBy('created_at', 'desc');
}
if ($request->filled('uploads')) {
$actionlogs = $actionlogs->whereNotNull('filename');
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
}
$allowed_columns = [
'id',
'created_at',
'target_id',
'created_by',
'user_id',
'accept_signature',
'action_type',
'note',
'remote_ip',
'user_agent',
'target_type',
'item_type',
'action_source',
'action_date',
];
@@ -90,11 +86,11 @@ class ReportsController extends Controller
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
switch ($request->input('sort')) {
case 'created_by':
$actionlogs->OrderByCreatedBy($order);
case 'admin':
$actionlogs->OrderAdmin($order);
break;
default:
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'action_logs.created_at';
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$actionlogs = $actionlogs->orderBy($sort, $order);
break;
}

View File

@@ -25,17 +25,9 @@ class StatuslabelsController extends Controller
public function index(Request $request) : array
{
$this->authorize('view', Statuslabel::class);
$allowed_columns = [
'id',
'name',
'created_at',
'assets_count',
'color',
'notes',
'default_label'
];
$allowed_columns = ['id', 'name', 'created_at', 'assets_count', 'color', 'notes', 'default_label'];
$statuslabels = Statuslabel::with('adminuser')->withCount('assets as assets_count');
$statuslabels = Statuslabel::withCount('assets as assets_count');
if ($request->filled('search')) {
$statuslabels = $statuslabels->TextSearch($request->input('search'));
@@ -62,18 +54,10 @@ class StatuslabelsController extends Controller
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : app('api_offset_value');
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort_override) {
case 'created_by':
$statuslabels = $statuslabels->OrderByCreatedBy($order);
break;
default:
$statuslabels = $statuslabels->orderBy($column_sort, $order);
break;
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$statuslabels->orderBy($sort, $order);
$total = $statuslabels->count();
$statuslabels = $statuslabels->skip($offset)->take($limit)->get();
@@ -95,8 +79,7 @@ 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.']]));
return response()->json(Helper::formatStandardApiResponse('error', null, ['type' => ['Status label type is required.']]), 500);
}
$statuslabel = new Statuslabel;

View File

@@ -14,13 +14,11 @@ 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;
use App\Notifications\CurrentInventory;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
@@ -44,14 +42,13 @@ 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',
@@ -70,6 +67,7 @@ class UsersController extends Controller
'users.state',
'users.two_factor_enrolled',
'users.two_factor_optin',
'users.updated_at',
'users.username',
'users.zip',
'users.remote',
@@ -81,16 +79,7 @@ class UsersController extends Controller
'users.website',
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
->withCount([
'assets as assets_count' => function(Builder $query) {
$query->withoutTrashed();
},
'licenses as licenses_count',
'accessories as accessories_count',
'consumables as consumables_count',
'managesUsers as manages_users_count',
'managedLocations as manages_locations_count'
]);
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'managesUsers as manages_users_count', 'managedLocations as manages_locations_count');
if ($request->filled('search') != '') {
@@ -217,10 +206,6 @@ class UsersController extends Controller
$users->where('autoassign_licenses', '=', $request->input('autoassign_licenses'));
}
if ($request->filled('locale')) {
$users = $users->where('users.locale', '=', $request->input('locale'));
}
if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) {
$users = $users->onlyTrashed();
@@ -266,7 +251,6 @@ class UsersController extends Controller
'groups',
'activated',
'created_at',
'updated_at',
'two_factor_enrolled',
'two_factor_optin',
'last_login',
@@ -292,8 +276,6 @@ class UsersController extends Controller
'end_date',
'autoassign_licenses',
'website',
'locale',
'notes',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
@@ -383,7 +365,6 @@ 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')) {
@@ -465,10 +446,6 @@ 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'));
}
@@ -491,11 +468,10 @@ class UsersController extends Controller
$user->permissions = $permissions_array;
}
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)]);
}
// 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()) {
@@ -710,7 +686,7 @@ class UsersController extends Controller
$logaction->item_type = User::class;
$logaction->item_id = $user->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->created_by = auth()->id();
$logaction->user_id = auth()->id();
$logaction->logaction('2FA reset');
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
@@ -760,7 +736,7 @@ class UsersController extends Controller
$logaction->item_type = User::class;
$logaction->item_id = $user->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->created_by = auth()->id();
$logaction->user_id = auth()->id();
$logaction->logaction('restore');
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.restored')), 200);

View File

@@ -109,7 +109,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->created_by = auth()->id();
$assetMaintenance->user_id = Auth::id();
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
@@ -139,12 +139,19 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
public function edit($assetMaintenanceId = null) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
// Check if the asset maintenance exists
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif ((!$assetMaintenance->asset) || ($assetMaintenance->asset->deleted_at!='')) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
@@ -154,7 +161,7 @@ class AssetMaintenancesController extends Controller
return view('asset_maintenances/edit')
->with('selectedAsset', null)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', $maintenance);
->with('item', $assetMaintenance);
}
/**
@@ -167,20 +174,24 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
public function update(Request $request, $assetMaintenanceId = null) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif ((!$assetMaintenance->asset) || ($assetMaintenance->asset->deleted_at!='')) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
$maintenance->supplier_id = $request->input('supplier_id');
$maintenance->is_warranty = $request->input('is_warranty');
$maintenance->cost = $request->input('cost');
$maintenance->notes = $request->input('notes');
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = $request->input('notes');
$asset = Asset::find(request('asset_id'));
@@ -189,39 +200,39 @@ class AssetMaintenancesController extends Controller
}
// Save the asset maintenance data
$maintenance->asset_id = $request->input('asset_id');
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$maintenance->title = $request->input('title');
$maintenance->start_date = $request->input('start_date');
$maintenance->completion_date = $request->input('completion_date');
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
if (($maintenance->completion_date == null)
if (($assetMaintenance->completion_date == null)
) {
if (($maintenance->asset_maintenance_time !== 0)
|| (! is_null($maintenance->asset_maintenance_time))
if (($assetMaintenance->asset_maintenance_time !== 0)
|| (! is_null($assetMaintenance->asset_maintenance_time))
) {
$maintenance->asset_maintenance_time = null;
$assetMaintenance->asset_maintenance_time = null;
}
}
if (($maintenance->completion_date !== null)
&& ($maintenance->start_date !== '')
&& ($maintenance->start_date !== '0000-00-00')
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($maintenance->start_date);
$completionDate = Carbon::parse($maintenance->completion_date);
$maintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
}
// Was the asset maintenance created?
if ($maintenance->save()) {
if ($assetMaintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.edit.success'));
->with('success', trans('admin/asset_maintenances/message.edit.success'));
}
return redirect()->back()->withInput()->withErrors($maintenance->getErrors());
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
/**
@@ -260,13 +271,19 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function show(AssetMaintenance $maintenance) : View | RedirectResponse
public function show($assetMaintenanceId) : View | RedirectResponse
{
$this->authorize('view', Asset::class);
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
return view('asset_maintenances/view')->with('assetMaintenance', $maintenance);
return view('asset_maintenances/view')->with('assetMaintenance', $assetMaintenance);
}
}

View File

@@ -18,7 +18,6 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\MessageBag;
/**
@@ -30,7 +29,6 @@ use Illuminate\Support\MessageBag;
*/
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.
@@ -80,7 +78,7 @@ class AssetModelsController extends Controller
$model->manufacturer_id = $request->input('manufacturer_id');
$model->category_id = $request->input('category_id');
$model->notes = $request->input('notes');
$model->created_by = auth()->id();
$model->user_id = Auth::id();
$model->requestable = $request->has('requestable');
if ($request->input('fieldset_id') != '') {
@@ -109,11 +107,16 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @param int $modelId
*/
public function edit(AssetModel $model) : View | RedirectResponse
public function edit($modelId = null) : View | RedirectResponse
{
$this->authorize('update', AssetModel::class);
$category_type = 'asset';
return view('models/edit', compact('category_type'))->with('item', $model)->with('depreciation_list', Helper::depreciationList());
if ($item = AssetModel::find($modelId)) {
$category_type = 'asset';
return view('models/edit', compact('item', 'category_type'))->with('depreciation_list', Helper::depreciationList());
}
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
@@ -128,11 +131,16 @@ class AssetModelsController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(StoreAssetModelRequest $request, AssetModel $model) : RedirectResponse
public function update(StoreAssetModelRequest $request, $modelId) : RedirectResponse
{
$this->authorize('update', AssetModel::class);
if (is_null($model = AssetModel::find($modelId))) {
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
$model = $request->handleImages($model);
$model->depreciation_id = $request->input('depreciation_id');
$model->eol = $request->input('eol');
$model->name = $request->input('name');
@@ -150,7 +158,7 @@ class AssetModelsController extends Controller
if ($this->shouldAddDefaultValues($request->input())) {
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))) {
return redirect()->back()->withInput()->withErrors($this->validatorErrors);
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
}
}
@@ -178,16 +186,28 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @param int $modelId
*/
public function destroy(AssetModel $model) : RedirectResponse
public function destroy($modelId) : RedirectResponse
{
$this->authorize('delete', AssetModel::class);
// Check if the model exists
if (is_null($model = AssetModel::find($modelId))) {
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
if ($model->assets()->count() > 0) {
// Throw an error that this model is associated with assets
return redirect()->route('models.index')->with('error', trans('admin/models/message.assoc_users'));
}
if ($model->image) {
try {
Storage::disk('public')->delete('models/'.$model->image);
$model->update(['image' => null]);
} catch (\Exception $e) {
Log::info($e);
}
}
// Delete the model
$model->delete();
@@ -217,7 +237,7 @@ class AssetModelsController extends Controller
$logaction->item_type = AssetModel::class;
$logaction->item_id = $model->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->created_by = auth()->id();
$logaction->user_id = auth()->id();
$logaction->logaction('restore');
@@ -245,10 +265,16 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @param int $modelId
*/
public function show(AssetModel $model) : View | RedirectResponse
public function show($modelId = null) : View | RedirectResponse
{
$this->authorize('view', AssetModel::class);
return view('models/view', compact('model'));
$model = AssetModel::withTrashed()->find($modelId);
if (isset($model->id)) {
return view('models/view', compact('model'));
}
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
/**
@@ -258,20 +284,23 @@ class AssetModelsController extends Controller
* @since [v1.0]
* @param int $modelId
*/
public function getClone(AssetModel $model) : View | RedirectResponse
public function getClone($modelId = null) : View | RedirectResponse
{
$this->authorize('create', AssetModel::class);
// Check if the model exists
if (is_null($model_to_clone = AssetModel::find($modelId))) {
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
}
$cloned_model = clone $model;
$model = clone $model_to_clone;
$model->id = null;
$model->deleted_at = null;
// Show the page
return view('models/edit')
->with('depreciation_list', Helper::depreciationList())
->with('item', $model)
->with('model_id', $model->id)
->with('clone_model', $cloned_model);
->with('model_id', $model_to_clone->id)
->with('clone_model', $model_to_clone);
}
@@ -290,7 +319,7 @@ class AssetModelsController extends Controller
/**
* Returns a view that allows the user to bulk edit model attributes
* Returns a view that allows the user to bulk edit model attrbutes
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.7]
@@ -452,15 +481,9 @@ class AssetModelsController extends Controller
$rules[$field] = $validation;
}
$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);
$validator = Validator::make($data, $rules);
if($validator->fails()){
$this->validatorErrors = $validator->errors();
return false;
}

View File

@@ -44,10 +44,10 @@ class AssetModelsFilesController extends Controller
$model->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
return redirect()->back()->with('success', trans('general.file_upload_success'));
}
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
@@ -58,9 +58,11 @@ class AssetModelsFilesController extends Controller
* @param int $fileId
* @since [v1.0]
*/
public function show(AssetModel $model, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
public function show($modelId = null, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
{
$model = AssetModel::find($modelId);
// the asset is valid
if (isset($model->id)) {
$this->authorize('view', $model);
if (! $log = Actionlog::find($fileId)) {
@@ -85,6 +87,12 @@ class AssetModelsFilesController extends Controller
}
return StorageHelper::downloader($file);
}
// 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);
}
/**
@@ -95,21 +103,30 @@ class AssetModelsFilesController extends Controller
* @param int $fileId
* @since [v1.0]
*/
public function destroy(AssetModel $model, $fileId = null) : RedirectResponse
public function destroy($modelId = null, $fileId = null) : RedirectResponse
{
$rel_path = 'private_uploads/assetmodels';
$model = AssetModel::find($modelId);
$this->authorize('update', $model);
$log = Actionlog::find($fileId);
if ($log) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
$rel_path = 'private_uploads/assetmodels';
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
// the asset is valid
if (isset($model->id)) {
$this->authorize('update', $model);
$log = Actionlog::find($fileId);
if ($log) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
// Redirect to the hardware management page
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
}

View File

@@ -27,12 +27,18 @@ class AssetCheckinController extends Controller
* @param string $backto
* @since [v1.0]
*/
public function create(Asset $asset, $backto = null) : View | RedirectResponse
public function create($assetId, $backto = null) : View | RedirectResponse
{
// Check if the asset exists
if (is_null($asset = Asset::find($assetId))) {
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('checkin', $asset);
// This asset is already checked in, redirect
if (is_null($asset->assignedTo)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
}
@@ -41,11 +47,7 @@ class AssetCheckinController extends Controller
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
}
return view('hardware/checkin', compact('asset'))
->with('item', $asset)
->with('statusLabel_list', Helper::statusLabelList())
->with('backto', $backto)
->with('table_name', 'Assets');
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto)->with('table_name', 'Assets');
}
/**
@@ -89,9 +91,6 @@ class AssetCheckinController extends Controller
$asset->status_id = e($request->get('status_id'));
}
// Add any custom fields that should be included in the checkout
$asset->customFieldsForCheckinCheckout('display_checkin');
$this->migrateLegacyLocations($asset);
$asset->location_id = $asset->rtd_location_id;
@@ -129,9 +128,6 @@ class AssetCheckinController extends Controller
session()->put('redirect_option', $request->get('redirect_option'));
// Add any custom fields that should be included in the checkout
$asset->customFieldsForCheckinCheckout('display_checkin');
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));

View File

@@ -26,25 +26,27 @@ class AssetCheckoutController extends Controller
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function create(Asset $asset) : View | RedirectResponse
public function create($assetId) : View | RedirectResponse
{
// Check if the asset exists
if (is_null($asset = Asset::with('company')->find(e($assetId)))) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('checkout', $asset);
if (!$asset->model) {
return redirect()->route('hardware.show', $asset)
->with('error', trans('admin/hardware/general.model_invalid_fix'));
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
}
if ($asset->availableForCheckout()) {
return view('hardware/checkout', compact('asset'))
->with('statusLabel_list', Helper::deployableStatusLabelList())
->with('table_name', 'Assets')
->with('item', $asset);
->with('table_name', 'Assets');
}
return redirect()->route('hardware.index')
->with('error', trans('admin/hardware/message.checkout.not_available'));
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
}
/**
@@ -66,7 +68,7 @@ class AssetCheckoutController extends Controller
$this->authorize('checkout', $asset);
if (!$asset->model) {
return redirect()->route('hardware.show', $asset)->with('error', trans('admin/hardware/general.model_invalid_fix'));
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
}
$admin = auth()->user();
@@ -89,7 +91,6 @@ class AssetCheckoutController extends Controller
$asset->status_id = $request->get('status_id');
}
if(!empty($asset->licenseseats->all())){
if(request('checkout_to_type') == 'user') {
foreach ($asset->licenseseats as $seat){
@@ -99,26 +100,23 @@ class AssetCheckoutController extends Controller
}
}
// Add any custom fields that should be included in the checkout
$asset->customFieldsForCheckinCheckout('display_checkout');
$settings = \App\Models\Setting::getSettings();
// We have to check whether $target->company_id is null here since locations don't have a company yet
if (($settings->full_multiple_companies_support) && ((!is_null($target->company_id)) && (!is_null($asset->company_id)))) {
if ($target->company_id != $asset->company_id){
return redirect()->route('hardware.checkout.create', $asset)->with('error', trans('general.error_user_company'));
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('general.error_user_company'));
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->route("hardware.checkout.create", $asset)->with('error', trans('admin/hardware/message.checkout.error').$asset->getErrors());
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error').$asset->getErrors());
} catch (ModelNotFoundException $e) {
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
} catch (CheckoutNotAllowed $e) {

View File

@@ -26,8 +26,11 @@ class AssetFilesController extends Controller
*@since [v1.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, Asset $asset) : RedirectResponse
public function store(UploadFileRequest $request, $assetId = null) : RedirectResponse
{
if (! $asset = Asset::find($assetId)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('update', $asset);
@@ -42,7 +45,7 @@ class AssetFilesController extends Controller
$asset->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success'));
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
}
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
@@ -56,29 +59,45 @@ class AssetFilesController extends Controller
* @param int $fileId
* @since [v1.0]
*/
public function show(Asset $asset, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse
public function show($assetId = null, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse
{
$asset = Asset::find($assetId);
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
$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', $asset)->with('error', trans('general.file_not_found'));
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);
}
// Prepare the error message
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
// Redirect to the hardware management page
return redirect()->route('hardware.index')->with('error', $error);
}
/**
@@ -89,20 +108,29 @@ class AssetFilesController extends Controller
* @param int $fileId
* @since [v1.0]
*/
public function destroy(Asset $asset, $fileId = null) : RedirectResponse
public function destroy($assetId = null, $fileId = null) : RedirectResponse
{
$asset = Asset::find($assetId);
$this->authorize('update', $asset);
$rel_path = 'private_uploads/assets';
if ($log = Actionlog::find($fileId)) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
// the asset is valid
if (isset($asset->id)) {
$this->authorize('update', $asset);
$log = Actionlog::find($fileId);
if ($log) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
}
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
}

View File

@@ -2,7 +2,6 @@
namespace App\Http\Controllers\Assets;
use App\Events\CheckoutableCheckedIn;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
@@ -17,6 +16,7 @@ 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;
@@ -30,7 +30,6 @@ use Illuminate\Http\Response;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use TypeError;
/**
* This class controls all actions related to assets for
@@ -112,10 +111,8 @@ class AssetsController extends Controller
$settings = Setting::getSettings();
$successes = [];
$failures = [];
$success = false;
$serials = $request->input('serials');
$asset = null;
for ($a = 1; $a <= count($asset_tags); $a++) {
$asset = new Asset();
@@ -135,7 +132,7 @@ class AssetsController extends Controller
$asset->model_id = $request->input('model_id');
$asset->order_number = $request->input('order_number');
$asset->notes = $request->input('notes');
$asset->created_by = auth()->id();
$asset->user_id = Auth::id();
$asset->status_id = request('status_id');
$asset->warranty_months = request('warranty_months', null);
$asset->purchase_cost = request('purchase_cost');
@@ -202,34 +199,19 @@ 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);
}
$successes[] = "<a href='" . route('hardware.show', $asset) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
$success = true;
} else {
$failures[] = join(",", $asset->getErrors()->all());
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
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', $asset), '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)]));
}
}
if ($success) {
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)]));
}
@@ -241,14 +223,20 @@ class AssetsController extends Controller
* Returns a view that presents a form to edit an existing asset.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function edit(Asset $asset) : View | RedirectResponse
public function edit($assetId = null) : View | RedirectResponse
{
$this->authorize($asset);
return view('hardware/edit')
->with('item', $asset)
if (! $item = Asset::find($assetId)) {
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
//Handles company checks and permissions.
$this->authorize($item);
return view('hardware/edit', compact('item'))
->with('statuslabel_list', Helper::statusLabelList())
->with('statuslabel_types', Helper::statusTypeList());
}
@@ -262,14 +250,15 @@ class AssetsController extends Controller
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function show(Asset $asset) : View | RedirectResponse
public function show($assetId = null) : View | RedirectResponse
{
$asset = Asset::withTrashed()->find($assetId);
$this->authorize('view', $asset);
$settings = Setting::getSettings();
if (isset($asset)) {
$audit_log = Actionlog::where('action_type', '=', 'audit')
->where('item_id', '=', $asset->id)
->where('item_id', '=', $assetId)
->where('item_type', '=', Asset::class)
->orderBy('created_at', 'DESC')->first();
@@ -285,7 +274,7 @@ class AssetsController extends Controller
$qr_code = (object) [
'display' => $settings->qr_code == '1',
'url' => route('qr_code/hardware', $asset),
'url' => route('qr_code/hardware', $asset->id),
];
return view('hardware/view', compact('asset', 'qr_code', 'settings'))
@@ -302,9 +291,14 @@ class AssetsController extends Controller
* @since [v1.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function update(ImageUploadRequest $request, Asset $asset) : RedirectResponse
public function update(ImageUploadRequest $request, $assetId = null) : RedirectResponse
{
// Check if the asset exists
if (! $asset = Asset::find($assetId)) {
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize($asset);
$asset->status_id = $request->input('status_id', null);
@@ -334,21 +328,16 @@ class AssetsController extends Controller
}
$asset->supplier_id = $request->input('supplier_id', null);
$asset->expected_checkin = $request->input('expected_checkin', null);
$asset->requestable = $request->input('requestable', 0);
// If the box isn't checked, it's not in the request at all.
$asset->requestable = $request->filled('requestable');
$asset->rtd_location_id = $request->input('rtd_location_id', null);
$asset->byod = $request->input('byod', 0);
$status = Statuslabel::find($request->input('status_id'));
$status = Statuslabel::find($asset->status_id);
// This is a non-deployable status label - we should check the asset back in.
if (($status && $status->getStatuslabelType() != 'deployable') && ($target = $asset->assignedTo)) {
$originalValues = $asset->getRawOriginal();
if ($status && $status->archived) {
$asset->assigned_to = null;
$asset->assigned_type = null;
$asset->accepted = null;
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update', date('Y-m-d H:i:s'), $originalValues));
}
if ($asset->assigned_to == '') {
@@ -419,7 +408,7 @@ class AssetsController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->save()) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return redirect()->to(Helper::getRedirectOption($request, $assetId, 'Assets'))
->with('success', trans('admin/hardware/message.update.success'));
}
@@ -433,7 +422,7 @@ class AssetsController extends Controller
* @param int $assetId
* @since [v1.0]
*/
public function destroy(Request $request, $assetId) : RedirectResponse
public function destroy($assetId) : RedirectResponse
{
// Check if the asset exists
if (is_null($asset = Asset::find($assetId))) {
@@ -443,17 +432,9 @@ class AssetsController extends Controller
$this->authorize('delete', $asset);
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]);
}
DB::table('assets')
->where('id', $asset->id)
->update(['assigned_to' => null]);
if ($asset->image) {
try {
@@ -520,14 +501,14 @@ class AssetsController extends Controller
* @param int $assetId
* @since [v1.0]
*/
public function getQrCode(Asset $asset) : Response | BinaryFileResponse | string | bool
public function getQrCode($assetId = null) : Response | BinaryFileResponse | string | bool
{
$settings = Setting::getSettings();
if (($settings->qr_code == '1') && ($settings->label2_2d_type !== 'none')) {
if ($settings->qr_code == '1') {
$asset = Asset::withTrashed()->find($assetId);
if ($asset) {
$size = Helper::barcodeDimensions($settings->label2_2d_type);
$size = Helper::barcodeDimensions($settings->barcode_type);
$qr_file = public_path().'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png';
if (isset($asset->id, $asset->asset_tag)) {
@@ -537,7 +518,7 @@ class AssetsController extends Controller
return response()->file($qr_file, $header);
} else {
$barcode = new \Com\Tecnick\Barcode\Barcode();
$barcode_obj = $barcode->getBarcodeObj($settings->label2_2d_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', [-2, -2, -2, -2]);
$barcode_obj = $barcode->getBarcodeObj($settings->barcode_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', [-2, -2, -2, -2]);
file_put_contents($qr_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
@@ -562,7 +543,7 @@ class AssetsController extends Controller
{
$settings = Setting::getSettings();
if ($asset = Asset::withTrashed()->find($assetId)) {
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->label2_1d_type).'-'.str_slug($asset->asset_tag).'.png';
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->alt_barcode).'-'.str_slug($asset->asset_tag).'.png';
if (isset($asset->id, $asset->asset_tag)) {
if (file_exists($barcode_file)) {
@@ -575,11 +556,11 @@ class AssetsController extends Controller
$barcode = new \Com\Tecnick\Barcode\Barcode();
try {
$barcode_obj = $barcode->getBarcodeObj($settings->label2_1d_type, $asset->asset_tag, ($barcode_width < 300 ? $barcode_width : 300), 50);
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode, $asset->asset_tag, ($barcode_width < 300 ? $barcode_width : 300), 50);
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
} catch (\Exception|TypeError $e) {
} catch (\Exception $e) {
Log::debug('The barcode format is invalid.');
return response(file_get_contents(public_path('uploads/barcodes/invalid_barcode.gif')))->header('Content-type', 'image/gif');
@@ -758,7 +739,7 @@ class AssetsController extends Controller
Actionlog::firstOrCreate([
'item_id' => $asset->id,
'item_type' => Asset::class,
'created_by' => auth()->id(),
'user_id' => auth()->id(),
'note' => 'Checkout imported by '.auth()->user()->present()->fullName().' from history importer',
'target_id' => $item[$asset_tag][$batch_counter]['user_id'],
'target_type' => User::class,
@@ -786,7 +767,7 @@ class AssetsController extends Controller
Actionlog::firstOrCreate([
'item_id' => $item[$asset_tag][$batch_counter]['asset_id'],
'item_type' => Asset::class,
'created_by' => auth()->id(),
'user_id' => auth()->id(),
'note' => 'Checkin imported by '.auth()->user()->present()->fullName().' from history importer',
'target_id' => null,
'created_at' => $checkin_date,
@@ -854,8 +835,8 @@ class AssetsController extends Controller
public function quickScan()
{
$this->authorize('audit', Asset::class);
$settings = Setting::getSettings();
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
$dt = Carbon::now()->addMonths(12)->toDateString();
return view('hardware/quickscan')->with('next_audit_date', $dt);
}
@@ -866,11 +847,13 @@ class AssetsController extends Controller
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
}
public function audit(Asset $asset)
public function audit($id)
{
$settings = Setting::getSettings();
$this->authorize('audit', Asset::class);
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
$asset = Asset::findOrFail($id);
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
}
@@ -889,7 +872,7 @@ class AssetsController extends Controller
}
public function auditStore(UploadFileRequest $request, Asset $asset)
public function auditStore(UploadFileRequest $request, $id)
{
$this->authorize('audit', Asset::class);
@@ -904,6 +887,8 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
}
$asset = Asset::findOrFail($id);
/**
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
* which would trigger the Asset Observer and would log an asset *update* log entry (because the

View File

@@ -10,7 +10,6 @@ use App\Models\AssetModel;
use App\Models\Statuslabel;
use App\Models\Setting;
use App\View\Label;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
@@ -52,10 +51,6 @@ 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');
@@ -245,12 +240,10 @@ 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))
) {
@@ -273,29 +266,11 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('requestable')
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date')
->conditionallyAddItem('asset_eol_date');
->conditionallyAddItem('next_audit_date');
foreach ($custom_field_columns as $key => $custom_field_column) {
$this->conditionallyAddItem($custom_field_column);
}
if (!($asset->eol_explicit)) {
if ($request->filled('model_id')) {
$model = AssetModel::find($request->input('model_id'));
if ($model->eol > 0) {
if ($request->filled('purchase_date')) {
$this->update_array['asset_eol_date'] = Carbon::parse($request->input('purchase_date'))->addMonths($model->eol)->format('Y-m-d');
} else {
$this->update_array['asset_eol_date'] = Carbon::parse($asset->purchase_date)->addMonths($model->eol)->format('Y-m-d');
}
} else {
$this->update_array['asset_eol_date'] = null;
}
} elseif (($request->filled('purchase_date')) && ($asset->model->eol > 0)) {
$this->update_array['asset_eol_date'] = Carbon::parse($request->input('purchase_date'))->addMonths($asset->model->eol)->format('Y-m-d');
}
}
/**
* Blank out fields that were requested to be blanked out via checkbox
*/
@@ -306,9 +281,6 @@ class BulkAssetsController extends Controller
if ($request->input('null_purchase_date')=='1') {
$this->update_array['purchase_date'] = null;
if (!($asset->eol_explicit)) {
$this->update_array['asset_eol_date'] = null;
}
}
if ($request->input('null_expected_checkin_date')=='1') {
@@ -319,17 +291,6 @@ 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');
}
@@ -525,31 +486,21 @@ class BulkAssetsController extends Controller
$this->authorize('delete', Asset::class);
$bulk_back_url = route('hardware.index');
if ($request->session()->has('bulk_back_url')) {
$bulk_back_url = $request->session()->pull('bulk_back_url');
}
$assetIds = $request->get('ids');
if(empty($assetIds)) {
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.delete.nothing_updated'));
}
$assignedAssets = Asset::whereIn('id', $assetIds)->whereNotNull('assigned_to')->get();
if($assignedAssets->isNotEmpty()) {
//if assets are checked out, return a list of asset tags that would need to be checked in first.
$assetTags = $assignedAssets->pluck('asset_tag')->implode(', ');
return redirect($bulk_back_url)->with('error', trans_choice('admin/hardware/message.delete.assigned_to_error', $assignedAssets->count(), ['asset_tag' => $assetTags] ));
}
foreach (Asset::wherein('id', $assetIds)->get() as $asset) {
if ($request->filled('ids')) {
$assets = Asset::find($request->get('ids'));
foreach ($assets as $asset) {
$asset->delete();
}
} // endforeach
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.delete.success'));
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.delete.success'));
// no values given, nothing to update
}
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.delete.nothing_updated'));
}
/**
@@ -599,34 +550,31 @@ class BulkAssetsController extends Controller
}
$errors = [];
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference!
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
foreach ($asset_ids as $asset_id) {
$asset = Asset::findOrFail($asset_id);
$this->authorize('checkout', $asset);
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
$error = $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;
// TODO - I don't know why this is being saved without events
$asset::withoutEvents(function () use ($asset) {
$asset->save();
});
$asset->unsetEventDispatcher();
$asset->save();
}
if (!$checkout_success) {
$errors = array_merge_recursive($errors, $asset->getErrors()->toArray());
if ($error) {
array_merge_recursive($errors, $asset->getErrors()->toArray());
}
}
});
if (! $errors) {
// Redirect to the new asset page
return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids));
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors);
return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
} catch (ModelNotFoundException $e) {
return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors());
}

View File

@@ -50,14 +50,14 @@ class ForgotPasswordController extends Controller
*/
public function sendResetLinkEmail(Request $request)
{
/**
* Let's set a max character count here to prevent potential
* buffer overflow issues with attackers sending very large
* payloads through. The addition of the string rule prevents attackers
* sending arrays through and causing 500s
* payloads through.
*/
$request->validate([
'username' => ['required', 'max:255', 'string'],
'username' => ['required', 'max:255'],
]);
/**

View File

@@ -103,24 +103,22 @@ class ResetPasswordController extends Controller
], $messages);
}
if ($user->ldap_import != '1') {
// set the response
$response = $broker->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
});
// set the response
$response = $broker->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
});
// Check if the password reset above actually worked
if ($response == \Password::PASSWORD_RESET) {
Log::debug('Password reset for ' . $user->username . ' worked');
return redirect()->guest('login')->with('success', trans('passwords.reset'));
}
Log::debug('Password reset for ' . $user->username . ' FAILED - this user exists but the token is not valid');
return redirect()->back()->withInput($request->only('email'))->with('success', trans('passwords.reset'));
// Check if the password reset above actually worked
if ($response == \Password::PASSWORD_RESET) {
Log::debug('Password reset for '.$user->username.' worked');
return redirect()->guest('login')->with('success', trans('passwords.reset'));
}
Log::debug('Password reset for '.$user->username.' FAILED - this user exists but the token is not valid');
return redirect()->back()->withInput($request->only('email'))->with('success', trans('passwords.reset'));
}

View File

@@ -69,8 +69,7 @@ class CategoriesController extends Controller
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');
$category->created_by = auth()->id();
$category->user_id = Auth::id();
$category = $request->handleImages($category);
if ($category->save()) {
@@ -88,10 +87,14 @@ class CategoriesController extends Controller
* @param int $categoryId
* @since [v1.0]
*/
public function edit(Category $category) : RedirectResponse | View
public function edit($categoryId = null) : RedirectResponse | View
{
$this->authorize('update', Category::class);
return view('categories/edit')->with('item', $category)
if (is_null($item = Category::find($categoryId))) {
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}
return view('categories/edit', compact('item'))
->with('category_types', Helper::categoryTypeList());
}
@@ -104,10 +107,19 @@ class CategoriesController extends Controller
* @param int $categoryId
* @since [v1.0]
*/
public function update(ImageUploadRequest $request, Category $category) : RedirectResponse
public function update(ImageUploadRequest $request, $categoryId = null) : RedirectResponse
{
$this->authorize('update', Category::class);
if (is_null($category = Category::find($categoryId))) {
// Redirect to the categories management page
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}
// Update the category data
$category->name = $request->input('name');
// If the item count is > 0, we disable the category type in the edit. Disabled items
// don't POST, so if the category_type is blank we just set it to the default.
// Don't allow the user to change the category_type once it's been created
if (($request->filled('category_type') && ($category->itemCount() > 0))) {
@@ -122,7 +134,6 @@ class CategoriesController extends Controller
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');
$category = $request->handleImages($category);
@@ -168,10 +179,10 @@ class CategoriesController extends Controller
* @param $id
* @since [v1.8]
*/
public function show(Category $category) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
$this->authorize('view', Category::class);
if ($category = Category::find($id)) {
if ($category->category_type == 'asset') {
$category_type = 'hardware';
$category_type_route = 'assets';
@@ -186,5 +197,8 @@ class CategoriesController extends Controller
return view('categories/view', compact('category'))
->with('category_type', $category_type)
->with('category_type_route', $category_type_route);
}
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
}
}

View File

@@ -60,8 +60,6 @@ final class CompaniesController extends Controller
$company->phone = $request->input('phone');
$company->fax = $request->input('fax');
$company->email = $request->input('email');
$company->notes = $request->input('notes');
$company->created_by = auth()->id();
$company = $request->handleImages($company);
@@ -80,10 +78,16 @@ final class CompaniesController extends Controller
* @since [v1.8]
* @param int $companyId
*/
public function edit(Company $company) : View | RedirectResponse
public function edit($companyId) : View | RedirectResponse
{
$this->authorize('update', $company);
return view('companies/edit')->with('item', $company);
if (is_null($item = Company::find($companyId))) {
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.does_not_exist'));
}
$this->authorize('update', $item);
return view('companies/edit')->with('item', $item);
}
/**
@@ -94,15 +98,18 @@ final class CompaniesController extends Controller
* @param ImageUploadRequest $request
* @param int $companyId
*/
public function update(ImageUploadRequest $request, Company $company) : RedirectResponse
public function update(ImageUploadRequest $request, $companyId) : RedirectResponse
{
if (is_null($company = Company::find($companyId))) {
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
}
$this->authorize('update', $company);
$company->name = $request->input('name');
$company->phone = $request->input('phone');
$company->fax = $request->input('fax');
$company->email = $request->input('email');
$company->notes = $request->input('notes');
$company = $request->handleImages($company);
@@ -148,9 +155,15 @@ final class CompaniesController extends Controller
->with('success', trans('admin/companies/message.delete.success'));
}
public function show(Company $company) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
$this->authorize('view', Company::class);
if (is_null($company = Company::find($id))) {
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.not_found'));
}
return view('companies/view')->with('company', $company);
}
}

View File

@@ -106,7 +106,7 @@ class ComponentCheckoutController extends Controller
$component->asset_id = $request->input('asset_id');
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'created_by' => auth()->user()->id,
'user_id' => auth()->user()->id,
'created_at' => date('Y-m-d H:i:s'),
'assigned_qty' => $request->input('assigned_qty'),
'asset_id' => $request->input('asset_id'),

View File

@@ -73,8 +73,6 @@ 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);
@@ -83,7 +81,7 @@ class ComponentsController extends Controller
$component->purchase_date = $request->input('purchase_date', null);
$component->purchase_cost = $request->input('purchase_cost', null);
$component->qty = $request->input('qty');
$component->created_by = auth()->id();
$component->user_id = Auth::id();
$component->notes = $request->input('notes');
$component = $request->handleImages($component);
@@ -107,13 +105,15 @@ class ComponentsController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function edit(Component $component)
public function edit($componentId = null)
{
if ($item = Component::find($componentId)) {
$this->authorize('update', $item);
$this->authorize('update', $component);
return view('components/edit')
->with('item', $component)
->with('category_type', 'component');
return view('components/edit', compact('item'))->with('category_type', 'component');
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
@@ -128,8 +128,11 @@ class ComponentsController extends Controller
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v3.0]
*/
public function update(ImageUploadRequest $request, Component $component)
public function update(ImageUploadRequest $request, $componentId = null)
{
if (is_null($component = Component::find($componentId))) {
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
}
$min = $component->numCheckedOut();
$validator = Validator::make($request->all(), [
'qty' => "required|numeric|min:$min",
@@ -147,8 +150,6 @@ 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');
@@ -188,7 +189,7 @@ class ComponentsController extends Controller
$this->authorize('delete', $component);
// Remove the image if one exists
if ($component->image && Storage::disk('public')->exists('components/' . $component->image)) {
if (Storage::disk('public')->exists('components/'.$component->image)) {
try {
Storage::disk('public')->delete('components/'.$component->image);
} catch (\Exception $e) {
@@ -196,10 +197,6 @@ class ComponentsController extends Controller
}
}
if ($component->numCheckedOut() > 0) {
return redirect()->route('components.index')->with('error', trans('admin/components/message.delete.error_qty'));
}
$component->delete();
return redirect()->route('components.index')->with('success', trans('admin/components/message.delete.success'));
@@ -215,9 +212,17 @@ class ComponentsController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show(Component $component)
public function show($componentId = null)
{
$component = Component::find($componentId);
if (isset($component->id)) {
$this->authorize('view', $component);
return view('components/view', compact('component'));
}
// Redirect to the user management page
return redirect()->route('components.index')
->with('error', trans('admin/components/message.does_not_exist'));
}
}

View File

@@ -50,7 +50,7 @@ class ComponentsFilesController extends Controller
}
return redirect()->route('components.show', $component->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
return redirect()->route('components.show', $component->id)->with('success', trans('general.file_upload_success'));
}
@@ -91,7 +91,7 @@ class ComponentsFilesController extends Controller
$log->delete();
return redirect()->back()->withFragment('files')
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
}
@@ -112,25 +112,40 @@ 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 ($component = Component::find($componentId)) {
if (isset($component->id)) {
$this->authorize('view', $component);
$this->authorize('components.files', $component);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
$file = 'private_uploads/components/'.$log->filename;
$file = 'private_uploads/components/'.$log->filename;
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 (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);
}
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]));

View File

@@ -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('checkout_qty');
$quantity = $request->input('qty');
if (!isset($quantity) || !ctype_digit((string)$quantity) || $quantity <= 0) {
$quantity = 1;
}
@@ -92,16 +92,14 @@ 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,
'user_id' => $admin_user->id,
'assigned_to' => e($request->input('assigned_to')),
'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']);

View File

@@ -50,7 +50,7 @@ class ConsumablesController extends Controller
{
$this->authorize('create', Consumable::class);
return view('consumables.edit')->with('category_type', 'consumable')
return view('consumables/edit')->with('category_type', 'consumable')
->with('item', new Consumable);
}
@@ -81,7 +81,7 @@ class ConsumablesController extends Controller
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = $request->input('purchase_cost');
$consumable->qty = $request->input('qty');
$consumable->created_by = auth()->id();
$consumable->user_id = Auth::id();
$consumable->notes = $request->input('notes');
@@ -104,13 +104,15 @@ class ConsumablesController extends Controller
* @see ConsumablesController::postEdit() method that stores the form data.
* @since [v1.0]
*/
public function edit(Consumable $consumable) : View | RedirectResponse
public function edit($consumableId = null) : View | RedirectResponse
{
$this->authorize($consumable);
return view('consumables/edit')
->with('item', $consumable)
->with('category_type', 'consumable');
if ($item = Consumable::find($consumableId)) {
$this->authorize($item);
return view('consumables/edit', compact('item'))->with('category_type', 'consumable');
}
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
/**
@@ -124,8 +126,11 @@ class ConsumablesController extends Controller
* @see ConsumablesController::getEdit() method that stores the form data.
* @since [v1.0]
*/
public function update(StoreConsumableRequest $request, Consumable $consumable)
public function update(StoreConsumableRequest $request, $consumableId = null)
{
if (is_null($consumable = Consumable::find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
$min = $consumable->numCheckedOut();
$validator = Validator::make($request->all(), [
@@ -197,11 +202,16 @@ class ConsumablesController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show(Consumable $consumable)
public function show($consumableId = null)
{
$consumable = Consumable::withCount('users as users_consumables')->find($consumable->id);
$consumable = Consumable::withCount('users as users_consumables')->find($consumableId);
$this->authorize($consumable);
return view('consumables/view', compact('consumable'));
if (isset($consumable->id)) {
return view('consumables/view', compact('consumable'));
}
return redirect()->route('consumables.index')
->with('error', trans('admin/consumables/message.does_not_exist'));
}
public function clone(Consumable $consumable) : View
@@ -211,7 +221,7 @@ class ConsumablesController extends Controller
$consumable = clone $consumable_to_close;
$consumable->id = null;
$consumable->image = null;
$consumable->created_by = null;
$consumable->user_id = null;
return view('consumables/edit')->with('item', $consumable);
}

View File

@@ -48,7 +48,7 @@ class ConsumablesFilesController extends Controller
}
return redirect()->route('consumables.show', $consumable->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
return redirect()->route('consumables.show', $consumable->id)->with('success', trans('general.file_upload_success'));
}
@@ -89,7 +89,7 @@ class ConsumablesFilesController extends Controller
$log->delete();
return redirect()->back()->withFragment('files')
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
}
@@ -104,6 +104,7 @@ 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)
@@ -115,18 +116,36 @@ 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)) {
$file = 'private_uploads/consumables/'.$log->filename;
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');
}
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found'));
$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);
}
}
// 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]));

View File

@@ -104,9 +104,7 @@ class CustomFieldsController extends Controller
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
"show_in_listview" => $request->get("show_in_listview", 0),
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
"display_checkin" => $request->get("display_checkin", 0),
"display_checkout" => $request->get("display_checkout", 0),
"created_by" => auth()->id()
"user_id" => Auth::id()
]);
@@ -195,8 +193,10 @@ class CustomFieldsController extends Controller
* @param int $id
* @since [v4.0]
*/
public function edit(Request $request, CustomField $field) : View | RedirectResponse
public function edit(Request $request, $id) : View | RedirectResponse
{
if ($field = CustomField::find($id)) {
$this->authorize('update', $field);
$fieldsets = CustomFieldset::get();
$customFormat = '';
@@ -210,6 +210,10 @@ class CustomFieldsController extends Controller
'fieldsets' => $fieldsets,
'predefinedFormats' => Helper::predefined_formats(),
]);
}
return redirect()->route("fields.index")
->with("error", trans('admin/custom_fields/message.field.invalid'));
}
@@ -225,9 +229,13 @@ class CustomFieldsController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(CustomFieldRequest $request, CustomField $field) : RedirectResponse
public function update(CustomFieldRequest $request, $id) : RedirectResponse
{
$field = CustomField::find($id);
$this->authorize('update', $field);
$show_in_email = $request->get("show_in_email", 0);
$display_in_user_view = $request->get("display_in_user_view", 0);
@@ -240,7 +248,7 @@ class CustomFieldsController extends Controller
$field->name = trim(e($request->get("name")));
$field->element = e($request->get("element"));
$field->field_values = $request->get("field_values");
$field->created_by = auth()->id();
$field->user_id = Auth::id();
$field->help_text = $request->get("help_text");
$field->show_in_email = $show_in_email;
$field->is_unique = $request->get("is_unique", 0);
@@ -248,8 +256,6 @@ class CustomFieldsController extends Controller
$field->auto_add_to_fieldsets = $request->get("auto_add_to_fieldsets", 0);
$field->show_in_listview = $request->get("show_in_listview", 0);
$field->show_in_requestable_list = $request->get("show_in_requestable_list", 0);
$field->display_checkin = $request->get("display_checkin", 0);
$field->display_checkout = $request->get("display_checkout", 0);
if ($request->get('format') == 'CUSTOM REGEX') {
$field->format = e($request->get('custom_format'));

View File

@@ -35,12 +35,10 @@ class CustomFieldsetsController extends Controller
* @param int $id
* @since [v1.8]
*/
public function show(CustomFieldset $fieldset) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
$cfset = CustomFieldset::with('fields')
->where('id', '=', $fieldset->id)
->orderBy('id', 'ASC')
->first();
->where('id', '=', $id)->orderBy('id', 'ASC')->first();
$this->authorize('view', $cfset);
@@ -92,7 +90,7 @@ class CustomFieldsetsController extends Controller
$fieldset = new CustomFieldset([
'name' => $request->get('name'),
'created_by' => auth()->id(),
'user_id' => auth()->id(),
]);
$validator = Validator::make($request->all(), $fieldset->rules);
@@ -124,10 +122,16 @@ class CustomFieldsetsController extends Controller
* @param int $id
* @since [v6.0.14]
*/
public function edit(CustomFieldset $fieldset) : View | RedirectResponse
public function edit($id) : View | RedirectResponse
{
$this->authorize('create', CustomField::class);
return view('custom_fields.fieldsets.edit')->with('item', $fieldset);
if ($fieldset = CustomFieldset::find($id)) {
return view('custom_fields.fieldsets.edit')->with('item', $fieldset);
}
return redirect()->route('fields.index')->with('error', trans('admin/custom_fields/general.fieldset_does_not_exist', ['id' => $id]));
}
/**
@@ -137,18 +141,23 @@ class CustomFieldsetsController extends Controller
* @param int $id
* @since [v6.0.14]
*/
public function update(Request $request, CustomFieldset $fieldset) : RedirectResponse
public function update(Request $request, $id) : RedirectResponse
{
$this->authorize('create', CustomField::class);
$fieldset->name = $request->input('name');
if ($fieldset = CustomFieldset::find($id)) {
$fieldset->name = $request->input('name');
if ($fieldset->save()) {
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/general.fieldset_updated'));
}
return redirect()->back()->withInput()->withErrors($fieldset->getErrors());
if ($fieldset->save()) {
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/general.fieldset_updated'));
}
return redirect()->back()->withInput()->withErrors($fieldset->getErrors());
return redirect()->route('fields.index')->with('error', trans('admin/custom_fields/general.fieldset_does_not_exist', ['id' => $id]));
}
/**

View File

@@ -5,7 +5,6 @@ namespace App\Http\Controllers;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Session;
/**
@@ -45,8 +44,6 @@ class DashboardController extends Controller
return view('dashboard')->with('asset_stats', $asset_stats)->with('counts', $counts);
} else {
Session::reflash();
// Redirect to the profile page
return redirect()->intended('account/view-assets');
}

View File

@@ -51,11 +51,10 @@ class DepartmentsController extends Controller
$this->authorize('create', Department::class);
$department = new Department;
$department->fill($request->all());
$department->created_by = auth()->id();
$department->user_id = auth()->id();
$department->manager_id = ($request->filled('manager_id') ? $request->input('manager_id') : null);
$department->location_id = ($request->filled('location_id') ? $request->input('location_id') : null);
$department->company_id = ($request->filled('company_id') ? $request->input('company_id') : null);
$department->notes = $request->input('notes');
$department = $request->handleImages($department);
if ($department->save()) {
@@ -73,10 +72,17 @@ class DepartmentsController extends Controller
* @param int $id
* @since [v4.0]
*/
public function show(Department $department) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
$department = Department::find($id);
$this->authorize('view', $department);
return view('departments/view', compact('department'));
if (isset($department->id)) {
return view('departments/view', compact('department'));
}
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
}
/**
@@ -132,10 +138,15 @@ class DepartmentsController extends Controller
* @param int $departmentId
* @since [v1.0]
*/
public function edit(Department $department) : View | RedirectResponse
public function edit($departmentId = null) : View | RedirectResponse
{
$this->authorize('update', $department);
return view('departments/edit')->with('item', $department);
if (is_null($item = Department::find($departmentId))) {
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
}
$this->authorize('update', $item);
return view('departments/edit', compact('item'));
}
/**
@@ -146,8 +157,11 @@ class DepartmentsController extends Controller
* @param int $departmentId
* @since [v1.0]
*/
public function update(ImageUploadRequest $request, Department $department) : RedirectResponse
public function update(ImageUploadRequest $request, $id) : RedirectResponse
{
if (is_null($department = Department::find($id))) {
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
}
$this->authorize('update', $department);
@@ -157,7 +171,7 @@ class DepartmentsController extends Controller
$department->company_id = ($request->filled('company_id') ? $request->input('company_id') : null);
$department->phone = $request->input('phone');
$department->fax = $request->input('fax');
$department->notes = $request->input('notes');
$department = $request->handleImages($department);
if ($department->save()) {

View File

@@ -61,7 +61,7 @@ class DepreciationsController extends Controller
// Depreciation data
$depreciation->name = $request->input('name');
$depreciation->months = $request->input('months');
$depreciation->created_by = auth()->id();
$depreciation->user_id = Auth::id();
$request->validate([
'depreciation_min' => [
@@ -95,11 +95,17 @@ class DepreciationsController extends Controller
* @param int $depreciationId
* @since [v1.0]
*/
public function edit(Depreciation $depreciation) : RedirectResponse | View
public function edit($depreciationId = null) : RedirectResponse | View
{
// Check if the depreciation exists
if (is_null($item = Depreciation::find($depreciationId))) {
// Redirect to the blogs management page
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
}
$this->authorize('update', $depreciation);
return view('depreciations/edit')->with('item', $depreciation);
$this->authorize('update', $item);
return view('depreciations/edit', compact('item'));
}
/**
@@ -111,10 +117,17 @@ class DepreciationsController extends Controller
* @param int $depreciationId
* @since [v1.0]
*/
public function update(Request $request, Depreciation $depreciation) : RedirectResponse
public function update(Request $request, $depreciationId = null) : RedirectResponse
{
// Check if the depreciation exists
if (is_null($depreciation = Depreciation::find($depreciationId))) {
// Redirect to the blogs management page
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
}
$this->authorize('update', $depreciation);
// Depreciation data
$depreciation->name = $request->input('name');
$depreciation->months = $request->input('months');
@@ -178,12 +191,12 @@ class DepreciationsController extends Controller
* @param int $depreciationId
* @since [v1.0]
*/
public function show(Depreciation $depreciation) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
$depreciation = Depreciation::withCount('assets as assets_count')
->withCount('models as models_count')
->withCount('licenses as licenses_count')
->find($depreciation->id);
->find($id);
$this->authorize('view', $depreciation);

View File

@@ -62,7 +62,6 @@ class GroupsController extends Controller
$group->name = $request->input('name');
$group->permissions = json_encode($request->input('permission'));
$group->created_by = auth()->id();
$group->notes = $request->input('notes');
if ($group->save()) {
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
@@ -79,12 +78,19 @@ class GroupsController extends Controller
* @param int $id
* @since [v1.0]
*/
public function edit(Group $group) : View | RedirectResponse
public function edit($id) : View | RedirectResponse
{
$permissions = config('permissions');
$groupPermissions = $group->decodePermissions();
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
$group = Group::find($id);
if ($group) {
$permissions = config('permissions');
$groupPermissions = $group->decodePermissions();
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
}
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id]));
}
/**
@@ -95,11 +101,13 @@ class GroupsController extends Controller
* @param int $id
* @since [v1.0]
*/
public function update(Request $request, Group $group) : RedirectResponse
public function update(Request $request, $id = null) : RedirectResponse
{
if (! $group = Group::find($id)) {
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id]));
}
$group->name = $request->input('name');
$group->permissions = json_encode($request->input('permission'));
$group->notes = $request->input('notes');
if (! config('app.lock_passwords')) {
if ($group->save()) {
@@ -141,8 +149,14 @@ class GroupsController extends Controller
* @param $id
* @since [v4.0.11]
*/
public function show(Group $group) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
return view('groups/view', compact('group'));
$group = Group::find($id);
if ($group) {
return view('groups/view', compact('group'));
}
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', ['id' => $id]));
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\DB;
/**
* This controller provide the health route for
@@ -16,35 +15,13 @@ use Illuminate\Support\Facades\DB;
*/
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()
{
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);
}
return response()->json([
'status' => 'ok',
]);
}
}

View File

@@ -4,8 +4,10 @@ namespace App\Http\Controllers\Kits;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\PredefinedKit;
use App\Models\Asset;
use App\Models\PredefinedLicence;
use App\Models\PredefinedModel;
use App\Models\User;
use App\Services\PredefinedKitCheckoutService;
use Illuminate\Http\Request;
@@ -33,9 +35,12 @@ class CheckoutKitController extends Controller
* @author [D. Minaev.] [<dmitriy.minaev.v@gmail.com>]
* @return \Illuminate\Contracts\View\View View to checkout
*/
public function showCheckout(PredefinedKit $kit)
public function showCheckout($kit_id)
{
$this->authorize('checkout', Asset::class);
$kit = PredefinedKit::findOrFail($kit_id);
return view('kits/checkout')->with('kit', $kit);
}

View File

@@ -55,7 +55,6 @@ class PredefinedKitsController extends Controller
// Create a new Predefined Kit
$kit = new PredefinedKit;
$kit->name = $request->input('name');
$kit->created_by = auth()->id();
if (! $kit->save()) {
return redirect()->back()->withInput()->withErrors($kit->getErrors());
@@ -76,15 +75,17 @@ class PredefinedKitsController extends Controller
* @param int $kit_id
* @return \Illuminate\Contracts\View\View
*/
public function edit(PredefinedKit $kit)
public function edit($kit_id = null)
{
$this->authorize('update', PredefinedKit::class);
if ($kit = PredefinedKit::find($kit_id)) {
return view('kits/edit')
->with('item', $kit)
->with('models', $kit->models)
->with('licenses', $kit->licenses);
}
return redirect()->route('kits.index')->with('error', trans('admin/kits/general.kit_none'));
}
/**
@@ -96,9 +97,15 @@ class PredefinedKitsController extends Controller
* @param int $kit_id
* @return \Illuminate\Http\RedirectResponse
*/
public function update(ImageUploadRequest $request, PredefinedKit $kit)
public function update(ImageUploadRequest $request, $kit_id = null)
{
$this->authorize('update', PredefinedKit::class);
// Check if the kit exists
if (is_null($kit = PredefinedKit::find($kit_id))) {
// Redirect to the kits management page
return redirect()->route('kits.index')->with('error', trans('admin/kits/general.kit_none'));
}
$kit->name = $request->input('name');
if ($kit->save()) {
@@ -145,9 +152,9 @@ class PredefinedKitsController extends Controller
* @param int $modelId
* @return \Illuminate\Contracts\View\View
*/
public function show(PredefinedKit $kit)
public function show($kit_id = null)
{
return $this->edit($kit);
return $this->edit($kit_id);
}
/**

View File

@@ -28,11 +28,16 @@ class LicenseCheckinController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(LicenseSeat $licenseSeat, $backTo = null)
public function create($seatId = null, $backTo = null)
{
// Check if the asset exists
$license = License::find($licenseSeat->license_id);
if (is_null($licenseSeat = LicenseSeat::find($seatId)) || is_null($license = License::find($licenseSeat->license_id))) {
// Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
}
$this->authorize('checkout', $license);
return view('licenses/checkin', compact('licenseSeat'))->with('backto', $backTo);
}
@@ -66,7 +71,7 @@ class LicenseCheckinController extends Controller
if (! $license->reassignable) {
// Not allowed to checkin
Session::flash('error', trans('admin/licenses/message.checkin.not_reassignable') . '.');
Session::flash('error', 'License not reassignable.');
return redirect()->back()->withInput();
}

View File

@@ -28,24 +28,33 @@ class LicenseCheckoutController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(License $license)
public function create($id)
{
$this->authorize('checkout', $license);
if ($license->category) {
if ($license = License::find($id)) {
// Make sure there is at least one available to checkout
if ($license->availCount()->count() < 1) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats'));
$this->authorize('checkout', $license);
if ($license->category) {
// Make sure there is at least one available to checkout
if ($license->availCount()->count() < 1){
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats'));
}
// Return the checkout view
return view('licenses/checkout', compact('license'));
}
// Return the checkout view
return view('licenses/checkout', compact('license'));
// Invalid category
return redirect()->route('licenses.edit', ['license' => $license->id])
->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.license')]));
}
// Invalid category
return redirect()->route('licenses.edit', ['license' => $license->id])
->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.license')]));
// Not found
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
}
@@ -68,7 +77,7 @@ class LicenseCheckoutController extends Controller
$this->authorize('checkout', $license);
$licenseSeat = $this->findLicenseSeatToCheckout($license, $seatId);
$licenseSeat->created_by = auth()->id();
$licenseSeat->user_id = Auth::id();
$licenseSeat->notes = $request->input('notes');

View File

@@ -112,19 +112,37 @@ 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)) {
$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'));
}
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');
}
// The log record doesn't exist somehow
return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found'));
$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);
}
}
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId]));

View File

@@ -99,7 +99,7 @@ class LicensesController extends Controller
$license->supplier_id = $request->input('supplier_id');
$license->category_id = $request->input('category_id');
$license->termination_date = $request->input('termination_date');
$license->created_by = auth()->id();
$license->user_id = Auth::id();
$license->min_amt = $request->input('min_amt');
session()->put(['redirect_option' => $request->get('redirect_option')]);
@@ -121,10 +121,13 @@ class LicensesController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function edit(License $license)
public function edit($licenseId = null)
{
if (is_null($item = License::find($licenseId))) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
$this->authorize('update', $license);
$this->authorize('update', $item);
$maintained_list = [
'' => 'Maintained',
@@ -132,8 +135,7 @@ class LicensesController extends Controller
'0' => 'No',
];
return view('licenses/edit')
->with('item', $license)
return view('licenses/edit', compact('item'))
->with('depreciation_list', Helper::depreciationList())
->with('maintained_list', $maintained_list);
}
@@ -151,9 +153,11 @@ class LicensesController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, License $license)
public function update(Request $request, $licenseId = null)
{
if (is_null($license = License::find($licenseId))) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
$this->authorize('update', $license);
@@ -197,10 +201,10 @@ class LicensesController extends Controller
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy(License $license)
public function destroy($licenseId)
{
// Check if the license exists
if (is_null($license = License::find($license->id))) {
if (is_null($license = License::find($licenseId))) {
// Redirect to the license management page
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
}
@@ -234,9 +238,14 @@ class LicensesController extends Controller
* @return \Illuminate\Contracts\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show(License $license)
public function show($licenseId = null)
{
$license = License::with('assignedusers')->find($license->id);
$license = License::with('assignedusers')->find($licenseId);
if (!$license) {
return redirect()->route('licenses.index')
->with('error', trans('admin/licenses/message.does_not_exist'));
}
$users_count = User::where('autoassign_licenses', '1')->count();
$total_seats_count = $license->totalSeatsByLicenseID();
@@ -258,10 +267,10 @@ class LicensesController extends Controller
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $licenseId
* @return \Illuminate\Http\RedirectResponse | \Illuminate\Contracts\View\View
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function getClone($licenseId = null) : \Illuminate\Contracts\View\View | \Illuminate\Http\RedirectResponse
public function getClone($licenseId = null)
{
if (is_null($license_to_clone = License::find($licenseId))) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));

View File

@@ -75,10 +75,9 @@ class LocationsController extends Controller
$location->zip = $request->input('zip');
$location->ldap_ou = $request->input('ldap_ou');
$location->manager_id = $request->input('manager_id');
$location->created_by = auth()->id();
$location->user_id = auth()->id();
$location->phone = request('phone');
$location->fax = request('fax');
$location->notes = $request->input('notes');
$location = $request->handleImages($location);
@@ -97,10 +96,15 @@ class LocationsController extends Controller
* @param int $locationId
* @since [v1.0]
*/
public function edit(Location $location) : View | RedirectResponse
public function edit($locationId = null) : View | RedirectResponse
{
$this->authorize('update', Location::class);
return view('locations/edit')->with('item', $location);
// Check if the location exists
if (is_null($item = Location::find($locationId))) {
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
return view('locations/edit', compact('item'));
}
/**
@@ -112,10 +116,15 @@ class LocationsController extends Controller
* @param int $locationId
* @since [v1.0]
*/
public function update(ImageUploadRequest $request, Location $location) : RedirectResponse
public function update(ImageUploadRequest $request, $locationId = null) : RedirectResponse
{
$this->authorize('update', Location::class);
// Check if the location exists
if (is_null($location = Location::find($locationId))) {
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
// Update the location data
$location->name = $request->input('name');
$location->parent_id = $request->input('parent_id', null);
$location->currency = $request->input('currency', '$');
@@ -129,7 +138,6 @@ class LocationsController extends Controller
$location->fax = request('fax');
$location->ldap_ou = $request->input('ldap_ou');
$location->manager_id = $request->input('manager_id');
$location->notes = $request->input('notes');
$location = $request->handleImages($location);
@@ -184,7 +192,7 @@ class LocationsController extends Controller
* @param int $id
* @since [v1.0]
*/
public function show(Location $location) : View | RedirectResponse
public function show($id = null) : View | RedirectResponse
{
$location = Location::withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
@@ -192,7 +200,7 @@ class LocationsController extends Controller
->withCount('children as children_count')
->withCount('users as users_count')
->withTrashed()
->find($location->id);
->find($id);
if (isset($location->id)) {
return view('locations/view', compact('location'));
@@ -270,7 +278,7 @@ class LocationsController extends Controller
$logaction->item_type = Location::class;
$logaction->item_id = $location->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->created_by = auth()->id();
$logaction->user_id = auth()->id();
$logaction->logaction('restore');
return redirect()->route('locations.index')->with('success', trans('admin/locations/message.restore.success'));

View File

@@ -61,13 +61,12 @@ class ManufacturersController extends Controller
$this->authorize('create', Manufacturer::class);
$manufacturer = new Manufacturer;
$manufacturer->name = $request->input('name');
$manufacturer->created_by = auth()->id();
$manufacturer->user_id = Auth::id();
$manufacturer->url = $request->input('url');
$manufacturer->support_url = $request->input('support_url');
$manufacturer->warranty_lookup_url = $request->input('warranty_lookup_url');
$manufacturer->support_phone = $request->input('support_phone');
$manufacturer->support_email = $request->input('support_email');
$manufacturer->notes = $request->input('notes');
$manufacturer = $request->handleImages($manufacturer);
if ($manufacturer->save()) {
@@ -85,10 +84,18 @@ class ManufacturersController extends Controller
* @param int $manufacturerId
* @since [v1.0]
*/
public function edit(Manufacturer $manufacturer) : View | RedirectResponse
public function edit($manufacturerId = null) : View | RedirectResponse
{
// Handles manufacturer checks and permissions.
$this->authorize('update', Manufacturer::class);
return view('manufacturers/edit')->with('item', $manufacturer);
// Check if the manufacturer exists
if (! $item = Manufacturer::find($manufacturerId)) {
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
}
// Show the page
return view('manufacturers/edit', compact('item'));
}
/**
@@ -100,17 +107,22 @@ class ManufacturersController extends Controller
* @param int $manufacturerId
* @since [v1.0]
*/
public function update(ImageUploadRequest $request, Manufacturer $manufacturer) : RedirectResponse
public function update(ImageUploadRequest $request, $manufacturerId = null) : RedirectResponse
{
$this->authorize('update', Manufacturer::class);
// Check if the manufacturer exists
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
// Redirect to the manufacturer page
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
}
// Save the data
$manufacturer->name = $request->input('name');
$manufacturer->url = $request->input('url');
$manufacturer->support_url = $request->input('support_url');
$manufacturer->warranty_lookup_url = $request->input('warranty_lookup_url');
$manufacturer->support_phone = $request->input('support_phone');
$manufacturer->support_email = $request->input('support_email');
$manufacturer->notes = $request->input('notes');
// Set the model's image property to null if the image is being deleted
if ($request->input('image_delete') == 1) {
@@ -171,10 +183,18 @@ class ManufacturersController extends Controller
* @param int $manufacturerId
* @since [v1.0]
*/
public function show(Manufacturer $manufacturer) : View | RedirectResponse
public function show($manufacturerId = null) : View | RedirectResponse
{
$this->authorize('view', Manufacturer::class);
return view('manufacturers/view', compact('manufacturer'));
$manufacturer = Manufacturer::find($manufacturerId);
if (isset($manufacturer->id)) {
return view('manufacturers/view', compact('manufacturer'));
}
$error = trans('admin/manufacturers/message.does_not_exist');
// Redirect to the user management page
return redirect()->route('manufacturers.index')->with('error', $error);
}
/**
@@ -199,7 +219,7 @@ class ManufacturersController extends Controller
$logaction->item_type = Manufacturer::class;
$logaction->item_id = $manufacturer->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->created_by = auth()->id();
$logaction->user_id = auth()->id();
$logaction->logaction('restore');
// Redirect them to the deleted page if there are more, otherwise the section index

View File

@@ -33,7 +33,6 @@ class ModalController extends Controller
'supplier',
'upload-file',
'user',
'add-note',
];

View File

@@ -1,42 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
class NotesController extends Controller
{
public function store(Request $request)
{
$this->authorize('update', Asset::class);
$validated = $request->validate([
'id' => 'required',
'note' => 'required|string|max:50000',
'type' => [
'required',
Rule::in(['asset']),
],
]);
$item = Asset::findOrFail($validated['id']);
$this->authorize('update', $item);
$logaction = new Actionlog;
$logaction->item_id = $item->id;
$logaction->item_type = get_class($item);
$logaction->note = $validated['note'];
$logaction->created_by = Auth::id();
$logaction->logaction('note added');
return redirect()
->route('hardware.show', $validated['id'])
->withFragment('history')
->with('success', trans('general.note_added'));
}
}

View File

@@ -99,13 +99,9 @@ class ProfileController extends Controller
* User change email page.
*
*/
public function password() : View | RedirectResponse
public function password() : View
{
$user = auth()->user();
if ($user->ldap_import=='1') {
return redirect()->route('account')->with('error', trans('admin/users/message.error.password_ldap'));
}
return view('account/change-password', compact('user'));
}
@@ -120,7 +116,7 @@ class ProfileController extends Controller
$user = auth()->user();
if ($user->ldap_import == '1') {
return redirect()->route('account')->with('error', trans('admin/users/message.error.password_ldap'));
return redirect()->route('account.password.index')->with('error', trans('admin/users/message.error.password_ldap'));
}
$rules = [
@@ -198,14 +194,14 @@ class ProfileController extends Controller
*/
public function printInventory() : View
{
$show_users = User::where('id',auth()->user()->id)->get();
$show_user = auth()->user();
return view('users/print')
->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('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('settings', Setting::getSettings());
}
@@ -226,12 +222,7 @@ class ProfileController extends Controller
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
}
try {
$user->notify((new CurrentInventory($user)));
} catch (\Exception $e) {
\Log::error($e);
}
$user->notify((new CurrentInventory($user)));
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
}
}

View File

@@ -1,80 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\CustomField;
use App\Models\ReportTemplate;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class ReportTemplatesController extends Controller
{
public function store(Request $request): RedirectResponse
{
$this->authorize('reports.view');
// Ignore "options" rules since data does not come in under that key...
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
$report = $request->user()->reportTemplates()->create([
'name' => $validated['name'],
'options' => $request->except(['_token', 'name']),
]);
session()->flash('success', trans('admin/reports/message.create.success'));
return redirect()->route('report-templates.show', $report->id);
}
public function show(ReportTemplate $reportTemplate)
{
$this->authorize('reports.view');
$customfields = CustomField::get();
$report_templates = ReportTemplate::orderBy('name')->get();
return view('reports/custom', [
'customfields' => $customfields,
'report_templates' => $report_templates,
'template' => $reportTemplate,
]);
}
public function edit(ReportTemplate $reportTemplate)
{
$this->authorize('reports.view');
return view('reports/custom', [
'customfields' => CustomField::get(),
'template' => $reportTemplate,
]);
}
public function update(Request $request, ReportTemplate $reportTemplate): RedirectResponse
{
$this->authorize('reports.view');
// Ignore "options" rules since data does not come in under that key...
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
$reportTemplate->update([
'name' => $validated['name'],
'options' => $request->except(['_token', 'name']),
]);
session()->flash('success', trans('admin/reports/message.update.success'));
return redirect()->route('report-templates.show', $reportTemplate->id);
}
public function destroy(ReportTemplate $reportTemplate): RedirectResponse
{
$this->authorize('reports.view');
$reportTemplate->delete();
return redirect()->route('reports/custom')
->with('success', trans('admin/reports/message.delete.success'));
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Mail\CheckoutAssetMail;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
@@ -11,18 +10,14 @@ use App\Models\AssetModel;
use App\Models\Category;
use App\Models\AssetMaintenance;
use App\Models\CheckoutAcceptance;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\Depreciation;
use App\Models\License;
use App\Models\ReportTemplate;
use App\Models\Setting;
use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\MorphTo;
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;
@@ -264,7 +259,7 @@ class ReportsController extends Controller
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
Log::debug('Added headers: '.$executionTime);
$actionlogs = Actionlog::with('item', 'user', 'target', 'location', 'adminuser')
$actionlogs = Actionlog::with('item', 'user', 'target', 'location')
->orderBy('created_at', 'DESC')
->chunk(20, function ($actionlogs) use ($handle) {
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
@@ -291,7 +286,7 @@ class ReportsController extends Controller
$row = [
$actionlog->created_at,
($actionlog->adminuser) ? e($actionlog->adminuser->getFullNameAttribute()) : '',
($actionlog->admin) ? e($actionlog->admin->getFullNameAttribute()) : '',
$actionlog->present()->actionType(),
e($actionlog->itemType()),
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
@@ -397,27 +392,12 @@ class ReportsController extends Controller
* @see ReportsController::postCustomReport() method that generates the CSV
* @since [v1.0]
*/
public function getCustomReport(Request $request) : View
public function getCustomReport() : View
{
$this->authorize('reports.view');
$customfields = CustomField::get();
$report_templates = ReportTemplate::orderBy('name')->get();
// The view needs a template to render correctly, even if it is empty...
$template = new ReportTemplate;
// Set the report's input values in the cases we were redirected back
// with validation errors so the report is populated as expected.
if ($request->old()) {
$template->name = $request->old('name');
$template->options = $request->old();
}
return view('reports/custom', [
'customfields' => $customfields,
'report_templates' => $report_templates,
'template' => $template,
]);
return view('reports/custom')->with('customfields', $customfields);
}
/**
@@ -723,10 +703,6 @@ class ReportsController extends Controller
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
}
if (($request->filled('asset_eol_date_start')) && ($request->filled('asset_eol_date_end'))) {
$assets->whereBetween('assets.asset_eol_date', [$request->input('asset_eol_date_start'), $request->input('asset_eol_date_end')]);
}
if (($request->filled('last_audit_start')) && ($request->filled('last_audit_end'))) {
$last_audit_start = Carbon::parse($request->input('last_audit_start'))->startOfDay();
$last_audit_end = Carbon::parse($request->input('last_audit_end'))->endOfDay();
@@ -802,7 +778,7 @@ class ReportsController extends Controller
}
if ($request->filled('eol')) {
$row[] = ($asset->purchase_date != '') ? $asset->asset_eol_date : '';
$row[] = ($asset->asset_eol_date) ? $asset->asset_eol_date : '';
}
if ($request->filled('order')) {
@@ -1111,31 +1087,28 @@ class ReportsController extends Controller
$this->authorize('reports.view');
$showDeleted = $deleted == 'deleted';
$query = CheckoutAcceptance::pending()
->where('checkoutable_type', 'App\Models\Asset')
->with([
'checkoutable' => function (MorphTo $query) {
$query->morphWith([
AssetModel::class => ['model'],
Company::class => ['company'],
Asset::class => ['assignedTo'],
])->with('model.category');
},
'assignedTo' => function($query){
$query->withTrashed();
}
]);
if ($showDeleted) {
$query->withTrashed();
/**
* Get all assets with pending checkout acceptances
*/
if($showDeleted) {
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->withTrashed()->with(['assignedTo' , 'checkoutable.assignedTo', 'checkoutable.model'])->get();
} else {
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->with(['assignedTo' => function ($query) {
$query->withTrashed();
}, 'checkoutable.assignedTo', 'checkoutable.model'])->get();
}
$assetsForReport = $query->get()
->map(function ($acceptance) {
return [
'assetItem' => $acceptance->checkoutable,
'acceptance' => $acceptance,
];
$assetsForReport = $acceptances
->filter(function ($acceptance) {
$acceptance_checkoutable_flag = false;
if ($acceptance->checkoutable){
$acceptance_checkoutable_flag = $acceptance->checkoutable->checkedOutToUser();
}
return $acceptance->checkoutable_type == 'App\Models\Asset' && $acceptance_checkoutable_flag;
})
->map(function($acceptance) {
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
});
return view('reports/unaccepted_assets', compact('assetsForReport','showDeleted' ));
@@ -1173,17 +1146,24 @@ class ReportsController extends Controller
}
$logItem = $logItem_res[0];
}
$email = $assetItem->assignedTo?->email;
$locale = $assetItem->assignedTo?->locale;
// Only send notification if assigned
if ($locale && $email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note))->locale($locale));
if ($assetItem->assignedTo) {
} elseif ($email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)));
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)
);
}
}
if ($email == ''){
if ($assetItem->assignedTo->email == ''){
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
}

View File

@@ -7,11 +7,6 @@ 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;
@@ -186,13 +181,12 @@ class SettingsController extends Controller
$settings->brand = 1;
$settings->locale = $request->input('locale', 'en-US');
$settings->default_currency = $request->input('default_currency', 'USD');
$settings->created_by = 1;
$settings->user_id = 1;
$settings->email_domain = $request->input('email_domain');
$settings->email_format = $request->input('email_format');
$settings->next_auto_tag_base = 1;
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
$settings->zerofill_count = $request->input('zerofill_count') ?: 0;
if ((! $user->isValid()) || (! $settings->isValid())) {
return redirect()->back()->withInput()->withErrors($user->getErrors())->withErrors($settings->getErrors());
@@ -279,6 +273,20 @@ class SettingsController extends Controller
return view('settings/index', compact('settings'));
}
/**
* Return the admin settings page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @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.
@@ -335,8 +343,6 @@ 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');
@@ -480,7 +486,7 @@ class SettingsController extends Controller
*
* @since [v1.0]
*/
public function postSecurity(StoreSecuritySettings $request) : RedirectResponse
public function postSecurity(Request $request) : RedirectResponse
{
$this->validate($request, [
'pwd_secure_complexity' => 'array',
@@ -550,7 +556,7 @@ class SettingsController extends Controller
*
* @since [v1.0]
*/
public function postLocalization(StoreLocalizationSettings $request) : RedirectResponse
public function postLocalization(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -593,7 +599,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
*/
public function postAlerts(StoreNotificationSettings $request) : RedirectResponse
public function postAlerts(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -696,6 +702,48 @@ class SettingsController extends Controller
return redirect()->back()->withInput()->withErrors($setting->getErrors());
}
/**
* Return a form to allow a super admin to update settings.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v1.0]
*/
public function getBarcodes() : View
{
$setting = Setting::getSettings();
$is_gd_installed = extension_loaded('gd');
return view('settings.barcodes', compact('setting'))->with('is_gd_installed', $is_gd_installed);
}
/**
* Saves settings from form.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v1.0]
*/
public function postBarcodes(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}
$setting->qr_code = $request->input('qr_code', '0');
$setting->alt_barcode = $request->input('alt_barcode');
$setting->alt_barcode_enabled = $request->input('alt_barcode_enabled', '0');
$setting->barcode_type = $request->input('barcode_type');
$setting->qr_text = $request->input('qr_text');
if ($setting->save()) {
return redirect()->route('settings.index')
->with('success', trans('admin/settings/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($setting->getErrors());
}
/**
* Return a form to allow a super admin to update settings.
*
@@ -721,11 +769,8 @@ class SettingsController extends Controller
*/
public function getLabels() : View
{
$is_gd_installed = extension_loaded('gd');
return view('settings.labels')
->with('setting', Setting::getSettings())
->with('is_gd_installed', $is_gd_installed)
->with('customFields', CustomField::where('field_encrypted', '=', 0)->get());
}
@@ -735,7 +780,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function postLabels(StoreLabelSettings $request) : RedirectResponse
public function postLabels(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -761,13 +806,9 @@ class SettingsController extends Controller
$setting->labels_pagewidth = $request->input('labels_pagewidth');
$setting->labels_pageheight = $request->input('labels_pageheight');
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
//Barcodes
$setting->qr_code = $request->input('qr_code', '0');
//1D-Barcode
$setting->alt_barcode_enabled = $request->input('alt_barcode_enabled', '0');
//QR-Code
$setting->qr_text = $request->input('qr_text');
if ($request->filled('labels_display_name')) {
$setting->labels_display_name = 1;
@@ -800,7 +841,6 @@ class SettingsController extends Controller
}
if ($setting->save()) {
return redirect()->route('settings.labels.index')
->with('success', trans('admin/settings/message.update.success'));
}
@@ -819,7 +859,26 @@ class SettingsController extends Controller
{
$setting = Setting::getSettings();
$groups = Group::pluck('name', 'id');
return view('settings.ldap', compact('setting', 'groups'));
/**
* This validator is only temporary (famous last words.) - @snipe
*/
$messages = [
'ldap_username_field.not_in' => '<code>sAMAccountName</code> (mixed case) will likely not work. You should use <code>samaccountname</code> (lowercase) instead. ',
'ldap_auth_filter_query.not_in' => '<code>uid=samaccountname</code> is probably not a valid auth filter. You probably want <code>uid=</code> ',
'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);
}
/**
@@ -828,7 +887,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function postLdapSettings(StoreLdapSettings $request) : RedirectResponse
public function postLdapSettings(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -870,6 +929,7 @@ class SettingsController extends Controller
}
if ($setting->save()) {
$setting->update_client_side_cert_files();
return redirect()->route('settings.ldap.index')
->with('success', trans('admin/settings/message.update.success'));
}

View File

@@ -26,10 +26,14 @@ class StatuslabelsController extends Controller
return view('statuslabels.index');
}
public function show(Statuslabel $statuslabel) : View | RedirectResponse
public function show($id) : View | RedirectResponse
{
$this->authorize('view', Statuslabel::class);
return view('statuslabels.view')->with('statuslabel', $statuslabel);
if ($statuslabel = Statuslabel::find($id)) {
return view('statuslabels.view')->with('statuslabel', $statuslabel);
}
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
}
/**
@@ -65,7 +69,7 @@ class StatuslabelsController extends Controller
// Save the Statuslabel data
$statusLabel->name = $request->input('name');
$statusLabel->created_by = auth()->id();
$statusLabel->user_id = Auth::id();
$statusLabel->notes = $request->input('notes');
$statusLabel->deployable = $statusType['deployable'];
$statusLabel->pending = $statusType['pending'];
@@ -87,15 +91,20 @@ class StatuslabelsController extends Controller
*
* @param int $statuslabelId
*/
public function edit(Statuslabel $statuslabel) : View | RedirectResponse
public function edit($statuslabelId = null) : View | RedirectResponse
{
$this->authorize('update', Statuslabel::class);
// Check if the Statuslabel exists
if (is_null($item = Statuslabel::find($statuslabelId))) {
// Redirect to the blogs management page
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
}
$use_statuslabel_type = $item->getStatuslabelType();
$statuslabel_types = ['' => trans('admin/hardware/form.select_statustype')] + ['undeployable' => trans('admin/hardware/general.undeployable')] + ['pending' => trans('admin/hardware/general.pending')] + ['archived' => trans('admin/hardware/general.archived')] + ['deployable' => trans('admin/hardware/general.deployable')];
return view('statuslabels/edit', compact('statuslabel_types'))
->with('item', $statuslabel)
->with('use_statuslabel_type', $statuslabel);
return view('statuslabels/edit', compact('item', 'statuslabel_types'))->with('use_statuslabel_type', $use_statuslabel_type);
}
/**
@@ -103,9 +112,14 @@ class StatuslabelsController extends Controller
*
* @param int $statuslabelId
*/
public function update(Request $request, Statuslabel $statuslabel) : RedirectResponse
public function update(Request $request, $statuslabelId = null) : RedirectResponse
{
$this->authorize('update', Statuslabel::class);
// Check if the Statuslabel exists
if (is_null($statuslabel = Statuslabel::find($statuslabelId))) {
// Redirect to the blogs management page
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
}
if (! $request->filled('statuslabel_types')) {
return redirect()->back()->withInput()->withErrors(['statuslabel_types' => trans('validation.statuslabel_type')]);

View File

@@ -62,7 +62,7 @@ class SuppliersController extends Controller
$supplier->email = request('email');
$supplier->notes = request('notes');
$supplier->url = $supplier->addhttp(request('url'));
$supplier->created_by = auth()->id();
$supplier->user_id = Auth::id();
$supplier = $request->handleImages($supplier);
if ($supplier->save()) {
@@ -77,10 +77,17 @@ class SuppliersController extends Controller
*
* @param int $supplierId
*/
public function edit(Supplier $supplier) : View | RedirectResponse
public function edit($supplierId = null) : View | RedirectResponse
{
$this->authorize('update', Supplier::class);
return view('suppliers/edit')->with('item', $supplier);
// Check if the supplier exists
if (is_null($item = Supplier::find($supplierId))) {
// Redirect to the supplier page
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.does_not_exist'));
}
// Show the page
return view('suppliers/edit', compact('item'));
}
/**
@@ -88,9 +95,14 @@ class SuppliersController extends Controller
*
* @param int $supplierId
*/
public function update(ImageUploadRequest $request, Supplier $supplier) : RedirectResponse
public function update($supplierId, ImageUploadRequest $request) : RedirectResponse
{
$this->authorize('update', Supplier::class);
if (is_null($supplier = Supplier::find($supplierId))) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.does_not_exist'));
}
// Save the data
$supplier->name = request('name');
$supplier->address = request('address');
@@ -151,10 +163,15 @@ class SuppliersController extends Controller
* @param null $supplierId
* @internal param int $assetId
*/
public function show(Supplier $supplier) : View | RedirectResponse
public function show($supplierId = null) : View | RedirectResponse
{
$this->authorize('view', Supplier::class);
return view('suppliers/view', compact('supplier'));
$supplier = Supplier::find($supplierId);
if (isset($supplier->id)) {
return view('suppliers/view', compact('supplier'));
}
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.does_not_exist'));
}
}

Some files were not shown because too many files have changed in this diff Show More