Merge branch 'develop' into bug/sc-17129/v6-integration-pie-chart-disappears-if-you

This commit is contained in:
snipe 2021-10-28 17:13:37 -07:00 committed by GitHub
commit 3951de1669
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1269 changed files with 51380 additions and 31177 deletions

View file

@ -2360,6 +2360,123 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "Delta5",
"name": "Evan Taylor",
"avatar_url": "https://avatars.githubusercontent.com/u/1975640?v=4",
"profile": "https://github.com/Delta5",
"contributions": [
"code"
]
},
{
"login": "PetriAsi",
"name": "Petri Asikainen",
"avatar_url": "https://avatars.githubusercontent.com/u/8735148?v=4",
"profile": "https://github.com/PetriAsi",
"contributions": [
"code"
]
},
{
"login": "derdeagle",
"name": "derdeagle",
"avatar_url": "https://avatars.githubusercontent.com/u/11424540?v=4",
"profile": "https://github.com/derdeagle",
"contributions": [
"code"
]
},
{
"login": "vapier",
"name": "Mike Frysinger",
"avatar_url": "https://avatars.githubusercontent.com/u/176950?v=4",
"profile": "https://wh0rd.org/",
"contributions": [
"code"
]
},
{
"login": "AL4AL",
"name": "ALPHA",
"avatar_url": "https://avatars.githubusercontent.com/u/22044358?v=4",
"profile": "https://github.com/AL4AL",
"contributions": [
"code"
]
},
{
"login": "FliegenKLATSCH",
"name": "FliegenKLATSCH",
"avatar_url": "https://avatars.githubusercontent.com/u/1042587?v=4",
"profile": "https://www.ifern.de",
"contributions": [
"code"
]
},
{
"login": "jerm",
"name": "Jeremy Price",
"avatar_url": "https://avatars.githubusercontent.com/u/442138?v=4",
"profile": "https://github.com/jerm",
"contributions": [
"code"
]
},
{
"login": "Toreg87",
"name": "Toreg87",
"avatar_url": "https://avatars.githubusercontent.com/u/84392209?v=4",
"profile": "https://github.com/Toreg87",
"contributions": [
"code"
]
},
{
"login": "Computroniks",
"name": "Matthew Nickson",
"avatar_url": "https://avatars.githubusercontent.com/u/67638596?v=4",
"profile": "https://github.com/Computroniks",
"contributions": [
"code"
]
},
{
"login": "jethron",
"name": "Jethro Nederhof",
"avatar_url": "https://avatars.githubusercontent.com/u/1646397?v=4",
"profile": "https://jethron.id.au",
"contributions": [
"code"
]
},
{
"login": "01ste02",
"name": "Oskar Stenberg",
"avatar_url": "https://avatars.githubusercontent.com/u/23289826?v=4",
"profile": "https://github.com/01ste02",
"contributions": [
"code"
]
},
{
"login": "Robert-Azelis",
"name": "Robert-Azelis",
"avatar_url": "https://avatars.githubusercontent.com/u/82208283?v=4",
"profile": "https://github.com/Robert-Azelis",
"contributions": [
"code"
]
},
{
"login": "alwism",
"name": "Alexander William Smith",
"avatar_url": "https://avatars.githubusercontent.com/u/60648387?v=4",
"profile": "https://github.com/alwism",
"contributions": [
"code"
]
} }
] ]
} }

View file

@ -1,62 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
#### 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)
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server (please complete the following information):**
- Snipe-IT Version
- OS: [e.g. Ubuntu, CentOS]
- Web Server: [e.g. Apache, IIS]
- PHP Version
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Error Messages**
- 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.)
**Additional context**
- 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.
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.

View file

@ -1,23 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Server (please complete the following information):**
- Snipe-IT Version
- OS: [e.g. Ubuntu, CentOS]
- Web Server: [e.g. Apache, IIS]
- PHP Version
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

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,42 @@
name: Feature Request
description: Suggest an idea for this project
body:
- 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: textarea
attributes:
label: Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
attributes:
label: Additional context Add any other context or screenshots about the feature request here.

2
.gitignore vendored
View file

@ -63,3 +63,5 @@ _ide_helper.php
.phpstorm.meta.php .phpstorm.meta.php
_ide_helper_models.php _ide_helper_models.php
/.phplint-cache /.phplint-cache
storage/ldap_client_tls.cert
storage/ldap_client_tls.key

View file

@ -1,5 +1,5 @@
FROM ubuntu:bionic FROM ubuntu:20.04
LABEL maintainer Brady Wetherington <uberbrady@gmail.com> LABEL maintainer="Brady Wetherington <bwetherington@grokability.com>"
# No need to add `apt-get clean` here, reference: # No need to add `apt-get clean` here, reference:
# - https://github.com/snipe/snipe-it/pull/9201 # - https://github.com/snipe/snipe-it/pull/9201
@ -14,15 +14,15 @@ RUN export DEBIAN_FRONTEND=noninteractive; \
apt-utils \ apt-utils \
apache2 \ apache2 \
apache2-bin \ apache2-bin \
libapache2-mod-php7.2 \ libapache2-mod-php7.4 \
php7.2-curl \ php7.4-curl \
php7.2-ldap \ php7.4-ldap \
php7.2-mysql \ php7.4-mysql \
php7.2-gd \ php7.4-gd \
php7.2-xml \ php7.4-xml \
php7.2-mbstring \ php7.4-mbstring \
php7.2-zip \ php7.4-zip \
php7.2-bcmath \ php7.4-bcmath \
patch \ patch \
curl \ curl \
wget \ wget \
@ -38,7 +38,7 @@ autoconf \
libc-dev \ libc-dev \
pkg-config \ pkg-config \
libmcrypt-dev \ libmcrypt-dev \
php7.2-dev \ php7.4-dev \
ca-certificates \ ca-certificates \
unzip \ unzip \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@ -47,16 +47,16 @@ unzip \
RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar
RUN php go-pear.phar RUN php go-pear.phar
RUN pecl install mcrypt-1.0.2 RUN pecl install mcrypt-1.0.3
RUN bash -c "echo extension=/usr/lib/php/20170718/mcrypt.so > /etc/php/7.2/mods-available/mcrypt.ini" RUN bash -c "echo extension=/usr/lib/php/20190902/mcrypt.so > /etc/php/7.4/mods-available/mcrypt.ini"
RUN phpenmod mcrypt RUN phpenmod mcrypt
RUN phpenmod gd RUN phpenmod gd
RUN phpenmod bcmath RUN phpenmod bcmath
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.2/apache2/php.ini RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.4/apache2/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.2/cli/php.ini RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.4/cli/php.ini
RUN useradd -m --uid 1000 --gid 50 docker RUN useradd -m --uid 1000 --gid 50 docker

View file

@ -1,4 +1,4 @@
FROM alpine:3.13 FROM alpine:3.14.2
# Apache + PHP # Apache + PHP
RUN apk add --no-cache \ RUN apk add --no-cache \
apache2 \ apache2 \

1
Procfile Normal file
View file

@ -0,0 +1 @@
web: php heroku/startup.php && heroku-php-apache2 public/

View file

@ -1,5 +1,5 @@
![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=snipe/snipe-it&amp;utm_campaign=Badge_Grade) ![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=snipe/snipe-it&amp;utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-259-orange.svg?style=flat-square)](#contributors) [![All Contributors](https://img.shields.io/badge/all_contributors-271-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
## Snipe-IT - Open Source Asset Management System ## Snipe-IT - Open Source Asset Management System
@ -19,6 +19,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. 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 ### User's Manual
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview). For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
@ -55,11 +57,11 @@ Since the release of the JSON REST API, several third-party developers have been
- [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer) - [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey) - [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
- [InQRy](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft) - [InQRy -unmaintained-](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it - [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
- [jamf2snipe](https://github.com/ParadoxGuitarist/jamf2snipe) by [@ParadoxGuitarist](https://github.com/ParadoxGuitarist) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance - [jamf2snipe](https://github.com/ParadoxGuitarist/jamf2snipe) by [@ParadoxGuitarist](https://github.com/ParadoxGuitarist) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
- [Marksman](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT - [Marksman](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
- [Snipe-IT plugin for Jira Service Desk (beta)](https://marketplace.atlassian.com/apps/1220379/snipe-it-for-jira-service-desk-beta?hosting=cloud&tab=overview) - for the upcoming Snipe-IT v5 only - [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira)
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag. - [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit). - [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it. - [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it.
@ -126,6 +128,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") | | [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") |
| [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") | | [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") |
| [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/5553884?v=4" width="110px;"/><br /><sub>JohnnyPicnic</sub>](https://github.com/JohnnyPicnic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JohnnyPicnic "Code") | [<img src="https://avatars.githubusercontent.com/u/8799594?v=4" width="110px;"/><br /><sub>markbrule</sub>](https://github.com/markbrule)<br />[💻](https://github.com/snipe/snipe-it/commits?author=markbrule "Code") | [<img src="https://avatars.githubusercontent.com/u/1962801?v=4" width="110px;"/><br /><sub>Mike Campbell</sub>](https://github.com/mikecmpbll)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikecmpbll "Code") | [<img src="https://avatars.githubusercontent.com/u/11973217?v=4" width="110px;"/><br /><sub>tbrconnect</sub>](https://github.com/tbrconnect)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tbrconnect "Code") | [<img src="https://avatars.githubusercontent.com/u/12447225?v=4" width="110px;"/><br /><sub>kcoyo</sub>](https://github.com/kcoyo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kcoyo "Code") | [<img src="https://avatars.githubusercontent.com/u/494017?v=4" width="110px;"/><br /><sub>Travis Miller</sub>](https://travismiller.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=travismiller "Code") | | [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/5553884?v=4" width="110px;"/><br /><sub>JohnnyPicnic</sub>](https://github.com/JohnnyPicnic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JohnnyPicnic "Code") | [<img src="https://avatars.githubusercontent.com/u/8799594?v=4" width="110px;"/><br /><sub>markbrule</sub>](https://github.com/markbrule)<br />[💻](https://github.com/snipe/snipe-it/commits?author=markbrule "Code") | [<img src="https://avatars.githubusercontent.com/u/1962801?v=4" width="110px;"/><br /><sub>Mike Campbell</sub>](https://github.com/mikecmpbll)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikecmpbll "Code") | [<img src="https://avatars.githubusercontent.com/u/11973217?v=4" width="110px;"/><br /><sub>tbrconnect</sub>](https://github.com/tbrconnect)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tbrconnect "Code") | [<img src="https://avatars.githubusercontent.com/u/12447225?v=4" width="110px;"/><br /><sub>kcoyo</sub>](https://github.com/kcoyo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kcoyo "Code") | [<img src="https://avatars.githubusercontent.com/u/494017?v=4" width="110px;"/><br /><sub>Travis Miller</sub>](https://travismiller.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=travismiller "Code") |
| [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") | [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END --> <!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

154
app.json Normal file
View file

@ -0,0 +1,154 @@
{
"name": "Snipe-IT",
"description": "Open source asset management.",
"keywords": [
"asset management",
"it asset"
],
"website": "https://snipeitapp.com/",
"repository": "https://github.com/snipe/snipe-it",
"logo": "https://pbs.twimg.com/profile_images/976748875733020672/K-HnZCCK_400x400.jpg",
"success_url": "/setup",
"env": {
"APP_ENV": {
"description": "Laravel environment mode. Unless developing the application, this should be production.",
"value": "production"
},
"APP_DEBUG": {
"description": "Laravel debug mode. Unless developing the application or actively debugging a problem, this should be set to false.",
"value": "false"
},
"APP_KEY": {
"description": "A secret key for verifying the integrity of signed cookies. (See either https://snipe-it.readme.io/docs/generate-your-app-key or generate at https://coderstoolbox.online/toolbox/generate-symfony-secret)",
"value": ""
},
"APP_URL": {
"description": "URL where your Snipe-IT install will be available at.",
"value": "https://your-app-name.herokuapp.com"
},
"APP_TIMEZONE": {
"description": "Which timezone do you want to use for your install? (http://php.net/manual/en/timezones.php)",
"value": "UTC"
},
"APP_LOCALE": {
"description": "Which language do you want to use for your install? (https://snipe-it.readme.io/docs/configuration#setting-a-language)",
"value": "en"
},
"MAX_RESULTS": {
"description": "The maximum number of search results that can be returned at one time.",
"value": "500"
},
"MAIL_DRIVER": {
"description": "Mail driver - Generally SMTP on Heroku - https://snipe-it.readme.io/docs/configuration#required-outgoing-mail-settings",
"value": "smtp"
},
"MAIL_HOST": {
"description": "SMTP Server Hostname",
"value": "smtp.your.domain.name"
},
"MAIL_PORT": {
"description": "SMTP Server Port",
"value": "25"
},
"MAIL_USERNAME": {
"description": "SMTP Server Username",
"value": "YOURUSERNAME"
},
"MAIL_PASSWORD": {
"description": "SMTP Server Password",
"value": "YOURPASSWORD"
},
"MAIL_ENCRYPTION": {
"description": "Encryption protocol for email sending.",
"value": "null"
},
"MAIL_FROM_ADDR": {
"description": "Email from address",
"value": "no-reply@domain.name"
},
"MAIL_FROM_NAME": {
"description": "Email from Name",
"value": "Snipe-IT"
},
"MAIL_REPLYTO_ADDR": {
"description": "Email Reply-To address",
"value": "your@domain.name"
},
"MAIL_REPLYTO_NAME": {
"description": "Email Reply-To Name",
"value": "Snipe-IT"
},
"MAIL_AUTO_EMBED": {
"description": "Whether or not to embed images in emails (via CID or base64) versus linking to them.",
"value": "true"
},
"MAIL_AUTO_EMBED_METHOD": {
"description": "Method that should be used for attaching inline images.",
"value": "base64"
},
"SESSION_LIFETIME": {
"description": "Specify the time in minutes that the session should remain valid.",
"value": "12000"
},
"EXPIRE_ON_CLOSE": {
"description": "Specify whether or not the logged in session should be expired when the user closes their browser window.",
"value": "false"
},
"ENCRYPT": {
"description": "Specify whether you wish to use encrypted cookies for your Snipe-IT sessions.",
"value": "true"
},
"COOKIE_NAME": {
"description": "The name of the cookie set by Snipe-IT for session management.",
"value": "snipeit_session"
},
"COOKIE_DOMAIN": {
"description": "The domain name that the session cookie should be sent for.",
"value": "your-app-name.herokuapp.com"
},
"SECURE_COOKIES": {
"description": "Should cookies only be sent for HTTPS connections? Generally true on Heroku.",
"value": "true"
},
"LOGIN_MAX_ATTEMPTS": {
"description": "The maximum number of failed attempts allowed before the user is throttled.",
"value": "5"
},
"LOGIN_LOCKOUT_DURATION": {
"description": "The duration (in seconds) that the user should be blocked from attempting to authenticate again.",
"value": "60"
},
"APP_LOG": {
"description": "Driver to send logs to. (errorlog for stderr)",
"value": "errorlog"
},
"ALLOW_IFRAMING": {
"description": "Allow Snipe-IT to be loaded using an iFrame?",
"value": "false"
},
"GOOGLE_MAPS_API": {
"description": "Include your Google Maps API key here if you'd like Snipe-IT to load maps from Google on your locations and suppliers pages.",
"required": false
},
"BACKUP_ENV": {
"description": "Set this to true if you wish to backup your .env file in your Admin > Backups process.",
"value": "true"
},
"ENABLE_HSTS": {
"description": "Whether or not to send the HSTS security policy header.",
"value": "false"
}
},
"formation": {
"web": {
"quantity": 1,
"size": "free"
}
},
"image": "heroku/php",
"addons": [
"cleardb:ignite",
"heroku-redis:hobby-dev",
"papertrail:choklad"
]
}

View file

@ -2,23 +2,9 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\Accessory;
use App\Models\Asset; use App\Models\Asset;
use App\Models\AssetModel; use App\Models\CustomField;
use App\Models\Category; use Schema;
use App\Models\Company;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\Department;
use App\Models\Depreciation;
use App\Models\Group;
use App\Models\Import;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\Location;
use App\Models\Manufacturer;
use App\Models\Statuslabel;
use App\Models\Supplier;
use DB; use DB;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -29,15 +15,14 @@ class PaveIt extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'snipeit:pave protected $signature = 'snipeit:pave {--force : Skip the interactive yes/no prompt for confirmation}';
{--soft : Perform a "Soft" Delete, leaving all migrations, table structure, and the first user in place.}';
/** /**
* The console command description. * The console command description.
* *
* @var string * @var string
*/ */
protected $description = 'Pave the database to start over. This should ALMOST NEVER BE USED. (It is primarily a quick tool for developers.)'; protected $description = 'Clear the database tables, leaving all migrations, table structure, and the first user in place. (It is primarily a quick tool for developers.) If you want to destroy all tables as well, use php artisan db:wipe.';
/** /**
* Create a new command instance. * Create a new command instance.
@ -56,106 +41,51 @@ class PaveIt extends Command
*/ */
public function handle() public function handle()
{ {
if ($this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE DATA IN YOUR DATABASE. \nThere is NO undo. This WILL destroy ALL of your data. \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) {
if ($this->option('soft')) {
Accessory::getQuery()->delete();
Asset::getQuery()->delete();
Category::getQuery()->delete();
Company::getQuery()->delete();
Component::getQuery()->delete();
Consumable::getQuery()->delete();
Department::getQuery()->delete();
Depreciation::getQuery()->delete();
License::getQuery()->delete();
LicenseSeat::getQuery()->delete();
Location::getQuery()->delete();
Manufacturer::getQuery()->delete();
AssetModel::getQuery()->delete();
Statuslabel::getQuery()->delete();
Supplier::getQuery()->delete();
Group::getQuery()->delete();
Import::getQuery()->delete();
DB::statement('delete from accessories_users'); if (!$this->option('force')) {
DB::statement('delete from asset_logs'); $confirmation = $this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE DATA IN YOUR DATABASE. \nThere is NO undo. This WILL destroy ALL of your data, \nINCLUDING ANY non-Snipe-IT tables you have in this database. \n****************************************************\n\nDo you wish to continue? No backsies! ");
DB::statement('delete from asset_maintenances'); if (!$confirmation) {
DB::statement('delete from login_attempts'); $this->error('ABORTING');
DB::statement('delete from asset_uploads'); exit(-1);
DB::statement('delete from action_logs'); }
DB::statement('delete from checkout_requests'); }
DB::statement('delete from checkout_acceptances');
DB::statement('delete from consumables_users'); // List all the tables in the database so we don't have to worry about missing some as the app grows
DB::statement('delete from custom_field_custom_fieldset'); $tables = DB::connection()->getDoctrineSchemaManager()->listTableNames();
DB::statement('delete from custom_fields');
DB::statement('delete from custom_fieldsets'); $except_tables = [
DB::statement('delete from components_assets'); 'oauth_access_tokens',
DB::statement('delete from kits'); 'oauth_clients',
DB::statement('delete from kits_accessories'); 'oauth_personal_access_clients',
DB::statement('delete from kits_consumables'); 'migrations',
DB::statement('delete from kits_licenses'); 'settings',
DB::statement('delete from kits_models'); 'users',
DB::statement('delete from login_attempts'); ];
DB::statement('delete from models_custom_fields');
DB::statement('delete from permission_groups'); // We only need to find out what these are so we can nuke these columns on the assets table.
DB::statement('delete from password_resets'); $custom_fields = CustomField::get();
DB::statement('delete from requested_assets'); foreach ($custom_fields as $custom_field) {
DB::statement('delete from requests'); $this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
DB::statement('delete from throttle');
DB::statement('delete from users_groups'); if (\Schema::hasColumn('assets', $custom_field->db_column)) {
DB::statement('delete from users WHERE id!=1'); \Schema::table('assets', function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
}
}
foreach ($tables as $table) {
if (in_array($table, $except_tables)) {
$this->info($table. ' is SKIPPED.');
} else { } else {
\DB::statement('drop table IF EXISTS accessories_users'); \DB::statement('truncate '.$table);
\DB::statement('drop table IF EXISTS accessories'); $this->info($table. ' is TRUNCATED.');
\DB::statement('drop table IF EXISTS asset_logs');
\DB::statement('drop table IF EXISTS action_logs');
\DB::statement('drop table IF EXISTS asset_maintenances');
\DB::statement('drop table IF EXISTS asset_uploads');
\DB::statement('drop table IF EXISTS assets');
\DB::statement('drop table IF EXISTS categories');
\DB::statement('drop table IF EXISTS checkout_requests');
\DB::statement('drop table IF EXISTS checkout_acceptances');
\DB::statement('drop table IF EXISTS companies');
\DB::statement('drop table IF EXISTS components');
\DB::statement('drop table IF EXISTS components_assets');
\DB::statement('drop table IF EXISTS consumables_users');
\DB::statement('drop table IF EXISTS consumables');
\DB::statement('drop table IF EXISTS custom_field_custom_fieldset');
\DB::statement('drop table IF EXISTS custom_fields');
\DB::statement('drop table IF EXISTS custom_fieldsets');
\DB::statement('drop table IF EXISTS depreciations');
\DB::statement('drop table IF EXISTS departments');
\DB::statement('drop table IF EXISTS groups');
\DB::statement('drop table IF EXISTS history');
\DB::statement('drop table IF EXISTS kits');
\DB::statement('drop table IF EXISTS kits_accessories');
\DB::statement('drop table IF EXISTS kits_consumables');
\DB::statement('drop table IF EXISTS kits_licenses');
\DB::statement('drop table IF EXISTS kits_models');
\DB::statement('drop table IF EXISTS models_custom_fields');
\DB::statement('drop table IF EXISTS permission_groups');
\DB::statement('drop table IF EXISTS license_seats');
\DB::statement('drop table IF EXISTS licenses');
\DB::statement('drop table IF EXISTS locations');
\DB::statement('drop table IF EXISTS login_attempts');
\DB::statement('drop table IF EXISTS manufacturers');
\DB::statement('drop table IF EXISTS models');
\DB::statement('drop table IF EXISTS migrations');
\DB::statement('drop table IF EXISTS oauth_access_tokens');
\DB::statement('drop table IF EXISTS oauth_auth_codes');
\DB::statement('drop table IF EXISTS oauth_clients');
\DB::statement('drop table IF EXISTS oauth_personal_access_clients');
\DB::statement('drop table IF EXISTS oauth_refresh_tokens');
\DB::statement('drop table IF EXISTS password_resets');
\DB::statement('drop table IF EXISTS requested_assets');
\DB::statement('drop table IF EXISTS requests');
\DB::statement('drop table IF EXISTS settings');
\DB::statement('drop table IF EXISTS status_labels');
\DB::statement('drop table IF EXISTS suppliers');
\DB::statement('drop table IF EXISTS throttle');
\DB::statement('drop table IF EXISTS users_groups');
\DB::statement('drop table IF EXISTS users');
\DB::statement('drop table IF EXISTS imports');
} }
} }
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
\DB::statement('delete from oauth_clients WHERE id > 2');
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
} }
} }

View file

@ -2,6 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -39,6 +40,7 @@ class ResetDemoSettings extends Command
*/ */
public function handle() public function handle()
{ {
$this->info('Resetting the demo settings.'); $this->info('Resetting the demo settings.');
$settings = Setting::first(); $settings = Setting::first();
$settings->per_page = 20; $settings->per_page = 20;
@ -71,6 +73,7 @@ class ResetDemoSettings extends Command
$settings->saml_slo = null; $settings->saml_slo = null;
$settings->saml_custom_settings = null; $settings->saml_custom_settings = null;
$settings->save(); $settings->save();
if ($user = User::where('username', '=', 'admin')->first()) { if ($user = User::where('username', '=', 'admin')->first()) {
@ -78,7 +81,9 @@ class ResetDemoSettings extends Command
$user->save(); $user->save();
} }
\Storage::disk('local_public')->put('snipe-logo.png', file_get_contents(public_path('img/demo/snipe-logo.png'))); \Storage::disk('public')->put('snipe-logo.png', file_get_contents(public_path('img/demo/snipe-logo.png')));
\Storage::disk('local_public')->put('snipe-logo-lg.png', file_get_contents(public_path('img/demo/snipe-logo-lg.png'))); \Storage::disk('public')->put('snipe-logo-lg.png', file_get_contents(public_path('img/demo/snipe-logo-lg.png')));
} }
} }

View file

@ -60,7 +60,7 @@ class RestoreFromBackup extends Command
$za = new ZipArchive(); $za = new ZipArchive();
$errcode = $za->open($filename, ZipArchive::RDONLY); $errcode = $za->open($filename/* , ZipArchive::RDONLY */); // that constant only exists in PHP 7.4 and higher
if ($errcode !== true) { if ($errcode !== true) {
$errors = [ $errors = [
ZipArchive::ER_EXISTS => 'File already exists.', ZipArchive::ER_EXISTS => 'File already exists.',
@ -248,8 +248,13 @@ class RestoreFromBackup extends Command
} }
fclose($pipes[0]); fclose($pipes[0]);
fclose($sql_contents); fclose($sql_contents);
$this->line(stream_get_contents($pipes[1]));
fclose($pipes[1]); fclose($pipes[1]);
$this->error(stream_get_contents($pipes[2]));
fclose($pipes[2]); fclose($pipes[2]);
//wait, have to do fclose() on all pipes first? //wait, have to do fclose() on all pipes first?
$close_results = proc_close($proc_results); $close_results = proc_close($proc_results);
if ($close_results != 0) { if ($close_results != 0) {

View file

@ -54,6 +54,7 @@ class Helper
return $cost; return $cost;
} }
/** /**
* Static colors for pie charts. * Static colors for pie charts.
* *
@ -332,6 +333,8 @@ class Helper
'#92896B', '#92896B',
]; ];
return $colors[$index]; return $colors[$index];
} }
@ -389,6 +392,7 @@ class Helper
return $colors; return $colors;
} }
/** /**
* Format currency using comma for thousands until local info is property used. * Format currency using comma for thousands until local info is property used.
* *
@ -398,6 +402,19 @@ class Helper
*/ */
public static function ParseFloat($floatString) public static function ParseFloat($floatString)
{ {
/*******
*
* WARNING: This does conversions based on *locale* - a Unix-ey-like thing.
*
* Everything else in the system tends to convert based on the Snipe-IT settings
*
* So it's very likely this is *not* what you want - instead look for the new
*
* ParseCurrency($currencyString)
*
* Which should be directly below here
*
*/
$LocaleInfo = localeconv(); $LocaleInfo = localeconv();
$floatString = str_replace(',', '', $floatString); $floatString = str_replace(',', '', $floatString);
$floatString = str_replace($LocaleInfo['decimal_point'], '.', $floatString); $floatString = str_replace($LocaleInfo['decimal_point'], '.', $floatString);
@ -413,6 +430,26 @@ class Helper
return floatval($floatString); return floatval($floatString);
} }
/**
* Format currency using comma or period for thousands, and period or comma for decimal, based on settings.
*
* @author [B. Wetherington] [<bwetherington@grokability.com>]
* @since [v5.2]
* @return Float
*/
public static function ParseCurrency($currencyString) {
$without_currency = str_replace(Setting::getSettings()->default_currency, '', $currencyString); //generally shouldn't come up, since we don't do this in fields, but just in case it does...
if(Setting::getSettings()->digit_separator=='1.234,56') {
//EU format
$without_thousands = str_replace('.', '', $without_currency);
$corrected_decimal = str_replace(',', '.', $without_thousands);
} else {
$without_thousands = str_replace(',', '', $without_currency);
$corrected_decimal = $without_thousands; // decimal is already OK
}
return floatval($corrected_decimal);
}
/** /**
* Get the list of status labels in an array to make a dropdown menu * Get the list of status labels in an array to make a dropdown menu
* *
@ -784,8 +821,8 @@ class Helper
return $string; return $string;
} }
public static function formatStandardApiResponse($status, $payload = null, $messages = null) public static function formatStandardApiResponse($status, $payload = null, $messages = null)
{ {
$array['status'] = $status; $array['status'] = $status;
$array['messages'] = $messages; $array['messages'] = $messages;
@ -869,7 +906,8 @@ class Helper
// If upload_max_size is less, then reduce. Except if upload_max_size is // If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit. // zero, which indicates no limit.
$upload_max = self::parse_size(ini_get('upload_max_filesize')); $upload_max = self::parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
if ($upload_max > 0 && $upload_max < $post_max_size) {
$max_size = ini_get('upload_max_filesize'); $max_size = ini_get('upload_max_filesize');
} }
} }
@ -895,33 +933,33 @@ class Helper
$allowedExtensionMap = [ $allowedExtensionMap = [
// Images // Images
'jpg' => 'fa fa-file-image-o', 'jpg' => 'far fa-image',
'jpeg' => 'fa fa-file-image-o', 'jpeg' => 'far fa-image',
'gif' => 'fa fa-file-image-o', 'gif' => 'far fa-image',
'png' => 'fa fa-file-image-o', 'png' => 'far fa-image',
// word // word
'doc' => 'fa fa-file-word-o', 'doc' => 'far fa-file-word',
'docx' => 'fa fa-file-word-o', 'docx' => 'far fa-file-word',
// Excel // Excel
'xls' => 'fa fa-file-excel-o', 'xls' => 'far fa-file-excel',
'xlsx' => 'fa fa-file-excel-o', 'xlsx' => 'far fa-file-excel',
// archive // archive
'zip' => 'fa fa-file-archive-o', 'zip' => 'fas fa-file-archive',
'rar' => 'fa fa-file-archive-o', 'rar' => 'fas fa-file-archive',
//Text //Text
'txt' => 'fa fa-file-text-o', 'txt' => 'far fa-file-alt',
'rtf' => 'fa fa-file-text-o', 'rtf' => 'far fa-file-alt',
'xml' => 'fa fa-file-text-o', 'xml' => 'far fa-file-alt',
// Misc // Misc
'pdf' => 'fa fa-file-pdf-o', 'pdf' => 'far fa-file-pdf',
'lic' => 'fa fa-file-floppy-o', 'lic' => 'far fa-save',
]; ];
if ($extension && array_key_exists($extension, $allowedExtensionMap)) { if ($extension && array_key_exists($extension, $allowedExtensionMap)) {
return $allowedExtensionMap[$extension]; return $allowedExtensionMap[$extension];
} }
return 'fa fa-file-o'; return 'far fa-file';
} }
public static function show_file_inline($filename) public static function show_file_inline($filename)

View file

@ -75,7 +75,7 @@ class AccessoriesController extends Controller
$accessory->manufacturer_id = request('manufacturer_id'); $accessory->manufacturer_id = request('manufacturer_id');
$accessory->model_number = request('model_number'); $accessory->model_number = request('model_number');
$accessory->purchase_date = request('purchase_date'); $accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost')); $accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
$accessory->qty = request('qty'); $accessory->qty = request('qty');
$accessory->user_id = Auth::user()->id; $accessory->user_id = Auth::user()->id;
$accessory->supplier_id = request('supplier_id'); $accessory->supplier_id = request('supplier_id');
@ -101,6 +101,7 @@ class AccessoriesController extends Controller
*/ */
public function edit($accessoryId = null) public function edit($accessoryId = null)
{ {
if ($item = Accessory::find($accessoryId)) { if ($item = Accessory::find($accessoryId)) {
$this->authorize($item); $this->authorize($item);
@ -108,8 +109,10 @@ class AccessoriesController extends Controller
} }
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist')); return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
} }
/** /**
* Save edited Accessory from form post * Save edited Accessory from form post
* *
@ -137,7 +140,7 @@ class AccessoriesController extends Controller
$accessory->order_number = request('order_number'); $accessory->order_number = request('order_number');
$accessory->model_number = request('model_number'); $accessory->model_number = request('model_number');
$accessory->purchase_date = request('purchase_date'); $accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost'); $accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
$accessory->qty = request('qty'); $accessory->qty = request('qty');
$accessory->supplier_id = request('supplier_id'); $accessory->supplier_id = request('supplier_id');
@ -167,6 +170,7 @@ class AccessoriesController extends Controller
$this->authorize($accessory); $this->authorize($accessory);
if ($accessory->hasUsers() > 0) { if ($accessory->hasUsers() > 0) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()])); return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()]));
} }
@ -184,6 +188,7 @@ class AccessoriesController extends Controller
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.delete.success')); return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.delete.success'));
} }
/** /**
* Returns a view that invokes the ajax table which contains * Returns a view that invokes the ajax table which contains
* the content for the accessory detail view, which is generated in getDataView. * the content for the accessory detail view, which is generated in getDataView.

View file

@ -29,7 +29,22 @@ class AccessoriesController extends Controller
$this->authorize('view', Accessory::class); $this->authorize('view', Accessory::class);
$allowed_columns = ['id', 'name', 'model_number', 'eol', 'notes', 'created_at', 'min_amt', 'company_id']; $allowed_columns = ['id', 'name', 'model_number', 'eol', 'notes', 'created_at', 'min_amt', 'company_id'];
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location'); // This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
// Relations will be handled in query scopes a little further down.
$allowed_columns =
[
'id',
'name',
'model_number',
'eol',
'notes',
'created_at',
'min_amt',
'company_id'
];
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier');
if ($request->filled('search')) { if ($request->filled('search')) {
$accessories = $accessories->TextSearch($request->input('search')); $accessories = $accessories->TextSearch($request->input('search'));
@ -51,6 +66,10 @@ class AccessoriesController extends Controller
$accessories->where('supplier_id', '=', $request->input('supplier_id')); $accessories->where('supplier_id', '=', $request->input('supplier_id'));
} }
if ($request->filled('location_id')) {
$accessories->where('location_id','=',$request->input('location_id'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items. // case we override with the actual count, so we should return 0 items.
$offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0); $offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
@ -59,28 +78,37 @@ class AccessoriesController extends Controller
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; $sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort) { switch ($sort_override) {
case 'category': case 'category':
$accessories = $accessories->OrderCategory($order); $accessories = $accessories->OrderCategory($order);
break; break;
case 'company': case 'company':
$accessories = $accessories->OrderCompany($order); $accessories = $accessories->OrderCompany($order);
break; break;
case 'location':
$accessories = $accessories->OrderLocation($order);
break;
case 'manufacturer':
$accessories = $accessories->OrderManufacturer($order);
break;
case 'supplier':
$accessories = $accessories->OrderSupplier($order);
break;
default: default:
$accessories = $accessories->orderBy($sort, $order); $accessories = $accessories->orderBy($column_sort, $order);
break; break;
} }
$accessories->orderBy($sort, $order);
$total = $accessories->count(); $total = $accessories->count();
$accessories = $accessories->skip($offset)->take($limit)->get(); $accessories = $accessories->skip($offset)->take($limit)->get();
return (new AccessoriesTransformer)->transformAccessories($accessories, $total); return (new AccessoriesTransformer)->transformAccessories($accessories, $total);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -94,12 +122,14 @@ class AccessoriesController extends Controller
$this->authorize('create', Accessory::class); $this->authorize('create', Accessory::class);
$accessory = new Accessory; $accessory = new Accessory;
$accessory->fill($request->all()); $accessory->fill($request->all());
$accessory = $request->handleImages($accessory);
if ($accessory->save()) { if ($accessory->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.create.success'))); return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.create.success')));
} }
return response()->json(Helper::formatStandardApiResponse('error', null, $accessory->getErrors())); return response()->json(Helper::formatStandardApiResponse('error', null, $accessory->getErrors()));
} }
/** /**
@ -118,6 +148,7 @@ class AccessoriesController extends Controller
return (new AccessoriesTransformer)->transformAccessory($accessory); return (new AccessoriesTransformer)->transformAccessory($accessory);
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
@ -134,6 +165,7 @@ class AccessoriesController extends Controller
return (new AccessoriesTransformer)->transformAccessory($accessory); return (new AccessoriesTransformer)->transformAccessory($accessory);
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
@ -165,8 +197,12 @@ class AccessoriesController extends Controller
if ($request->filled('search')) { if ($request->filled('search')) {
$accessory_users = $accessory->users() $accessory_users = $accessory->users()
->where('first_name', 'like', '%'.$request->input('search').'%') ->where(function ($query) use ($request) {
->orWhere('last_name', 'like', '%'.$request->input('search').'%') $search_str = '%' . $request->input('search') . '%';
$query->where('first_name', 'like', $search_str)
->orWhere('last_name', 'like', $search_str)
->orWhere('note', 'like', $search_str);
})
->get(); ->get();
$total = $accessory_users->count(); $total = $accessory_users->count();
} }
@ -174,6 +210,7 @@ class AccessoriesController extends Controller
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total); return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
@ -220,6 +257,7 @@ class AccessoriesController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.delete.success'))); return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.delete.success')));
} }
/** /**
* Save the Accessory checkout information. * Save the Accessory checkout information.
* *
@ -239,7 +277,9 @@ class AccessoriesController extends Controller
$this->authorize('checkout', $accessory); $this->authorize('checkout', $accessory);
if ($accessory->numRemaining() > 0) { if ($accessory->numRemaining() > 0) {
if (! $user = User::find($request->input('assigned_to'))) { if (! $user = User::find($request->input('assigned_to'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkout.user_does_not_exist'))); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkout.user_does_not_exist')));
} }
@ -261,6 +301,7 @@ class AccessoriesController extends Controller
} }
return response()->json(Helper::formatStandardApiResponse('error', null, 'No accessories remaining')); return response()->json(Helper::formatStandardApiResponse('error', null, 'No accessories remaining'));
} }
/** /**
@ -303,15 +344,19 @@ class AccessoriesController extends Controller
} }
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkin.error'))); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkin.error')));
} }
/** /**
* Gets a paginated collection for the select2 menus * Gets a paginated collection for the select2 menus
* *
* @see \App\Http\Transformers\SelectlistTransformer * @see \App\Http\Transformers\SelectlistTransformer
*
*/ */
public function selectlist(Request $request) public function selectlist(Request $request)
{ {
$accessories = Accessory::select([ $accessories = Accessory::select([
'accessories.id', 'accessories.id',
'accessories.name', 'accessories.name',
@ -325,4 +370,5 @@ class AccessoriesController extends Controller
return (new SelectlistTransformer)->transformSelectlist($accessories); return (new SelectlistTransformer)->transformSelectlist($accessories);
} }
} }

View file

@ -21,6 +21,8 @@ use Illuminate\Support\Facades\Input;
*/ */
class AssetMaintenancesController extends Controller class AssetMaintenancesController extends Controller
{ {
/** /**
* Generates the JSON response for asset maintenances listing view. * Generates the JSON response for asset maintenances listing view.
* *
@ -82,10 +84,12 @@ class AssetMaintenancesController extends Controller
$total = $maintenances->count(); $total = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get(); $maintenances = $maintenances->skip($offset)->take($limit)->get();
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total); return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
} }
/** /**
* Validates and stores the new asset maintenance * Validates and stores the new asset maintenance
* *
@ -101,7 +105,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance = new AssetMaintenance(); $assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id'); $assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty'); $assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = e($request->input('cost')); $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
$assetMaintenance->notes = e($request->input('notes')); $assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id'))); $asset = Asset::find(e($request->input('asset_id')));
@ -129,9 +133,11 @@ class AssetMaintenancesController extends Controller
// Was the asset maintenance created? // Was the asset maintenance created?
if ($assetMaintenance->save()) { if ($assetMaintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success'))); return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success')));
} }
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors())); return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
} }
/** /**
@ -155,7 +161,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance->supplier_id = e($request->input('supplier_id')); $assetMaintenance->supplier_id = e($request->input('supplier_id'));
$assetMaintenance->is_warranty = e($request->input('is_warranty')); $assetMaintenance->is_warranty = e($request->input('is_warranty'));
$assetMaintenance->cost = Helper::ParseFloat(e($request->input('cost'))); $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
$assetMaintenance->notes = e($request->input('notes')); $assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(request('asset_id')); $asset = Asset::find(request('asset_id'));
@ -192,6 +198,7 @@ class AssetMaintenancesController extends Controller
// Was the asset maintenance created? // Was the asset maintenance created?
if ($assetMaintenance->save()) { if ($assetMaintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success'))); return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success')));
} }
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors())); return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
@ -218,6 +225,8 @@ class AssetMaintenancesController extends Controller
$assetMaintenance->delete(); $assetMaintenance->delete();
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success'))); return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
} }
/** /**
@ -237,5 +246,6 @@ class AssetMaintenancesController extends Controller
} }
return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance); return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance);
} }
} }

View file

@ -66,7 +66,7 @@ class AssetModelsController extends Controller
->with('category', 'depreciation', 'manufacturer', 'fieldset') ->with('category', 'depreciation', 'manufacturer', 'fieldset')
->withCount('assets as assets_count'); ->withCount('assets as assets_count');
if ($request->filled('status')) { if ($request->input('status')=='deleted') {
$assetmodels->onlyTrashed(); $assetmodels->onlyTrashed();
} }
@ -102,6 +102,7 @@ class AssetModelsController extends Controller
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total); return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -120,8 +121,9 @@ class AssetModelsController extends Controller
if ($assetmodel->save()) { if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success'))); return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
} }
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors())); return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
} }
/** /**
@ -156,6 +158,7 @@ class AssetModelsController extends Controller
return (new AssetsTransformer)->transformAssets($assets, $assets->count()); return (new AssetsTransformer)->transformAssets($assets, $assets->count());
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
@ -184,6 +187,7 @@ class AssetModelsController extends Controller
$assetmodel->fieldset_id = $request->get('custom_fieldset_id'); $assetmodel->fieldset_id = $request->get('custom_fieldset_id');
} }
if ($assetmodel->save()) { if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success'))); return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
} }

View file

@ -2,12 +2,16 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Events\CheckoutableCheckedIn;
use Illuminate\Support\Facades\Gate;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\AssetCheckoutRequest; use App\Http\Requests\AssetCheckoutRequest;
use App\Http\Transformers\AssetsTransformer; use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\DepreciationReportTransformer;
use App\Http\Transformers\LicensesTransformer; use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer; use App\Http\Transformers\SelectlistTransformer;
use App\Models\Actionlog;
use App\Models\Asset; use App\Models\Asset;
use App\Models\AssetModel; use App\Models\AssetModel;
use App\Models\Company; use App\Models\Company;
@ -20,7 +24,6 @@ use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use Input; use Input;
use Paginator; use Paginator;
@ -28,6 +31,7 @@ use Slack;
use Str; use Str;
use TCPDF; use TCPDF;
use Validator; use Validator;
use Route;
/** /**
* This class controls all actions related to assets for * This class controls all actions related to assets for
@ -48,7 +52,30 @@ class AssetsController extends Controller
*/ */
public function index(Request $request, $audit = null) public function index(Request $request, $audit = null)
{ {
\Log::debug(Route::currentRouteName());
/**
* This looks MAD janky (and it is), but the AssetsController@index does a LOT of heavy lifting throughout the
* app. This bit here just makes sure that someone without permission to view assets doesn't
* end up with priv escalations because they asked for a different endpoint.
*
* Since we never gave the specification for which transformer to use before, it should default
* gracefully to just use the AssetTransformer by default, which shouldn't break anything.
*
* It was either this mess, or repeating ALL of the searching and sorting and filtering code,
* which would have been far worse of a mess. *sad face* - snipe (Sept 1, 2021)
*/
if (Route::currentRouteName()=='api.depreciation-report.index') {
$transformer = 'App\Http\Transformers\DepreciationReportTransformer';
$this->authorize('reports.view');
} else {
$transformer = 'App\Http\Transformers\AssetsTransformer';
$this->authorize('index', Asset::class); $this->authorize('index', Asset::class);
}
$settings = Setting::getSettings(); $settings = Setting::getSettings();
$allowed_columns = [ $allowed_columns = [
@ -161,6 +188,8 @@ class AssetsController extends Controller
} }
} }
// This is used by the sidenav, mostly // This is used by the sidenav, mostly
// We switched from using query scopes here because of a Laravel bug // We switched from using query scopes here because of a Laravel bug
@ -230,12 +259,14 @@ class AssetsController extends Controller
} }
if ((! is_null($filter)) && (count($filter)) > 0) { if ((! is_null($filter)) && (count($filter)) > 0) {
$assets->ByFilter($filter); $assets->ByFilter($filter);
} elseif ($request->filled('search')) { } elseif ($request->filled('search')) {
$assets->TextSearch($request->input('search')); $assets->TextSearch($request->input('search'));
} }
// This is kinda gross, but we need to do this because the Bootstrap Tables // This is kinda gross, but we need to do this because the Bootstrap Tables
// API passes custom field ordering as custom_fields.fieldname, and we have to strip // API passes custom field ordering as custom_fields.fieldname, and we have to strip
// that out to let the default sorter below order them correctly on the assets table. // that out to let the default sorter below order them correctly on the assets table.
@ -245,6 +276,7 @@ class AssetsController extends Controller
// in the allowed_columns array) // in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at'; $column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
switch ($sort_override) { switch ($sort_override) {
case 'model': case 'model':
$assets->OrderModels($order); $assets->OrderModels($order);
@ -280,10 +312,28 @@ class AssetsController extends Controller
break; break;
} }
$total = $assets->count(); $total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get(); $assets = $assets->skip($offset)->take($limit)->get();
// dd($assets);
return (new AssetsTransformer)->transformAssets($assets, $total);
/**
* Include additional associated relationships
*/
if ($request->input('components')) {
$assets->loadMissing(['components' => function ($query) {
$query->orderBy('created_at', 'desc');
}]);
}
/**
* Here we're just determining which Transformer (via $transformer) to use based on the
* variables we set earlier on in this method - we default to AssetsTransformer.
*/
return (new $transformer)->transformAssets($assets, $total, $request);
} }
/** /**
@ -294,15 +344,16 @@ class AssetsController extends Controller
* @since [v4.2.1] * @since [v4.2.1]
* @return JsonResponse * @return JsonResponse
*/ */
public function showByTag($tag) public function showByTag(Request $request, $tag)
{ {
if ($asset = Asset::with('assetstatus')->with('assignedTo')->where('asset_tag', $tag)->first()) { if ($asset = Asset::with('assetstatus')->with('assignedTo')->where('asset_tag', $tag)->first()) {
$this->authorize('view', $asset); $this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset); return (new AssetsTransformer)->transformAsset($asset, $request);
} }
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200); return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
} }
/** /**
@ -313,17 +364,19 @@ class AssetsController extends Controller
* @since [v4.2.1] * @since [v4.2.1]
* @return JsonResponse * @return JsonResponse
*/ */
public function showBySerial($serial) public function showBySerial(Request $request, $serial)
{ {
$this->authorize('index', Asset::class); $this->authorize('index', Asset::class);
if ($assets = Asset::with('assetstatus')->with('assignedTo') if ($assets = Asset::with('assetstatus')->with('assignedTo')
->withTrashed()->where('serial', $serial)->get()) { ->withTrashed()->where('serial', $serial)->get()) {
return (new AssetsTransformer)->transformAssets($assets, $assets->count()); return (new AssetsTransformer)->transformAssets($assets, $assets->count());
} }
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200); return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
} }
/** /**
* Returns JSON with information about an asset for detail view. * Returns JSON with information about an asset for detail view.
* *
@ -332,17 +385,19 @@ class AssetsController extends Controller
* @since [v4.0] * @since [v4.0]
* @return JsonResponse * @return JsonResponse
*/ */
public function show($id) public function show(Request $request, $id)
{ {
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed() if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->findOrFail($id)) { ->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->findOrFail($id)) {
$this->authorize('view', $asset); $this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset); return (new AssetsTransformer)->transformAsset($asset, $request->input('components') );
}
} }
public function licenses($id)
}
public function licenses(Request $request, $id)
{ {
$this->authorize('view', Asset::class); $this->authorize('view', Asset::class);
$this->authorize('view', License::class); $this->authorize('view', License::class);
@ -352,15 +407,18 @@ class AssetsController extends Controller
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count()); return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
} }
/** /**
* Gets a paginated collection for the select2 menus * Gets a paginated collection for the select2 menus
* *
* @author [A. Gianotto] [<snipe@snipe.net>] * @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16] * @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer * @see \App\Http\Transformers\SelectlistTransformer
*
*/ */
public function selectlist(Request $request) public function selectlist(Request $request)
{ {
$assets = Company::scopeCompanyables(Asset::select([ $assets = Company::scopeCompanyables(Asset::select([
'assets.id', 'assets.id',
'assets.name', 'assets.name',
@ -379,18 +437,22 @@ class AssetsController extends Controller
$assets = $assets->AssignedSearch($request->input('search')); $assets = $assets->AssignedSearch($request->input('search'));
} }
$assets = $assets->paginate(50); $assets = $assets->paginate(50);
// Loop through and set some custom properties for the transformer to use. // Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where // This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway // they may not have a ->name value but we want to display something anyway
foreach ($assets as $asset) { foreach ($assets as $asset) {
$asset->use_text = $asset->present()->fullName; $asset->use_text = $asset->present()->fullName;
if (($asset->checkedOutToUser()) && ($asset->assigned)) { if (($asset->checkedOutToUser()) && ($asset->assigned)) {
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute(); $asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
} }
if ($asset->assetstatus->getStatuslabelType() == 'pending') { if ($asset->assetstatus->getStatuslabelType() == 'pending') {
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')'; $asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
} }
@ -401,6 +463,7 @@ class AssetsController extends Controller
return (new SelectlistTransformer)->transformSelectlist($assets); return (new SelectlistTransformer)->transformSelectlist($assets);
} }
/** /**
* Accepts a POST request to create a new asset * Accepts a POST request to create a new asset
* *
@ -429,7 +492,7 @@ class AssetsController extends Controller
$asset->depreciate = '0'; $asset->depreciate = '0';
$asset->status_id = $request->get('status_id', 0); $asset->status_id = $request->get('status_id', 0);
$asset->warranty_months = $request->get('warranty_months', null); $asset->warranty_months = $request->get('warranty_months', null);
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost')); $asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost')); // this is the API's store method, so I don't know that I want to do this? Confusing. FIXME (or not?!)
$asset->purchase_date = $request->get('purchase_date', null); $asset->purchase_date = $request->get('purchase_date', null);
$asset->assigned_to = $request->get('assigned_to', null); $asset->assigned_to = $request->get('assigned_to', null);
$asset->supplier_id = $request->get('supplier_id', 0); $asset->supplier_id = $request->get('supplier_id', 0);
@ -478,6 +541,7 @@ class AssetsController extends Controller
} }
} }
$asset->{$field->convertUnicodeDbSlug()} = $field_val; $asset->{$field->convertUnicodeDbSlug()} = $field_val;
} }
} }
@ -504,6 +568,7 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200); return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
} }
/** /**
* Accepts a POST request to update an asset * Accepts a POST request to update an asset
* *
@ -554,6 +619,7 @@ class AssetsController extends Controller
} }
} }
if ($asset->save()) { if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) { if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$location = $target->location_id; $location = $target->location_id;
@ -583,6 +649,7 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
} }
/** /**
* Delete a given asset (mark as deleted). * Delete a given asset (mark as deleted).
* *
@ -610,6 +677,39 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
} }
/**
* Restore a soft-deleted asset.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v5.1.18]
* @return JsonResponse
*/
public function restore($assetId = null)
{
// Get asset information
$asset = Asset::withTrashed()->find($assetId);
$this->authorize('delete', $asset);
if (isset($asset->id)) {
// Restore the asset
Asset::withTrashed()->where('id', $assetId)->restore();
$logaction = new Actionlog();
$logaction->item_type = Asset::class;
$logaction->item_id = $asset->id;
$logaction->created_at = date("Y-m-d H:i:s");
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restored');
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.restore.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/** /**
* Checkout an asset * Checkout an asset
* *
@ -635,12 +735,14 @@ class AssetsController extends Controller
'asset_tag' => $asset->asset_tag, 'asset_tag' => $asset->asset_tag,
]; ];
// This item is checked out to a location // This item is checked out to a location
if (request('checkout_to_type') == 'location') { if (request('checkout_to_type') == 'location') {
$target = Location::find(request('assigned_location')); $target = Location::find(request('assigned_location'));
$asset->location_id = ($target) ? $target->id : ''; $asset->location_id = ($target) ? $target->id : '';
$error_payload['target_id'] = $request->input('assigned_location'); $error_payload['target_id'] = $request->input('assigned_location');
$error_payload['target_type'] = 'location'; $error_payload['target_type'] = 'location';
} elseif (request('checkout_to_type') == 'asset') { } elseif (request('checkout_to_type') == 'asset') {
$target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset')); $target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset'));
$asset->location_id = $target->rtd_location_id; $asset->location_id = $target->rtd_location_id;
@ -648,6 +750,7 @@ class AssetsController extends Controller
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : ''; $asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_asset'); $error_payload['target_id'] = $request->input('assigned_asset');
$error_payload['target_type'] = 'asset'; $error_payload['target_type'] = 'asset';
} elseif (request('checkout_to_type') == 'user') { } elseif (request('checkout_to_type') == 'user') {
// Fetch the target and set the asset's new location_id // Fetch the target and set the asset's new location_id
$target = User::find(request('assigned_user')); $target = User::find(request('assigned_user'));
@ -656,10 +759,14 @@ class AssetsController extends Controller
$error_payload['target_type'] = 'user'; $error_payload['target_type'] = 'user';
} }
if (! isset($target)) { 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')); $checkout_at = request('checkout_at', date('Y-m-d H:i:s'));
$expected_checkin = request('expected_checkin', null); $expected_checkin = request('expected_checkin', null);
$note = request('note', null); $note = request('note', null);
@ -669,10 +776,13 @@ class AssetsController extends Controller
// Wait, why are we doing this? This overrides the stuff we set further up, which makes no sense. // Wait, why are we doing this? This overrides the stuff we set further up, which makes no sense.
// TODO: Follow up here. WTF. Commented out for now. // TODO: Follow up here. WTF. Commented out for now.
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) { // if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
// $asset->location_id = $target->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)) { 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')));
} }
@ -680,6 +790,7 @@ class AssetsController extends Controller
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')));
} }
/** /**
* Checkin an asset * Checkin an asset
* *
@ -694,6 +805,7 @@ class AssetsController extends Controller
$asset = Asset::findOrFail($asset_id); $asset = Asset::findOrFail($asset_id);
$this->authorize('checkin', $asset); $this->authorize('checkin', $asset);
$user = $asset->assignedUser; $user = $asset->assignedUser;
if (is_null($target = $asset->assignedTo)) { if (is_null($target = $asset->assignedTo)) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.already_checked_in'))); return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.already_checked_in')));
@ -720,7 +832,7 @@ class AssetsController extends Controller
} }
if ($asset->save()) { if ($asset->save()) {
$asset->logCheckin($target, e($request->input('note'))); event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note')));
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success'))); return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
} }
@ -728,6 +840,7 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error'))); return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
} }
/** /**
* Mark an asset as audited * Mark an asset as audited
* *
@ -738,6 +851,8 @@ class AssetsController extends Controller
*/ */
public function audit(Request $request) public function audit(Request $request)
{ {
$this->authorize('audit', Asset::class); $this->authorize('audit', Asset::class);
$rules = [ $rules = [
'asset_tag' => 'required', 'asset_tag' => 'required',
@ -755,6 +870,7 @@ class AssetsController extends Controller
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first(); $asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
if ($asset) { if ($asset) {
// We don't want to log this as a normal update, so let's bypass that // We don't want to log this as a normal update, so let's bypass that
$asset->unsetEventDispatcher(); $asset->unsetEventDispatcher();
@ -784,8 +900,15 @@ class AssetsController extends Controller
} }
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.$request->input('asset_tag').' not found')); return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.$request->input('asset_tag').' not found'));
} }
/** /**
* Returns JSON listing of all requestable assets * Returns JSON listing of all requestable assets
* *

View file

@ -47,6 +47,7 @@ class CategoriesController extends Controller
$categories = $categories->skip($offset)->take($limit)->get(); $categories = $categories->skip($offset)->take($limit)->get();
return (new CategoriesTransformer)->transformCategories($categories, $total); return (new CategoriesTransformer)->transformCategories($categories, $total);
} }
/** /**
@ -62,6 +63,7 @@ class CategoriesController extends Controller
$this->authorize('create', Category::class); $this->authorize('create', Category::class);
$category = new Category; $category = new Category;
$category->fill($request->all()); $category->fill($request->all());
$category->category_type = strtolower($request->input('category_type'));
$category = $request->handleImages($category); $category = $request->handleImages($category);
if ($category->save()) { if ($category->save()) {
@ -101,6 +103,7 @@ class CategoriesController extends Controller
$this->authorize('update', Category::class); $this->authorize('update', Category::class);
$category = Category::findOrFail($id); $category = Category::findOrFail($id);
$category->fill($request->all()); $category->fill($request->all());
$category->category_type = strtolower($request->input('category_type'));
$category = $request->handleImages($category); $category = $request->handleImages($category);
if ($category->save()) { if ($category->save()) {

View file

@ -9,6 +9,9 @@ use App\Models\Company;
use App\Models\Component; use App\Models\Component;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use App\Events\CheckoutableCheckedIn;
use App\Events\ComponentCheckedIn;
use App\Models\Asset;
class ComponentsController extends Controller class ComponentsController extends Controller
{ {
@ -23,8 +26,25 @@ class ComponentsController extends Controller
public function index(Request $request) public function index(Request $request)
{ {
$this->authorize('view', Component::class); $this->authorize('view', Component::class);
// This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
// Relations will be handled in query scopes a little further down.
$allowed_columns =
[
'id',
'name',
'min_amt',
'order_number',
'serial',
'purchase_date',
'purchase_cost',
'qty',
'image',
];
$components = Company::scopeCompanyables(Component::select('components.*') $components = Company::scopeCompanyables(Component::select('components.*')
->with('company', 'location', 'category')); ->with('company', 'location', 'category', 'assets'));
if ($request->filled('search')) { if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search')); $components = $components->TextSearch($request->input('search'));
@ -49,11 +69,12 @@ class ComponentsController extends Controller
// Check to make sure the limit is not higher than the max allowed // Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
$allowed_columns = ['id', 'name', 'min_amt', 'order_number', 'serial', 'purchase_date', 'purchase_cost', 'company', 'category', 'qty', 'location', 'image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($sort) { $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 'category': case 'category':
$components = $components->OrderCategory($order); $components = $components->OrderCategory($order);
break; break;
@ -64,7 +85,7 @@ class ComponentsController extends Controller
$components = $components->OrderCompany($order); $components = $components->OrderCompany($order);
break; break;
default: default:
$components = $components->orderBy($sort, $order); $components = $components->orderBy($column_sort, $order);
break; break;
} }
@ -74,6 +95,7 @@ class ComponentsController extends Controller
return (new ComponentsTransformer)->transformComponents($components, $total); return (new ComponentsTransformer)->transformComponents($components, $total);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -178,4 +200,119 @@ class ComponentsController extends Controller
return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total); return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total);
} }
/**
* Validate and checkout the component.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* t
* @since [v5.1.8]
* @param Request $request
* @param int $componentId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function checkout(Request $request, $componentId)
{
// Check if the component exists
if (is_null($component = Component::find($componentId))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist')));
}
$this->authorize('checkout', $component);
if ($component->numRemaining() >= $request->get('assigned_qty')) {
if (!$asset = Asset::find($request->input('assigned_to'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')));
}
// Update the accessory data
$component->assigned_to = $request->input('assigned_to');
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'created_at' => \Carbon::now(),
'assigned_qty' => $request->get('assigned_qty', 1),
'user_id' => \Auth::id(),
'asset_id' => $request->get('assigned_to')
]);
$component->logCheckout($request->input('note'), $asset);
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Not enough components remaining: '.$component->numRemaining().' remaining, '.$request->get('assigned_qty').' requested.'));
}
/**
* Validate and store checkin data.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.1.8]
* @param Request $request
* @param $component_asset_id
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function checkin(Request $request, $component_asset_id)
{
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')));
}
$this->authorize('checkin', $component);
$max_to_checkin = $component_assets->assigned_qty;
if ($max_to_checkin > 1) {
$validator = \Validator::make($request->all(), [
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Checkin quantity must be between 1 and '.$max_to_checkin));
}
}
// 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));
// We have to modify the record to reflect the new qty that's
// actually checked out.
$component_assets->assigned_qty = $qty_remaining_in_checkout;
\Log::debug($component_asset_id.' - '.$qty_remaining_in_checkout.' remaining in record '.$component_assets->id);
\DB::table('components_assets')->where('id',
$component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
// 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) {
\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

@ -25,6 +25,26 @@ class ConsumablesController extends Controller
public function index(Request $request) public function index(Request $request)
{ {
$this->authorize('index', Consumable::class); $this->authorize('index', Consumable::class);
// This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
// Relations will be handled in query scopes a little further down.
$allowed_columns =
[
'id',
'name',
'order_number',
'min_amt',
'purchase_date',
'purchase_cost',
'company',
'category',
'model_number',
'item_no',
'qty',
'image',
];
$consumables = Company::scopeCompanyables( $consumables = Company::scopeCompanyables(
Consumable::select('consumables.*') Consumable::select('consumables.*')
->with('company', 'location', 'category', 'users', 'manufacturer') ->with('company', 'location', 'category', 'users', 'manufacturer')
@ -42,10 +62,19 @@ class ConsumablesController extends Controller
$consumables->where('category_id', '=', $request->input('category_id')); $consumables->where('category_id', '=', $request->input('category_id'));
} }
if ($request->filled('model_number')) {
$consumables->where('model_number','=',$request->input('model_number'));
}
if ($request->filled('manufacturer_id')) { if ($request->filled('manufacturer_id')) {
$consumables->where('manufacturer_id', '=', $request->input('manufacturer_id')); $consumables->where('manufacturer_id', '=', $request->input('manufacturer_id'));
} }
if ($request->filled('location_id')) {
$consumables->where('location_id','=',$request->input('location_id'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items. // case we override with the actual count, so we should return 0 items.
$offset = (($consumables) && ($request->get('offset') > $consumables->count())) ? $consumables->count() : $request->get('offset', 0); $offset = (($consumables) && ($request->get('offset') > $consumables->count())) ? $consumables->count() : $request->get('offset', 0);
@ -55,9 +84,12 @@ class ConsumablesController extends Controller
$allowed_columns = ['id', 'name', 'order_number', 'min_amt', 'purchase_date', 'purchase_cost', 'company', 'category', 'model_number', 'item_no', 'manufacturer', 'location', 'qty', 'image']; $allowed_columns = ['id', 'name', 'order_number', 'min_amt', 'purchase_date', 'purchase_cost', 'company', 'category', 'model_number', 'item_no', 'manufacturer', 'location', 'qty', 'image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc'; $order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($sort) { $sort_override = $request->input('sort');
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
switch ($sort_override) {
case 'category': case 'category':
$consumables = $consumables->OrderCategory($order); $consumables = $consumables->OrderCategory($order);
break; break;
@ -71,7 +103,7 @@ class ConsumablesController extends Controller
$consumables = $consumables->OrderCompany($order); $consumables = $consumables->OrderCompany($order);
break; break;
default: default:
$consumables = $consumables->orderBy($sort, $order); $consumables = $consumables->orderBy($column_sort, $order);
break; break;
} }

View file

@ -3,12 +3,12 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Transformers\LocationsTransformer; use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer; use App\Http\Transformers\SelectlistTransformer;
use App\Models\Location; use App\Models\Location;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -79,6 +79,7 @@ class LocationsController extends Controller
return (new LocationsTransformer)->transformLocations($locations, $total); return (new LocationsTransformer)->transformLocations($locations, $total);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *

View file

@ -47,6 +47,7 @@ class StatuslabelsController extends Controller
return (new StatuslabelsTransformer)->transformStatuslabels($statuslabels, $total); return (new StatuslabelsTransformer)->transformStatuslabels($statuslabels, $total);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -71,6 +72,10 @@ class StatuslabelsController extends Controller
$statuslabel->deployable = $statusType['deployable']; $statuslabel->deployable = $statusType['deployable'];
$statuslabel->pending = $statusType['pending']; $statuslabel->pending = $statusType['pending'];
$statuslabel->archived = $statusType['archived']; $statuslabel->archived = $statusType['archived'];
$statuslabel->color = $request->input('color');
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
$statuslabel->default_label = $request->input('default_label', 0);
if ($statuslabel->save()) { if ($statuslabel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.create.success'))); return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.create.success')));
@ -121,6 +126,9 @@ class StatuslabelsController extends Controller
$statuslabel->deployable = $statusType['deployable']; $statuslabel->deployable = $statusType['deployable'];
$statuslabel->pending = $statusType['pending']; $statuslabel->pending = $statusType['pending'];
$statuslabel->archived = $statusType['archived']; $statuslabel->archived = $statusType['archived'];
$statuslabel->color = $request->input('color');
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
$statuslabel->default_label = $request->input('default_label', 0);
if ($statuslabel->save()) { if ($statuslabel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.update.success'))); return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.update.success')));
@ -164,10 +172,7 @@ class StatuslabelsController extends Controller
{ {
$this->authorize('view', Statuslabel::class); $this->authorize('view', Statuslabel::class);
$statuslabels = Statuslabel::with('assets') $statuslabels = Statuslabel::withCount('assets')->get();
->groupBy('id')
->withCount('assets as assets_count')
->get();
$labels = []; $labels = [];
$points = []; $points = [];
@ -183,8 +188,8 @@ class StatuslabelsController extends Controller
$colors_array[] = $statuslabel->color; $colors_array[] = $statuslabel->color;
} else { } else {
$colors_array[] = Helper::defaultChartColors($default_color_count); $colors_array[] = Helper::defaultChartColors($default_color_count);
$default_color_count++;
} }
$default_color_count++;
} }
} }
@ -228,9 +233,11 @@ class StatuslabelsController extends Controller
$total = $assets->count(); $total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get(); $assets = $assets->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformAssets($assets, $total); return (new AssetsTransformer)->transformAssets($assets, $total);
} }
/** /**
* Returns a boolean response based on whether the status label * Returns a boolean response based on whether the status label
* is one that is deployable. * is one that is deployable.

View file

@ -8,7 +8,7 @@ use App\Http\Transformers\SelectlistTransformer;
use App\Http\Transformers\SuppliersTransformer; use App\Http\Transformers\SuppliersTransformer;
use App\Models\Supplier; use App\Models\Supplier;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class SuppliersController extends Controller class SuppliersController extends Controller
@ -29,6 +29,7 @@ class SuppliersController extends Controller
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes'] ['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes']
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count'); )->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count');
if ($request->filled('search')) { if ($request->filled('search')) {
$suppliers = $suppliers->TextSearch($request->input('search')); $suppliers = $suppliers->TextSearch($request->input('search'));
} }
@ -50,6 +51,7 @@ class SuppliersController extends Controller
return (new SuppliersTransformer)->transformSuppliers($suppliers, $total); return (new SuppliersTransformer)->transformSuppliers($suppliers, $total);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *

View file

@ -68,12 +68,17 @@ class UsersController extends Controller
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count'); ->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count');
$users = Company::scopeCompanyables($users); $users = Company::scopeCompanyables($users);
if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) { if (($request->filled('deleted')) && ($request->input('deleted') == 'true')) {
$users = $users->onlyTrashed(); $users = $users->onlyTrashed();
} elseif (($request->filled('all')) && ($request->input('all') == 'true')) { } elseif (($request->filled('all')) && ($request->input('all') == 'true')) {
$users = $users->withTrashed(); $users = $users->withTrashed();
} }
if ($request->filled('activated')) {
$users = $users->where('users.activated', '=', $request->input('activated'));
}
if ($request->filled('company_id')) { if ($request->filled('company_id')) {
$users = $users->where('users.company_id', '=', $request->input('company_id')); $users = $users->where('users.company_id', '=', $request->input('company_id'));
} }
@ -90,6 +95,30 @@ class UsersController extends Controller
$users = $users->where('users.username', '=', $request->input('username')); $users = $users->where('users.username', '=', $request->input('username'));
} }
if ($request->filled('first_name')) {
$users = $users->where('users.first_name', '=', $request->input('first_name'));
}
if ($request->filled('last_name')) {
$users = $users->where('users.last_name', '=', $request->input('last_name'));
}
if ($request->filled('employee_num')) {
$users = $users->where('users.employee_num', '=', $request->input('employee_num'));
}
if ($request->filled('state')) {
$users = $users->where('users.state', '=', $request->input('state'));
}
if ($request->filled('country')) {
$users = $users->where('users.country', '=', $request->input('country'));
}
if ($request->filled('zip')) {
$users = $users->where('users.zip', '=', $request->input('zip'));
}
if ($request->filled('group_id')) { if ($request->filled('group_id')) {
$users = $users->ByGroup($request->get('group_id')); $users = $users->ByGroup($request->get('group_id'));
} }
@ -98,6 +127,10 @@ class UsersController extends Controller
$users = $users->where('users.department_id', '=', $request->input('department_id')); $users = $users->where('users.department_id', '=', $request->input('department_id'));
} }
if ($request->filled('manager_id')) {
$users = $users->where('users.manager_id','=',$request->input('manager_id'));
}
if ($request->filled('search')) { if ($request->filled('search')) {
$users = $users->TextSearch($request->input('search')); $users = $users->TextSearch($request->input('search'));
} }
@ -112,6 +145,7 @@ class UsersController extends Controller
// Check to make sure the limit is not higher than the max allowed // Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results'); ((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
switch ($request->input('sort')) { switch ($request->input('sort')) {
case 'manager': case 'manager':
$users = $users->OrderManager($order); $users = $users->OrderManager($order);
@ -201,6 +235,8 @@ class UsersController extends Controller
return (new SelectlistTransformer)->transformSelectlist($users); return (new SelectlistTransformer)->transformSelectlist($users);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -259,6 +295,7 @@ class UsersController extends Controller
return (new UsersTransformer)->transformUser($user); return (new UsersTransformer)->transformUser($user);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
@ -274,14 +311,21 @@ class UsersController extends Controller
$user = User::findOrFail($id); $user = User::findOrFail($id);
// This is a janky hack to prevent people from changing admin demo user data on the public demo. /**
// The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder. * This is a janky hack to prevent people from changing admin demo user data on the public demo.
// Thanks, jerks. You are why we can't have nice things. - snipe *
* The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
*
* Thanks, jerks. You are why we can't have nice things. - snipe
*
*/
if ((($id == 1) || ($id == 2)) && (config('app.lock_passwords'))) { if ((($id == 1) || ($id == 2)) && (config('app.lock_passwords'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Permission denied. You cannot update user information via API on the demo.')); return response()->json(Helper::formatStandardApiResponse('error', null, 'Permission denied. You cannot update user information via API on the demo.'));
} }
$user->fill($request->all()); $user->fill($request->all());
if ($user->id == $request->input('manager_id')) { if ($user->id == $request->input('manager_id')) {
@ -305,6 +349,8 @@ class UsersController extends Controller
$user->permissions = $permissions_array; $user->permissions = $permissions_array;
} }
// Update the location of any assets checked out to this user // Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class) Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]); ->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
@ -328,6 +374,7 @@ class UsersController extends Controller
$user->groups()->sync([]); $user->groups()->sync([]);
} }
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update'))); return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
} }
@ -389,13 +436,13 @@ class UsersController extends Controller
* @param $userId * @param $userId
* @return string JSON * @return string JSON
*/ */
public function assets($id) public function assets(Request $request, $id)
{ {
$this->authorize('view', User::class); $this->authorize('view', User::class);
$this->authorize('view', Asset::class); $this->authorize('view', Asset::class);
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get(); $assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count()); return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
} }
/** /**
@ -458,8 +505,9 @@ class UsersController extends Controller
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500); return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
} }
} }
return response()->json(['message' => 'No ID provided'], 500); return response()->json(['message' => 'No ID provided'], 500);
} }
/** /**
@ -474,4 +522,28 @@ class UsersController extends Controller
{ {
return (new UsersTransformer)->transformUser($request->user()); return (new UsersTransformer)->transformUser($request->user());
} }
/**
* Restore a soft-deleted user.
*
* @author [E. Taylor] [<dev@evantaylor.name>]
* @param int $userId
* @since [v6.0.0]
* @return JsonResponse
*/
public function restore($userId = null)
{
// Get asset information
$user = User::withTrashed()->find($userId);
$this->authorize('delete', $user);
if (isset($user->id)) {
// Restore the user
User::withTrashed()->where('id', $userId)->restore();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.restored')));
}
$id = $userId;
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found', compact('id'))), 200);
}
} }

View file

@ -98,7 +98,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance = new AssetMaintenance(); $assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id'); $assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty'); $assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost'); $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
$assetMaintenance->notes = $request->input('notes'); $assetMaintenance->notes = $request->input('notes');
$asset = Asset::find($request->input('asset_id')); $asset = Asset::find($request->input('asset_id'));
@ -204,10 +204,10 @@ class AssetMaintenancesController extends Controller
return static::getInsufficientPermissionsRedirect(); return static::getInsufficientPermissionsRedirect();
} }
$assetMaintenance->supplier_id = e($request->input('supplier_id')); $assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = e($request->input('is_warranty')); $assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = Helper::ParseFloat(e($request->input('cost'))); $assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
$assetMaintenance->notes = e($request->input('notes')); $assetMaintenance->notes = $request->input('notes');
$asset = Asset::find(request('asset_id')); $asset = Asset::find(request('asset_id'));

View file

@ -7,6 +7,8 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\AssetCheckinRequest; use App\Http\Requests\AssetCheckinRequest;
use App\Models\Asset; use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect; use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
@ -109,6 +111,16 @@ class AssetCheckinController extends Controller
$checkin_at = $request->input('checkin_at'); $checkin_at = $request->input('checkin_at');
} }
// Get all pending Acceptances for this asset and delete them
$acceptances = CheckoutAcceptance::pending()->whereHasMorph('checkoutable',
[Asset::class],
function (Builder $query) use ($asset) {
$query->where('id', $asset->id);
})->get();
$acceptances->map(function($acceptance) {
$acceptance->delete();
});
// Was the asset updated? // Was the asset updated?
if ($asset->save()) { if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at)); event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at));

View file

@ -9,6 +9,7 @@ use App\Models\Actionlog;
use App\Models\Asset; use App\Models\Asset;
use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use enshrined\svgSanitize\Sanitizer;
class AssetFilesController extends Controller class AssetFilesController extends Controller
{ {
@ -36,9 +37,29 @@ class AssetFilesController extends Controller
} }
foreach ($request->file('file') as $file) { foreach ($request->file('file') as $file) {
$extension = $file->getClientOriginalExtension(); $extension = $file->getClientOriginalExtension();
$file_name = 'hardware-'.$asset->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension; $file_name = 'hardware-'.$asset->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
// Check for SVG and sanitize it
if ($extension=='svg') {
\Log::debug('This is an SVG');
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::put('private_uploads/assets/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug('Upload no workie :( ');
\Log::debug($e);
}
} else {
Storage::put('private_uploads/assets/'.$file_name, file_get_contents($file)); Storage::put('private_uploads/assets/'.$file_name, file_get_contents($file));
}
$asset->logUpload($file_name, e($request->get('notes'))); $asset->logUpload($file_name, e($request->get('notes')));
} }
@ -127,7 +148,6 @@ class AssetFilesController extends Controller
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success')); return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
} }
$log->delete();
return redirect()->back() return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success')); ->with('success', trans('admin/hardware/message.deletefile.success'));

View file

@ -139,7 +139,7 @@ class AssetsController extends Controller
$asset->depreciate = '0'; $asset->depreciate = '0';
$asset->status_id = request('status_id', 0); $asset->status_id = request('status_id', 0);
$asset->warranty_months = request('warranty_months', null); $asset->warranty_months = request('warranty_months', null);
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost')); $asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost'));
$asset->purchase_date = request('purchase_date', null); $asset->purchase_date = request('purchase_date', null);
$asset->assigned_to = request('assigned_to', null); $asset->assigned_to = request('assigned_to', null);
$asset->supplier_id = request('supplier_id', 0); $asset->supplier_id = request('supplier_id', 0);
@ -296,7 +296,7 @@ class AssetsController extends Controller
$asset->status_id = $request->input('status_id', null); $asset->status_id = $request->input('status_id', null);
$asset->warranty_months = $request->input('warranty_months', null); $asset->warranty_months = $request->input('warranty_months', null);
$asset->purchase_cost = Helper::ParseFloat($request->input('purchase_cost', null)); $asset->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null));
$asset->purchase_date = $request->input('purchase_date', null); $asset->purchase_date = $request->input('purchase_date', null);
$asset->supplier_id = $request->input('supplier_id', null); $asset->supplier_id = $request->input('supplier_id', null);
$asset->expected_checkin = $request->input('expected_checkin', null); $asset->expected_checkin = $request->input('expected_checkin', null);
@ -357,6 +357,7 @@ class AssetsController extends Controller
} }
} }
if ($asset->save()) { if ($asset->save()) {
return redirect()->route('hardware.show', $assetId) return redirect()->route('hardware.show', $assetId)
->with('success', trans('admin/hardware/message.update.success')); ->with('success', trans('admin/hardware/message.update.success'));
@ -828,8 +829,8 @@ class AssetsController extends Controller
Storage::putFileAs($path, $upload, $file_name); Storage::putFileAs($path, $upload, $file_name);
} }
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.audit.success')); return redirect()->to('hardware')->with('success', trans('admin/hardware/message.audit.success'));
} }
} }

View file

@ -32,7 +32,8 @@ class BulkAssetsController extends Controller
return redirect()->back()->with('error', 'No assets selected'); return redirect()->back()->with('error', 'No assets selected');
} }
$asset_ids = array_keys($request->input('ids'));
$asset_ids = array_values(array_unique($request->input('ids')));
if ($request->filled('bulk_actions')) { if ($request->filled('bulk_actions')) {
switch ($request->input('bulk_actions')) { switch ($request->input('bulk_actions')) {
@ -51,7 +52,7 @@ class BulkAssetsController extends Controller
return view('hardware/bulk-delete')->with('assets', $assets); return view('hardware/bulk-delete')->with('assets', $assets);
case 'edit': case 'edit':
return view('hardware/bulk') return view('hardware/bulk')
->with('assets', request('ids')) ->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList()); ->with('statuslabel_list', Helper::statusLabelList());
} }
} }
@ -92,6 +93,7 @@ class BulkAssetsController extends Controller
|| ($request->filled('model_id')) || ($request->filled('model_id'))
) { ) {
foreach ($assets as $assetId) { foreach ($assets as $assetId) {
$this->update_array = []; $this->update_array = [];
$this->conditionallyAddItem('purchase_date') $this->conditionallyAddItem('purchase_date')
@ -104,7 +106,7 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('warranty_months'); ->conditionallyAddItem('warranty_months');
if ($request->filled('purchase_cost')) { if ($request->filled('purchase_cost')) {
$this->update_array['purchase_cost'] = Helper::ParseFloat($request->input('purchase_cost')); $this->update_array['purchase_cost'] = Helper::ParseCurrency($request->input('purchase_cost'));
} }
if ($request->filled('company_id')) { if ($request->filled('company_id')) {

View file

@ -29,6 +29,7 @@ class ForgotPasswordController extends Controller
public function __construct() public function __construct()
{ {
$this->middleware('guest'); $this->middleware('guest');
$this->middleware('throttle:5,1', ['except' => 'showLinkRequestForm']);
} }
/** /**
@ -66,6 +67,10 @@ class ForgotPasswordController extends Controller
* Once we have attempted to send the link, we will examine the response * Once we have attempted to send the link, we will examine the response
* then see the message we need to show to the user. Finally, we'll send out a proper response. * then see the message we need to show to the user. Finally, we'll send out a proper response.
*/ */
$response = null;
try {
$response = $this->broker()->sendResetLink( $response = $this->broker()->sendResetLink(
array_merge( array_merge(
$request->only('username'), $request->only('username'),
@ -73,6 +78,10 @@ class ForgotPasswordController extends Controller
['ldap_import' => '0'] ['ldap_import' => '0']
) )
); );
} catch(\Exception $e) {
\Log::info('Password reset attempt: User '.$request->input('username').'failed with exception: '.$e );
}
if ($response === \Password::RESET_LINK_SENT) { if ($response === \Password::RESET_LINK_SENT) {
\Log::info('Password reset attempt: User '.$request->input('username').' WAS found, password reset sent'); \Log::info('Password reset attempt: User '.$request->input('username').' WAS found, password reset sent');

View file

@ -154,6 +154,9 @@ class LoginController extends Controller
{ {
$header_name = Setting::getSettings()->login_remote_user_header_name ?: 'REMOTE_USER'; $header_name = Setting::getSettings()->login_remote_user_header_name ?: 'REMOTE_USER';
$remote_user = $request->server($header_name); $remote_user = $request->server($header_name);
if (!isset($remote_user)) {
$remote_user = $request->server('REDIRECT_'.$header_name);
}
if (Setting::getSettings()->login_remote_user_enabled == '1' && isset($remote_user) && ! empty($remote_user)) { if (Setting::getSettings()->login_remote_user_enabled == '1' && isset($remote_user) && ! empty($remote_user)) {
Log::debug("Authenticating via HTTP header $header_name."); Log::debug("Authenticating via HTTP header $header_name.");

View file

@ -80,6 +80,11 @@ class BulkAssetModelsController extends Controller
$update_array['depreciation_id'] = $request->input('depreciation_id'); $update_array['depreciation_id'] = $request->input('depreciation_id');
} }
if ($request->filled('requestable') != '') {
$update_array['requestable'] = $request->input('requestable');
}
if (count($update_array) > 0) { if (count($update_array) > 0) {
AssetModel::whereIn('id', $models_raw_array)->update($update_array); AssetModel::whereIn('id', $models_raw_array)->update($update_array);

View file

@ -39,6 +39,8 @@ trait CheckInOutRequest
switch (request('checkout_to_type')) { switch (request('checkout_to_type')) {
case 'location': case 'location':
$asset->location_id = $target->id; $asset->location_id = $target->id;
Asset::where('assigned_type', 'App\Models\Asset')->where('assigned_to', $asset->id)
->update(['location_id' => $asset->location_id]);
break; break;
case 'asset': case 'asset':
$asset->location_id = $target->rtd_location_id; $asset->location_id = $target->rtd_location_id;

View file

@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use App\Models\Company; use App\Models\Company;
use App\Models\Component; use App\Models\Component;
use App\Helpers\Helper;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -36,6 +37,7 @@ class ComponentsController extends Controller
return view('components/index'); return view('components/index');
} }
/** /**
* Returns a form to create a new component. * Returns a form to create a new component.
* *
@ -75,7 +77,7 @@ class ComponentsController extends Controller
$component->min_amt = $request->input('min_amt', null); $component->min_amt = $request->input('min_amt', null);
$component->serial = $request->input('serial', null); $component->serial = $request->input('serial', null);
$component->purchase_date = $request->input('purchase_date', null); $component->purchase_date = $request->input('purchase_date', null);
$component->purchase_cost = $request->input('purchase_cost', null); $component->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null));
$component->qty = $request->input('qty'); $component->qty = $request->input('qty');
$component->user_id = Auth::id(); $component->user_id = Auth::id();
@ -109,6 +111,7 @@ class ComponentsController extends Controller
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist')); return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
} }
/** /**
* Return a view to edit a component. * Return a view to edit a component.
* *
@ -147,7 +150,7 @@ class ComponentsController extends Controller
$component->min_amt = $request->input('min_amt'); $component->min_amt = $request->input('min_amt');
$component->serial = $request->input('serial'); $component->serial = $request->input('serial');
$component->purchase_date = $request->input('purchase_date'); $component->purchase_date = $request->input('purchase_date');
$component->purchase_cost = request('purchase_cost'); $component->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
$component->qty = $request->input('qty'); $component->qty = $request->input('qty');
$component = $request->handleImages($component); $component = $request->handleImages($component);

View file

@ -75,7 +75,7 @@ class ConsumablesController extends Controller
$consumable->model_number = $request->input('model_number'); $consumable->model_number = $request->input('model_number');
$consumable->item_no = $request->input('item_no'); $consumable->item_no = $request->input('item_no');
$consumable->purchase_date = $request->input('purchase_date'); $consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat($request->input('purchase_cost')); $consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
$consumable->qty = $request->input('qty'); $consumable->qty = $request->input('qty');
$consumable->user_id = Auth::id(); $consumable->user_id = Auth::id();
@ -138,7 +138,7 @@ class ConsumablesController extends Controller
$consumable->model_number = $request->input('model_number'); $consumable->model_number = $request->input('model_number');
$consumable->item_no = $request->input('item_no'); $consumable->item_no = $request->input('item_no');
$consumable->purchase_date = $request->input('purchase_date'); $consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseFloat($request->input('purchase_cost')); $consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
$consumable->qty = Helper::ParseFloat($request->input('qty')); $consumable->qty = Helper::ParseFloat($request->input('qty'));
$consumable = $request->handleImages($consumable); $consumable = $request->handleImages($consumable);

View file

@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Models\License; use App\Models\License;
use App\Models\LicenseSeat; use App\Models\LicenseSeat;
use App\Models\User; use App\Models\User;
use App\Models\Asset;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Input;
@ -80,7 +81,12 @@ class LicenseCheckinController extends Controller
// Ooops.. something went wrong // Ooops.. something went wrong
return redirect()->back()->withInput()->withErrors($validator); return redirect()->back()->withInput()->withErrors($validator);
} }
if($licenseSeat->assigned_to != null){
$return_to = User::find($licenseSeat->assigned_to); $return_to = User::find($licenseSeat->assigned_to);
} else {
$return_to = Asset::find($licenseSeat->asset_id);
}
// Update the asset data // Update the asset data
$licenseSeat->assigned_to = null; $licenseSeat->assigned_to = null;

View file

@ -7,10 +7,10 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\AssetFileRequest; use App\Http\Requests\AssetFileRequest;
use App\Models\Actionlog; use App\Models\Actionlog;
use App\Models\License; use App\Models\License;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use enshrined\svgSanitize\Sanitizer;
class LicenseFilesController extends Controller class LicenseFilesController extends Controller
{ {
@ -37,26 +37,39 @@ class LicenseFilesController extends Controller
Storage::makeDirectory('private_uploads/licenses', 775); Storage::makeDirectory('private_uploads/licenses', 775);
} }
$upload_success = false;
foreach ($request->file('file') as $file) { foreach ($request->file('file') as $file) {
$file_name = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$file->getClientOriginalExtension())).'.'.$file->getClientOriginalExtension();
$upload_success = $file->storeAs('private_uploads/licenses', $file_name); $extension = $file->getClientOriginalExtension();
// $upload_success = $file->storeAs('private_uploads/licenses/'.$file_name, $file); $file_name = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
// Check for SVG and sanitize it
if ($extension == 'svg') {
\Log::debug('This is an SVG');
\Log::debug($file_name);
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::put('private_uploads/licenses/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug('Upload no workie :( ');
\Log::debug($e);
}
} else {
Storage::put('private_uploads/licenses/'.$file_name, file_get_contents($file));
}
//Log the upload to the log //Log the upload to the log
$license->logUpload($file_name, e($request->input('notes'))); $license->logUpload($file_name, e($request->input('notes')));
} }
// This being called from a modal seems to confuse redirect()->back()
// It thinks we should go to the dashboard. As this is only used
// from the modal at present, hardcode the redirect. Longterm
// maybe we evaluate something else.
if ($upload_success) {
return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
}
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.error')); return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
} }
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles')); return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
@ -153,6 +166,7 @@ class LicenseFilesController extends Controller
} }
return StorageHelper::downloader($file); return StorageHelper::downloader($file);
} }
} }
} }

View file

@ -86,7 +86,7 @@ class LicensesController extends Controller
$license->name = $request->input('name'); $license->name = $request->input('name');
$license->notes = $request->input('notes'); $license->notes = $request->input('notes');
$license->order_number = $request->input('order_number'); $license->order_number = $request->input('order_number');
$license->purchase_cost = $request->input('purchase_cost'); $license->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
$license->purchase_date = $request->input('purchase_date'); $license->purchase_date = $request->input('purchase_date');
$license->purchase_order = $request->input('purchase_order'); $license->purchase_order = $request->input('purchase_order');
$license->purchase_order = $request->input('purchase_order'); $license->purchase_order = $request->input('purchase_order');
@ -159,11 +159,11 @@ class LicensesController extends Controller
$license->expiration_date = $request->input('expiration_date'); $license->expiration_date = $request->input('expiration_date');
$license->license_email = $request->input('license_email'); $license->license_email = $request->input('license_email');
$license->license_name = $request->input('license_name'); $license->license_name = $request->input('license_name');
$license->maintained = $request->input('maintained', 0); $license->maintained = $request->input('maintained',0);
$license->name = $request->input('name'); $license->name = $request->input('name');
$license->notes = $request->input('notes'); $license->notes = $request->input('notes');
$license->order_number = $request->input('order_number'); $license->order_number = $request->input('order_number');
$license->purchase_cost = $request->input('purchase_cost'); $license->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
$license->purchase_date = $request->input('purchase_date'); $license->purchase_date = $request->input('purchase_date');
$license->purchase_order = $request->input('purchase_order'); $license->purchase_order = $request->input('purchase_order');
$license->reassignable = $request->input('reassignable', 0); $license->reassignable = $request->input('reassignable', 0);

View file

@ -12,8 +12,10 @@ use App\Models\CustomField;
use App\Models\Depreciation; use App\Models\Depreciation;
use App\Models\License; use App\Models\License;
use App\Models\Setting; use App\Models\Setting;
use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
use Input; use Input;
@ -73,7 +75,7 @@ class ReportsController extends Controller
trans('admin/accessories/general.remaining'), trans('admin/accessories/general.remaining'),
]; ];
$header = array_map('trim', $header); $header = array_map('trim', $header);
$rows[] = implode($header, ', '); $rows[] = implode(', ', $header);
// Row per accessory // Row per accessory
foreach ($accessories as $accessory) { foreach ($accessories as $accessory) {
@ -83,10 +85,10 @@ class ReportsController extends Controller
$row[] = e($accessory->total); $row[] = e($accessory->total);
$row[] = e($accessory->remaining); $row[] = e($accessory->remaining);
$rows[] = implode($row, ','); $rows[] = implode(',', $row);
} }
$csv = implode($rows, "\n"); $csv = implode("\n", $rows);
$response = Response::make($csv, 200); $response = Response::make($csv, 200);
$response->header('Content-Type', 'text/csv'); $response->header('Content-Type', 'text/csv');
$response->header('Content-disposition', 'attachment;filename=report.csv'); $response->header('Content-disposition', 'attachment;filename=report.csv');
@ -105,11 +107,7 @@ class ReportsController extends Controller
{ {
$this->authorize('reports.view'); $this->authorize('reports.view');
$depreciations = Depreciation::get(); $depreciations = Depreciation::get();
// Grab all the assets return view('reports/depreciation')->with('depreciations',$depreciations);
$assets = Asset::with('assignedTo', 'assetstatus', 'defaultLoc', 'location', 'company', 'model.category', 'model.depreciation')
->orderBy('created_at', 'DESC')->get();
return view('reports/depreciation', compact('assets'))->with('depreciations', $depreciations);
} }
/** /**
@ -190,6 +188,7 @@ class ReportsController extends Controller
die; die;
} }
/** /**
* Displays audit report. * Displays audit report.
* *
@ -204,6 +203,7 @@ class ReportsController extends Controller
return view('reports/audit'); return view('reports/audit');
} }
/** /**
* Displays activity report. * Displays activity report.
* *
@ -274,6 +274,7 @@ class ReportsController extends Controller
} }
} }
$row = [ $row = [
$actionlog->created_at, $actionlog->created_at,
($actionlog->user) ? e($actionlog->user->getFullNameAttribute()) : '', ($actionlog->user) ? e($actionlog->user->getFullNameAttribute()) : '',
@ -297,9 +298,11 @@ class ReportsController extends Controller
'Content-Disposition' => 'attachment; filename="activity-report-'.date('Y-m-d-his').'.csv"', 'Content-Disposition' => 'attachment; filename="activity-report-'.date('Y-m-d-his').'.csv"',
]); ]);
return $response; return $response;
} }
/** /**
* Displays license report * Displays license report
* *
@ -343,7 +346,7 @@ class ReportsController extends Controller
]; ];
$header = array_map('trim', $header); $header = array_map('trim', $header);
$rows[] = implode($header, ', '); $rows[] = implode(', ', $header);
// Row per license // Row per license
foreach ($licenses as $license) { foreach ($licenses as $license) {
@ -357,10 +360,11 @@ class ReportsController extends Controller
$row[] = ($license->depreciation != '') ? '' : e($license->depreciation->name); $row[] = ($license->depreciation != '') ? '' : e($license->depreciation->name);
$row[] = '"'.Helper::formatCurrencyOutput($license->purchase_cost).'"'; $row[] = '"'.Helper::formatCurrencyOutput($license->purchase_cost).'"';
$rows[] = implode($row, ','); $rows[] = implode(',', $row);
} }
$csv = implode($rows, "\n");
$csv = implode("\n", $rows);
$response = Response::make($csv, 200); $response = Response::make($csv, 200);
$response->header('Content-Type', 'text/csv'); $response->header('Content-Type', 'text/csv');
$response->header('Content-disposition', 'attachment;filename=report.csv'); $response->header('Content-disposition', 'attachment;filename=report.csv');
@ -412,6 +416,7 @@ class ReportsController extends Controller
$header = []; $header = [];
if ($request->filled('company')) { if ($request->filled('company')) {
$header[] = trans('general.company'); $header[] = trans('general.company');
} }
@ -485,6 +490,7 @@ class ReportsController extends Controller
$header[] = trans('general.zip'); $header[] = trans('general.zip');
} }
if ($request->filled('assigned_to')) { if ($request->filled('assigned_to')) {
$header[] = trans('admin/hardware/table.checkoutto'); $header[] = trans('admin/hardware/table.checkoutto');
$header[] = trans('general.type'); $header[] = trans('general.type');
@ -548,8 +554,9 @@ class ReportsController extends Controller
$header[] = trans('general.notes'); $header[] = trans('general.notes');
} }
foreach ($customfields as $customfield) { foreach ($customfields as $customfield) {
if (e($request->input($customfield->db_column_name())) == '1') { if ($request->input($customfield->db_column_name()) == '1') {
$header[] = $customfield->name; $header[] = $customfield->name;
} }
} }
@ -625,8 +632,8 @@ class ReportsController extends Controller
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) { if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]); $assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
} }
$assets->orderBy('assets.id', 'ASC')->chunk(20, function ($assets) use ($handle, $customfields, $request) { $assets->orderBy('assets.id', 'ASC')->chunk(20, function ($assets) use ($handle, $customfields, $request) {
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']; $executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
\Log::debug('Walking results: '.$executionTime); \Log::debug('Walking results: '.$executionTime);
$count = 0; $count = 0;
@ -865,7 +872,7 @@ class ReportsController extends Controller
]; ];
$header = array_map('trim', $header); $header = array_map('trim', $header);
$rows[] = implode($header, ','); $rows[] = implode(',', $header);
foreach ($assetMaintenances as $assetMaintenance) { foreach ($assetMaintenances as $assetMaintenance) {
$row = []; $row = [];
@ -883,12 +890,12 @@ class ReportsController extends Controller
$improvementTime = intval($assetMaintenance->asset_maintenance_time); $improvementTime = intval($assetMaintenance->asset_maintenance_time);
} }
$row[] = $improvementTime; $row[] = $improvementTime;
$row[] = trans('general.currency').Helper::formatCurrencyOutput($assetMaintenance->cost); $row[] = trans('general.currency') . Helper::formatCurrencyOutput($assetMaintenance->cost);
$rows[] = implode($row, ','); $rows[] = implode(',', $row);
} }
// spit out a csv // spit out a csv
$csv = implode($rows, "\n"); $csv = implode("\n", $rows);
$response = Response::make($csv, 200); $response = Response::make($csv, 200);
$response->header('Content-Type', 'text/csv'); $response->header('Content-Type', 'text/csv');
$response->header('Content-disposition', 'attachment;filename=report.csv'); $response->header('Content-disposition', 'attachment;filename=report.csv');
@ -900,42 +907,124 @@ class ReportsController extends Controller
* getAssetAcceptanceReport * getAssetAcceptanceReport
* *
* @return mixed * @return mixed
* @throws \Illuminate\Auth\Access\AuthorizationException
* @author Vincent Sposato <vincent.sposato@gmail.com> * @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0 * @version v1.0
*/ */
public function getAssetAcceptanceReport() public function getAssetAcceptanceReport($deleted = false)
{ {
$this->authorize('reports.view'); $this->authorize('reports.view');
$showDeleted = $deleted == 'deleted';
/** /**
* Get all assets with pending checkout acceptances * Get all assets with pending checkout acceptances
*/ */
$acceptances = CheckoutAcceptance::pending()->get(); if($showDeleted) {
$acceptances = CheckoutAcceptance::pending()->withTrashed()->with(['assignedTo' , 'checkoutable.assignedTo', 'checkoutable.model'])->get();
} else {
$acceptances = CheckoutAcceptance::pending()->with(['assignedTo' => function ($query) {
$query->withTrashed();
}, 'checkoutable.assignedTo', 'checkoutable.model'])->get();
}
$assetsForReport = $acceptances $assetsForReport = $acceptances
->filter(function ($acceptance) { ->filter(function ($acceptance) {
return $acceptance->checkoutable_type == \App\Models\Asset::class; return $acceptance->checkoutable_type == \App\Models\Asset::class;
}) })
->map(function ($acceptance) { ->map(function($acceptance) {
return $acceptance->checkoutable; return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
}); });
return view('reports/unaccepted_assets', compact('assetsForReport')); return view('reports/unaccepted_assets', compact('assetsForReport','showDeleted' ));
} }
/** /**
* exportAssetAcceptanceReport * sentAssetAcceptanceReminder
*
* @param integer|null $acceptanceId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @version v1.0
*/
public function sentAssetAcceptanceReminder($acceptanceId = null)
{
$this->authorize('reports.view');
if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) {
// Redirect to the unaccepted assets report page with error
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
}
$assetItem = $acceptance->checkoutable;
$logItem = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get()[0];
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)
);
}
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.reminder_sent'));
}
/**
* sentAssetAcceptanceReminder
*
* @param integer|null $acceptanceId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @version v1.0
*/
public function deleteAssetAcceptance($acceptanceId = null)
{
$this->authorize('reports.view');
if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) {
// Redirect to the unaccepted assets report page with error
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
}
if($acceptance->delete()) {
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.acceptance_deleted'));
} else {
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.deletion_failed'));
}
}
/**
* Exports the AssetAcceptance report to CSV
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
* @author Vincent Sposato <vincent.sposato@gmail.com> * @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0 * @version v1.0
*/ */
public function exportAssetAcceptanceReport() public function postAssetAcceptanceReport($deleted = false)
{ {
$this->authorize('reports.view'); $this->authorize('reports.view');
// Grab all the improvements $showDeleted = $deleted == 'deleted';
$assetsForReport = Actionlog::whereIn('id', $this->getAssetsNotAcceptedYet())
->get(); /**
* Get all assets with pending checkout acceptances
*/
if($showDeleted) {
$acceptances = CheckoutAcceptance::pending()->withTrashed()->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model'])->get();
} else {
$acceptances = CheckoutAcceptance::pending()->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model'])->get();
}
$assetsForReport = $acceptances
->filter(function($acceptance) {
return $acceptance->checkoutable_type == 'App\Models\Asset';
})
->map(function($acceptance) {
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
});
$rows = []; $rows = [];
@ -948,20 +1037,20 @@ class ReportsController extends Controller
]; ];
$header = array_map('trim', $header); $header = array_map('trim', $header);
$rows[] = implode($header, ','); $rows[] = implode(',', $header);
foreach ($assetsForReport as $assetItem) { foreach ($assetsForReport as $item) {
$row = []; $row = [ ];
$row[] = str_replace(',', '', e($assetItem->assetlog->model->category->name)); $row[] = str_replace(',', '', e($item['assetItem']->model->category->name));
$row[] = str_replace(',', '', e($assetItem->assetlog->model->name)); $row[] = str_replace(',', '', e($item['assetItem']->model->name));
$row[] = str_replace(',', '', e($assetItem->assetlog->present()->name())); $row[] = str_replace(',', '', e($item['assetItem']->name));
$row[] = str_replace(',', '', e($assetItem->assetlog->asset_tag)); $row[] = str_replace(',', '', e($item['assetItem']->asset_tag));
$row[] = str_replace(',', '', e($assetItem->assetlog->assignedTo->present()->name())); $row[] = str_replace(',', '', e(($item['acceptance']->assignedTo) ? $item['acceptance']->assignedTo->present()->name() : trans('admin/reports/general.deleted_user')));
$rows[] = implode($row, ','); $rows[] = implode(',', $row);
} }
// spit out a csv // spit out a csv
$csv = implode($rows, "\n"); $csv = implode("\n", $rows);
$response = Response::make($csv, 200); $response = Response::make($csv, 200);
$response->header('Content-Type', 'text/csv'); $response->header('Content-Type', 'text/csv');
$response->header('Content-disposition', 'attachment;filename=report.csv'); $response->header('Content-disposition', 'attachment;filename=report.csv');

View file

@ -360,6 +360,7 @@ class SettingsController extends Controller
return redirect()->back()->withInput()->withErrors($setting->getErrors()); return redirect()->back()->withInput()->withErrors($setting->getErrors());
} }
/** /**
* Return a form to allow a super admin to update settings. * Return a form to allow a super admin to update settings.
* *
@ -418,6 +419,7 @@ class SettingsController extends Controller
$setting = $request->handleImages($setting, 600, 'email_logo', '', 'email_logo'); $setting = $request->handleImages($setting, 600, 'email_logo', '', 'email_logo');
if ('1' == $request->input('clear_email_logo')) { if ('1' == $request->input('clear_email_logo')) {
Storage::disk('public')->delete($setting->email_logo); Storage::disk('public')->delete($setting->email_logo);
$setting->email_logo = null; $setting->email_logo = null;
@ -431,6 +433,7 @@ class SettingsController extends Controller
$setting->label_logo = null; $setting->label_logo = null;
} }
// If the user wants to clear the favicon... // If the user wants to clear the favicon...
if ($request->hasFile('favicon')) { if ($request->hasFile('favicon')) {
$favicon_image = $favicon_upload = $request->file('favicon'); $favicon_image = $favicon_upload = $request->file('favicon');
@ -449,6 +452,7 @@ class SettingsController extends Controller
Storage::disk('public')->put($favicon_file_name, file_get_contents($request->file('favicon'))); Storage::disk('public')->put($favicon_file_name, file_get_contents($request->file('favicon')));
} }
// Remove Current image if exists // Remove Current image if exists
if (($setting->favicon) && (file_exists($favicon_file_name))) { if (($setting->favicon) && (file_exists($favicon_file_name))) {
Storage::disk('public')->delete($favicon_file_name); Storage::disk('public')->delete($favicon_file_name);
@ -516,6 +520,7 @@ class SettingsController extends Controller
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min'); $setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
$setting->pwd_secure_complexity = ''; $setting->pwd_secure_complexity = '';
if ($request->filled('pwd_secure_complexity')) { if ($request->filled('pwd_secure_complexity')) {
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity')); $setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
} }
@ -928,9 +933,14 @@ class SettingsController extends Controller
$setting->ldap_jobtitle = $request->input('ldap_jobtitle'); $setting->ldap_jobtitle = $request->input('ldap_jobtitle');
$setting->ldap_country = $request->input('ldap_country'); $setting->ldap_country = $request->input('ldap_country');
$setting->ldap_dept = $request->input('ldap_dept'); $setting->ldap_dept = $request->input('ldap_dept');
$setting->ldap_client_tls_cert = $request->input('ldap_client_tls_cert');
$setting->ldap_client_tls_key = $request->input('ldap_client_tls_key');
} }
if ($setting->save()) { if ($setting->save()) {
$setting->update_client_side_cert_files();
return redirect()->route('settings.ldap.index') return redirect()->route('settings.ldap.index')
->with('success', trans('admin/settings/message.update.success')); ->with('success', trans('admin/settings/message.update.success'));
} }

View file

@ -33,7 +33,8 @@ class BulkUsersController extends Controller
// Make sure there were users selected // Make sure there were users selected
if (($request->filled('ids')) && (count($request->input('ids')) > 0)) { if (($request->filled('ids')) && (count($request->input('ids')) > 0)) {
// Get the list of affected users // Get the list of affected users
$users = User::whereIn('id', array_keys(request('ids'))) $user_raw_array = request('ids');
$users = User::whereIn('id', $user_raw_array)
->with('groups', 'assets', 'licenses', 'accessories')->get(); ->with('groups', 'assets', 'licenses', 'accessories')->get();
if ($request->input('bulk_actions') == 'edit') { if ($request->input('bulk_actions') == 'edit') {

View file

@ -10,6 +10,8 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Support\Facades\Storage;
class UserFilesController extends Controller class UserFilesController extends Controller
{ {
@ -38,12 +40,32 @@ class UserFilesController extends Controller
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles')); return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
} }
foreach ($files as $file) { foreach ($files as $file) {
$extension = $file->getClientOriginalExtension(); $extension = $file->getClientOriginalExtension();
$filename = 'user-'.$user->id.'-'.str_random(8); $file_name = 'user-'.$user->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
$filename .= '-'.str_slug($file->getClientOriginalName()).'.'.$extension;
if (! $file->move($destinationPath, $filename)) {
return redirect()->back()->with('error', trans('admin/users/message.upload.invalidfiles')); // Check for SVG and sanitize it
if ($extension == 'svg') {
\Log::debug('This is an SVG');
\Log::debug($file_name);
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::put('private_uploads/users/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug('Upload no workie :( ');
\Log::debug($e);
} }
} else {
Storage::put('private_uploads/users/'.$file_name, file_get_contents($file));
}
//Log the uploaded file to the log //Log the uploaded file to the log
$logAction = new Actionlog(); $logAction = new Actionlog();
$logAction->item_id = $user->id; $logAction->item_id = $user->id;
@ -51,8 +73,8 @@ class UserFilesController extends Controller
$logAction->user_id = Auth::id(); $logAction->user_id = Auth::id();
$logAction->note = $request->input('notes'); $logAction->note = $request->input('notes');
$logAction->target_id = null; $logAction->target_id = null;
$logAction->created_at = date('Y-m-d H:i:s'); $logAction->created_at = date("Y-m-d H:i:s");
$logAction->filename = $filename; $logAction->filename = $file_name;
$logAction->action_type = 'uploaded'; $logAction->action_type = 'uploaded';
if (! $logAction->save()) { if (! $logAction->save()) {
@ -63,8 +85,9 @@ class UserFilesController extends Controller
// dd($logActions); // dd($logActions);
return redirect()->back()->with('success', trans('admin/users/message.upload.success')); return redirect()->back()->with('success', trans('admin/users/message.upload.success'));
} }
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles')); return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
} }
/** /**
@ -97,6 +120,7 @@ class UserFilesController extends Controller
$error = trans('admin/users/message.user_not_found', ['id' => $userId]); $error = trans('admin/users/message.user_not_found', ['id' => $userId]);
// Redirect to the licence management page // Redirect to the licence management page
return redirect()->route('users.index')->with('error', $error); return redirect()->route('users.index')->with('error', $error);
} }
/** /**
@ -128,4 +152,5 @@ class UserFilesController extends Controller
// Redirect to the licence management page // Redirect to the licence management page
return redirect()->route('users.index')->with('error', $error); return redirect()->route('users.index')->with('error', $error);
} }
} }

View file

@ -23,6 +23,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\CheckForDebug::class, \App\Http\Middleware\CheckForDebug::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\SecurityHeaders::class, \App\Http\Middleware\SecurityHeaders::class,
\App\Http\Middleware\PreventBackHistory::class,
]; ];

View file

@ -15,27 +15,21 @@ class CustomFieldSetDefaultValuesForModel extends Component
public $fields; public $fields;
public $model_id; public $model_id;
public function __construct()
{
\Log::info("INSTANTIATING A THING!!!"); // WORKS!
}
public function foo()
{
\Log::info("Uh, foo?");
}
public function mount() public function mount()
{ {
$this->fieldset_id = AssetModel::find($this->model_id)->fieldset_id; $this->model = AssetModel::find($this->model_id); // It's possible to do some clever route-model binding here, but let's keep it simple, shall we?
\Log::error("Mount at least fired, that's got to count for something, yeah?"); //WORKS! YAY! $this->fieldset_id = $this->model->fieldset_id;
$this->fields = CustomFieldset::find($this->fieldset_id)->fields;
$this->add_default_values = ( $this->model->defaultValues->count() > 0);
}
public function updatedFieldsetId()
{
$this->fields = CustomFieldset::find($this->fieldset_id)->fields;
} }
public function render() public function render()
{ {
// return 'fart<div>Hi: {{ $this->add_default_values }} yeah?</div>';
return view('livewire.custom-field-set-default-values-for-model'); return view('livewire.custom-field-set-default-values-for-model');
} }
} }

View file

@ -1,21 +0,0 @@
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\CustomFieldset;
class CustomFieldsForFieldset extends Component
{
public $fieldset_id;
public $fields;
public function render()
{
if($this->fieldset_id) {
$this->fields = CustomFieldset::find($this->fieldset_id)->fields()->get();
}
return view('livewire.custom-fields-for-fieldset');
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use Closure;
class PreventBackHistory
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$headers = [
'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate',
'Pragma' => 'no-cache',
'Expires' => 'Sun, 02 Jan 1990 00:00:00 GMT'
];
$response = $next($request);
foreach($headers as $key => $value) {
$response->headers->set($key, $value);
}
return $response;
}
}

View file

@ -21,7 +21,7 @@ class AssetFileRequest extends Request
*/ */
public function rules() public function rules()
{ {
$max_file_size = \App\Helpers\Helper::file_upload_max_size(); $max_file_size = Helper::file_upload_max_size();
return [ return [
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,webp|max:'.$max_file_size, 'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,webp|max:'.$max_file_size,

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Traits;
trait TwoColumnUniqueUndeletedTrait
{
/**
* Prepare a unique_ids rule, adding a model identifier if required.
*
* @param array $parameters
* @param string $field
* @return string
*/
protected function prepareTwoColumnUniqueUndeletedRule($parameters, $field)
{
$column = $parameters[0];
$value = $this->{$parameters[0]};
if ($this->exists) {
return 'two_column_unique_undeleted:'.$this->table.','.$this->getKey().','.$column.','.$value;
}
return 'two_column_unique_undeleted:'.$this->table.',0,'.$column.','.$value;
}
}

View file

@ -24,7 +24,7 @@ class ActionlogsTransformer
{ {
$icon = $actionlog->present()->icon(); $icon = $actionlog->present()->icon();
if ($actionlog->filename != '') { if ($actionlog->filename != '') {
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename)); $icon = e(Helper::filetype_icon($actionlog->filename));
} }
// This is necessary since we can't escape special characters within a JSON object // This is necessary since we can't escape special characters within a JSON object
@ -75,7 +75,7 @@ class ActionlogsTransformer
[ [
'url' => route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]), 'url' => route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]),
'filename' => $actionlog->filename, 'filename' => $actionlog->filename,
'inlineable' => (bool) \App\Helpers\Helper::show_file_inline($actionlog->filename), 'inlineable' => (bool) Helper::show_file_inline($actionlog->filename),
] : null, ] : null,
'item' => ($actionlog->item) ? [ 'item' => ($actionlog->item) ? [
@ -104,10 +104,10 @@ class ActionlogsTransformer
'type' => e($actionlog->targetType()), 'type' => e($actionlog->targetType()),
] : null, ] : null,
'note' => ($actionlog->note) ? e($actionlog->note) : null, 'note' => ($actionlog->note) ? e($actionlog->note): null,
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature]) : null, 'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta : null, 'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime') : null, 'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
]; ];

View file

@ -83,12 +83,13 @@ class AssetsTransformer
'user_can_checkout' => (bool) $asset->availableForCheckout(), 'user_can_checkout' => (bool) $asset->availableForCheckout(),
]; ];
if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) { if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) {
$fields_array = []; $fields_array = [];
foreach ($asset->model->fieldset->fields as $field) { foreach ($asset->model->fieldset->fields as $field) {
if ($field->isFieldDecryptable($asset->{$field->convertUnicodeDbSlug()})) { if ($field->isFieldDecryptable($asset->{$field->convertUnicodeDbSlug()})) {
$decrypted = \App\Helpers\Helper::gracefulDecrypt($field, $asset->{$field->convertUnicodeDbSlug()}); $decrypted = Helper::gracefulDecrypt($field, $asset->{$field->convertUnicodeDbSlug()});
$value = (Gate::allows('superadmin')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted')); $value = (Gate::allows('superadmin')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted'));
$fields_array[$field->name] = [ $fields_array[$field->name] = [
@ -96,12 +97,15 @@ class AssetsTransformer
'value' => $value, 'value' => $value,
'field_format' => $field->format, 'field_format' => $field->format,
]; ];
} else { } else {
$fields_array[$field->name] = [ $fields_array[$field->name] = [
'field' => $field->convertUnicodeDbSlug(), 'field' => $field->convertUnicodeDbSlug(),
'value' => $asset->{$field->convertUnicodeDbSlug()}, 'value' => $asset->{$field->convertUnicodeDbSlug()},
'field_format' => $field->format, 'field_format' => $field->format,
]; ];
} }
$array['custom_fields'] = $fields_array; $array['custom_fields'] = $fields_array;
} }
@ -112,7 +116,7 @@ class AssetsTransformer
$permissions_array['available_actions'] = [ $permissions_array['available_actions'] = [
'checkout' => Gate::allows('checkout', Asset::class), 'checkout' => Gate::allows('checkout', Asset::class),
'checkin' => Gate::allows('checkin', Asset::class), 'checkin' => Gate::allows('checkin', Asset::class),
'clone' => Gate::allows('create', Asset::class), 'clone' => false,
'restore' => false, 'restore' => false,
'update' => (bool) Gate::allows('update', Asset::class), 'update' => (bool) Gate::allows('update', Asset::class),
'delete' => ($asset->assigned_to == '' && Gate::allows('delete', Asset::class)), 'delete' => ($asset->assigned_to == '' && Gate::allows('delete', Asset::class)),
@ -129,6 +133,29 @@ class AssetsTransformer
]; ];
} }
if (request('components')=='true') {
if ($asset->components) {
$array['components'] = [];
foreach ($asset->components as $component) {
$array['components'][] = [
'id' => $component->id,
'pivot_id' => $component->pivot->id,
'name' => $component->name,
'qty' => $component->pivot->assigned_qty,
'price_cost' => $component->purchase_cost,
'purchase_total' => $component->purchase_cost * $component->pivot->assigned_qty,
'checkout_date' => Helper::getFormattedDateObject($component->pivot->created_at, 'datetime') ,
];
}
}
}
$array += $permissions_array; $array += $permissions_array;
return $array; return $array;
@ -160,6 +187,7 @@ class AssetsTransformer
] : null; ] : null;
} }
public function transformRequestedAssets(Collection $assets, $total) public function transformRequestedAssets(Collection $assets, $total)
{ {
$array = []; $array = [];
@ -192,7 +220,8 @@ class AssetsTransformer
]; ];
$array += $permissions_array; $array += $permissions_array;
return $array; return $array;
} }
} }

View file

@ -29,6 +29,7 @@ class CategoriesTransformer
'image' => ($category->image) ? Storage::disk('public')->url('categories/'.e($category->image)) : null, 'image' => ($category->image) ? Storage::disk('public')->url('categories/'.e($category->image)) : null,
'category_type' => ucwords(e($category->category_type)), 'category_type' => ucwords(e($category->category_type)),
'has_eula' => ($category->getEula() ? true : false), 'has_eula' => ($category->getEula() ? true : false),
'use_default_eula' => ($category->use_default_eula=='1' ? true : false),
'eula' => ($category->getEula()), 'eula' => ($category->getEula()),
'checkin_email' => ($category->checkin_email == '1'), 'checkin_email' => ($category->checkin_email == '1'),
'require_acceptance' => ($category->require_acceptance == '1'), 'require_acceptance' => ($category->require_acceptance == '1'),

View file

@ -0,0 +1,120 @@
<?php
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Asset;
use Illuminate\Database\Eloquent\Collection;
/**
* This tranformer looks like it's extraneous, since we return as much or more
* info in the AssetsTransformer, but we want to flatten these results out so that they
* don't dislose more information than we want. Folks with depreciation powers don't necessaily
* have the right to see additional info, and inspecting the API call here could disclose
* info they're not supposed to see.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.2.0]
*/
class DepreciationReportTransformer
{
public function transformAssets(Collection $assets, $total)
{
$array = array();
foreach ($assets as $asset) {
$array[] = self::transformAsset($asset);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformAsset(Asset $asset)
{
/**
* Set some default values here
*/
$purchase_cost = null;
$depreciated_value = null;
$monthly_depreciation = null;
$diff = null;
$checkout_target = null;
/**
* If there is a location set and a currency set, use that for display
*/
if ($asset->location && $asset->location->currency) {
$purchase_cost_currency = $asset->location->currency;
} else {
$purchase_cost_currency = \App\Models\Setting::getSettings()->default_currency;
}
/**
* If there is a NOT an empty purchase cost (meaning not null or '' but it *could* be zero),
* format the purchase cost. We coould do this inline in the transformer, but we need that value
* for the other calculations that come after, like diff, etc.
*/
if ($asset->purchase_cost!='') {
$purchase_cost = $asset->purchase_cost;
}
/**
* Override the previously set null values if there is a valid model and associated depreciation
*/
if (($asset->model) && ($asset->model->depreciation)) {
$depreciated_value = Helper::formatCurrencyOutput($asset->getDepreciatedValue());
$monthly_depreciation = Helper::formatCurrencyOutput(($asset->model->eol > 0 ? ($asset->purchase_cost / $asset->model->eol) : 0));
$diff = Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
}
if ($asset->assigned) {
$checkout_target = $asset->assigned->name;
if ($asset->checkedOutToUser()) {
$checkout_target = $asset->assigned->getFullNameAttribute();
}
}
$array = [
'company' => ($asset->company) ? e($asset->company->name) : null,
'name' => e($asset->name),
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
'model' => ($asset->model) ? e($asset->model->name) : null,
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'eol' => ($asset->purchase_date!='') ? Helper::getFormattedDateObject($asset->present()->eol_date(), 'date') : null ,
'status_label' => ($asset->assetstatus) ? e($asset->assetstatus->name) : null,
'status' => ($asset->assetstatus) ? e($asset->present()->statusMeta) : null,
'category' => (($asset->model) && ($asset->model->category)) ? e($asset->model->category->name) : null,
'manufacturer' => (($asset->model) && ($asset->model->manufacturer)) ? e($asset->model->manufacturer->name) : null,
'supplier' => ($asset->supplier) ? e($asset->supplier->name) : null,
'notes' => ($asset->notes) ? e($asset->notes) : null,
'order_number' => ($asset->order_number) ? e($asset->order_number) : null,
'location' => ($asset->location) ? e($asset->location->name) : null,
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
'currency' => $purchase_cost_currency,
'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($asset->purchase_cost),
'book_value' => Helper::formatCurrencyOutput($depreciated_value),
'monthly_depreciation' => $monthly_depreciation,
'checked_out_to' => $checkout_target,
'diff' => Helper::formatCurrencyOutput($diff),
'number_of_months' => ($asset->model && $asset->model->depreciation) ? e($asset->model->depreciation->months) : null,
'depreciation' => (($asset->model) && ($asset->model->depreciation)) ? e($asset->model->depreciation->name) : null,
];
return $array;
}
public function transformAssetsDatatable($assets)
{
return (new DatatablesTransformer)->transformDatatables($assets);
}
}

View file

@ -25,6 +25,7 @@ class DepreciationsTransformer
'id' => (int) $depreciation->id, 'id' => (int) $depreciation->id,
'name' => e($depreciation->name), 'name' => e($depreciation->name),
'months' => $depreciation->months.' '.trans('general.months'), 'months' => $depreciation->months.' '.trans('general.months'),
'depreciation_min' => $depreciation->depreciation_min,
'created_at' => Helper::getFormattedDateObject($depreciation->created_at, 'datetime'), 'created_at' => Helper::getFormattedDateObject($depreciation->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($depreciation->updated_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($depreciation->updated_at, 'datetime'),
'depreciation_min' =>($depreciation->depreciation_min), 'depreciation_min' =>($depreciation->depreciation_min),

View file

@ -31,8 +31,9 @@ class LicensesTransformer
'purchase_order' => e($license->purchase_order), 'purchase_order' => e($license->purchase_order),
'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'), 'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'),
'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'), 'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'),
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id, 'name'=> e($license->depreciation->name)] : null, 'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
'purchase_cost' => e($license->purchase_cost), 'purchase_cost' => Helper::formatCurrencyOutput($license->purchase_cost),
'purchase_cost_numeric' => $license->purchase_cost,
'notes' => e($license->notes), 'notes' => e($license->notes),
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'), 'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
'seats' => (int) $license->seats, 'seats' => (int) $license->seats,
@ -65,4 +66,7 @@ class LicensesTransformer
{ {
return (new DatatablesTransformer)->transformDatatables($licenses); return (new DatatablesTransformer)->transformDatatables($licenses);
} }
} }

View file

@ -14,7 +14,7 @@ class AccessoryImporter extends ItemImporter
protected function handle($row) protected function handle($row)
{ {
parent::handle($row); // TODO: Change the autogenerated stub parent::handle($row); // TODO: Change the autogenerated stub
$this->createAccessoryIfNotExists(); $this->createAccessoryIfNotExists($row);
} }
/** /**
@ -23,7 +23,7 @@ class AccessoryImporter extends ItemImporter
* @author Daniel Melzter * @author Daniel Melzter
* @since 3.0 * @since 3.0
*/ */
public function createAccessoryIfNotExists() public function createAccessoryIfNotExists($row)
{ {
$accessory = Accessory::where('name', $this->item['name'])->first(); $accessory = Accessory::where('name', $this->item['name'])->first();
if ($accessory) { if ($accessory) {
@ -34,6 +34,7 @@ class AccessoryImporter extends ItemImporter
} }
$this->log('Updating Accessory'); $this->log('Updating Accessory');
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");
$accessory->update($this->sanitizeItemForUpdating($accessory)); $accessory->update($this->sanitizeItemForUpdating($accessory));
$accessory->save(); $accessory->save();
@ -41,6 +42,7 @@ class AccessoryImporter extends ItemImporter
} }
$this->log('No Matching Accessory, Creating a new one'); $this->log('No Matching Accessory, Creating a new one');
$accessory = new Accessory(); $accessory = new Accessory();
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");
$accessory->fill($this->sanitizeItemForStoring($accessory)); $accessory->fill($this->sanitizeItemForStoring($accessory));
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything. //FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.

View file

@ -101,7 +101,7 @@ class ItemImporter extends Importer
return $this->createOrFetchUser($row); return $this->createOrFetchUser($row);
} }
if ($this->item['checkout_class'] === 'location') { if (strtolower($this->item['checkout_class']) === 'location') {
return Location::findOrFail($this->createOrFetchLocation($this->findCsvMatch($row, 'checkout_location'))); return Location::findOrFail($this->createOrFetchLocation($this->findCsvMatch($row, 'checkout_location')));
} }

View file

@ -74,6 +74,7 @@ class UserImporter extends ItemImporter
return; return;
} }
// This needs to be applied after the update logic, otherwise we'll overwrite user passwords // This needs to be applied after the update logic, otherwise we'll overwrite user passwords
// Issue #5408 // Issue #5408
$this->item['password'] = bcrypt($this->tempPassword); $this->item['password'] = bcrypt($this->tempPassword);
@ -106,6 +107,7 @@ class UserImporter extends ItemImporter
} }
$this->logError($user, 'User'); $this->logError($user, 'User');
return;
} }
/** /**
@ -118,13 +120,15 @@ class UserImporter extends ItemImporter
*/ */
public function createOrFetchDepartment($department_name) public function createOrFetchDepartment($department_name)
{ {
if (is_null($department_name) || $department_name == ''){
return null;
}
$department = Department::where(['name' => $department_name])->first(); $department = Department::where(['name' => $department_name])->first();
if ($department) { if ($department) {
$this->log('A matching department '.$department_name.' already exists'); $this->log('A matching department ' . $department_name . ' already exists');
return $department->id; return $department->id;
} else {
return null;
} }
$department = new department(); $department = new department();
@ -132,16 +136,15 @@ class UserImporter extends ItemImporter
$department->user_id = $this->user_id; $department->user_id = $this->user_id;
if ($department->save()) { if ($department->save()) {
$this->log('department '.$department_name.' was created'); $this->log('department ' . $department_name . ' was created');
return $department->id; return $department->id;
} }
$this->logError($department, 'Department'); $this->logError($department, 'Department');
return null; return null;
} }
public function sendWelcome($send = true) public function sendWelcome($send = true)
{ {
$this->send_welcome = $send; $this->send_welcome = $send;

View file

@ -72,6 +72,15 @@ class CheckoutableListener
/** /**
* Send the appropriate notification * Send the appropriate notification
*/ */
$acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id)
->where('assigned_to_id', $event->checkedOutTo->id)
->get();
foreach($acceptances as $acceptance){
if($acceptance->isPending()){
$acceptance->delete();
}
}
\Log::debug('checked out to a user'); \Log::debug('checked out to a user');
if (! $event->checkedOutTo->locale) { if (! $event->checkedOutTo->locale) {
\Log::debug('Use default settings locale'); \Log::debug('Use default settings locale');
@ -144,6 +153,8 @@ class CheckoutableListener
// $model = get_class($event->checkoutable); // $model = get_class($event->checkoutable);
$notificationClass = null; $notificationClass = null;
switch (get_class($event->checkoutable)) { switch (get_class($event->checkoutable)) {

View file

@ -92,9 +92,12 @@ class Accessory extends SnipeModel
'supplier_id', 'supplier_id',
'image', 'image',
'qty', 'qty',
'min_amt',
'requestable', 'requestable',
]; ];
/** /**
* Establishes the accessory -> supplier relationship * Establishes the accessory -> supplier relationship
* *
@ -201,6 +204,7 @@ class Accessory extends SnipeModel
return $this->assetlog()->where('action_type', '=', 'checkout')->take(1); return $this->assetlog()->where('action_type', '=', 'checkout')->take(1);
} }
/** /**
* Sets the full image url * Sets the full image url
* *
@ -373,4 +377,17 @@ class Accessory extends SnipeModel
{ {
return $query->leftJoin('manufacturers', 'accessories.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order); return $query->leftJoin('manufacturers', 'accessories.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
} }
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'accessories.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
}
} }

View file

@ -29,6 +29,8 @@ class Asset extends Depreciable
{ {
protected $presenter = \App\Presenters\AssetPresenter::class; protected $presenter = \App\Presenters\AssetPresenter::class;
use CompanyableTrait;
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait; use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait;
const LOCATION = 'location'; const LOCATION = 'location';
@ -68,6 +70,17 @@ class Asset extends Depreciable
protected $injectUniqueIdentifier = true; protected $injectUniqueIdentifier = true;
// We set these as protected dates so that they will be easily accessible via Carbon // We set these as protected dates so that they will be easily accessible via Carbon
protected $dates = [
'created_at',
'updated_at',
'deleted_at',
'purchase_date',
'last_checkout',
'expected_checkin',
'last_audit_date',
'next_audit_date'
];
protected $casts = [ protected $casts = [
'purchase_date' => 'datetime', 'purchase_date' => 'datetime',
@ -169,6 +182,7 @@ class Asset extends Depreciable
'model.manufacturer' => ['name'], 'model.manufacturer' => ['name'],
]; ];
/** /**
* This handles the custom field validation for assets * This handles the custom field validation for assets
* *
@ -187,6 +201,7 @@ class Asset extends Depreciable
return parent::save($params); return parent::save($params);
} }
public function getDisplayNameAttribute() public function getDisplayNameAttribute()
{ {
return $this->present()->name(); return $this->present()->name();
@ -212,6 +227,7 @@ class Asset extends Depreciable
return null; return null;
} }
/** /**
* Establishes the asset -> company relationship * Establishes the asset -> company relationship
* *
@ -242,7 +258,8 @@ class Asset extends Depreciable
// The asset status is not archived and is deployable // The asset status is not archived and is deployable
if (($this->assetstatus) && ($this->assetstatus->archived == '0') if (($this->assetstatus) && ($this->assetstatus->archived == '0')
&& ($this->assetstatus->deployable == '1')) { && ($this->assetstatus->deployable == '1'))
{
return true; return true;
} }
} }
@ -346,6 +363,7 @@ class Asset extends Depreciable
return $this->rules; return $this->rules;
} }
/** /**
* Establishes the asset -> depreciation relationship * Establishes the asset -> depreciation relationship
* *
@ -358,6 +376,7 @@ class Asset extends Depreciable
return $this->model->belongsTo(\App\Models\Depreciation::class, 'depreciation_id'); return $this->model->belongsTo(\App\Models\Depreciation::class, 'depreciation_id');
} }
/** /**
* Get components assigned to this asset * Get components assigned to this asset
* *
@ -367,9 +386,10 @@ class Asset extends Depreciable
*/ */
public function components() public function components()
{ {
return $this->belongsToMany(\App\Models\Component::class, 'components_assets', 'asset_id', 'component_id')->withPivot('id', 'assigned_qty')->withTrashed(); return $this->belongsToMany('\App\Models\Component', 'components_assets', 'asset_id', 'component_id')->withPivot('id', 'assigned_qty', 'created_at')->withTrashed();
} }
/** /**
* Get depreciation attribute from associated asset model * Get depreciation attribute from associated asset model
* *
@ -386,6 +406,7 @@ class Asset extends Depreciable
} }
} }
/** /**
* Get uploads for this asset * Get uploads for this asset
* *
@ -395,8 +416,8 @@ class Asset extends Depreciable
*/ */
public function uploads() public function uploads()
{ {
return $this->hasMany(\App\Models\Actionlog::class, 'item_id') return $this->hasMany('\App\Models\Actionlog', 'item_id')
->where('item_type', '=', self::class) ->where('item_type', '=', Asset::class)
->where('action_type', '=', 'uploaded') ->where('action_type', '=', 'uploaded')
->whereNotNull('filename') ->whereNotNull('filename')
->orderBy('created_at', 'desc'); ->orderBy('created_at', 'desc');
@ -443,6 +464,7 @@ class Asset extends Depreciable
return $this->morphMany(self::class, 'assigned', 'assigned_type', 'assigned_to')->withTrashed(); return $this->morphMany(self::class, 'assigned', 'assigned_type', 'assigned_to')->withTrashed();
} }
/** /**
* Get the asset's location based on the assigned user * Get the asset's location based on the assigned user
* *
@ -471,6 +493,7 @@ class Asset extends Depreciable
if ($this->assignedTo) { if ($this->assignedTo) {
return $this->assignedTo; return $this->assignedTo;
} }
} }
if ($this->assignedType() == self::USER) { if ($this->assignedType() == self::USER) {
if (($this->assignedTo) && $this->assignedTo->userLoc) { if (($this->assignedTo) && $this->assignedTo->userLoc) {
@ -529,6 +552,7 @@ class Asset extends Depreciable
return false; return false;
} }
/** /**
* Get the asset's logs * Get the asset's logs
* *
@ -588,6 +612,7 @@ class Asset extends Depreciable
->withTrashed(); ->withTrashed();
} }
/** /**
* Get maintenances for this asset * Get maintenances for this asset
* *
@ -613,6 +638,8 @@ class Asset extends Depreciable
return $this->belongsTo(\App\Models\User::class, 'user_id'); return $this->belongsTo(\App\Models\User::class, 'user_id');
} }
/** /**
* Establishes the asset -> status relationship * Establishes the asset -> status relationship
* *
@ -658,6 +685,7 @@ class Asset extends Depreciable
->get(); ->get();
} }
/** /**
* Establishes the asset -> assigned licenses relationship * Establishes the asset -> assigned licenses relationship
* *
@ -706,6 +734,8 @@ class Asset extends Depreciable
return $this->belongsTo(\App\Models\Location::class, 'location_id'); return $this->belongsTo(\App\Models\Location::class, 'location_id');
} }
/** /**
* Get the next autoincremented asset tag * Get the next autoincremented asset tag
* *
@ -717,6 +747,7 @@ class Asset extends Depreciable
{ {
$settings = \App\Models\Setting::getSettings(); $settings = \App\Models\Setting::getSettings();
if ($settings->auto_increment_assets == '1') { if ($settings->auto_increment_assets == '1') {
$temp_asset_tag = \DB::table('assets') $temp_asset_tag = \DB::table('assets')
->where('physical', '=', '1') ->where('physical', '=', '1')
@ -735,6 +766,7 @@ class Asset extends Depreciable
} }
} }
/** /**
* Get the next base number for the auto-incrementer. * Get the next base number for the auto-incrementer.
* *
@ -746,23 +778,28 @@ class Asset extends Depreciable
*/ */
public static function nextAutoIncrement($assets) public static function nextAutoIncrement($assets)
{ {
$max = 1; $max = 1;
foreach ($assets as $asset) { foreach ($assets as $asset) {
$results = preg_match("/\d+$/", $asset['asset_tag'], $matches); $results = preg_match("/\d+$/", $asset['asset_tag'], $matches);
if ($results) { if ($results)
{
$number = $matches[0]; $number = $matches[0];
if ($number > $max) { if ($number > $max)
{
$max = $number; $max = $number;
} }
} }
} }
return $max + 1;
} }
/** /**
* Add zerofilling based on Settings * Add zerofilling based on Settings
* *
@ -802,6 +839,7 @@ class Asset extends Depreciable
if (($this->model) && ($this->model->category)) { if (($this->model) && ($this->model->category)) {
return $this->model->category->require_acceptance; return $this->model->category->require_acceptance;
} }
} }
/** /**
@ -829,6 +867,7 @@ class Asset extends Depreciable
return false; return false;
} }
/** /**
* ----------------------------------------------- * -----------------------------------------------
* BEGIN QUERY SCOPES * BEGIN QUERY SCOPES
@ -854,11 +893,13 @@ class Asset extends Depreciable
}); });
foreach ($terms as $term) { foreach ($terms as $term) {
$query = $query $query = $query
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%", "%$term%"]); ->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%"]);
} }
/** /**
@ -870,6 +911,7 @@ class Asset extends Depreciable
}); });
foreach ($terms as $term) { foreach ($terms as $term) {
$query = $query->orWhere('assets_locations.name', 'LIKE', '%'.$term.'%'); $query = $query->orWhere('assets_locations.name', 'LIKE', '%'.$term.'%');
} }
@ -883,11 +925,13 @@ class Asset extends Depreciable
foreach ($terms as $term) { foreach ($terms as $term) {
$query = $query->orWhere('assigned_assets.name', 'LIKE', '%'.$term.'%'); $query = $query->orWhere('assigned_assets.name', 'LIKE', '%'.$term.'%');
} }
return $query; return $query;
} }
/** /**
* Query builder scope for hardware * Query builder scope for hardware
* *
@ -895,6 +939,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeHardware($query) public function scopeHardware($query)
{ {
return $query->where('physical', '=', '1'); return $query->where('physical', '=', '1');
@ -907,6 +952,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopePending($query) public function scopePending($query)
{ {
return $query->whereHas('assetstatus', function ($query) { return $query->whereHas('assetstatus', function ($query) {
@ -916,6 +962,7 @@ class Asset extends Depreciable
}); });
} }
/** /**
* Query builder scope for searching location * Query builder scope for searching location
* *
@ -923,6 +970,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeAssetsByLocation($query, $location) public function scopeAssetsByLocation($query, $location)
{ {
return $query->where(function ($query) use ($location) { return $query->where(function ($query) use ($location) {
@ -944,6 +992,7 @@ class Asset extends Depreciable
}); });
} }
/** /**
* Query builder scope for RTD assets * Query builder scope for RTD assets
* *
@ -951,6 +1000,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeRTD($query) public function scopeRTD($query)
{ {
return $query->whereNull('assets.assigned_to') return $query->whereNull('assets.assigned_to')
@ -968,6 +1018,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeUndeployable($query) public function scopeUndeployable($query)
{ {
return $query->whereHas('assetstatus', function ($query) { return $query->whereHas('assetstatus', function ($query) {
@ -984,6 +1035,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeNotArchived($query) public function scopeNotArchived($query)
{ {
return $query->whereHas('assetstatus', function ($query) { return $query->whereHas('assetstatus', function ($query) {
@ -1013,6 +1065,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeDueForAudit($query, $settings) public function scopeDueForAudit($query, $settings)
{ {
$interval = $settings->audit_warning_days ?? 0; $interval = $settings->audit_warning_days ?? 0;
@ -1037,6 +1090,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeOverdueForAudit($query) public function scopeOverdueForAudit($query)
{ {
return $query->whereNotNull('assets.next_audit_date') return $query->whereNotNull('assets.next_audit_date')
@ -1058,6 +1112,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeDueOrOverdueForAudit($query, $settings) public function scopeDueOrOverdueForAudit($query, $settings)
{ {
$interval = $settings->audit_warning_days ?? 0; $interval = $settings->audit_warning_days ?? 0;
@ -1068,6 +1123,7 @@ class Asset extends Depreciable
->NotArchived(); ->NotArchived();
} }
/** /**
* Query builder scope for Archived assets * Query builder scope for Archived assets
* *
@ -1075,6 +1131,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeArchived($query) public function scopeArchived($query)
{ {
return $query->whereHas('assetstatus', function ($query) { return $query->whereHas('assetstatus', function ($query) {
@ -1091,6 +1148,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeDeployed($query) public function scopeDeployed($query)
{ {
return $query->where('assigned_to', '>', '0'); return $query->where('assigned_to', '>', '0');
@ -1103,6 +1161,7 @@ class Asset extends Depreciable
* *
* @return \Illuminate\Database\Query\Builder Modified query builder * @return \Illuminate\Database\Query\Builder Modified query builder
*/ */
public function scopeRequestableAssets($query) public function scopeRequestableAssets($query)
{ {
return Company::scopeCompanyables($query->where('requestable', '=', 1)) return Company::scopeCompanyables($query->where('requestable', '=', 1))
@ -1113,6 +1172,7 @@ class Asset extends Depreciable
}); });
} }
/** /**
* scopeInModelList * scopeInModelList
* Get all assets in the provided listing of model ids * Get all assets in the provided listing of model ids
@ -1153,6 +1213,7 @@ class Asset extends Depreciable
return $query->where('accepted', '=', 'rejected'); return $query->where('accepted', '=', 'rejected');
} }
/** /**
* Query builder scope to get accepted assets * Query builder scope to get accepted assets
* *
@ -1205,7 +1266,7 @@ class Asset extends Depreciable
})->orWhere(function ($query) use ($search) { })->orWhere(function ($query) use ($search) {
$query->where('assets_users.first_name', 'LIKE', '%'.$search.'%') $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%", "%$search%"]) ->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%"])
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%') ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%') ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%'); ->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
@ -1215,6 +1276,7 @@ class Asset extends Depreciable
->orWhere('assets.order_number', 'LIKE', '%'.$search.'%') ->orWhere('assets.order_number', 'LIKE', '%'.$search.'%')
->orWhere('assets.notes', 'LIKE', '%'.$search.'%'); ->orWhere('assets.notes', 'LIKE', '%'.$search.'%');
} }
})->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug })->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
} }
@ -1234,9 +1296,12 @@ class Asset extends Depreciable
->where('assets.assigned_type', '=', User::class); ->where('assets.assigned_type', '=', User::class);
})->where(function ($query) use ($search) { })->where(function ($query) use ($search) {
$query->where('assets_dept_users.department_id', '=', $search); $query->where('assets_dept_users.department_id', '=', $search);
})->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug })->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
} }
/** /**
* Query builder scope to search on text filters for complex Bootstrap Tables API * Query builder scope to search on text filters for complex Bootstrap Tables API
* *
@ -1259,7 +1324,8 @@ class Asset extends Depreciable
$query->where('assets.name', 'LIKE', '%'.$search_val.'%'); $query->where('assets.name', 'LIKE', '%'.$search_val.'%');
} }
if ($fieldname == 'product_key') {
if ($fieldname =='serial') {
$query->where('assets.serial', 'LIKE', '%'.$search_val.'%'); $query->where('assets.serial', 'LIKE', '%'.$search_val.'%');
} }
@ -1300,6 +1366,7 @@ class Asset extends Depreciable
}); });
} }
if ($fieldname == 'manufacturer') { if ($fieldname == 'manufacturer') {
$query->whereHas('model', function ($query) use ($search_val) { $query->whereHas('model', function ($query) use ($search_val) {
$query->whereHas('manufacturer', function ($query) use ($search_val) { $query->whereHas('manufacturer', function ($query) use ($search_val) {
@ -1338,6 +1405,7 @@ class Asset extends Depreciable
}); });
} }
if ($fieldname == 'company') { if ($fieldname == 'company') {
$query->where(function ($query) use ($search_val) { $query->where(function ($query) use ($search_val) {
$query->whereHas('company', function ($query) use ($search_val) { $query->whereHas('company', function ($query) use ($search_val) {
@ -1375,14 +1443,22 @@ class Asset extends Depreciable
* against those relationships earlier in this method. * against those relationships earlier in this method.
* *
* - snipe * - snipe
*
*/ */
if (($fieldname != 'category') && ($fieldname != 'model_number') && ($fieldname != 'rtd_location') && ($fieldname != 'location') && ($fieldname != 'supplier')
&& ($fieldname != 'status_label') && ($fieldname != 'model') && ($fieldname != 'company') && ($fieldname != 'manufacturer')) { if (($fieldname!='category') && ($fieldname!='model_number') && ($fieldname!='rtd_location') && ($fieldname!='location') && ($fieldname!='supplier')
$query->orWhere('assets.'.$fieldname, 'LIKE', '%'.$search_val.'%'); && ($fieldname!='status_label') && ($fieldname!='model') && ($fieldname!='company') && ($fieldname!='manufacturer')) {
$query->where('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
} }
}); });
} }
/** /**
* Query builder scope to order on model * Query builder scope to order on model
* *
@ -1409,6 +1485,7 @@ class Asset extends Depreciable
return $query->join('models', 'assets.model_id', '=', 'models.id')->orderBy('models.model_number', $order); return $query->join('models', 'assets.model_id', '=', 'models.id')->orderBy('models.model_number', $order);
} }
/** /**
* Query builder scope to order on assigned user * Query builder scope to order on assigned user
* *
@ -1448,6 +1525,7 @@ class Asset extends Depreciable
return $query->leftJoin('companies as company_sort', 'assets.company_id', '=', 'company_sort.id')->orderBy('company_sort.name', $order); return $query->leftJoin('companies as company_sort', 'assets.company_id', '=', 'company_sort.id')->orderBy('company_sort.name', $order);
} }
/** /**
* Query builder scope to return results of a category * Query builder scope to return results of a category
* *
@ -1476,6 +1554,8 @@ class Asset extends Depreciable
->join('manufacturers', 'models.manufacturer_id', '=', 'manufacturers.id')->where('models.manufacturer_id', '=', $manufacturer_id); ->join('manufacturers', 'models.manufacturer_id', '=', 'manufacturers.id')->where('models.manufacturer_id', '=', $manufacturer_id);
} }
/** /**
* Query builder scope to order on category * Query builder scope to order on category
* *
@ -1491,6 +1571,7 @@ class Asset extends Depreciable
->orderBy('category_order.name', $order); ->orderBy('category_order.name', $order);
} }
/** /**
* Query builder scope to order on manufacturer * Query builder scope to order on manufacturer
* *
@ -1531,6 +1612,7 @@ class Asset extends Depreciable
return $query->leftJoin('locations as rtd_asset_locations', 'rtd_asset_locations.id', '=', 'assets.rtd_location_id')->orderBy('rtd_asset_locations.name', $order); return $query->leftJoin('locations as rtd_asset_locations', 'rtd_asset_locations.id', '=', 'assets.rtd_location_id')->orderBy('rtd_asset_locations.name', $order);
} }
/** /**
* Query builder scope to order on supplier name * Query builder scope to order on supplier name
* *
@ -1559,8 +1641,10 @@ class Asset extends Depreciable
$query->where('locations.id', '=', $search); $query->where('locations.id', '=', $search);
}); });
}); });
} }
/** /**
* Query builder scope to search on depreciation name * Query builder scope to search on depreciation name
* @param \Illuminate\Database\Query\Builder $query Query builder instance * @param \Illuminate\Database\Query\Builder $query Query builder instance
@ -1572,5 +1656,8 @@ class Asset extends Depreciable
{ {
return $query->join('models', 'assets.model_id', '=', 'models.id') return $query->join('models', 'assets.model_id', '=', 'models.id')
->join('depreciations', 'models.depreciation_id', '=', 'depreciations.id')->where('models.depreciation_id', '=', $search); ->join('depreciations', 'models.depreciation_id', '=', 'depreciations.id')->where('models.depreciation_id', '=', $search);
} }
} }

View file

@ -2,7 +2,7 @@
namespace App\Models; namespace App\Models;
use App\Http\Traits\UniqueUndeletedTrait; use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
use App\Models\Traits\Searchable; use App\Models\Traits\Searchable;
use App\Presenters\Presentable; use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -38,7 +38,7 @@ class Category extends SnipeModel
*/ */
public $rules = [ public $rules = [
'user_id' => 'numeric|nullable', 'user_id' => 'numeric|nullable',
'name' => 'required|min:1|max:255|unique_undeleted', 'name' => 'required|min:1|max:255|two_column_unique_undeleted:category_type',
'require_acceptance' => 'boolean', 'require_acceptance' => 'boolean',
'use_default_eula' => 'boolean', 'use_default_eula' => 'boolean',
'category_type' => 'required|in:asset,accessory,consumable,component,license', 'category_type' => 'required|in:asset,accessory,consumable,component,license',
@ -53,7 +53,8 @@ class Category extends SnipeModel
*/ */
protected $injectUniqueIdentifier = true; protected $injectUniqueIdentifier = true;
use ValidatingTrait; use ValidatingTrait;
use UniqueUndeletedTrait; use TwoColumnUniqueUndeletedTrait;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.

View file

@ -81,7 +81,7 @@ final class Company extends SnipeModel
$company_id = null; $company_id = null;
} }
$table = ($table_name) ? $table_name.'.' : ''; $table = ($table_name) ? $table_name."." : $query->getModel()->getTable().".";
if (\Schema::hasColumn($query->getModel()->getTable(), $column)) { if (\Schema::hasColumn($query->getModel()->getTable(), $column)) {
return $query->where($table.$column, '=', $company_id); return $query->where($table.$column, '=', $company_id);

View file

@ -78,7 +78,7 @@ class Consumable extends SnipeModel
* *
* @var array * @var array
*/ */
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no']; protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number'];
/** /**
* The relations and their attributes that should be included when searching the model. * The relations and their attributes that should be included when searching the model.

View file

@ -47,13 +47,20 @@ class Ldap extends Model
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version); ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version);
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20); ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20);
if ($ldap_use_tls == '1') { if (Setting::getSettings()->ldap_client_tls_cert && Setting::getSettings()->ldap_client_tls_key) {
ldap_set_option($connection, LDAP_OPT_X_TLS_CERTFILE, Setting::get_client_side_cert_path());
ldap_set_option($connection, LDAP_OPT_X_TLS_KEYFILE, Setting::get_client_side_key_path());
}
if ($ldap_use_tls=='1') {
ldap_start_tls($connection); ldap_start_tls($connection);
} }
return $connection; return $connection;
} }
/** /**
* Binds/authenticates the user to LDAP, and returns their attributes. * Binds/authenticates the user to LDAP, and returns their attributes.
* *
@ -128,6 +135,8 @@ class Ldap extends Model
{ {
$ldap_username = Setting::getSettings()->ldap_uname; $ldap_username = Setting::getSettings()->ldap_uname;
$ldap_username = Setting::getSettings()->ldap_uname;
// Lets return some nicer messages for users who donked their app key, and disable LDAP // Lets return some nicer messages for users who donked their app key, and disable LDAP
try { try {
$ldap_pass = \Crypt::decrypt(Setting::getSettings()->ldap_pword); $ldap_pass = \Crypt::decrypt(Setting::getSettings()->ldap_pword);
@ -140,6 +149,7 @@ class Ldap extends Model
} }
} }
/** /**
* Parse and map LDAP attributes based on settings * Parse and map LDAP attributes based on settings
* *

View file

@ -341,8 +341,60 @@ class Setting extends Model
'is_ad', 'is_ad',
'ad_domain', 'ad_domain',
'ad_append_domain', 'ad_append_domain',
'ldap_client_tls_key',
'ldap_client_tls_cert'
])->first()->getAttributes(); ])->first()->getAttributes();
return collect($ldapSettings); return collect($ldapSettings);
} }
/**
* Return the filename for the client-side SSL cert
*
* @var string
*/
public static function get_client_side_cert_path()
{
return storage_path().'/ldap_client_tls.cert';
}
/**
* Return the filename for the client-side SSL key
*
* @var string
*/
public static function get_client_side_key_path()
{
return storage_path().'/ldap_client_tls.key';
}
public function update_client_side_cert_files()
{
/**
* I'm not sure if it makes sense to have a cert but no key
* nor vice versa, but for now I'm just leaving it like this.
*
* Also, we could easily set this up with an event handler and
* self::saved() or something like that but there's literally only
* one place where we will do that, so I'll just explicitly call
* this method at that spot instead. It'll be easier to debug and understand.
*/
if ($this->ldap_client_tls_cert) {
file_put_contents(self::get_client_side_cert_path(), $this->ldap_client_tls_cert);
} else {
if (file_exists(self::get_client_side_cert_path())) {
unlink(self::get_client_side_cert_path());
}
}
if ($this->ldap_client_tls_key) {
file_put_contents(self::get_client_side_key_path(), $this->ldap_client_tls_key);
} else {
if (file_exists(self::get_client_side_key_path())) {
unlink(self::get_client_side_key_path());
}
}
}
} }

View file

@ -122,8 +122,8 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
protected function checkPermissionSection($section) protected function checkPermissionSection($section)
{ {
$user_groups = $this->groups; $user_groups = $this->groups;
if (($this->permissions == '') && (count($user_groups) == 0)) { if (($this->permissions == '') && (count($user_groups) == 0)) {
return false; return false;
} }
@ -181,6 +181,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return $this->checkPermissionSection('superuser'); return $this->checkPermissionSection('superuser');
} }
/** /**
* Establishes the user -> company relationship * Establishes the user -> company relationship
* *
@ -439,6 +440,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at'); return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at');
} }
/** /**
* Query builder scope to return NOT-deleted users * Query builder scope to return NOT-deleted users
* @author A. Gianotto <snipe@snipe.net> * @author A. Gianotto <snipe@snipe.net>
@ -583,6 +585,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return false; return false;
} }
public function decodePermissions() public function decodePermissions()
{ {
return json_decode($this->permissions, true); return json_decode($this->permissions, true);
@ -601,11 +604,12 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
{ {
$query = $query->where('first_name', 'LIKE', '%'.$search.'%') $query = $query->where('first_name', 'LIKE', '%'.$search.'%')
->orWhere('last_name', 'LIKE', '%'.$search.'%') ->orWhere('last_name', 'LIKE', '%'.$search.'%')
->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%$search%", "%$search%"]); ->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%$search%"]);
return $query; return $query;
} }
/** /**
* Run additional, advanced searches. * Run additional, advanced searches.
* *
@ -613,10 +617,10 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
* @param array $terms The search terms * @param array $terms The search terms
* @return \Illuminate\Database\Eloquent\Builder * @return \Illuminate\Database\Eloquent\Builder
*/ */
public function advancedTextSearch(Builder $query, array $terms) public function advancedTextSearch(Builder $query, array $terms) {
{
foreach ($terms as $term) { foreach($terms as $term) {
$query = $query->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%$term%", "%$term%"]); $query = $query->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%$term%"]);
} }
return $query; return $query;

View file

@ -30,7 +30,7 @@ class CheckinAssetNotification extends Notification
$this->expected_checkin = ''; $this->expected_checkin = '';
if ($this->item->expected_checkin) { if ($this->item->expected_checkin) {
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date', $this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false); false);
} }
} }

View file

@ -2,6 +2,7 @@
namespace App\Notifications; namespace App\Notifications;
use App\Helpers\Helper;
use App\Models\Asset; use App\Models\Asset;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
@ -33,12 +34,12 @@ class CheckoutAssetNotification extends Notification
$this->expected_checkin = ''; $this->expected_checkin = '';
if ($this->item->last_checkout) { if ($this->item->last_checkout) {
$this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date', $this->last_checkout = Helper::getFormattedDateObject($this->item->last_checkout, 'date',
false); false);
} }
if ($this->item->expected_checkin) { if ($this->item->expected_checkin) {
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date', $this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
false); false);
} }
} }

View file

@ -38,21 +38,20 @@ class ActionlogPresenter extends Presenter
public function icon() public function icon()
{ {
$itemicon = 'fa fa-paperclip'; $itemicon = 'fas fa-paperclip';
if ($this->itemType() == 'asset') { if ($this->itemType() == 'asset') {
$itemicon = 'fa fa-barcode'; return 'fas fa-barcode';
} elseif ($this->itemType() == 'accessory') { } elseif ($this->itemType() == 'accessory') {
$itemicon = 'fa fa-keyboard-o'; return 'far fa-keyboard';
} elseif ($this->itemType() == 'consumable') { } elseif ($this->itemType() == 'consumable') {
$itemicon = 'fa fa-tint'; return 'fas fa-tint';
} elseif ($this->itemType() == 'license') { } elseif ($this->itemType() == 'license') {
$itemicon = 'fa fa-floppy-o'; return 'far fa-save';
} elseif ($this->itemType() == 'component') { } elseif ($this->itemType() == 'component') {
$itemicon = 'fa fa-hdd-o'; return 'far fa-hdd';
} }
return $itemicon;
} }
public function actionType() public function actionType()

View file

@ -248,7 +248,7 @@ class AssetAuditPresenter extends Presenter
'sortable' => true, 'sortable' => true,
'visible' => false, 'visible' => false,
'switchable' => true, 'switchable' => true,
'title' => ($field->field_encrypted == '1') ? '<i class="fa fa-lock"></i> '.e($field->name) : e($field->name), 'title' => ($field->field_encrypted == '1') ? '<i class="fas fa-lock"></i> '.e($field->name) : e($field->name),
'formatter' => 'customFieldsFormatter', 'formatter' => 'customFieldsFormatter',
]; ];
} }

View file

@ -106,6 +106,14 @@ class AssetModelPresenter extends Presenter
'visible' => true, 'visible' => true,
'formatter' => 'fieldsetsLinkObjFormatter', 'formatter' => 'fieldsetsLinkObjFormatter',
], ],
[
'field' => 'requestable',
'searchable' => false,
'sortable' => true,
'visible' => false,
'title' => trans('admin/hardware/general.requestable'),
'formatter' => 'trueFalseFormatter',
],
[ [
'field' => 'notes', 'field' => 'notes',
'searchable' => true, 'searchable' => true,

View file

@ -523,6 +523,6 @@ class AssetPresenter extends Presenter
public function glyph() public function glyph()
{ {
return '<i class="fa fa-barcode" aria-hidden="true"></i>'; return '<i class="fas fa-barcode" aria-hidden="true"></i>';
} }
} }

View file

@ -75,7 +75,25 @@ class CategoryPresenter extends Presenter
'sortable' => false, 'sortable' => false,
'switchable' => false, 'switchable' => false,
'title' => trans('table.actions'), 'title' => trans('table.actions'),
],[
"field" => "use_default_eula",
"searchable" => false,
"sortable" => true,
"title" => trans('admin/categories/general.use_default_eula_column'),
'visible' => true, 'visible' => true,
"formatter" => 'trueFalseFormatter',
],[
"field" => "checkin_email",
"searchable" => false,
"sortable" => true,
"class" => 'css-envelope',
"title" => 'Send Email',
"visible" => true,
"formatter" => 'trueFalseFormatter',
],[
"field" => "require_acceptance",
"searchable" => false,
"sortable" => true,
'formatter' => 'categoriesActionsFormatter', 'formatter' => 'categoriesActionsFormatter',
], ],
[ [

View file

@ -41,14 +41,14 @@ class CompanyPresenter extends Presenter
'field' => 'users_count', 'field' => 'users_count',
'searchable' => false, 'searchable' => false,
'sortable' => true, 'sortable' => true,
'title' => '<span class="hidden-xs"><i class="fa fa-users"></i></span><span class="hidden-md hidden-lg">'.trans('general.users').'</span></th>', 'title' => '<span class="hidden-xs"><i class="fas fa-users"></i></span><span class="hidden-md hidden-lg">'.trans('general.users').'</span></th>',
'visible' => true, 'visible' => true,
], [ ], [
'field' => 'assets_count', 'field' => 'assets_count',
'searchable' => false, 'searchable' => false,
'sortable' => true, 'sortable' => true,
'title' => '<span class="hidden-xs"><i class="fa fa-barcode" aria-hidden="true"></i></span><span class="hidden-md hidden-lg">'.trans('general.assets').'</span>', 'title' => '<span class="hidden-xs"><i class="fas fa-barcode" aria-hidden="true"></i></span><span class="hidden-md hidden-lg">'.trans('general.assets').'</span>',
'visible' => true, 'visible' => true,
], [ ], [
@ -56,25 +56,25 @@ class CompanyPresenter extends Presenter
'searchable' => false, 'searchable' => false,
'sortable' => true, 'sortable' => true,
'visible' => true, 'visible' => true,
'title' => ' <span class="hidden-xs"><i class="fa fa-floppy-o"></i></span><span class="hidden-md hidden-lg">'.trans('general.licenses').'</span>', 'title' => ' <span class="hidden-xs"><i class="far fa-save"></i></span><span class="hidden-md hidden-lg">'.trans('general.licenses').'</span>',
], [ ], [
'field' => 'accessories_count', 'field' => 'accessories_count',
'searchable' => false, 'searchable' => false,
'sortable' => true, 'sortable' => true,
'visible' => true, 'visible' => true,
'title' => ' <span class="hidden-xs"><i class="fa fa-keyboard-o"></i></span><span class="hidden-md hidden-lg">'.trans('general.accessories').'</span>', 'title' => ' <span class="hidden-xs"><i class="far fa-keyboard"></i></span><span class="hidden-md hidden-lg">'.trans('general.accessories').'</span>',
], [ ], [
'field' => 'consumables_count', 'field' => 'consumables_count',
'searchable' => false, 'searchable' => false,
'sortable' => true, 'sortable' => true,
'visible' => true, 'visible' => true,
'title' => ' <span class="hidden-xs"><i class="fa fa-tint"></i></span><span class="hidden-md hidden-lg">'.trans('general.consumables').'</span>', 'title' => ' <span class="hidden-xs"><i class="fas fa-tint"></i></span><span class="hidden-md hidden-lg">'.trans('general.consumables').'</span>',
], [ ], [
'field' => 'components_count', 'field' => 'components_count',
'searchable' => false, 'searchable' => false,
'sortable' => true, 'sortable' => true,
'visible' => true, 'visible' => true,
'title' => ' <span class="hidden-xs"><i class="fa fa-hdd-o"></i></span><span class="hidden-md hidden-lg">'.trans('general.components').'</span>', 'title' => ' <span class="hidden-xs"><i class="far fa-hdd"></i></span><span class="hidden-md hidden-lg">'.trans('general.components').'</span>',
], [ ], [
'field' => 'updated_at', 'field' => 'updated_at',
'searchable' => false, 'searchable' => false,

View file

@ -0,0 +1,401 @@
<?php
namespace App\Presenters;
use DateTime;
/**
* Class DepreciationReportPresenter
* @package App\Presenters
*/
class DepreciationReportPresenter extends Presenter
{
/**
* Json Column Layout for bootstrap table
* @return string
*/
public static function dataTableLayout()
{
$layout = [
[
"field" => "company",
"searchable" => true,
"sortable" => true,
"switchable" => true,
"title" => trans('general.company'),
"visible" => false,
], [
"field" => "category",
"searchable" => true,
"sortable" => true,
"title" => trans('general.category'),
"visible" => true,
], [
"field" => "name",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/hardware/form.name'),
"visible" => false,
], [
"field" => "asset_tag",
"searchable" => true,
"sortable" => true,
"title" => trans('general.asset_tag'),
"visible" => true,
],[
"field" => "model",
"searchable" => true,
"sortable" => true,
"title" => trans('general.asset_model'),
"visible" => true,
], [
"field" => "model",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/hardware/form.model'),
"visible" => true,
], [
"field" => "model_number",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/models/table.modelnumber'),
"visible" => false
], [
"field" => "serial",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/hardware/form.serial'),
"visible" => true,
], [
"field" => "depreciation",
"searchable" => true,
"sortable" => true,
"title" => trans('general.depreciation'),
"visible" => true,
], [
"field" => "number_of_months",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/depreciations/general.number_of_months'),
"visible" => true,
], [
"field" => "status",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/hardware/table.status'),
"visible" => true,
], [
"field" => "checked_out_to",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/hardware/table.checkoutto'),
"visible" => false,
], [
"field" => "location",
"searchable" => true,
"sortable" => true,
"title" => trans('admin/hardware/table.location'),
"visible" => true,
], [
"field" => "manufacturer",
"searchable" => true,
"sortable" => true,
"title" => trans('general.manufacturer'),
"visible" => false,
],[
"field" => "supplier",
"searchable" => true,
"sortable" => true,
"title" => trans('general.supplier'),
"visible" => false,
], [
"field" => "purchase_date",
"searchable" => true,
"sortable" => true,
"visible" => true,
"title" => trans('general.purchase_date'),
"formatter" => "dateDisplayFormatter"
], [
"field" => "currency",
"searchable" => false,
"sortable" => false,
"visible" => false,
"title" => 'Currency',
], [
"field" => "purchase_cost",
"searchable" => true,
"sortable" => true,
"visible" => true,
"title" => trans('general.purchase_cost'),
"footerFormatter" => 'sumFormatter',
], [
"field" => "order_number",
"searchable" => true,
"sortable" => true,
"visible" => false,
"title" => trans('general.order_number'),
], [
"field" => "eol",
"searchable" => false,
"sortable" => false,
"visible" => false,
"title" => trans('general.eol'),
"formatter" => "dateDisplayFormatter"
], [
"field" => "book_value",
"searchable" => true,
"sortable" => true,
"visible" => true,
"title" => trans('admin/hardware/table.book_value'),
"footerFormatter" => 'sumFormatter',
], [
"field" => "monthly_depreciation",
"searchable" => true,
"sortable" => true,
"visible" => true,
"title" => trans('admin/hardware/table.monthly_depreciation')
],[
"field" => "diff",
"searchable" => false,
"sortable" => false,
"visible" => true,
"title" => trans('admin/hardware/table.diff'),
"footerFormatter" => 'sumFormatter',
],[
"field" => "warranty_expires",
"searchable" => false,
"sortable" => false,
"visible" => false,
"title" => trans('admin/hardware/form.warranty_expires'),
"formatter" => "dateDisplayFormatter"
],
];
return json_encode($layout);
}
/**
* Generate html link to this items name.
* @return string
*/
public function nameUrl()
{
return (string) link_to_route('hardware.show', e($this->name), $this->id);
}
public function modelUrl()
{
if ($this->model->model) {
return $this->model->model->present()->nameUrl();
}
return '';
}
/**
* Generate img tag to this items image.
* @return mixed|string
*/
public function imageUrl()
{
$imagePath = '';
if ($this->image && !empty($this->image)) {
$imagePath = $this->image;
$imageAlt = $this->name;
} elseif ($this->model && !empty($this->model->image)) {
$imagePath = $this->model->image;
$imageAlt = $this->model->name;
}
$url = config('app.url');
if (!empty($imagePath)) {
$imagePath = '<img src="'.$url.'/uploads/assets/'.$imagePath.' height="50" width="50" alt="'.$imageAlt.'">';
}
return $imagePath;
}
/**
* Generate img tag to this items image.
* @return mixed|string
*/
public function imageSrc()
{
$imagePath = '';
if ($this->image && !empty($this->image)) {
$imagePath = $this->image;
} elseif ($this->model && !empty($this->model->image)) {
$imagePath = $this->model->image;
}
if (!empty($imagePath)) {
return config('app.url').'/uploads/assets/'.$imagePath;
}
return $imagePath;
}
/**
* Get Displayable Name
* @return string
*
* @todo this should be factored out - it should be subsumed by fullName (below)
*
**/
public function name()
{
return $this->fullName;
}
/**
* Helper for notification polymorphism.
* @return mixed
*/
public function fullName()
{
$str = '';
// Asset name
if ($this->model->name) {
$str .= $this->model->name;
}
// Asset tag
if ($this->asset_tag) {
$str .= ' ('.$this->model->asset_tag.')';
}
// Asset Model name
if ($this->model->model) {
$str .= ' - '.$this->model->model->name;
}
return $str;
}
/**
* Returns the date this item hits EOL.
* @return false|string
*/
public function eol_date()
{
if (( $this->purchase_date ) && ( $this->model->model ) && ($this->model->model->eol) ) {
$date = date_create($this->purchase_date);
date_add($date, date_interval_create_from_date_string($this->model->model->eol . ' months'));
return date_format($date, 'Y-m-d');
}
}
/**
* How many months until this asset hits EOL.
* @return null
*/
public function months_until_eol()
{
$today = date("Y-m-d");
$d1 = new DateTime($today);
$d2 = new DateTime($this->eol_date());
if ($this->eol_date() > $today) {
$interval = $d2->diff($d1);
} else {
$interval = null;
}
return $interval;
}
/**
* @return string
* This handles the status label "meta" status of "deployed" if
* it's assigned. Should maybe deprecate.
*/
public function statusMeta()
{
if ($this->model->assigned) {
return 'deployed';
}
return $this->model->assetstatus->getStatuslabelType();
}
/**
* @return string
* This handles the status label "meta" status of "deployed" if
* it's assigned. Should maybe deprecate.
*/
public function statusText()
{
if ($this->model->assigned) {
return trans('general.deployed');
}
return $this->model->assetstatus->name;
}
/**
* @return string
* This handles the status label "meta" status of "deployed" if
* it's assigned. Results look like:
*
* (if assigned and the status label is "Ready to Deploy"):
* (Deployed)
*
* (f assigned and status label is not "Ready to Deploy":)
* Deployed (Another Status Label)
*
* (if not deployed:)
* Another Status Label
*/
public function fullStatusText() {
// Make sure the status is valid
if ($this->assetstatus) {
// If the status is assigned to someone or something...
if ($this->model->assigned) {
// If it's assigned and not set to the default "ready to deploy" status
if ($this->assetstatus->name != trans('general.ready_to_deploy')) {
return trans('general.deployed'). ' (' . $this->model->assetstatus->name.')';
}
// If it's assigned to the default "ready to deploy" status, just
// say it's deployed - otherwise it's confusing to have a status that is
// both "ready to deploy" and deployed at the same time.
return trans('general.deployed');
}
// Return just the status name
return $this->model->assetstatus->name;
}
// This status doesn't seem valid - either data has been manually edited or
// the status label was deleted.
return 'Invalid status';
}
/**
* Date the warantee expires.
* @return false|string
*/
public function warrantee_expires()
{
if (($this->purchase_date) && ($this->warranty_months)) {
$date = date_create($this->purchase_date);
date_add($date, date_interval_create_from_date_string($this->warranty_months . ' months'));
return date_format($date, 'Y-m-d');
}
return false;
}
/**
* Url to view this item.
* @return string
*/
public function viewUrl()
{
return route('hardware.show', $this->id);
}
public function glyph()
{
return '<i class="fas fa-barcode" aria-hidden="true"></i>';
}
}

View file

@ -193,7 +193,7 @@ class LocationPresenter extends Presenter
public function glyph() public function glyph()
{ {
return '<i class="fa fa-map-marker" aria-hidden="true"></i>'; return '<i class="fas fa-map-marker-alt" aria-hidden="true"></i>';
} }
public function fullName() public function fullName()

View file

@ -85,7 +85,7 @@ class ManufacturerPresenter extends Presenter
'sortable' => true, 'sortable' => true,
'switchable' => true, 'switchable' => true,
'title' => ' <span class="hidden-md hidden-lg">Assets</span>' 'title' => ' <span class="hidden-md hidden-lg">Assets</span>'
.'<span class="hidden-xs"><i class="fa fa-barcode fa-lg"></i></span>', .'<span class="hidden-xs"><i class="fas fa-barcode fa-lg"></i></span>',
'visible' => true, 'visible' => true,
], ],
[ [
@ -94,7 +94,7 @@ class ManufacturerPresenter extends Presenter
'sortable' => true, 'sortable' => true,
'switchable' => true, 'switchable' => true,
'title' => ' <span class="hidden-md hidden-lg">Licenses</span>' 'title' => ' <span class="hidden-md hidden-lg">Licenses</span>'
.'<span class="hidden-xs"><i class="fa fa-floppy-o fa-lg"></i></span>', .'<span class="hidden-xs"><i class="far fa-save fa-lg"></i></span>',
'visible' => true, 'visible' => true,
], ],
[ [
@ -103,7 +103,7 @@ class ManufacturerPresenter extends Presenter
'sortable' => true, 'sortable' => true,
'switchable' => true, 'switchable' => true,
'title' => ' <span class="hidden-md hidden-lg">Consumables</span>' 'title' => ' <span class="hidden-md hidden-lg">Consumables</span>'
.'<span class="hidden-xs"><i class="fa fa-tint fa-lg"></i></span>', .'<span class="hidden-xs"><i class="fas fa-tint fa-lg"></i></span>',
'visible' => true, 'visible' => true,
], ],
[ [
@ -112,7 +112,7 @@ class ManufacturerPresenter extends Presenter
'sortable' => true, 'sortable' => true,
'switchable' => true, 'switchable' => true,
'title' => ' <span class="hidden-md hidden-lg">Accessories</span>' 'title' => ' <span class="hidden-md hidden-lg">Accessories</span>'
.'<span class="hidden-xs"><i class="fa fa-keyboard-o fa-lg"></i></span>', .'<span class="hidden-xs"><i class="far fa-keyboard fa-lg"></i></span>',
'visible' => true, 'visible' => true,
], ],
[ [

View file

@ -312,7 +312,7 @@ class UserPresenter extends Presenter
{ {
if ($this->email) { if ($this->email) {
return '<a href="mailto:'.$this->email.'">'.$this->email.'</a>' return '<a href="mailto:'.$this->email.'">'.$this->email.'</a>'
.'<a href="mailto:'.$this->email.'" class="hidden-xs hidden-sm"><i class="fa fa-envelope"></i></a>'; .'<a href="mailto:'.$this->email.'" class="hidden-xs hidden-sm"><i class="far fa-envelope"></i></a>';
} }
return ''; return '';
@ -386,6 +386,6 @@ class UserPresenter extends Presenter
public function glyph() public function glyph()
{ {
return '<i class="fa fa-user" aria-hidden="true"></i>'; return '<i class="fas fa-user" aria-hidden="true"></i>';
} }
} }

View file

@ -54,6 +54,20 @@ class ValidationServiceProvider extends ServiceProvider
} }
}); });
// Unique if undeleted for two columns
// Same as unique_undeleted but taking the combination of two columns as unique constrain.
Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) {
if (count($parameters)) {
$count = DB::table($parameters[0])
->select('id')->where($attribute, '=', $value)
->whereNull('deleted_at')
->where('id', '!=', $parameters[1])
->where($parameters[2], $parameters[3])->count();
return $count < 1;
}
});
// Prevent circular references // Prevent circular references
// //
// Example usage in Location model where parent_id references another Location: // Example usage in Location model where parent_id references another Location:

View file

@ -157,7 +157,7 @@ class LdapAdConfiguration
private function setLdapConnectionConfiguration(): array private function setLdapConnectionConfiguration(): array
{ {
// Create the configuration array. // Create the configuration array.
return [ $ldap_settings = [
// Mandatory Configuration Options // Mandatory Configuration Options
'hosts' => $this->getServerUrlBase(), 'hosts' => $this->getServerUrlBase(),
'base_dn' => $this->ldapSettings['ldap_basedn'], 'base_dn' => $this->ldapSettings['ldap_basedn'],
@ -181,6 +181,14 @@ class LdapAdConfiguration
// LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD, // LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD,
], ],
]; ];
if($this->ldapSettings['ldap_client_tls_cert'] || $this->ldapSettings['ldap_client_tls_key']) {
$ldap_settings['custom_options'] = [
LDAP_OPT_X_TLS_CERTFILE => Setting::get_client_side_cert_path(),
LDAP_OPT_X_TLS_KEYFILE => Setting::get_client_side_key_path()
];
}
return $ldap_settings;
} }
/** /**

1036
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -250,6 +250,9 @@ return [
'enable_csp' => env('ENABLE_CSP', false), 'enable_csp' => env('ENABLE_CSP', false),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Demo Mode Lockdown | Demo Mode Lockdown
@ -273,6 +276,7 @@ return [
'min_php' => '7.2.5', 'min_php' => '7.2.5',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Autoloaded Service Providers | Autoloaded Service Providers
@ -397,6 +401,8 @@ return [
'Google2FA' => PragmaRX\Google2FALaravel\Facade::class, 'Google2FA' => PragmaRX\Google2FALaravel\Facade::class,
'Image' => Intervention\Image\ImageServiceProvider::class, 'Image' => Intervention\Image\ImageServiceProvider::class,
'Carbon' => Carbon\Carbon::class, 'Carbon' => Carbon\Carbon::class,
'Helper' => App\Helpers\Helper::class, // makes it much easier to use 'Helper::blah' in blades (which is where we usually use this)
], ],

View file

@ -54,6 +54,7 @@ class UserFactory extends Factory
'first_name' => 'Admin', 'first_name' => 'Admin',
'last_name' => 'User', 'last_name' => 'User',
'username' => 'admin', 'username' => 'admin',
'avatar' => '1.jpg',
'permissions' => '{"superuser":"1"}', 'permissions' => '{"superuser":"1"}',
]; ];
}); });
@ -66,6 +67,7 @@ class UserFactory extends Factory
'first_name' => 'Snipe E.', 'first_name' => 'Snipe E.',
'last_name' => 'Head', 'last_name' => 'Head',
'username' => 'snipe', 'username' => 'snipe',
'avatar' => '2.jpg',
'email' => 'snipe@snipe.net', 'email' => 'snipe@snipe.net',
'permissions' => '{"superuser":"1"}', 'permissions' => '{"superuser":"1"}',
]; ];

View file

@ -1,41 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
class CreateLicensesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('licenses', function ($table) {
$table->increments('id');
$table->string('name');
$table->integer('model_id');
$table->text('serial');
$table->string('license_email');
$table->string('license_name');
$table->date('purchase_date')->nullable();
$table->decimal('purchase_cost', 8, 2)->nullable();
$table->string('order_number');
$table->integer('assigned_to');
$table->text('notes');
$table->integer('user_id')->nullable();
$table->timestamps();
$table->engine = 'InnoDB';
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('licenses');
}
}

View file

@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
class CreateTempLicensesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('licenses')) {
Schema::create('licenses', function ($table) {
$table->increments('id');
$table->string('name');
$table->integer('model_id');
$table->text('serial');
$table->string('license_email');
$table->string('license_name');
$table->date('purchase_date')->nullable();
$table->decimal('purchase_cost', 8, 2)->nullable();
$table->string('order_number');
$table->integer('assigned_to');
$table->text('notes');
$table->integer('user_id')->nullable();
$table->timestamps();
$table->engine = 'InnoDB';
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('licenses');
}
}

View file

@ -11,7 +11,7 @@ class DropLicensesTable extends Migration
*/ */
public function up() public function up()
{ {
Schema::drop('licenses'); Schema::dropIfExists('licenses');
} }
/** /**

View file

@ -13,7 +13,7 @@ class AddShowInNavToStatusLabels extends Migration
public function up() public function up()
{ {
Schema::table('status_labels', function (Blueprint $table) { Schema::table('status_labels', function (Blueprint $table) {
$table->boolean('show_in_nav')->default(0); $table->boolean('show_in_nav')->nullable()->default(0);
}); });
} }

View file

@ -14,7 +14,7 @@ class AddDefaultFlagOnStatuslabels extends Migration
public function up() public function up()
{ {
Schema::table('status_labels', function (Blueprint $table) { Schema::table('status_labels', function (Blueprint $table) {
$table->boolean('default_label')->default(0); $table->boolean('default_label')->nullable()->default(0);
}); });
} }

View file

@ -14,10 +14,17 @@ class MoveAccessoryCheckoutNoteToJoinTable extends Migration
* @return void * @return void
*/ */
public function up() public function up()
{
if (!Schema::hasColumn('accessories_users', 'note'))
{ {
Schema::table('accessories_users', function (Blueprint $table) { Schema::table('accessories_users', function (Blueprint $table) {
$table->string('note')->nullable(true)->default(null); $table->string('note')->nullable(true)->default(null);
}); });
}
// Loop through the checked out accessories, find their related action_log entry, and copy over the note // Loop through the checked out accessories, find their related action_log entry, and copy over the note
// to the newly created note field // to the newly created note field
@ -44,7 +51,9 @@ class MoveAccessoryCheckoutNoteToJoinTable extends Migration
->where('target_id', '=', $join_log->assigned_to) ->where('target_id', '=', $join_log->assigned_to)
->where('item_id', '=', $accessory->id) ->where('item_id', '=', $accessory->id)
->where('target_type', '=', \App\Models\User::class) ->where('target_type', '=', \App\Models\User::class)
->where('item_type', '=','App\\Models\\Accessory')
->where('action_type', '=', 'checkout') ->where('action_type', '=', 'checkout')
->where('note', '!=', '')
->orderBy('created_at', 'DESC')->get(); ->orderBy('created_at', 'DESC')->get();
\Log::debug($action_log_entries->count().' matching entries in the action_logs table'); \Log::debug($action_log_entries->count().' matching entries in the action_logs table');
@ -74,8 +83,15 @@ class MoveAccessoryCheckoutNoteToJoinTable extends Migration
*/ */
public function down() public function down()
{ {
Schema::table('accessories_users', function (Blueprint $table) {
if (Schema::hasColumn('accessories_users', 'note'))
{
Schema::table('accessories_users', function (Blueprint $table)
{
$table->dropColumn('note'); $table->dropColumn('note');
}); });
} }
}
} }

View file

@ -14,7 +14,7 @@ class AddDigitSeparatorToSettings extends Migration
public function up() public function up()
{ {
Schema::table('settings', function (Blueprint $table) { Schema::table('settings', function (Blueprint $table) {
$table->char('digit_separator')->nullable()->default('1234.56'); $table->char('digit_separator')->nullable()->default('1,234.56');
}); });
} }

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddClientSideLDAPCertToSettings extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->text('ldap_client_tls_cert')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('ldap_client_tls_cert');
});
}
}

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddClientSideLDAPKeyToSettings extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->text("ldap_client_tls_key")->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn("ldap_client_tls_key");
});
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class MakeLdapClientCertsNullable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
//
$table->text('ldap_client_tls_cert')->nullable()->change();
$table->text('ldap_client_tls_key')->nullable()->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
//
});
}
}

View file

@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\Statuslabel;
class ChangeDefaultLabelToNullable extends Migration
{
/**
* Run the migrations.
*
* This is stupid because it has a default valuye of 0 so it *should*
* default to 0, but it doesn't on some versions of MySQL.
*
* @return void
*/
public function up()
{
Statuslabel::whereNull('default_label')
->update(['default_label' => 0]);
Statuslabel::whereNull('show_in_nav')
->update(['show_in_nav' => 0]);
Schema::table('status_labels', function (Blueprint $table) {
$table->boolean('default_label')->nullable()->default(0)->change();
$table->boolean('show_in_nav')->nullable()->default(0)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

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