mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-24 05:04:07 -08:00
Merge branch 'develop' into snipeit_v7_laravel10
This commit is contained in:
commit
f734e196e5
|
@ -2997,6 +2997,15 @@
|
|||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "coach1988",
|
||||
"name": "coach1988",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2565989?v=4",
|
||||
"profile": "https://github.com/coach1988",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ APP_DEBUG=false
|
|||
APP_KEY=ChangeMe
|
||||
APP_URL=null
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE='en-US'
|
||||
MAX_RESULTS=500
|
||||
|
||||
# --------------------------------------------
|
||||
|
|
|
@ -6,7 +6,7 @@ APP_DEBUG=false
|
|||
APP_KEY='base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU='
|
||||
APP_URL='http://localhost:8000'
|
||||
APP_TIMEZONE='US/Pacific'
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE='en-US'
|
||||
FILESYSTEM_DISK=local
|
||||
|
||||
# --------------------------------------------
|
||||
|
|
|
@ -6,7 +6,7 @@ APP_DEBUG=true
|
|||
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
|
||||
APP_URL=http://localhost:8000
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE='en-US'
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: DATABASE SETTINGS
|
||||
|
|
6
.github/workflows/SA-codeql.yml
vendored
6
.github/workflows/SA-codeql.yml
vendored
|
@ -30,10 +30,10 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
|
2
.github/workflows/codacy-analysis.yml
vendored
2
.github/workflows/codacy-analysis.yml
vendored
|
@ -52,6 +52,6 @@ jobs:
|
|||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
22
.github/workflows/dockerhub-description.yml
vendored
Normal file
22
.github/workflows/dockerhub-description.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
name: Update Docker Hub Description
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
paths:
|
||||
- README.md
|
||||
- .github/workflows/dockerhub-description.yml
|
||||
jobs:
|
||||
dockerHubDescription:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Docker Hub Description
|
||||
uses: grokability/dockerhub-description@7ea9d275c7cdbe2b676a093a0308c50665e3b8b4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
repository: snipe/snipe-it
|
||||
readme-filepath: ./README.md
|
78
.github/workflows/tests-mysql.yml
vendored
Normal file
78
.github/workflows/tests-mysql.yml
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
name: Tests in MySQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: snipeit
|
||||
ports:
|
||||
- 33306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
|
||||
name: PHP ${{ matrix.php-version }}
|
||||
|
||||
steps:
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: none
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-
|
||||
|
||||
- name: Copy .env
|
||||
run: |
|
||||
cp -v .env.testing.example .env
|
||||
cp -v .env.testing.example .env.testing
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
|
||||
- name: Setup Laravel
|
||||
env:
|
||||
DB_CONNECTION: mysql
|
||||
DB_DATABASE: snipeit
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
run: |
|
||||
php artisan key:generate
|
||||
php artisan migrate --force
|
||||
php artisan passport:install
|
||||
chmod -R 777 storage bootstrap/cache
|
||||
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
DB_CONNECTION: mysql
|
||||
DB_DATABASE: snipeit
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
run: php artisan test --parallel
|
58
.github/workflows/tests-sqlite.yml
vendored
Normal file
58
.github/workflows/tests-sqlite.yml
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
name: Tests in SQLite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1.1"
|
||||
|
||||
name: PHP ${{ matrix.php-version }}
|
||||
|
||||
steps:
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: none
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-
|
||||
|
||||
- name: Copy .env
|
||||
run: |
|
||||
cp -v .env.testing.example .env
|
||||
cp -v .env.testing.example .env.testing
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
|
||||
- name: Generate key
|
||||
run: php artisan key:generate
|
||||
|
||||
- name: Directory Permissions
|
||||
run: chmod -R 777 storage bootstrap/cache
|
||||
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
DB_CONNECTION: sqlite_testing
|
||||
run: php artisan test --parallel
|
78
.github/workflows/tests.yml
vendored
78
.github/workflows/tests.yml
vendored
|
@ -1,78 +0,0 @@
|
|||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: snipeit
|
||||
ports:
|
||||
- 33306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
|
||||
name: PHP ${{ matrix.php-version }}
|
||||
|
||||
steps:
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: none
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-
|
||||
|
||||
- name: Copy .env
|
||||
run: |
|
||||
cp -v .env.testing.example .env
|
||||
cp -v .env.testing.example .env.testing
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
|
||||
- name: Setup Laravel
|
||||
env:
|
||||
DB_CONNECTION: mysql
|
||||
DB_DATABASE: snipeit
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
run: |
|
||||
php artisan key:generate
|
||||
php artisan migrate --force
|
||||
php artisan passport:install
|
||||
chmod -R 777 storage bootstrap/cache
|
||||
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
DB_CONNECTION: mysql
|
||||
DB_DATABASE: snipeit
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
run: php artisan test --parallel
|
|
@ -1,4 +1,4 @@
|
|||
FROM alpine:3.17.3
|
||||
FROM alpine:3.18.5
|
||||
# Apache + PHP
|
||||
RUN apk add --no-cache \
|
||||
apache2 \
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-330-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-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
|
||||
|
||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
|
@ -146,7 +148,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
|||
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
|
|
@ -66,6 +66,7 @@ class LdapSync extends Command
|
|||
$ldap_result_dept = Setting::getSettings()->ldap_dept;
|
||||
$ldap_result_manager = Setting::getSettings()->ldap_manager;
|
||||
$ldap_default_group = Setting::getSettings()->ldap_default_group;
|
||||
$search_base = Setting::getSettings()->ldap_base_dn;
|
||||
|
||||
try {
|
||||
$ldapconn = Ldap::connectToLdap();
|
||||
|
@ -83,26 +84,35 @@ class LdapSync extends Command
|
|||
$summary = [];
|
||||
|
||||
try {
|
||||
|
||||
/**
|
||||
* if a location ID has been specified, use that OU
|
||||
*/
|
||||
if ( $this->option('location_id') != '') {
|
||||
|
||||
foreach($this->option('location_id') as $location_id){
|
||||
$location_ou= Location::where('id', '=', $location_id)->value('ldap_ou');
|
||||
$location_ou = Location::where('id', '=', $location_id)->value('ldap_ou');
|
||||
$search_base = $location_ou;
|
||||
Log::debug('Importing users from specified location OU: \"'.$search_base.'\".');
|
||||
}
|
||||
}
|
||||
|
||||
else if ($this->option('base_dn') != '') {
|
||||
/**
|
||||
* Otherwise if a manual base DN has been specified, use that
|
||||
*/
|
||||
} elseif ($this->option('base_dn') != '') {
|
||||
$search_base = $this->option('base_dn');
|
||||
Log::debug('Importing users from specified base DN: \"'.$search_base.'\".');
|
||||
} else {
|
||||
$search_base = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a filter has been specified, use that
|
||||
*/
|
||||
if ($this->option('filter') != '') {
|
||||
$results = Ldap::findLdapUsers($search_base, -1, $this->option('filter'));
|
||||
} else {
|
||||
$results = Ldap::findLdapUsers($search_base);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = ['error' => true, 'error_message' => $e->getMessage(), 'summary' => []];
|
||||
|
|
|
@ -63,7 +63,7 @@ class ResetDemoSettings extends Command
|
|||
$settings->date_display_format = 'D M d, Y';
|
||||
$settings->time_display_format = 'g:iA';
|
||||
$settings->thumbnail_max_h = '30';
|
||||
$settings->locale = 'en';
|
||||
$settings->locale = 'en-US';
|
||||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = null;
|
||||
$settings->saml_enabled = '0';
|
||||
|
@ -78,7 +78,7 @@ class ResetDemoSettings extends Command
|
|||
$settings->save();
|
||||
|
||||
if ($user = User::where('username', '=', 'admin')->first()) {
|
||||
$user->locale = 'en';
|
||||
$user->locale = 'en-US';
|
||||
$user->save();
|
||||
}
|
||||
|
||||
|
|
|
@ -84,35 +84,36 @@ class RestoreFromBackup extends Command
|
|||
|
||||
|
||||
$private_dirs = [
|
||||
'storage/private_uploads/accessories',
|
||||
'storage/private_uploads/assetmodels',
|
||||
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
|
||||
'storage/private_uploads/audits',
|
||||
'storage/private_uploads/components',
|
||||
'storage/private_uploads/consumables',
|
||||
'storage/private_uploads/eula-pdfs',
|
||||
'storage/private_uploads/imports',
|
||||
'storage/private_uploads/assetmodels',
|
||||
'storage/private_uploads/users',
|
||||
'storage/private_uploads/licenses',
|
||||
'storage/private_uploads/signatures',
|
||||
'storage/private_uploads/users',
|
||||
];
|
||||
$private_files = [
|
||||
'storage/oauth-private.key',
|
||||
'storage/oauth-public.key',
|
||||
];
|
||||
$public_dirs = [
|
||||
'public/uploads/accessories',
|
||||
'public/uploads/assets', // these are asset _pictures_, not asset files
|
||||
'public/uploads/avatars',
|
||||
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
|
||||
'public/uploads/categories',
|
||||
'public/uploads/companies',
|
||||
'public/uploads/components',
|
||||
'public/uploads/categories',
|
||||
'public/uploads/manufacturers',
|
||||
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
|
||||
'public/uploads/consumables',
|
||||
'public/uploads/departments',
|
||||
'public/uploads/avatars',
|
||||
'public/uploads/suppliers',
|
||||
'public/uploads/assets', // these are asset _pictures_, not asset files
|
||||
'public/uploads/locations',
|
||||
'public/uploads/accessories',
|
||||
'public/uploads/models',
|
||||
'public/uploads/categories',
|
||||
'public/uploads/avatars',
|
||||
'public/uploads/manufacturers',
|
||||
'public/uploads/models',
|
||||
'public/uploads/suppliers',
|
||||
];
|
||||
|
||||
$public_files = [
|
||||
|
|
|
@ -7,6 +7,7 @@ use App\Models\CustomField;
|
|||
use App\Models\Setting;
|
||||
use Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
|
||||
class RotateAppKey extends Command
|
||||
|
@ -16,14 +17,17 @@ class RotateAppKey extends Command
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:rotate-key';
|
||||
protected $signature = 'snipeit:rotate-key
|
||||
{previous_key? : The previous key to rotate from}
|
||||
{--emergency : Emergency mode - rotate from .env APP_KEY to newly-generated one, modifying .env}
|
||||
{--force : Skip interactive confirmation}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
protected $description = 'Rotates APP_KEY to a new value, optionally taking the previous key as an argument';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
|
@ -42,26 +46,42 @@ class RotateAppKey extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->confirm("\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ")) {
|
||||
//make sure they specify only exactly one of --emergency, or a filename. Not neither, and not both.
|
||||
if ( (!$this->option('emergency') && !$this->argument('previous_key')) || ( $this->option('emergency') && $this->argument('previous_key'))) {
|
||||
$this->error("Specify only one of --emergency, or an app key value, in order to rotate keys");
|
||||
return 1;
|
||||
}
|
||||
if ( $this->option('emergency') ) {
|
||||
$msg = "\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ";
|
||||
} else {
|
||||
$msg = "\n****************************************************\nTHIS WILL DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND RE-ENCRYPT THEM WITH YOUR\nAPP_KEY.\n\nThere is NO undo. \n\nMake SURE you have a database backup BEFORE running this command. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup? ";
|
||||
}
|
||||
if ($this->option('force') || $this->confirm($msg)) {
|
||||
|
||||
// Get the existing app_key and ciphers
|
||||
// We put them in a variable since we clear the cache partway through here.
|
||||
$old_app_key = config('app.key');
|
||||
$cipher = config('app.cipher');
|
||||
if ($this->option('emergency')) {
|
||||
$old_app_key = config('app.key');
|
||||
$cipher = config('app.cipher');
|
||||
|
||||
// Generate a new one
|
||||
Artisan::call('key:generate', ['--show' => true]);
|
||||
$new_app_key = Artisan::output();
|
||||
// Generate a new one
|
||||
Artisan::call('key:generate', ['--show' => true]);
|
||||
$new_app_key = trim(Artisan::output());
|
||||
|
||||
// Clear the config cache
|
||||
Artisan::call('config:clear');
|
||||
// Clear the config cache
|
||||
Artisan::call('config:clear');
|
||||
|
||||
$this->warn('Your app cipher is: '.$cipher);
|
||||
$this->warn('Your old APP_KEY is: '.$old_app_key);
|
||||
$this->warn('Your new APP_KEY is: '.$new_app_key);
|
||||
// Write the new app key to the .env file
|
||||
$this->writeNewEnvironmentFileWith($new_app_key);
|
||||
} elseif ($this->argument('previous_key')) {
|
||||
$old_app_key = $this->argument('previous_key');
|
||||
$cipher = config('app.cipher'); // just a guess?
|
||||
$new_app_key = config('app.key');
|
||||
}
|
||||
|
||||
// Write the new app key to the .env file
|
||||
$this->writeNewEnvironmentFileWith($new_app_key);
|
||||
$this->warn('Your app cipher is: ' . $cipher);
|
||||
$this->warn('Your old APP_KEY is: ' . $old_app_key);
|
||||
$this->warn('Your new APP_KEY is: ' . $new_app_key);
|
||||
|
||||
// Manually create an old encrypter instance using the old app key
|
||||
// and also create a new encrypter instance so we can re-crypt the field
|
||||
|
@ -75,8 +95,16 @@ class RotateAppKey extends Command
|
|||
$assets = Asset::whereNotNull($field->db_column)->get();
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
|
||||
$this->line('DECRYPTED: '.$field->db_column);
|
||||
try {
|
||||
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
|
||||
$this->line('DECRYPTED: ' . $field->db_column);
|
||||
} catch (DecryptException $e) {
|
||||
$this->line('Could not decrypt '. $field->db_column.' using "old key" - skipping...');
|
||||
continue;
|
||||
} catch (\Exception $e) {
|
||||
$this->error("Error decrypting ".$field->db_column.", reason: ".$e->getMessage().". Aborting key rotation");
|
||||
throw $e;
|
||||
}
|
||||
$asset->{$field->db_column} = $newEncrypter->encrypt($asset->{$field->db_column});
|
||||
$this->line('ENCRYPTED: '.$field->db_column);
|
||||
$asset->save();
|
||||
|
@ -86,10 +114,14 @@ class RotateAppKey extends Command
|
|||
// Handle the LDAP password if one is provided
|
||||
$setting = Setting::first();
|
||||
if ($setting->ldap_pword != '') {
|
||||
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
|
||||
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
|
||||
$setting->save();
|
||||
$this->warn('LDAP password has been re-encrypted.');
|
||||
try {
|
||||
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
|
||||
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
|
||||
$setting->save();
|
||||
$this->warn('LDAP password has been re-encrypted.');
|
||||
} catch(DecryptException $e) {
|
||||
$this->warn("Unable to decrypt old LDAP password; skipping");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->info('This operation has been canceled. No changes have been made.');
|
||||
|
@ -106,7 +138,7 @@ class RotateAppKey extends Command
|
|||
{
|
||||
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
|
||||
$this->keyReplacementPattern(),
|
||||
'APP_KEY='.$key,
|
||||
'APP_KEY="'.$key.'"',
|
||||
file_get_contents($this->laravel->environmentFilePath())
|
||||
));
|
||||
}
|
||||
|
@ -118,7 +150,7 @@ class RotateAppKey extends Command
|
|||
*/
|
||||
protected function keyReplacementPattern()
|
||||
{
|
||||
$escaped = preg_quote('='.$this->laravel['config']['app.key'], '/');
|
||||
$escaped = '="?'.preg_quote($this->laravel['config']['app.key'], '/').'"?';
|
||||
|
||||
return "/^APP_KEY{$escaped}/m";
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use App\Notifications\ExpectedCheckinAdminNotification;
|
|||
use App\Notifications\ExpectedCheckinNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SendExpectedCheckinAlerts extends Command
|
||||
{
|
||||
|
@ -50,6 +51,7 @@ class SendExpectedCheckinAlerts extends Command
|
|||
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->assigned && $asset->checkedOutToUser()) {
|
||||
Log::info('Sending ExpectedCheckinNotification to ' . $asset->assigned->email);
|
||||
$asset->assigned->notify((new ExpectedCheckinNotification($asset)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,63 @@ use Carbon\Carbon;
|
|||
|
||||
class Helper
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* This is only used for reversing the migration that updates the locale to the 5-6 letter codes from two
|
||||
* letter codes. The normal dropdowns use the autoglossonyms in the language files located
|
||||
* in resources/en-US/localizations.php.
|
||||
*/
|
||||
public static $language_map = [
|
||||
'af' => 'af-ZA', // Afrikaans
|
||||
'am' => 'am-ET', // Amharic
|
||||
'ar' => 'ar-SA', // Arabic
|
||||
'bg' => 'bg-BG', // Bulgarian
|
||||
'ca' => 'ca-ES', // Catalan
|
||||
'cs' => 'cs-CZ', // Czech
|
||||
'cy' => 'cy-GB', // Welsh
|
||||
'da' => 'da-DK', // Danish
|
||||
'de-i' => 'de-if', // German informal
|
||||
'de' => 'de-DE', // German
|
||||
'el' => 'el-GR', // Greek
|
||||
'en' => 'en-US', // English
|
||||
'et' => 'et-EE', // Estonian
|
||||
'fa' => 'fa-IR', // Persian
|
||||
'fi' => 'fi-FI', // Finnish
|
||||
'fil' => 'fil-PH', // Filipino
|
||||
'fr' => 'fr-FR', // French
|
||||
'he' => 'he-IL', // Hebrew
|
||||
'hr' => 'hr-HR', // Croatian
|
||||
'hu' => 'hu-HU', // Hungarian
|
||||
'id' => 'id-ID', // Indonesian
|
||||
'is' => 'is-IS', // Icelandic
|
||||
'it' => 'it-IT', // Italian
|
||||
'iu' => 'iu-NU', // Inuktitut
|
||||
'ja' => 'ja-JP', // Japanese
|
||||
'ko' => 'ko-KR', // Korean
|
||||
'lt' => 'lt-LT', // Lithuanian
|
||||
'lv' => 'lv-LV', // Latvian
|
||||
'mi' => 'mi-NZ', // Maori
|
||||
'mk' => 'mk-MK', // Macedonian
|
||||
'mn' => 'mn-MN', // Mongolian
|
||||
'ms' => 'ms-MY', // Malay
|
||||
'nl' => 'nl-NL', // Dutch
|
||||
'no' => 'no-NO', // Norwegian
|
||||
'pl' => 'pl-PL', // Polish
|
||||
'ro' => 'ro-RO', // Romanian
|
||||
'ru' => 'ru-RU', // Russian
|
||||
'sk' => 'sk-SK', // Slovak
|
||||
'sl' => 'sl-SI', // Slovenian
|
||||
'so' => 'so-SO', // Somali
|
||||
'ta' => 'ta-IN', // Tamil
|
||||
'th' => 'th-TH', // Thai
|
||||
'tl' => 'tl-PH', // Tagalog
|
||||
'tr' => 'tr-TR', // Turkish
|
||||
'uk' => 'uk-UA', // Ukrainian
|
||||
'vi' => 'vi-VN', // Vietnamese
|
||||
'zu' => 'zu-ZA', // Zulu
|
||||
];
|
||||
|
||||
/**
|
||||
* Simple helper to invoke the markdown parser
|
||||
*
|
||||
|
@ -354,7 +411,7 @@ class Helper
|
|||
|
||||
if ($index >= $total_colors) {
|
||||
|
||||
\Log::error('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
\Log::info('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
//patch fix for array key overflow (color count starts at 1, array starts at 0)
|
||||
$index = $index - $total_colors - 1;
|
||||
|
||||
|
@ -1317,7 +1374,7 @@ class Helper
|
|||
|
||||
|
||||
/*
|
||||
* I know it's gauche to return a shitty HTML string, but this is just a helper and since it will be the same every single time,
|
||||
* I know it's gauche to return a shitty HTML string, but this is just a helper and since it will be the same every single time,
|
||||
* it seemed pretty safe to do here. Don't you judge me.
|
||||
*/
|
||||
public static function showDemoModeFieldWarning() {
|
||||
|
@ -1325,4 +1382,55 @@ class Helper
|
|||
return "<p class=\"text-warning\"><i class=\"fas fa-lock\"></i>" . trans('general.feature_disabled') . "</p>";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ah, legacy code.
|
||||
*
|
||||
* This corrects the original mistakes from 2013 where we used the wrong locale codes. Hopefully we
|
||||
* can get rid of this in a future version, but this should at least give us the belt and suspenders we need
|
||||
* to be sure this change is not too disruptive.
|
||||
*
|
||||
* In this array, we ONLY include the older languages where we weren't using the correct locale codes.
|
||||
*
|
||||
* @see public static $language_map in this file
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since 6.3.0
|
||||
*
|
||||
* @param $language_code
|
||||
* @return string []
|
||||
*/
|
||||
public static function mapLegacyLocale($language_code = null)
|
||||
{
|
||||
|
||||
if (strlen($language_code) > 4) {
|
||||
return $language_code;
|
||||
}
|
||||
|
||||
foreach (self::$language_map as $legacy => $new) {
|
||||
if ($language_code == $legacy) {
|
||||
\Log::debug('Current language is '.$legacy.', using '.$new.' instead');
|
||||
return $new;
|
||||
}
|
||||
}
|
||||
|
||||
// Return US english if we don't have a match
|
||||
return 'en-US';
|
||||
}
|
||||
|
||||
public static function mapBackToLegacyLocale($new_locale = null)
|
||||
{
|
||||
if (strlen($new_locale) <= 4) {
|
||||
return $new_locale; //"new locale" apparently wasn't quite so new
|
||||
}
|
||||
|
||||
// This does a *reverse* search against our new language map array - given the value, find the *key* for it
|
||||
$legacy_locale = array_search($new_locale, self::$language_map);
|
||||
|
||||
if($legacy_locale !== false) {
|
||||
return $legacy_locale;
|
||||
}
|
||||
return $new_locale; // better that you have some weird locale that doesn't fit into our mappings anywhere than 'void'
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,9 +60,10 @@ class AccessoryCheckinController extends Controller
|
|||
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$checkin_at = date('Y-m-d');
|
||||
$checkin_hours = date('H:i:s');
|
||||
$checkin_at = date('Y-m-d H:i:s');
|
||||
if ($request->filled('checkin_at')) {
|
||||
$checkin_at = $request->input('checkin_at');
|
||||
$checkin_at = $request->input('checkin_at').' '.$checkin_hours;
|
||||
}
|
||||
|
||||
// Was the accessory updated?
|
||||
|
|
|
@ -116,41 +116,17 @@ class AssetMaintenancesController extends Controller
|
|||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
$asset = Asset::find(e($request->input('asset_id')));
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot add a maintenance for that asset'));
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->user_id = Auth::id();
|
||||
|
||||
if (($assetMaintenance->completion_date !== null)
|
||||
&& ($assetMaintenance->start_date !== '')
|
||||
&& ($assetMaintenance->start_date !== '0000-00-00')
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
|
||||
}
|
||||
$maintenance = new AssetMaintenance();
|
||||
$maintenance->fill($request->all());
|
||||
$maintenance->user_id = Auth::id();
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success')));
|
||||
if ($maintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.create.success')));
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -158,65 +134,39 @@ class AssetMaintenancesController extends Controller
|
|||
* Validates and stores an update to an asset maintenance
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @param int $assetMaintenanceId
|
||||
* @param int $id
|
||||
* @param int $request
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
* @return string JSON
|
||||
*/
|
||||
public function update(Request $request, $assetMaintenanceId = null)
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
|
||||
}
|
||||
if ($maintenance = AssetMaintenance::with('asset')->find($id)) {
|
||||
|
||||
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
|
||||
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
|
||||
if (($assetMaintenance->completion_date == null)
|
||||
) {
|
||||
if (($assetMaintenance->asset_maintenance_time !== 0)
|
||||
|| (! is_null($assetMaintenance->asset_maintenance_time))
|
||||
) {
|
||||
$assetMaintenance->asset_maintenance_time = null;
|
||||
// Can this user manage this asset?
|
||||
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
|
||||
}
|
||||
|
||||
// The asset this miantenance is attached to is not valid or has been deleted
|
||||
if (!$maintenance->asset) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('general.asset'), 'id' => $id])));
|
||||
}
|
||||
|
||||
$maintenance->fill($request->all());
|
||||
|
||||
if ($maintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.edit.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
|
||||
}
|
||||
|
||||
if (($assetMaintenance->completion_date !== null)
|
||||
&& ($assetMaintenance->start_date !== '')
|
||||
&& ($assetMaintenance->start_date !== '0000-00-00')
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id])));
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success')));
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,15 +3,16 @@
|
|||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Http\Requests\StoreAssetRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\DepreciationReportTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
|
@ -20,11 +21,12 @@ use App\Models\License;
|
|||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use \Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Input;
|
||||
use Paginator;
|
||||
use Slack;
|
||||
|
@ -134,7 +136,7 @@ class AssetsController extends Controller
|
|||
|
||||
// Search custom fields by column name
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if ($request->filled($field->db_column_name())) {
|
||||
if ($request->filled($field->db_column_name()) && $field->db_column_name()) {
|
||||
$assets->where($field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
}
|
||||
}
|
||||
|
@ -531,37 +533,14 @@ class AssetsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
public function store(StoreAssetRequest $request): JsonResponse
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
|
||||
$asset = new Asset();
|
||||
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
|
||||
|
||||
$asset->name = $request->get('name');
|
||||
$asset->serial = $request->get('serial');
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id'));
|
||||
$asset->model_id = $request->get('model_id');
|
||||
$asset->order_number = $request->get('order_number');
|
||||
$asset->notes = $request->get('notes');
|
||||
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
|
||||
$asset->user_id = Auth::id();
|
||||
$asset->archived = '0';
|
||||
$asset->physical = '1';
|
||||
$asset->depreciate = '0';
|
||||
$asset->status_id = $request->get('status_id', 0);
|
||||
$asset->warranty_months = $request->get('warranty_months', null);
|
||||
$asset->purchase_cost = $request->get('purchase_cost');
|
||||
$asset->asset_eol_date = $request->get('asset_eol_date', $asset->present()->eol_date());
|
||||
$asset->purchase_date = $request->get('purchase_date', null);
|
||||
$asset->assigned_to = $request->get('assigned_to', null);
|
||||
$asset->supplier_id = $request->get('supplier_id');
|
||||
$asset->requestable = $request->get('requestable', 0);
|
||||
$asset->rtd_location_id = $request->get('rtd_location_id', null);
|
||||
$asset->location_id = $request->get('rtd_location_id', null);
|
||||
|
||||
$asset->fill($request->validated());
|
||||
$asset->user_id = Auth::id();
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
|
@ -574,10 +553,11 @@ class AssetsController extends Controller
|
|||
$asset = $request->handleImages($asset);
|
||||
|
||||
// Update custom fields in the database.
|
||||
// Validation for these fields is handled through the AssetRequest form request
|
||||
$model = AssetModel::find($request->get('model_id'));
|
||||
$model = AssetModel::find($request->input('model_id'));
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
// Check that it's an object and not a collection
|
||||
// (Sometimes people send arrays here and they shouldn't
|
||||
if (($model) && ($model instanceof AssetModel) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
|
||||
// Set the field value based on what was sent in the request
|
||||
|
@ -585,22 +565,22 @@ class AssetsController extends Controller
|
|||
|
||||
// If input value is null, use custom field's default value
|
||||
if ($field_val == null) {
|
||||
\Log::debug('Field value for '.$field->db_column.' is null');
|
||||
Log::debug('Field value for '.$field->db_column.' is null');
|
||||
$field_val = $field->defaultValue($request->get('model_id'));
|
||||
\Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
|
||||
Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
|
||||
}
|
||||
|
||||
// if the field is set to encrypted, make sure we encrypt the value
|
||||
if ($field->field_encrypted == '1') {
|
||||
\Log::debug('This model field is encrypted in this fieldset.');
|
||||
Log::debug('This model field is encrypted in this fieldset.');
|
||||
|
||||
if (Gate::allows('admin')) {
|
||||
|
||||
// If input value is null, use custom field's default value
|
||||
if (($field_val == null) && ($request->has('model_id') != '')) {
|
||||
$field_val = \Crypt::encrypt($field->defaultValue($request->get('model_id')));
|
||||
$field_val = Crypt::encrypt($field->defaultValue($request->get('model_id')));
|
||||
} else {
|
||||
$field_val = \Crypt::encrypt($request->input($field->db_column));
|
||||
$field_val = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -885,13 +865,17 @@ class AssetsController extends Controller
|
|||
public function checkin(Request $request, $asset_id)
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
$asset = Asset::with('model')->findOrFail($asset_id);
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
|
||||
$target = $asset->assignedTo;
|
||||
if (is_null($target)) {
|
||||
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_tag'=> e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.already_checked_in')));
|
||||
}
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
|
@ -925,7 +909,11 @@ class AssetsController extends Controller
|
|||
if ($asset->save()) {
|
||||
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
|
||||
|
|
|
@ -40,6 +40,14 @@ class ReportsController extends Controller
|
|||
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
if ($request->filled('action_source')) {
|
||||
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
if ($request->filled('remote_ip')) {
|
||||
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'))->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
if ($request->filled('uploads')) {
|
||||
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
@ -52,6 +60,9 @@ class ReportsController extends Controller
|
|||
'accept_signature',
|
||||
'action_type',
|
||||
'note',
|
||||
'remote_ip',
|
||||
'user_agent',
|
||||
'action_source',
|
||||
];
|
||||
|
||||
|
||||
|
|
|
@ -195,11 +195,6 @@ class UsersController extends Controller
|
|||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'manager':
|
||||
$users = $users->OrderManager($order);
|
||||
|
@ -276,7 +271,17 @@ class UsersController extends Controller
|
|||
}
|
||||
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
\Log::debug('Requested offset: '. $request->input('offset'));
|
||||
\Log::debug('App offset: '. app('api_offset_value'));
|
||||
\Log::debug('Actual offset: '. $offset);
|
||||
\Log::debug('Limit: '. $limit);
|
||||
|
||||
$total = $users->count();
|
||||
$users = $users->skip($offset)->take($limit)->get();
|
||||
|
||||
|
@ -356,6 +361,7 @@ class UsersController extends Controller
|
|||
|
||||
$user = new User;
|
||||
$user->fill($request->all());
|
||||
$user->created_by = Auth::user()->id;
|
||||
|
||||
if ($request->has('permissions')) {
|
||||
$permissions_array = $request->input('permissions');
|
||||
|
@ -709,11 +715,11 @@ class UsersController extends Controller
|
|||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restore');
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/users/message.restore.success')), 200);
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.restored')), 200);
|
||||
}
|
||||
|
||||
// Check validation to make sure we're not restoring a user with the same username as an existing user
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.user'), 'error' => $user->getErrors()->first()])), 200);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')), 200);
|
||||
|
|
|
@ -88,7 +88,7 @@ class AssetModelsController extends Controller
|
|||
$model->requestable = Request::has('requestable');
|
||||
|
||||
if ($request->input('fieldset_id') != '') {
|
||||
$model->fieldset_id = e($request->input('fieldset_id'));
|
||||
$model->fieldset_id = $request->input('fieldset_id');
|
||||
}
|
||||
|
||||
$model = $request->handleImages($model);
|
||||
|
@ -101,7 +101,6 @@ class AssetModelsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
// Redirect to the new model page
|
||||
return redirect()->route('models.index')->with('success', trans('admin/models/message.create.success'));
|
||||
}
|
||||
|
||||
|
@ -166,17 +165,14 @@ class AssetModelsController extends Controller
|
|||
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
|
||||
if ($request->input('fieldset_id') == '') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = $request->input('fieldset_id');
|
||||
$model->fieldset_id = $request->input('fieldset_id');
|
||||
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))){
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
|
||||
}
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))){
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class AssetCheckoutController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
|
|
|
@ -131,9 +131,6 @@ class AssetsController extends Controller
|
|||
$asset->order_number = $request->input('order_number');
|
||||
$asset->notes = $request->input('notes');
|
||||
$asset->user_id = Auth::id();
|
||||
$asset->archived = '0';
|
||||
$asset->physical = '1';
|
||||
$asset->depreciate = '0';
|
||||
$asset->status_id = request('status_id');
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = request('purchase_cost');
|
||||
|
@ -365,7 +362,6 @@ class AssetsController extends Controller
|
|||
$asset->order_number = $request->input('order_number');
|
||||
$asset->asset_tag = $asset_tag[1];
|
||||
$asset->notes = $request->input('notes');
|
||||
$asset->physical = '1';
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ use App\Helpers\Helper;
|
|||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Setting;
|
||||
use App\View\Label;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -23,6 +25,13 @@ class BulkAssetsController extends Controller
|
|||
/**
|
||||
* Display the bulk edit page.
|
||||
*
|
||||
* This method is super weird because it's kinda of like a controller within a controller.
|
||||
* It's main function is to determine what the bulk action in, and then return a view with
|
||||
* the information that view needs, be it bulk delete, bulk edit, restore, etc.
|
||||
*
|
||||
* This is something that made sense at the time, but sort of doesn't make sense now. A JS front-end to determine form
|
||||
* action would make a lot more sense here and make things a lot more clear.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param int $assetId
|
||||
|
@ -32,7 +41,10 @@ class BulkAssetsController extends Controller
|
|||
public function edit(Request $request)
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
|
||||
/**
|
||||
* No asset IDs were passed
|
||||
*/
|
||||
if (! $request->filled('ids')) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
|
@ -41,59 +53,52 @@ class BulkAssetsController extends Controller
|
|||
$bulk_back_url = request()->headers->get('referer');
|
||||
session(['bulk_back_url' => $bulk_back_url]);
|
||||
|
||||
$asset_ids = array_values(array_unique($request->input('ids')));
|
||||
|
||||
//custom fields logic
|
||||
$asset_custom_field = Asset::with(['model.fieldset.fields', 'model'])->whereIn('id', $asset_ids)->whereHas('model', function ($query) {
|
||||
return $query->where('fieldset_id', '!=', null);
|
||||
})->get();
|
||||
|
||||
$models = $asset_custom_field->unique('model_id');
|
||||
$asset_ids = $request->input('ids');
|
||||
$assets = Asset::with('assignedTo', 'location', 'model')->find($asset_ids);
|
||||
|
||||
$models = $assets->unique('model_id');
|
||||
$modelNames = [];
|
||||
foreach($models as $model) {
|
||||
$modelNames[] = $model->model->name;
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('bulk_actions')) {
|
||||
|
||||
|
||||
switch ($request->input('bulk_actions')) {
|
||||
case 'labels':
|
||||
$this->authorize('view', Asset::class);
|
||||
$assets_found = Asset::find($asset_ids);
|
||||
|
||||
if ($assets_found->isEmpty()){
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
return (new Label)
|
||||
->with('assets', $assets_found)
|
||||
->with('assets', $assets)
|
||||
->with('settings', Setting::getSettings())
|
||||
->with('bulkedit', true)
|
||||
->with('count', 0);
|
||||
|
||||
case 'delete':
|
||||
$this->authorize('delete', Asset::class);
|
||||
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
$assets->each(function ($assets) {
|
||||
$this->authorize('delete', $assets);
|
||||
});
|
||||
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
|
||||
case 'restore':
|
||||
$this->authorize('update', Asset::class);
|
||||
$assets = Asset::withTrashed()->find($asset_ids);
|
||||
$assets = Asset::withTrashed()->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
});
|
||||
|
||||
return view('hardware/bulk-restore')->with('assets', $assets);
|
||||
|
||||
case 'edit':
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
return view('hardware/bulk')
|
||||
->with('assets', $asset_ids)
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('models', $models->pluck(['model']))
|
||||
->with('models', $models->pluck(['model']))
|
||||
->with('modelNames', $modelNames);
|
||||
}
|
||||
}
|
||||
|
@ -117,25 +122,31 @@ class BulkAssetsController extends Controller
|
|||
|
||||
// Get the back url from the session and then destroy the session
|
||||
$bulk_back_url = route('hardware.index');
|
||||
|
||||
if ($request->session()->has('bulk_back_url')) {
|
||||
$bulk_back_url = $request->session()->pull('bulk_back_url');
|
||||
}
|
||||
|
||||
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
|
||||
|
||||
|
||||
if (Session::exists('ids')) {
|
||||
$assets = Session::get('ids');
|
||||
} elseif (! $request->filled('ids') || count($request->input('ids')) <= 0) {
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
|
||||
$assets = array_keys($request->input('ids'));
|
||||
|
||||
if ($request->anyFilled($custom_field_columns)) {
|
||||
$custom_fields_present = true;
|
||||
} else {
|
||||
$custom_fields_present = false;
|
||||
}
|
||||
|
||||
|
||||
$assets = Asset::whereIn('id', array_keys($request->input('ids')))->get();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If ANY of these are filled, prepare to update the values on the assets.
|
||||
*
|
||||
* Additional checks will be needed for some of them to make sure the values
|
||||
* make sense (for example, changing the status ID to something incompatible with
|
||||
* its checkout status.
|
||||
*/
|
||||
|
||||
if (($request->filled('purchase_date'))
|
||||
|| ($request->filled('expected_checkin'))
|
||||
|| ($request->filled('purchase_cost'))
|
||||
|
@ -154,22 +165,32 @@ class BulkAssetsController extends Controller
|
|||
|| ($request->anyFilled($custom_field_columns))
|
||||
|
||||
) {
|
||||
foreach ($assets as $assetId) {
|
||||
// Let's loop through those assets and build an update array
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$this->update_array = [];
|
||||
|
||||
/**
|
||||
* Leave out model_id and status here because we do math on that later. We have to do some extra
|
||||
* validation and checks on those two.
|
||||
*
|
||||
* It's tempting to make these match the request check above, but some of these values require
|
||||
* extra work to make sure the data makes sense.
|
||||
*/
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
->conditionallyAddItem('expected_checkin')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
->conditionallyAddItem('status_id')
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months')
|
||||
->conditionallyAddItem('next_audit_date');
|
||||
foreach ($custom_field_columns as $key => $custom_field_column) {
|
||||
$this->conditionallyAddItem($custom_field_column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blank out fields that were requested to be blanked out via checkbox
|
||||
*/
|
||||
if ($request->input('null_purchase_date')=='1') {
|
||||
$this->update_array['purchase_date'] = null;
|
||||
}
|
||||
|
@ -186,7 +207,6 @@ class BulkAssetsController extends Controller
|
|||
$this->update_array['purchase_cost'] = $request->input('purchase_cost');
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$this->update_array['company_id'] = $request->input('company_id');
|
||||
if ($request->input('company_id') == 'clear') {
|
||||
|
@ -194,88 +214,129 @@ class BulkAssetsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to change the model ID - we need to do some extra checks here to make sure
|
||||
* the custom field values work for the custom fieldset rules around this asset. Uniqueness
|
||||
* and requiredness across the fieldset is particularly important, since those are
|
||||
* fieldset-specific attributes.
|
||||
*/
|
||||
if ($request->filled('model_id')) {
|
||||
$this->update_array['model_id'] = AssetModel::find($request->input('model_id'))->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to change the status ID - we need to do some extra checks here to
|
||||
* make sure the status label type is one that makes sense for the state of the asset,
|
||||
* for example, we shouldn't be able to make an asset archived if it's currently assigned
|
||||
* to someone/something.
|
||||
*/
|
||||
if ($request->filled('status_id')) {
|
||||
$updated_status = Statuslabel::find($request->input('status_id'));
|
||||
|
||||
// We cannot assign a non-deployable status type if the asset is already assigned.
|
||||
// This could probably be added to a form request.
|
||||
// If the asset isn't assigned, we don't care what the status is.
|
||||
// Otherwise we need to make sure the status type is still a deployable one.
|
||||
if (
|
||||
($asset->assigned_to == '')
|
||||
|| ($updated_status->deployable == '1') && ($asset->assetstatus->deployable == '1')
|
||||
) {
|
||||
$this->update_array['status_id'] = $updated_status->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We're changing the location ID - figure out which location we should apply
|
||||
* this change to:
|
||||
*
|
||||
* 0 - RTD location only
|
||||
* 1 - location ID and RTD location ID
|
||||
* 2 - location ID only
|
||||
*
|
||||
* Note: this is kinda dumb and we should just use human-readable values IMHO. - snipe
|
||||
*/
|
||||
if ($request->filled('rtd_location_id')) {
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '0')) {
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '2')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------------
|
||||
* ANYTHING that happens past this foreach
|
||||
* WILL NOT BE logged in the edit log_meta data
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
$changed = [];
|
||||
$asset = Asset::find($assetId);
|
||||
|
||||
foreach ($this->update_array as $key => $value) {
|
||||
|
||||
if ($this->update_array[$key] != $asset->{$key}) {
|
||||
$changed[$key]['old'] = $asset->{$key};
|
||||
$changed[$key]['new'] = $this->update_array[$key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($custom_fields_present) {
|
||||
|
||||
$model = $asset->model()->first();
|
||||
/**
|
||||
* Start all the custom fields shenanigans
|
||||
*/
|
||||
|
||||
// Use the rules of the new model fieldsets if the model changed
|
||||
if ($request->filled('model_id')) {
|
||||
$this->update_array['model_id'] = $request->input('model_id');
|
||||
$model = \App\Models\AssetModel::find($request->input('model_id'));
|
||||
}
|
||||
// Does the model have a fieldset?
|
||||
if ($asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
|
||||
// Make sure this model is valid
|
||||
$assetCustomFields = ($model) ? $model->fieldset : null;
|
||||
|
||||
if ($assetCustomFields && $assetCustomFields->fields) {
|
||||
|
||||
foreach ($assetCustomFields->fields as $field) {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted=='1')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
|
||||
/*
|
||||
* Check if the decrypted existing value is different from one we just submitted
|
||||
* and if not, pull it out of the object since it shouldn't really be updating at all.
|
||||
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
|
||||
* different times will have different values, so it will *look* like it was updated
|
||||
* but it wasn't.
|
||||
*/
|
||||
if ($decrypted_old != $this->update_array[$field->db_column]) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]);
|
||||
} else {
|
||||
/*
|
||||
* Remove the encrypted custom field from the update_array, since nothing changed
|
||||
*/
|
||||
unset($this->update_array[$field->db_column]);
|
||||
unset($asset->{$field->db_column});
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the decrypted existing value is different from one we just submitted
|
||||
* and if not, pull it out of the object since it shouldn't really be updating at all.
|
||||
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
|
||||
* different times will have different values, so it will *look* like it was updated
|
||||
* but it wasn't.
|
||||
*/
|
||||
if ($decrypted_old != $this->update_array[$field->db_column]) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]);
|
||||
} else {
|
||||
/*
|
||||
* These custom fields aren't encrypted, just carry on as usual
|
||||
* Remove the encrypted custom field from the update_array, since nothing changed
|
||||
*/
|
||||
unset($this->update_array[$field->db_column]);
|
||||
unset($asset->{$field->db_column});
|
||||
}
|
||||
|
||||
/*
|
||||
* These custom fields aren't encrypted, just carry on as usual
|
||||
*/
|
||||
} else {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
|
||||
|
||||
// Check if this is an array, and if so, flatten it
|
||||
if (is_array($this->update_array[$field->db_column])) {
|
||||
$asset->{$field->db_column} = implode(', ', $this->update_array[$field->db_column]);
|
||||
} else {
|
||||
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
|
||||
|
||||
// Check if this is an array, and if so, flatten it
|
||||
if (is_array($this->update_array[$field->db_column])) {
|
||||
$asset->{$field->db_column} = implode(', ', $this->update_array[$field->db_column]);
|
||||
} else {
|
||||
$asset->{$field->db_column} = $this->update_array[$field->db_column];
|
||||
}
|
||||
}
|
||||
$asset->{$field->db_column} = $this->update_array[$field->db_column];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // endforeach
|
||||
} // end custom field check
|
||||
} // end custom fields handler
|
||||
|
||||
} // endforeach
|
||||
}
|
||||
|
||||
|
||||
// Check if it passes validation, and then try to save
|
||||
|
|
|
@ -56,7 +56,7 @@ class ProfileController extends Controller
|
|||
$user->phone = $request->input('phone');
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en');
|
||||
$user->locale = $request->input('locale', 'en-US');
|
||||
}
|
||||
|
||||
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled == '1') && (! config('app.lock_passwords')))) {
|
||||
|
|
|
@ -252,6 +252,9 @@ class ReportsController extends Controller
|
|||
trans('general.model_no'),
|
||||
'To',
|
||||
trans('general.notes'),
|
||||
trans('admin/settings/general.login_ip'),
|
||||
trans('admin/settings/general.login_user_agent'),
|
||||
trans('general.action_source'),
|
||||
'Changed',
|
||||
|
||||
];
|
||||
|
@ -298,6 +301,9 @@ class ReportsController extends Controller
|
|||
$target_name,
|
||||
($actionlog->note) ? e($actionlog->note) : '',
|
||||
$actionlog->log_meta,
|
||||
$actionlog->remote_ip,
|
||||
$actionlog->user_agent,
|
||||
$actionlog->action_source,
|
||||
];
|
||||
fputcsv($handle, $row);
|
||||
}
|
||||
|
|
|
@ -186,7 +186,7 @@ class SettingsController extends Controller
|
|||
$settings->alerts_enabled = 1;
|
||||
$settings->pwd_secure_min = 10;
|
||||
$settings->brand = 1;
|
||||
$settings->locale = $request->input('locale', 'en');
|
||||
$settings->locale = $request->input('locale', 'en-US');
|
||||
$settings->default_currency = $request->input('default_currency', 'USD');
|
||||
$settings->user_id = 1;
|
||||
$settings->email_domain = $request->input('email_domain');
|
||||
|
@ -586,7 +586,7 @@ class SettingsController extends Controller
|
|||
}
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$setting->locale = $request->input('locale', 'en');
|
||||
$setting->locale = $request->input('locale', 'en-US');
|
||||
}
|
||||
$setting->default_currency = $request->input('default_currency', '$');
|
||||
$setting->date_display_format = $request->input('date_display_format');
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Middleware;
|
|||
|
||||
use App\Models\Setting;
|
||||
use Closure;
|
||||
use \App\Helpers\Helper;
|
||||
|
||||
class CheckLocale
|
||||
{
|
||||
|
@ -18,22 +19,28 @@ class CheckLocale
|
|||
*/
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
{
|
||||
|
||||
// Default app settings from config
|
||||
$language = config('app.locale');
|
||||
|
||||
if ($settings = Setting::getSettings()) {
|
||||
|
||||
// User's preference
|
||||
if (($request->user()) && ($request->user()->locale)) {
|
||||
\App::setLocale($request->user()->locale);
|
||||
$language = $request->user()->locale;
|
||||
|
||||
// App setting preference
|
||||
} elseif ($settings->locale != '') {
|
||||
\App::setLocale($settings->locale);
|
||||
|
||||
// Default app setting
|
||||
} else {
|
||||
\App::setLocale(config('app.locale'));
|
||||
$language = $settings->locale;
|
||||
}
|
||||
}
|
||||
\App::setLocale(config('app.locale'));
|
||||
|
||||
}
|
||||
|
||||
if (config('app.locale') != Helper::mapLegacyLocale($language)) {
|
||||
\Log::warning('Your current APP_LOCALE in your .env is set to "'.config('app.locale').'" and should be updated to be "'.Helper::mapLegacyLocale($language).'" in '.base_path().'/.env. Translations may display unexpectedly until this is updated.');
|
||||
}
|
||||
|
||||
\App::setLocale(Helper::mapLegacyLocale($language));
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreAssetRequest extends ImageUploadRequest
|
||||
|
@ -20,7 +20,11 @@ class StoreAssetRequest extends ImageUploadRequest
|
|||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
//
|
||||
$this->merge([
|
||||
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
|
||||
'company_id' => Company::getIdForCurrentUser($this->company_id),
|
||||
'assigned_to' => $assigned_to ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Models\Actionlog;
|
|||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Company;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\Location;
|
||||
|
@ -181,6 +182,9 @@ class ActionlogsTransformer
|
|||
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
|
||||
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
||||
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
||||
'remote_ip' => ($actionlog->remote_ip) ?? null,
|
||||
'user_agent' => ($actionlog->user_agent) ?? null,
|
||||
'action_source' => ($actionlog->action_source) ?? null,
|
||||
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
|
@ -212,6 +216,7 @@ class ActionlogsTransformer
|
|||
{ $location = Location::withTrashed()->get();
|
||||
$supplier = Supplier::withTrashed()->get();
|
||||
$model = AssetModel::withTrashed()->get();
|
||||
$status = Statuslabel::withTrashed()->get();
|
||||
$company = Company::get();
|
||||
|
||||
|
||||
|
@ -285,6 +290,19 @@ class ActionlogsTransformer
|
|||
$clean_meta['Supplier'] = $clean_meta['supplier_id'];
|
||||
unset($clean_meta['supplier_id']);
|
||||
}
|
||||
if(array_key_exists('status_id', $clean_meta)) {
|
||||
|
||||
$oldStatus = $status->find($clean_meta['status_id']['old']);
|
||||
$oldStatusName = $oldStatus ? e($oldStatus->name) : trans('admin/statuslabels/message.deleted_label');
|
||||
|
||||
$newStatus = $status->find($clean_meta['status_id']['new']);
|
||||
$newStatusName = $newStatus ? e($newStatus->name) : trans('admin/statuslabels/message.deleted_label');
|
||||
|
||||
$clean_meta['status_id']['old'] = $clean_meta['status_id']['old'] ? "[id: ".$clean_meta['status_id']['old']."] ". $oldStatusName : trans('general.unassigned');
|
||||
$clean_meta['status_id']['new'] = $clean_meta['status_id']['new'] ? "[id: ".$clean_meta['status_id']['new']."] ". $newStatusName : trans('general.unassigned');
|
||||
$clean_meta['Status'] = $clean_meta['status_id'];
|
||||
unset($clean_meta['status_id']);
|
||||
}
|
||||
if(array_key_exists('asset_eol_date', $clean_meta)) {
|
||||
$clean_meta['EOL date'] = $clean_meta['asset_eol_date'];
|
||||
unset($clean_meta['asset_eol_date']);
|
||||
|
|
|
@ -45,7 +45,7 @@ class AssetMaintenancesTransformer
|
|||
'name'=> e($assetmaintenance->asset->location->name),
|
||||
|
||||
] : null,
|
||||
'rtd_location' => ($assetmaintenance->asset->defaultLoc) ? [
|
||||
'rtd_location' => (($assetmaintenance->asset) && ($assetmaintenance->asset->defaultLoc)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->defaultLoc->id,
|
||||
'name'=> e($assetmaintenance->asset->defaultLoc->name),
|
||||
] : null,
|
||||
|
|
|
@ -25,7 +25,17 @@ class Actionlog extends SnipeModel
|
|||
|
||||
protected $table = 'action_logs';
|
||||
public $timestamps = true;
|
||||
protected $fillable = ['created_at', 'item_type', 'user_id', 'item_id', 'action_type', 'note', 'target_id', 'target_type', 'stored_eula'];
|
||||
protected $fillable = [
|
||||
'created_at',
|
||||
'item_type',
|
||||
'user_id',
|
||||
'item_id',
|
||||
'action_type',
|
||||
'note',
|
||||
'target_id',
|
||||
'target_type',
|
||||
'stored_eula'
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
|
@ -34,7 +44,15 @@ class Actionlog extends SnipeModel
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['action_type', 'note', 'log_meta','user_id'];
|
||||
protected $searchableAttributes = [
|
||||
'action_type',
|
||||
'note',
|
||||
'log_meta',
|
||||
'user_id',
|
||||
'remote_ip',
|
||||
'user_agent',
|
||||
'action_source'
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
@ -248,6 +266,9 @@ class Actionlog extends SnipeModel
|
|||
public function logaction($actiontype)
|
||||
{
|
||||
$this->action_type = $actiontype;
|
||||
$this->remote_ip = request()->ip();
|
||||
$this->user_agent = request()->header('User-Agent');
|
||||
$this->action_source = $this->determineActionSource();
|
||||
|
||||
if ($this->save()) {
|
||||
return true;
|
||||
|
@ -312,4 +333,29 @@ class Actionlog extends SnipeModel
|
|||
->orderBy('created_at', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what the type of request is so we can log it to the action_log
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since v6.3.0
|
||||
* @return string
|
||||
*/
|
||||
public function determineActionSource() {
|
||||
|
||||
// This is an API call
|
||||
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
|
||||
&& (starts_with(request()->header('authorization'), 'Bearer '))) {
|
||||
return 'api';
|
||||
}
|
||||
|
||||
// This is probably NOT an API call
|
||||
if (request()->filled('_token')) {
|
||||
return 'gui';
|
||||
}
|
||||
|
||||
// We're not sure, probably cli
|
||||
return 'cli/unknown';
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,24 +89,27 @@ class Asset extends Depreciable
|
|||
];
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'max:255|nullable',
|
||||
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'company_id' => 'integer|nullable',
|
||||
'warranty_months' => 'numeric|nullable|digits_between:0,240',
|
||||
'physical' => 'numeric|max:1|nullable',
|
||||
'last_checkout' => 'date_format:Y-m-d H:i:s|nullable',
|
||||
'expected_checkin' => 'date|nullable',
|
||||
'location_id' => 'exists:locations,id|nullable',
|
||||
'rtd_location_id' => 'exists:locations,id|nullable',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array',
|
||||
'purchase_date' => 'date|date_format:Y-m-d|nullable',
|
||||
'serial' => 'unique_undeleted:assets,serial|nullable',
|
||||
'purchase_cost' => 'numeric|nullable|gte:0',
|
||||
'supplier_id' => 'exists:suppliers,id|nullable',
|
||||
'asset_eol_date' => 'date|nullable',
|
||||
'eol_explicit' => 'boolean|nullable',
|
||||
'byod' => 'boolean',
|
||||
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array',
|
||||
'name' => 'nullable|max:255',
|
||||
'company_id' => 'nullable|integer|exists:companies,id',
|
||||
'warranty_months' => 'nullable|numeric|digits_between:0,240',
|
||||
'last_checkout' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
'expected_checkin' => 'nullable|date',
|
||||
'location_id' => 'nullable|exists:locations,id',
|
||||
'rtd_location_id' => 'nullable|exists:locations,id',
|
||||
'purchase_date' => 'nullable|date|date_format:Y-m-d',
|
||||
'serial' => 'nullable|unique_undeleted:assets,serial',
|
||||
'purchase_cost' => 'nullable|numeric|gte:0',
|
||||
'supplier_id' => 'nullable|exists:suppliers,id',
|
||||
'asset_eol_date' => 'nullable|date',
|
||||
'eol_explicit' => 'nullable|boolean',
|
||||
'byod' => 'nullable|boolean',
|
||||
'order_number' => 'nullable|string|max:191',
|
||||
'notes' => 'nullable|string|max:65535',
|
||||
'assigned_to' => 'nullable|integer',
|
||||
'requestable' => 'nullable|boolean',
|
||||
];
|
||||
|
||||
|
||||
|
@ -266,7 +269,7 @@ class Asset extends Depreciable
|
|||
|
||||
/**
|
||||
* Determines if an asset is available for checkout.
|
||||
* This checks to see if the it's checked out to an invalid (deleted) user
|
||||
* This checks to see if it's checked out to an invalid (deleted) user
|
||||
* OR if the assigned_to and deleted_at fields on the asset are empty AND
|
||||
* that the status is deployable
|
||||
*
|
||||
|
@ -460,13 +463,22 @@ class Asset extends Depreciable
|
|||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return bool
|
||||
*/
|
||||
public function checkedOutToUser()
|
||||
public function checkedOutToUser(): bool
|
||||
{
|
||||
return $this->assignedType() === self::USER;
|
||||
}
|
||||
|
||||
public function checkedOutToLocation(): bool
|
||||
{
|
||||
return $this->assignedType() === self::LOCATION;
|
||||
}
|
||||
|
||||
public function checkedOutToAsset(): bool
|
||||
{
|
||||
return $this->assignedType() === self::ASSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target this asset is checked out to
|
||||
*
|
||||
|
@ -728,7 +740,7 @@ class Asset extends Depreciable
|
|||
{
|
||||
$days = (is_null($days)) ? 30 : $days;
|
||||
|
||||
return self::where('archived', '=', '0')
|
||||
return self::where('archived', '=', '0') // this can stay for right now, as `archived` defaults to 0 at the db level, but should probably be replaced with assetstatus->archived?
|
||||
->whereNotNull('warranty_months')
|
||||
->whereNotNull('purchase_date')
|
||||
->whereNull('deleted_at')
|
||||
|
@ -753,7 +765,7 @@ class Asset extends Depreciable
|
|||
}
|
||||
|
||||
/**
|
||||
* Establishes the asset -> status relationship
|
||||
* Establishes the asset -> license seats relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
|
@ -921,6 +933,27 @@ class Asset extends Depreciable
|
|||
return $cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN MUTATORS
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* This sets the requestable to a boolean 0 or 1. This accounts for forms or API calls that
|
||||
* explicitly pass the requestable field but it has a null or empty value.
|
||||
*
|
||||
* This will also correctly parse a 1/0 if "true"/"false" is passed.
|
||||
*
|
||||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
public function setRequestableAttribute($value)
|
||||
{
|
||||
$this->attributes['requestable'] = (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
|
|
|
@ -20,10 +20,9 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
use SoftDeletes;
|
||||
use CompanyableChildTrait;
|
||||
use ValidatingTrait;
|
||||
protected $casts = [
|
||||
'start_date' => 'datetime',
|
||||
'completion_date' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
|
||||
protected $table = 'asset_maintenances';
|
||||
protected $rules = [
|
||||
'asset_id' => 'required|integer',
|
||||
|
@ -31,12 +30,31 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
'asset_maintenance_type' => 'required',
|
||||
'title' => 'required|max:100',
|
||||
'is_warranty' => 'boolean',
|
||||
'start_date' => 'required|date',
|
||||
'completion_date' => 'nullable|date',
|
||||
'start_date' => 'required|date_format:Y-m-d',
|
||||
'completion_date' => 'date_format:Y-m-d|nullable',
|
||||
'notes' => 'string|nullable',
|
||||
'cost' => 'numeric|nullable',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'asset_id',
|
||||
'supplier_id',
|
||||
'asset_maintenance_type',
|
||||
'is_warranty',
|
||||
'start_date',
|
||||
'completion_date',
|
||||
'asset_maintenance_time',
|
||||
'notes',
|
||||
'cost',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,5 +86,13 @@ class LabelWriter_30252 extends LabelWriter
|
|||
);
|
||||
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
|
||||
}
|
||||
|
||||
if ($record->has('barcode1d')) {
|
||||
static::write1DBarcode(
|
||||
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
|
||||
$currentX, $barcodeSize + self::BARCODE_MARGIN, $usableWidth - self::TAG_SIZE, self::TAG_SIZE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -67,12 +67,16 @@
|
|||
"unicodeveloper/laravel-password": "^1.0",
|
||||
"watson/validating": "^8.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ldap": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^v6.4.4",
|
||||
"fakerphp/faker": "^1.16",
|
||||
"mockery/mockery": "^1.4",
|
||||
"nunomaduro/larastan": "^2.0",
|
||||
"nunomaduro/phpinsights": "^2.7",
|
||||
"php-mock/php-mock-phpunit": "^2.8",
|
||||
"phpunit/php-token-stream": "^3.1",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
|
|
2084
composer.lock
generated
2084
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -99,7 +99,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
'locale' => env('APP_LOCALE', 'en-US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -112,7 +112,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'fallback_locale' => 'en',
|
||||
'fallback_locale' => 'en-US',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
return array (
|
||||
'app_version' => 'v6.2.4-pre',
|
||||
'full_app_version' => 'v6.2.4-pre - build 12090-g776b16d37',
|
||||
'build_version' => '12090',
|
||||
'full_app_version' => 'v6.2.4-pre - build 12343-gb23ce6cfc',
|
||||
'build_version' => '12343',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'g776b16d37',
|
||||
'full_hash' => 'v6.2.4-pre-329-g776b16d37',
|
||||
'hash_version' => 'gb23ce6cfc',
|
||||
'full_hash' => 'v6.2.4-pre-582-gb23ce6cfc',
|
||||
'branch' => 'develop',
|
||||
);
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"files": [
|
||||
{
|
||||
"source" : "/resources/lang/en/**/*.php",
|
||||
"source" : "/resources/lang/en-US/**/*.php",
|
||||
# https://developer.crowdin.com/configuration-file/#placeholders
|
||||
"translation" : "/resources/lang/%locale%/**/%original_file_name%"
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ class AssetFactory extends Factory
|
|||
'notes' => 'Created by DB seeder',
|
||||
'purchase_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get())->format('Y-m-d'),
|
||||
'purchase_cost' => $this->faker->randomFloat(2, '299.99', '2999.99'),
|
||||
'order_number' => $this->faker->numberBetween(1000000, 50000000),
|
||||
'order_number' => (string) $this->faker->numberBetween(1000000, 50000000),
|
||||
'supplier_id' => Supplier::factory(),
|
||||
'requestable' => $this->faker->boolean(),
|
||||
'assigned_to' => null,
|
||||
|
|
|
@ -99,10 +99,22 @@ class CustomFieldFactory extends Factory
|
|||
return [
|
||||
'name' => 'Test Checkbox',
|
||||
'help_text' => 'This is a sample checkbox.',
|
||||
'field_values' => "One\nTwo\nThree",
|
||||
'field_values' => "One\r\nTwo\r\nThree",
|
||||
'element' => 'checkbox',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function testRadio()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'name' => 'Test Radio',
|
||||
'help_text' => 'This is a sample radio.',
|
||||
'field_values' => "One\r\nTwo\r\nThree",
|
||||
'element' => 'radio',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@ class SettingFactory extends Factory
|
|||
'alerts_enabled' => true,
|
||||
'brand' => 1,
|
||||
'default_currency' => $this->faker->currencyCode(),
|
||||
'locale' => 'en',
|
||||
'locale' => 'en-US',
|
||||
'pwd_secure_min' => 10, // Match web setup
|
||||
'email_domain' => 'test.com',
|
||||
'email_domain' => 'example.org',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class UserFactory extends Factory
|
|||
'first_name' => $this->faker->firstName(),
|
||||
'jobtitle' => $this->faker->jobTitle(),
|
||||
'last_name' => $this->faker->lastName(),
|
||||
'locale' => 'en',
|
||||
'locale' => 'en-US',
|
||||
'notes' => 'Created by DB seeder',
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'permissions' => '{}',
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddImageToAssets extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
/**
|
||||
* This had to be changed back in time :(
|
||||
*
|
||||
* This migration was named stupidly in 2015, and we're now seeing weird namespace errors
|
||||
* popping up in 2023, likely due to different laravel or PHP versions. This migration will run again on
|
||||
* more updated systems, since the name of the migration has changed and therefore will look "new" to the
|
||||
* migrations table/migration system, which is why we need to check if the
|
||||
* field already exists. Thanks, I hate it. - snipe
|
||||
*/
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('assets', 'image')) {
|
||||
$table->text('image')->after('notes')->nullable()->default(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
/**
|
||||
* I'm leaving this one out, since it could destroy data that was already long-existing.
|
||||
*/
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class Image extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->text('image')->after('notes')->nullable()->default(null);
|
||||
//
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('assets', function ($table) {
|
||||
$table->dropColumn('image');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
use App\Models\Asset;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
@ -25,24 +26,26 @@ class DenormalizedEolAndAddColumnForExplicitDateToAssets extends Migration
|
|||
|
||||
|
||||
// Update the eol_explicit column with the value from asset_eol_date if it exists and is different from the calculated value
|
||||
Asset::whereNotNull('asset_eol_date')->with('model')->chunkById(500, function ($assetsWithEolDates) {
|
||||
foreach ($assetsWithEolDates as $asset) {
|
||||
if ($asset->asset_eol_date && $asset->purchase_date) {
|
||||
try {
|
||||
$months = CarbonImmutable::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
|
||||
} catch (\Exception $e) {
|
||||
Log::info('asset_eol_date invalid for asset ' . $asset->id);
|
||||
}
|
||||
if ($asset->model->eol) {
|
||||
if ($months != $asset->model->eol) {
|
||||
$asset->update(['eol_explicit' => true]);
|
||||
}
|
||||
} else {
|
||||
$asset->update(['eol_explicit' => true]);
|
||||
Asset::whereNotNull('asset_eol_date')->with('model')->chunkById(500, function ($assetsWithEolDates) {
|
||||
foreach ($assetsWithEolDates as $asset) {
|
||||
if ($asset->asset_eol_date && $asset->purchase_date) {
|
||||
try {
|
||||
$months = CarbonImmutable::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
|
||||
} catch (\Exception $e) {
|
||||
Log::info('asset_eol_date invalid for asset ' . $asset->id);
|
||||
}
|
||||
if ($asset->model->eol) {
|
||||
if ($months != $asset->model->eol) {
|
||||
DB::table('assets')->where('id', $asset->id)->update(['eol_explicit' => true]);
|
||||
}
|
||||
}
|
||||
// if there is NO model eol, but there is a purchase date and an asset_eol_date (which is what is left over) the asset_eol_date has still been explicitly set
|
||||
else {
|
||||
DB::table('assets')->where('id', $asset->id)->update(['eol_explicit' => true]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
DB::table('assets')
|
||||
->whereNull('asset_eol_date')
|
||||
|
@ -50,7 +53,7 @@ class DenormalizedEolAndAddColumnForExplicitDateToAssets extends Migration
|
|||
->whereNotNull('model_id')
|
||||
->join('models', 'assets.model_id', '=', 'models.id')
|
||||
->update([
|
||||
'asset_eol_date' => DB::raw('DATE_ADD(purchase_date, INTERVAL ' . DB::getTablePrefix() . 'models.eol MONTH)')
|
||||
'asset_eol_date' => $this->eolUpdateExpression(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -63,7 +66,25 @@ class DenormalizedEolAndAddColumnForExplicitDateToAssets extends Migration
|
|||
public function down()
|
||||
{
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->dropColumn('eol_explicit');
|
||||
$table->dropColumn('eol_explicit');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the correct database expression for either
|
||||
* mysql, postgres, or sqlite depending on the driver being used.
|
||||
*/
|
||||
private function eolUpdateExpression(): Expression
|
||||
{
|
||||
if (DB::getDriverName() === 'sqlite') {
|
||||
return DB::raw("DATE(purchase_date, '+' || (SELECT eol FROM " . DB::getTablePrefix() . "models WHERE models.id = assets.model_id) || ' months')");
|
||||
}
|
||||
|
||||
if (DB::getDriverName() === 'pgsql') {
|
||||
return DB::raw("date(purchase_date + interval '1 month' * (SELECT eol FROM " . DB::getTablePrefix() . "models WHERE models.id = assets.model_id))");
|
||||
}
|
||||
|
||||
// Default to MySQL's method
|
||||
return DB::raw('DATE_ADD(purchase_date, INTERVAL ' . DB::getTablePrefix() . 'models.eol MONTH)');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddRemoteIpAndActionSourceToActionLogs extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->string('action_source')->nullable()->default(null);
|
||||
$table->ipAddress('remote_ip')->nullable()->default(null);
|
||||
$table->string('user_agent')->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('action_logs', 'action_source')) {
|
||||
$table->dropColumn('action_source');
|
||||
}
|
||||
if (Schema::hasColumn('action_logs', 'remote_ip')) {
|
||||
$table->dropColumn('remote_ip');
|
||||
}
|
||||
if (Schema::hasColumn('action_logs', 'user_agent')) {
|
||||
$table->dropColumn('user_agent');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddIndexesToNewActivityReportFields extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->index('action_type');
|
||||
$table->index('remote_ip');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->dropIndex(['action_type']);
|
||||
$table->dropIndex(['remote_ip']);
|
||||
});
|
||||
}
|
||||
}
|
60
database/migrations/2023_12_19_081112_fix_language_dirs.php
Normal file
60
database/migrations/2023_12_19_081112_fix_language_dirs.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use \App\Helpers\Helper;
|
||||
use \App\Models\Setting;
|
||||
use \App\Models\User;
|
||||
|
||||
class FixLanguageDirs extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
/**
|
||||
* Update the settings table
|
||||
*/
|
||||
$settings = Setting::getSettings();
|
||||
if (($settings) && ($settings->locale != '')) {
|
||||
DB::table('settings')->update(['locale' => Helper::mapLegacyLocale($settings->locale)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the users table
|
||||
*/
|
||||
$users = User::whereNotNull('locale')->get();
|
||||
// Skip the model in case the validation rules have changed
|
||||
foreach ($users as $user) {
|
||||
DB::table('users')->where('id', $user->id)->update(['locale' => Helper::mapLegacyLocale($user->locale)]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
if (($settings) && ($settings->locale != '')) {
|
||||
DB::table('settings')->update(['locale' => Helper::mapBackToLegacyLocale($settings->locale)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the users table
|
||||
*/
|
||||
$users = User::whereNotNull('locale')->whereNull('deleted_at')->get();
|
||||
// Skip the model in case the validation rules have changed
|
||||
foreach ($users as $user) {
|
||||
DB::table('users')->where('id', $user->id)->update(['locale' => Helper::mapBackToLegacyLocale($user->locale)]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ class CustomFieldSeeder extends Seeder
|
|||
CustomField::factory()->count(1)->macAddress()->create();
|
||||
CustomField::factory()->count(1)->testEncrypted()->create();
|
||||
CustomField::factory()->count(1)->testCheckbox()->create();
|
||||
CustomField::factory()->count(1)->testRadio()->create();
|
||||
|
||||
|
||||
DB::table('custom_field_custom_fieldset')->insert([
|
||||
|
@ -96,6 +97,19 @@ class CustomFieldSeeder extends Seeder
|
|||
'required' => 0,
|
||||
],
|
||||
|
||||
[
|
||||
'custom_field_id' => '8',
|
||||
'custom_fieldset_id' => '2',
|
||||
'order' => 0,
|
||||
'required' => 0,
|
||||
],
|
||||
[
|
||||
'custom_field_id' => '8',
|
||||
'custom_fieldset_id' => '1',
|
||||
'order' => 0,
|
||||
'required' => 0,
|
||||
],
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,14 +32,14 @@ class SettingsSeeder extends Seeder
|
|||
$settings->date_display_format = 'D M d, Y';
|
||||
$settings->time_display_format = 'g:iA';
|
||||
$settings->thumbnail_max_h = '30';
|
||||
$settings->locale = 'en';
|
||||
$settings->locale = 'en-US';
|
||||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = 'on';
|
||||
$settings->pwd_secure_min = '8';
|
||||
$settings->save();
|
||||
|
||||
if ($user = User::where('username', '=', 'admin')->first()) {
|
||||
$user->locale = 'en';
|
||||
$user->locale = 'en-US';
|
||||
$user->save();
|
||||
}
|
||||
|
||||
|
|
4107
package-lock.json
generated
4107
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -21,12 +21,12 @@
|
|||
"postcss": "^8.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||
"@fortawesome/fontawesome-free": "^6.5.0",
|
||||
"acorn": "^8.11.2",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
"ajv": "^6.12.6",
|
||||
"alpinejs": "^3.13.2",
|
||||
"alpinejs": "^3.13.3",
|
||||
"blueimp-file-upload": "^9.34.0",
|
||||
"bootstrap": "^3.4.1",
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
|
@ -40,10 +40,9 @@
|
|||
"imagemin": "^8.0.1",
|
||||
"jquery-slimscroll": "^1.3.8",
|
||||
"jquery-ui": "^1.13.2",
|
||||
"jquery-ui-bundle": "^1.12.1",
|
||||
"jquery-validation": "^1.20.0",
|
||||
"jquery.iframe-transport": "^1.0.0",
|
||||
"jspdf-autotable": "^3.7.1",
|
||||
"jquery-validation": "^1.20.0",
|
||||
"jspdf-autotable": "^3.8.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^6.0",
|
||||
"list.js": "^1.5.0",
|
||||
|
|
Binary file not shown.
Binary file not shown.
BIN
public/css/dist/all.css
vendored
BIN
public/css/dist/all.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.css
vendored
BIN
public/css/dist/skins/skin-black-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/js/dist/all.js
vendored
BIN
public/js/dist/all.js
vendored
Binary file not shown.
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=4e2935e5a3efd1c7012d8495effbf2c7",
|
||||
"/js/build/app.js": "/js/build/app.js?id=2004100dd5c106d15fa9b6a16d6dd341",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=d98b12844cffc1164b4b55914b2b4942",
|
||||
"/css/build/app.css": "/css/build/app.css?id=2feeba91641a015279abd18e96be92a6",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=77475bffdab35fb2cd9ebbcd3ebe6dd6",
|
||||
"/css/build/app.css": "/css/build/app.css?id=7cc9ef2d080c39e4b940df85f68fdf1b",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
|
||||
|
@ -12,32 +13,31 @@
|
|||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=9f944e8021781af1ce45d27765d1c0c2",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f4d95ad9d0944587549e35b6929b4b04",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=6cf460bed48ab738041f60231a3f005a",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=0ed42b67f9b02a74815e885bfd9e3f66",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=5da4021acc73e624ba356e6943178a76",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=2d918bd9fb07c257fa5a0069a68a6363",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=a656b2d865fe379d8851757e8e4001ef",
|
||||
"/css/webfonts/fa-brands-400.woff2": "/css/webfonts/fa-brands-400.woff2?id=a6761c03f9c021aeb9754283755b660e",
|
||||
"/css/webfonts/fa-regular-400.ttf": "/css/webfonts/fa-regular-400.ttf?id=75a44676b684c784508452a53867e8da",
|
||||
"/css/webfonts/fa-regular-400.woff2": "/css/webfonts/fa-regular-400.woff2?id=c612044da7f7b81ab8f2cadf94964208",
|
||||
"/css/webfonts/fa-solid-900.ttf": "/css/webfonts/fa-solid-900.ttf?id=3867ccc7ab681d3c27d1e97a792b46d3",
|
||||
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=7f63d634454e771396bce3e09dfcdbc5",
|
||||
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=70ad875b2378eb850254f01dec991ade",
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=d36941873b661076f146b0221f13497d",
|
||||
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=69e5d8e4e818f05fd882cceb758d1eba",
|
||||
"/css/webfonts/fa-brands-400.woff2": "/css/webfonts/fa-brands-400.woff2?id=189b85e9c72c6f75e464c3f58a6707cf",
|
||||
"/css/webfonts/fa-regular-400.ttf": "/css/webfonts/fa-regular-400.ttf?id=ed4c23399d1013809882e90bfe396d1b",
|
||||
"/css/webfonts/fa-regular-400.woff2": "/css/webfonts/fa-regular-400.woff2?id=be75b1958ae0da55e1eed562d9b7713d",
|
||||
"/css/webfonts/fa-solid-900.ttf": "/css/webfonts/fa-solid-900.ttf?id=dfdc7801582dd0d20ea75faa3b96c296",
|
||||
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=a0feb384c3c6071947a49708f2b0bc85",
|
||||
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=e24ec0b8661f7fa333b29444df39e399",
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=e11465c0eff0549edd4e8ea6bbcf242f",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=2bd29fa7f9d666800c246a52ce708633",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=ede02ee2aad89fe10c64a87f6c76838a",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=db2e005808d5a2d2e7f4a82059e5d16f",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=1f678160a05960c3087fb8263168ff41",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=b9dbb598e7c52f20fa595893cfa1199d",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=99dcf5ec2b67d0a21ebd3ef2c05b99e4",
|
||||
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=7f9a130eda6916eaa32a0a57e81918f3",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=0ed42b67f9b02a74815e885bfd9e3f66",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=f4d95ad9d0944587549e35b6929b4b04",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=6cf460bed48ab738041f60231a3f005a",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
|
|
|
@ -399,7 +399,7 @@ img.navbar-brand-img, .navbar-brand>img {
|
|||
}
|
||||
|
||||
.icon-med {
|
||||
font-size: 20px;
|
||||
font-size: 14px;
|
||||
color: #889195;
|
||||
}
|
||||
|
||||
|
@ -859,4 +859,32 @@ input[type="radio"]:checked::before {
|
|||
|
||||
/** --------------------------------------- **/
|
||||
/** End checkbox styles to replace iCheck **/
|
||||
/** --------------------------------------- **/
|
||||
/** --------------------------------------- **/
|
||||
|
||||
/**
|
||||
/** Separator styles with text in the middle. Currently only used by the login page but
|
||||
/** could be used elsewhere.
|
||||
*/
|
||||
|
||||
.separator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
color: #959495;
|
||||
}
|
||||
|
||||
.separator::before,
|
||||
.separator::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
border-bottom: 1px solid #959495;
|
||||
}
|
||||
|
||||
.separator:not(:empty)::before {
|
||||
margin-right: .25em;
|
||||
}
|
||||
|
||||
.separator:not(:empty)::after {
|
||||
margin-left: .25em;
|
||||
}
|
|
@ -124,11 +124,11 @@ a {
|
|||
--button-primary: darken(@black, 25%);
|
||||
--button-hover: darken(@black, 30%);
|
||||
--header: @black; /* Use same as Header picker */
|
||||
--text-main: #BBB;
|
||||
--text-main: #fff;
|
||||
--text-sub: #9b9b9b;
|
||||
--link: #AAA; /* Use same as Header picker, lighten by 70% */
|
||||
--visited-link: lighten(@black, 40%); /* Use same as Header picker, lighten by 70% */
|
||||
--hover-link: lighten(@black, 45%); /* Use same as Header picker, lighten by 70% */
|
||||
--link: #fff; /* Use same as Header picker, lighten by 70% */
|
||||
--visited-link: #fff; /* Use same as Header picker, lighten by 70% */
|
||||
--hover-link: #949494; /* Use same as Header picker, lighten by 70% */
|
||||
--nav-link: #FFF; /* Use same as Header picker */
|
||||
--light-link: #fff; /* Use same as Header picker */
|
||||
}
|
||||
|
@ -193,18 +193,6 @@ h2.task_menu{
|
|||
color: var(--text-main);
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--nav-link);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--hover-link);
|
||||
}
|
||||
|
||||
.btn-primary.hover {
|
||||
color: var(--nav-link);
|
||||
}
|
||||
|
@ -318,8 +306,11 @@ input[type=text], input[type=search] {
|
|||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.search-highlight, .search-highlight:hover{
|
||||
background-color: var(--back-sub) !important;
|
||||
}
|
||||
.input-group, .input-group-addon {
|
||||
background-color: var(--back-sub)!important;
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable>tbody>tr>td>nobr>a>i.fa {
|
||||
|
@ -363,11 +354,11 @@ input[type=text], input[type=search] {
|
|||
}
|
||||
.select2-container--default .select2-results__option[aria-selected=true], .select2-container--default .select2-results__option[aria-selected=true]:hover {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--header);
|
||||
color: var(--nav-link);
|
||||
}
|
||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: var(--header);
|
||||
color: var(--back-main);
|
||||
background-color: var(--back-sub);
|
||||
color: var(--visited-link);
|
||||
}
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
color: var(--text-main);
|
||||
|
@ -426,9 +417,6 @@ a {
|
|||
color: var(--hover-link);
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:visited {
|
||||
color: var(--visited-link)
|
||||
}
|
||||
}
|
||||
|
||||
.row-striped {
|
||||
|
@ -463,4 +451,7 @@ a {
|
|||
}
|
||||
div.container.row-new-striped{
|
||||
background-color: var(--back-sub);
|
||||
}
|
||||
.table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th {
|
||||
background-color: var(--back-sub);
|
||||
}
|
39
resources/lang/af-ZA/admin/accessories/message.php
Normal file
39
resources/lang/af-ZA/admin/accessories/message.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
|
||||
'does_not_exist' => 'The accessory [:id] does not exist.',
|
||||
'not_found' => 'That accessory was not found.',
|
||||
'assoc_users' => 'Hierdie bykomstige het tans: tel items wat uitgekontroleer is aan gebruikers. Kontroleer asseblief die bykomstighede en en probeer weer.',
|
||||
|
||||
'create' => array(
|
||||
'error' => 'Die bykomstigheid is nie geskep nie, probeer asseblief weer.',
|
||||
'success' => 'Die bykomstigheid is suksesvol geskep.'
|
||||
),
|
||||
|
||||
'update' => array(
|
||||
'error' => 'Die bykomstigheid is nie opgedateer nie, probeer asseblief weer',
|
||||
'success' => 'Die bykomstigheid is suksesvol opgedateer.'
|
||||
),
|
||||
|
||||
'delete' => array(
|
||||
'confirm' => 'Is jy seker jy wil hierdie toebehore uitvee?',
|
||||
'error' => 'Daar was \'n probleem met die verwydering van die bykomstigheid. Probeer asseblief weer.',
|
||||
'success' => 'Die bykomstigheid is suksesvol verwyder.'
|
||||
),
|
||||
|
||||
'checkout' => array(
|
||||
'error' => 'Toebehore is nie nagegaan nie, probeer asseblief weer',
|
||||
'success' => 'Toebehore suksesvol nagegaan.',
|
||||
'unavailable' => 'Accessory is not available for checkout. Check quantity available',
|
||||
'user_does_not_exist' => 'Die gebruiker is ongeldig. Probeer asseblief weer.'
|
||||
),
|
||||
|
||||
'checkin' => array(
|
||||
'error' => 'Toebehore is nie nagegaan nie, probeer asseblief weer',
|
||||
'success' => 'Toebehore is suksesvol nagegaan.',
|
||||
'user_does_not_exist' => 'Die gebruiker is ongeldig. Probeer asseblief weer.'
|
||||
)
|
||||
|
||||
|
||||
);
|
7
resources/lang/af-ZA/admin/companies/general.php
Normal file
7
resources/lang/af-ZA/admin/companies/general.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'select_company' => 'Kies Maatskappy',
|
||||
'about_companies' => 'Oor Maatskappye',
|
||||
'about_companies_description' => ' You can use companies as a simple informative field, or you can use them to restrict asset visibility and availability to users with a specific company by enabling Full Company Support in your Admin Settings.',
|
||||
];
|
61
resources/lang/af-ZA/admin/custom_fields/general.php
Normal file
61
resources/lang/af-ZA/admin/custom_fields/general.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'custom_fields' => 'Aangepaste velde',
|
||||
'manage' => 'Manage',
|
||||
'field' => 'veld',
|
||||
'about_fieldsets_title' => 'Oor Fieldsets',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used for specific asset model types.',
|
||||
'custom_format' => 'Custom Regex format...',
|
||||
'encrypt_field' => 'Enkripteer die waarde van hierdie veld in die databasis',
|
||||
'encrypt_field_help' => 'WAARSKUWING: Om \'n veld te enkripteer, maak dit onondersoekbaar.',
|
||||
'encrypted' => 'encrypted',
|
||||
'fieldset' => 'fieldset',
|
||||
'qty_fields' => 'Aantal velde',
|
||||
'fieldsets' => 'Fieldsets',
|
||||
'fieldset_name' => 'Veldsetnaam',
|
||||
'field_name' => 'Veldnaam',
|
||||
'field_values' => 'Veldwaardes',
|
||||
'field_values_help' => 'Voeg selekteer opsies by, een per reël. Blanke lyne behalwe die eerste reël sal geïgnoreer word.',
|
||||
'field_element' => 'Vorm Element',
|
||||
'field_element_short' => 'element',
|
||||
'field_format' => 'formaat',
|
||||
'field_custom_format' => 'Gepasmaakte formaat',
|
||||
'field_custom_format_help' => 'This field allows you to use a regex expression for validation. It should start with "regex:" - for example, to validate that a custom field value contains a valid IMEI (15 numeric digits), you would use <code>regex:/^[0-9]{15}$/</code>.',
|
||||
'required' => 'vereis',
|
||||
'req' => 'Req.',
|
||||
'used_by_models' => 'Gebruik deur modelle',
|
||||
'order' => 'Orde',
|
||||
'create_fieldset' => 'Nuwe Fieldset',
|
||||
'update_fieldset' => 'Update Fieldset',
|
||||
'fieldset_does_not_exist' => 'Fieldset :id does not exist',
|
||||
'fieldset_updated' => 'Fieldset updated',
|
||||
'create_fieldset_title' => 'Create a new fieldset',
|
||||
'create_field' => 'Nuwe aangepaste veld',
|
||||
'create_field_title' => 'Create a new custom field',
|
||||
'value_encrypted' => 'Die waarde van hierdie veld is geïnkripteer in die databasis. Slegs admingebruikers sal die gedecodeerde waarde kan sien',
|
||||
'show_in_email' => 'Include the value of this field in checkout emails sent to the user? Encrypted fields cannot be included in emails',
|
||||
'show_in_email_short' => 'Include in emails.',
|
||||
'help_text' => 'Help Text',
|
||||
'help_text_description' => 'This is optional text that will appear below the form elements while editing an asset to provide context on the field.',
|
||||
'about_custom_fields_title' => 'About Custom Fields',
|
||||
'about_custom_fields_text' => 'Custom fields allow you to add arbitrary attributes to assets.',
|
||||
'add_field_to_fieldset' => 'Add Field to Fieldset',
|
||||
'make_optional' => 'Required - click to make optional',
|
||||
'make_required' => 'Optional - click to make required',
|
||||
'reorder' => 'Reorder',
|
||||
'db_field' => 'DB Field',
|
||||
'db_convert_warning' => 'WARNING. This field is in the custom fields table as <code>:db_column</code> but should be <code>:expected</code>.',
|
||||
'is_unique' => 'This value must be unique across all assets',
|
||||
'unique' => 'Unique',
|
||||
'display_in_user_view' => 'Allow the checked out user to view these values in their View Assigned Assets page',
|
||||
'display_in_user_view_table' => 'Visible to User',
|
||||
'auto_add_to_fieldsets' => 'Automatically add this to every new fieldset',
|
||||
'add_to_preexisting_fieldsets' => 'Add to any existing fieldsets',
|
||||
'show_in_listview' => 'Show in list views by default. Authorized users will still be able to show/hide via the column selector',
|
||||
'show_in_listview_short' => 'Show in lists',
|
||||
'show_in_requestable_list_short' => 'Show in requestable assets list',
|
||||
'show_in_requestable_list' => 'Show value in requestable assets list. Encrypted fields will not be shown',
|
||||
'encrypted_options' => 'This field is encrypted, so some display options will not be available.',
|
||||
|
||||
];
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue