Compare commits
114 Commits
dockerhub-
...
rework_bul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa07f21d11 | ||
|
|
f3bfbc888a | ||
|
|
5c5405dbd5 | ||
|
|
b0451fc552 | ||
|
|
94e6e5e210 | ||
|
|
27c31312d6 | ||
|
|
98eaa9e2da | ||
|
|
82723a3eb6 | ||
|
|
f93089904a | ||
|
|
03efa06f90 | ||
|
|
16f316967e | ||
|
|
35cbd58dad | ||
|
|
d50bcd6f55 | ||
|
|
83565b2a1e | ||
|
|
cc01f15f28 | ||
|
|
e38ea126d5 | ||
|
|
a9a6a80e04 | ||
|
|
53f97680a4 | ||
|
|
9be02c62c8 | ||
|
|
1e4d452a03 | ||
|
|
0a4d6e3631 | ||
|
|
d862da2345 | ||
|
|
73ffa3d751 | ||
|
|
f77ed72111 | ||
|
|
702f1d0343 | ||
|
|
0bacbe175a | ||
|
|
851cb29fc6 | ||
|
|
e3e164fa3b | ||
|
|
7ef83dee56 | ||
|
|
7183cec43f | ||
|
|
3456c99255 | ||
|
|
966745e852 | ||
|
|
f9372df48f | ||
|
|
3e26acd5cb | ||
|
|
6cc10b9992 | ||
|
|
732e125a1d | ||
|
|
32f7eae6a3 | ||
|
|
bd635cedba | ||
|
|
676f94175e | ||
|
|
35b47cd57f | ||
|
|
5e1ee05e19 | ||
|
|
1413ba0b0b | ||
|
|
a182c43d8e | ||
|
|
dbd864bf32 | ||
|
|
0009142b76 | ||
|
|
4058312eb9 | ||
|
|
b65cb967be | ||
|
|
b5dc39e70e | ||
|
|
b81416456a | ||
|
|
2671e8197a | ||
|
|
9cf23d18e2 | ||
|
|
c5ecc8f839 | ||
|
|
c1c5814000 | ||
|
|
b68c2e0f11 | ||
|
|
9e3e04521e | ||
|
|
65dfbd02fe | ||
|
|
649ab53320 | ||
|
|
9250624f79 | ||
|
|
995e2090f5 | ||
|
|
9b91584776 | ||
|
|
8fd97ea501 | ||
|
|
a80b9ab362 | ||
|
|
556e1081b3 | ||
|
|
b070916f0b | ||
|
|
940caf14b0 | ||
|
|
76da1d6663 | ||
|
|
bafff9020a | ||
|
|
0d5dca6456 | ||
|
|
0f9b7119c0 | ||
|
|
d4181549e8 | ||
|
|
d57f56e44f | ||
|
|
7d9b87f059 | ||
|
|
c157f4190e | ||
|
|
9357eca1cd | ||
|
|
40c65a07a4 | ||
|
|
13521bcf75 | ||
|
|
1c09dc139a | ||
|
|
d5f955b1e0 | ||
|
|
9e6e8f0931 | ||
|
|
c93ef30801 | ||
|
|
3e0dec4856 | ||
|
|
0b167f5f6f | ||
|
|
f6b21fdb82 | ||
|
|
f151628808 | ||
|
|
e44aad0328 | ||
|
|
1881054c92 | ||
|
|
f7533c5e41 | ||
|
|
f181e0fa55 | ||
|
|
b04efdfefc | ||
|
|
352b935dee | ||
|
|
0ba3b9975a | ||
|
|
cc06187f31 | ||
|
|
a916767392 | ||
|
|
1c57bfaa39 | ||
|
|
4a5adeb661 | ||
|
|
01f9772291 | ||
|
|
960b3aebed | ||
|
|
d75de73867 | ||
|
|
e75df97902 | ||
|
|
5be14ec750 | ||
|
|
717a82f46a | ||
|
|
e40038900b | ||
|
|
099eabc240 | ||
|
|
3a4fa35398 | ||
|
|
500d6a0cc2 | ||
|
|
38e5bf71bc | ||
|
|
45ff195f11 | ||
|
|
ce543c8179 | ||
|
|
5c11a8c1e0 | ||
|
|
e3a2397b74 | ||
|
|
3b34654dd7 | ||
|
|
4b6437854c | ||
|
|
4ef161214d | ||
|
|
29d0380db3 |
4
.github/workflows/codacy-analysis.yml
vendored
4
.github/workflows/codacy-analysis.yml
vendored
@@ -10,10 +10,10 @@ name: Codacy Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
branches: [ develop ]
|
||||
schedule:
|
||||
- cron: '36 23 * * 3'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Snipe-IT Docker image build for hub.docker.com
|
||||
name: Docker Intel/amd64 images (Ubuntu)
|
||||
# Snipe-IT (Alpine) Docker image build for hub.docker.com
|
||||
name: Docker images (Alpine)
|
||||
|
||||
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
|
||||
# Also run for PRs to ensure PR doesn't break Docker build process
|
||||
@@ -19,72 +19,7 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
docker-ubuntu-intel:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
if: github.repository == 'grokability/snipe-it'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
|
||||
# For a new commit on other branches, use the branch name as the tag for Docker image.
|
||||
# For a new tag, copy that tag name as the tag for Docker image.
|
||||
IMAGE_TAGS: |
|
||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
|
||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
|
||||
type=ref,event=tag
|
||||
type=semver,pattern=v{{major}}-latest
|
||||
# Define default tag "flavor" for docker/metadata-action per
|
||||
# https://github.com/docker/metadata-action#flavor-input
|
||||
# We turn off 'latest' tag by default.
|
||||
TAGS_FLAVOR: |
|
||||
latest=false
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/checkout
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Login to DockerHub
|
||||
# Only login if not a PR, as PRs only trigger a Docker build and not a push
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
###############################################
|
||||
# Build/Push the 'snipe/snipe-it' image
|
||||
###############################################
|
||||
# https://github.com/docker/metadata-action
|
||||
# Get Metadata for docker_build step below
|
||||
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
|
||||
id: meta_build
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: snipe/snipe-it
|
||||
tags: ${{ env.IMAGE_TAGS }}
|
||||
flavor: ${{ env.TAGS_FLAVOR }}
|
||||
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push 'snipe-it' image
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
# Use tags / labels provided by 'docker/metadata-action' above
|
||||
tags: ${{ steps.meta_build.outputs.tags }}
|
||||
labels: ${{ steps.meta_build.outputs.labels }}
|
||||
docker-alpine-intel:
|
||||
docker:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
if: github.repository == 'grokability/snipe-it'
|
||||
runs-on: ubuntu-latest
|
||||
@@ -142,7 +77,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.alpine
|
||||
platforms: linux/amd64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
@@ -1,5 +1,5 @@
|
||||
# Snipe-IT Docker image build for hub.docker.com
|
||||
name: Docker ARM images (Ubuntu and Alpine)
|
||||
name: Docker images (Ubuntu)
|
||||
|
||||
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
|
||||
# Also run for PRs to ensure PR doesn't break Docker build process
|
||||
@@ -19,10 +19,10 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
docker-ubuntu-arm:
|
||||
docker:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
if: github.repository == 'grokability/snipe-it'
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
|
||||
@@ -77,72 +77,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/arm64
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
# Use tags / labels provided by 'docker/metadata-action' above
|
||||
tags: ${{ steps.meta_build.outputs.tags }}
|
||||
labels: ${{ steps.meta_build.outputs.labels }}
|
||||
docker-alpine-arm:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
if: github.repository == 'grokability/snipe-it'
|
||||
runs-on: ubuntu-24.04-arm
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
|
||||
# For a new commit on other branches, use the branch name as the tag for Docker image.
|
||||
# For a new tag, copy that tag name as the tag for Docker image.
|
||||
IMAGE_TAGS: |
|
||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
|
||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
|
||||
type=ref,event=tag,suffix=-alpine
|
||||
type=semver,pattern=v{{major}}-latest-alpine
|
||||
# Define default tag "flavor" for docker/metadata-action per
|
||||
# https://github.com/docker/metadata-action#flavor-input
|
||||
# We turn off 'latest' tag by default.
|
||||
TAGS_FLAVOR: |
|
||||
latest=false
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/checkout
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Login to DockerHub
|
||||
# Only login if not a PR, as PRs only trigger a Docker build and not a push
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
###############################################
|
||||
# Build/Push the 'snipe/snipe-it' image
|
||||
###############################################
|
||||
# https://github.com/docker/metadata-action
|
||||
# Get Metadata for docker_build step below
|
||||
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
|
||||
id: meta_build
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: snipe/snipe-it
|
||||
tags: ${{ env.IMAGE_TAGS }}
|
||||
flavor: ${{ env.TAGS_FLAVOR }}
|
||||
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push 'snipe-it' image
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.alpine
|
||||
platforms: linux/arm64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/grokability/snipe-it/actions/workflows/tests.yml)
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://app.codacy.com/gh/grokability/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/grokability/snipe-it/actions/workflows/tests.yml)
|
||||
[](#contributing) [](https://discord.gg/yZFtShAcKk)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
@@ -249,6 +249,7 @@ class RestoreFromBackup extends Command
|
||||
'storage/private_uploads/consumables',
|
||||
'storage/private_uploads/eula-pdfs',
|
||||
'storage/private_uploads/imports',
|
||||
'storage/private_uploads/locations',
|
||||
'storage/private_uploads/licenses',
|
||||
'storage/private_uploads/signatures',
|
||||
'storage/private_uploads/users',
|
||||
|
||||
@@ -61,14 +61,12 @@ class Handler extends ExceptionHandler
|
||||
public function render($request, Throwable $e)
|
||||
{
|
||||
|
||||
|
||||
// CSRF token mismatch error
|
||||
if ($e instanceof \Illuminate\Session\TokenMismatchException) {
|
||||
return redirect()->back()->with('error', trans('general.token_expired'));
|
||||
}
|
||||
|
||||
// Invalid JSON exception
|
||||
// TODO: don't understand why we have to do this when we have the invalidJson() method, below, but, well, whatever
|
||||
if ($e instanceof JsonException) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid JSON'), 422);
|
||||
}
|
||||
@@ -88,8 +86,9 @@ class Handler extends ExceptionHandler
|
||||
return redirect()->back()->withInput()->with('error', trans('validation.date', ['attribute' => 'date']));
|
||||
}
|
||||
|
||||
|
||||
// Handle API requests that fail
|
||||
if ($request->ajax() || $request->wantsJson()) {
|
||||
if ($request->ajax() || $request->wantsJson() || ($request->route() && $request->route()->named('api.files.*'))) {
|
||||
|
||||
// Handle API requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date)
|
||||
if ($e instanceof InvalidFormatException) {
|
||||
@@ -102,6 +101,7 @@ class Handler extends ExceptionHandler
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200);
|
||||
}
|
||||
|
||||
|
||||
// Handle API requests that fail because of an HTTP status code and return a useful error message
|
||||
if ($this->isHttpException($e)) {
|
||||
|
||||
@@ -116,12 +116,24 @@ class Handler extends ExceptionHandler
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
|
||||
default:
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// This handles API validation exceptions that happen at the Form Request level, so they
|
||||
// never even get to the controller where we normally nicely format JSON responses
|
||||
if ($e instanceof ValidationException) {
|
||||
$response = $this->invalidJson($request, $e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200);
|
||||
}
|
||||
|
||||
// return response()->json(Helper::formatStandardApiResponse('error', null, 'Undefined exception'), 200);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This is traaaaash but it handles models that are not found while using route model binding :(
|
||||
// The only alternative is to set that at *each* route, which is crazypants
|
||||
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
|
||||
|
||||
@@ -722,8 +722,8 @@ class Helper
|
||||
// The check and message that the user is still using the deprecated version
|
||||
$deprecations = [
|
||||
'ms_teams_deprecated' => array(
|
||||
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'),
|
||||
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Jan 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
|
||||
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows') && (Setting::getSettings()->webhook_selected === 'microsoft'),
|
||||
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Dec 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
|
||||
];
|
||||
|
||||
// if item of concern is being used and its being used with the deprecated values return the notification array.
|
||||
@@ -1487,6 +1487,7 @@ class Helper
|
||||
$redirect_option = Session::get('redirect_option');
|
||||
$checkout_to_type = Session::get('checkout_to_type');
|
||||
$checkedInFrom = Session::get('checkedInFrom');
|
||||
$other_redirect = Session::get('other_redirect');
|
||||
|
||||
// return to index
|
||||
if ($redirect_option == 'index') {
|
||||
@@ -1535,6 +1536,16 @@ class Helper
|
||||
return route('hardware.show', $request->assigned_asset ?? $checkedInFrom);
|
||||
}
|
||||
}
|
||||
|
||||
// return to somewhere else
|
||||
if ($redirect_option == 'other_redirect') {
|
||||
switch ($other_redirect) {
|
||||
case 'audit':
|
||||
return route('assets.audit.due');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
|
||||
}
|
||||
|
||||
|
||||
@@ -48,17 +48,29 @@ class StorageHelper
|
||||
'avif',
|
||||
'webp',
|
||||
'png',
|
||||
'gif',
|
||||
];
|
||||
|
||||
|
||||
// The file exists and is allowed to be displayed inline
|
||||
if (Storage::exists($file_with_path) && (in_array(pathinfo($file_with_path, PATHINFO_EXTENSION), $allowed_inline))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public static function getFiletype($file_with_path) {
|
||||
|
||||
// The file exists and is allowed to be displayed inline
|
||||
if (Storage::exists($file_with_path)) {
|
||||
return pathinfo($file_with_path, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether to show the file inline or download it.
|
||||
*/
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Transformers\UploadedFilesTransformer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assets')) {
|
||||
Storage::makeDirectory('private_uploads/assets', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
|
||||
|
||||
$asset->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function list(Asset $asset, Request $request) : JsonResponse | array
|
||||
{
|
||||
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'filename',
|
||||
'eol',
|
||||
'notes',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')->where('item_type', '=', Asset::class)->where('item_id', '=', $asset->id);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$files = $files->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $files->count()) ? $files->count() : abs($request->input('offset'));
|
||||
$limit = app('api_limit_value');
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$files = $files->orderBy($sort, $order);
|
||||
|
||||
$files = $files->skip($offset)->take($limit)->get();
|
||||
return (new UploadedFilesTransformer())->transformFiles($files, $files->count());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function show(Asset $asset, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assets/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function destroy(Asset $asset, $fileId = null) : JsonResponse
|
||||
{
|
||||
|
||||
$rel_path = 'private_uploads/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\AssetModelsTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetModelFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetModelId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assetmodels')) {
|
||||
Storage::makeDirectory('private_uploads/assetmodels', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
|
||||
|
||||
$assetModel->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetmodel
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function list($assetmodel_id) : JsonResponse | array
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetmodel_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
|
||||
$this->authorize('view', $assetmodel);
|
||||
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('view', $assetModel);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assetmodels/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$rel_path = 'private_uploads/assetmodels';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class CategoriesController extends Controller
|
||||
'notes',
|
||||
])
|
||||
->with('adminuser')
|
||||
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count');
|
||||
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
|
||||
|
||||
|
||||
/*
|
||||
@@ -212,7 +212,7 @@ class CategoriesController extends Controller
|
||||
public function destroy($id) : JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count')->findOrFail($id);
|
||||
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
|
||||
|
||||
if (! $category->isDeletable()) {
|
||||
return response()->json(
|
||||
|
||||
@@ -34,7 +34,7 @@ class LicenseSeatsController extends Controller
|
||||
if ($request->input('sort') == 'department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} else {
|
||||
$seats->orderBy('id', $order);
|
||||
$seats->orderBy('updated_at', $order);
|
||||
}
|
||||
|
||||
$total = $seats->count();
|
||||
|
||||
242
app/Http/Controllers/Api/UploadedFilesController.php
Normal file
242
app/Http/Controllers/Api/UploadedFilesController.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\UploadedFilesTransformer;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
|
||||
class UploadedFilesController extends Controller
|
||||
{
|
||||
|
||||
|
||||
static $map_object_type = [
|
||||
'accessories' => Accessory::class,
|
||||
'assets' => Asset::class,
|
||||
'components' => Component::class,
|
||||
'consumables' => Consumable::class,
|
||||
'locations' => Location::class,
|
||||
'models' => AssetModel::class,
|
||||
'users' => User::class,
|
||||
];
|
||||
|
||||
static $map_storage_path = [
|
||||
'accessories' => 'private_uploads/accessories/',
|
||||
'assets' => 'private_uploads/assets/',
|
||||
'components' => 'private_uploads/components/',
|
||||
'consumables' => 'private_uploads/consumables/',
|
||||
'locations' => 'private_uploads/locations/',
|
||||
'models' => 'private_uploads/assetmodels/',
|
||||
'users' => 'private_uploads/users/',
|
||||
];
|
||||
|
||||
static $map_file_prefix= [
|
||||
'accessories' => 'accessory',
|
||||
'assets' => 'asset',
|
||||
'components' => 'component',
|
||||
'consumables' => 'consumable',
|
||||
'locations' => 'location',
|
||||
'models' => 'model',
|
||||
'users' => 'user',
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List the files for an object.
|
||||
*
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function index(Request $request, $object_type, $id) : JsonResponse | array
|
||||
{
|
||||
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
// Columns allowed for sorting
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'filename',
|
||||
'action_type',
|
||||
'note',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
$uploads = $object->uploads();
|
||||
$offset = ($request->input('offset') > $object->count()) ? $object->count() : abs($request->input('offset'));
|
||||
$limit = app('api_limit_value');
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'action_logs.created_at';
|
||||
|
||||
// Text search on action_logs fields
|
||||
// We could use the normal Actionlogs text scope, but it's a very heavy query since it's searcghing across all relations
|
||||
// And we generally won't need that here
|
||||
if ($request->filled('search')) {
|
||||
|
||||
$uploads->where(function ($query) use ($request) {
|
||||
$query->where('filename', 'LIKE', '%' . $request->input('search') . '%')
|
||||
->orWhere('note', 'LIKE', '%' . $request->input('search') . '%');
|
||||
});
|
||||
}
|
||||
|
||||
$uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get();
|
||||
return (new UploadedFilesTransformer())->transformFiles($uploads, $uploads->count());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetModelId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $object_type, $id) : JsonResponse
|
||||
{
|
||||
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
// If the file storage directory doesn't exist, create it
|
||||
if (! Storage::exists(self::$map_storage_path[$object_type])) {
|
||||
Storage::makeDirectory(self::$map_storage_path[$object_type], 775);
|
||||
}
|
||||
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// Loop over the attached files and add them to the object
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile(self::$map_storage_path[$object_type],self::$map_file_prefix[$object_type].'-'.$object->id, $file);
|
||||
$files[] = $file_name;
|
||||
$object->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
|
||||
->where('item_type', '=', self::$map_object_type[$object_type])
|
||||
->where('item_id', '=', $id)->whereIn('filename', $files)
|
||||
->get();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files))));
|
||||
}
|
||||
|
||||
// No files were submitted
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.nofiles')));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param AssetModel $model
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function show($object_type, $id, $file_id) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')
|
||||
->where('item_type', AssetModel::class)
|
||||
->where('item_id', $object->id)->find($file_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_id')), 404);
|
||||
}
|
||||
|
||||
|
||||
if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.file_not_found'), 200));
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param AssetModel $model
|
||||
* @param int $fileId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function destroy($object_type, $id, $file_id) : JsonResponse
|
||||
{
|
||||
\Log::error('destroy called for '.$object_type.' with id '.$id.' and file_id '.$file_id);
|
||||
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('update', self::$map_object_type[$object_type]);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
|
||||
->where('item_id', $object->id)->first();
|
||||
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
|
||||
Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
if ($log->delete()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans_choice('general.file_upload_status.delete.success', 1)), 200);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,11 @@ class AssetCheckinController extends Controller
|
||||
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
// Validate custom fields on existing asset
|
||||
$validator = Validator::make($asset->toArray(), $asset->customFieldValidationRules());
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->route('hardware.edit', $asset)
|
||||
->withErrors($validator);
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
$target_option = match ($asset->assigned_type) {
|
||||
|
||||
@@ -37,13 +37,13 @@ class AssetCheckoutController extends Controller
|
||||
->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
// Validate custom fields on existing asset
|
||||
$validator = Validator::make($asset->toArray(), $asset->customFieldValidationRules());
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->route('hardware.edit', $asset)
|
||||
->withErrors($validator);
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'))
|
||||
|
||||
@@ -45,7 +45,7 @@ class AssetFilesController extends Controller
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
return redirect()->back()->with('error', trans('general.file_upload_status.nofiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -519,7 +519,7 @@ class AssetsController extends Controller
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (($settings->qr_code == '1') && ($settings->label2_2d_type !== 'none')) {
|
||||
if ($settings->label2_2d_type !== 'none') {
|
||||
|
||||
if ($asset) {
|
||||
$size = Helper::barcodeDimensions($settings->label2_2d_type);
|
||||
@@ -882,12 +882,12 @@ class AssetsController extends Controller
|
||||
$this->authorize('audit', Asset::class);
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
// Validate custom fields on existing asset
|
||||
$validator = Validator::make($asset->toArray(), $asset->customFieldValidationRules());
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->route('hardware.edit', $asset)
|
||||
->withErrors($validator);
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
@@ -899,6 +899,10 @@ class AssetsController extends Controller
|
||||
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
||||
session()->put('redirect_option', $request->get('redirect_option'));
|
||||
session()->put('other_redirect', 'audit');
|
||||
|
||||
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
@@ -974,7 +978,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
|
||||
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.audit.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
||||
|
||||
@@ -484,6 +484,7 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
$request->session()->regenerate(true);
|
||||
$request->session()->forget('2fa_authed');
|
||||
|
||||
if ($request->session()->has('password_hash_'.Auth::getDefaultDriver())){
|
||||
$request->session()->remove('password_hash_'.Auth::getDefaultDriver());
|
||||
|
||||
@@ -145,7 +145,7 @@ class CategoriesController extends Controller
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
// Check if the category exists
|
||||
if (is_null($category = Category::findOrFail($categoryId))) {
|
||||
if (is_null($category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($categoryId))) {
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
|
||||
}
|
||||
|
||||
@@ -155,7 +155,6 @@ class CategoriesController extends Controller
|
||||
|
||||
Storage::disk('public')->delete('categories'.'/'.$category->image);
|
||||
$category->delete();
|
||||
// Redirect to the locations management page
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -737,6 +737,11 @@ class ReportsController extends Controller
|
||||
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
|
||||
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
|
||||
}
|
||||
|
||||
if (($request->filled('last_updated_start')) && ($request->filled('last_updated_end'))) {
|
||||
$assets->whereBetween('assets.updated_at', [$request->input('last_updated_start'), $request->input('last_updated_end')]);
|
||||
}
|
||||
|
||||
if ($request->filled('exclude_archived')) {
|
||||
$assets->notArchived();
|
||||
}
|
||||
|
||||
@@ -25,32 +25,25 @@ class UserFilesController extends Controller
|
||||
public function store(UploadFileRequest $request, User $user)
|
||||
{
|
||||
$this->authorize('update', $user);
|
||||
$files = $request->file('file');
|
||||
|
||||
if (is_null($files)) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/users/', 'user-'.$user->id, $file);
|
||||
if ($request->hasFile('file')) {
|
||||
|
||||
//Log the uploaded file to the log
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->item_type = User::class;
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->note = $request->input('notes');
|
||||
$logAction->target_id = null;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->filename = $file_name;
|
||||
$logAction->action_type = 'uploaded';
|
||||
|
||||
if (! $logAction->save()) {
|
||||
return JsonResponse::create(['error' => 'Failed validation: '.print_r($logAction->getErrors(), true)], 500);
|
||||
if (! Storage::exists('private_uploads/users')) {
|
||||
Storage::makeDirectory('private_uploads/users', 775);
|
||||
}
|
||||
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success'));
|
||||
$file_count = 0;
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/users/','hardware-'.$user->id, $file);
|
||||
$user->logUpload($file_name, $request->get('notes'));
|
||||
$file_count++;
|
||||
}
|
||||
|
||||
return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.upload.success', $file_count));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('general.file_upload_status.nofiles'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ class StoreAssetModelRequest extends ImageUploadRequest
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
parent::prepareForValidation();
|
||||
|
||||
if ($this->category_id) {
|
||||
if ($category = Category::find($this->category_id)) {
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Http\Traits\ConvertsBase64ToFiles;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use \App\Helpers\Helper;
|
||||
|
||||
class UploadFileRequest extends Request
|
||||
{
|
||||
@@ -27,7 +28,7 @@ class UploadFileRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
|
||||
$max_file_size = Helper::file_upload_max_size();
|
||||
|
||||
return [
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
|
||||
@@ -37,34 +38,52 @@ class UploadFileRequest extends Request
|
||||
/**
|
||||
* Sanitizes (if needed) and Saves a file to the appropriate location
|
||||
* Returns the 'short' (storage-relative) filename
|
||||
*
|
||||
* TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there
|
||||
* a way to merge them or extend one into the other?
|
||||
*/
|
||||
public function handleFile(string $dirname, string $name_prefix, $file): string
|
||||
{
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
|
||||
|
||||
$file_name = $name_prefix.'-'.str_random(8).'-'.str_replace(' ', '-', $file->getClientOriginalName());
|
||||
|
||||
// Check for SVG and sanitize it
|
||||
if ($file->getMimeType() === 'image/svg+xml') {
|
||||
Log::debug('This is an SVG');
|
||||
Log::debug($file_name);
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
Storage::put($dirname.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug('Upload no workie :( ');
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
$uploaded_file = $this->handleSVG($file);
|
||||
} else {
|
||||
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
|
||||
$uploaded_file = file_get_contents($file);
|
||||
}
|
||||
|
||||
try {
|
||||
Storage::put($dirname.$file_name, $uploaded_file);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
return $file_name;
|
||||
}
|
||||
|
||||
public function handleSVG($file) {
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
return $sanitizer->sanitize($dirtySVG);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the validation error messages that apply to the request, but
|
||||
* replace the attribute name with the name of the file that was attempted and failed
|
||||
* to make it clearer to the user which file is the bad one.
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
$attributes = [];
|
||||
|
||||
if ($this->file) {
|
||||
for ($i = 0; $i < count($this->file); $i++) {
|
||||
$attributes['file.'.$i] = $this->file[$i]->getClientOriginalName();
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ namespace App\Http\Transformers;
|
||||
|
||||
class DatatablesTransformer
|
||||
{
|
||||
|
||||
/**
|
||||
* Transform data for bootstrap tables and API responses for lists of things
|
||||
**/
|
||||
public function transformDatatables($objects, $total = null)
|
||||
{
|
||||
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
|
||||
@@ -11,4 +15,15 @@ class DatatablesTransformer
|
||||
|
||||
return $objects_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform data for returning the status of items within a bulk action
|
||||
**/
|
||||
public function transformBulkResponseWithStatusAndObjects($objects, $total)
|
||||
{
|
||||
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
|
||||
$objects_array['rows'] = $objects;
|
||||
|
||||
return $objects_array;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,15 @@ class LicenseSeatsTransformer
|
||||
public function transformLicenseSeats(Collection $seats, $total)
|
||||
{
|
||||
$array = [];
|
||||
$seat_count = 0;
|
||||
|
||||
foreach ($seats as $seat) {
|
||||
$seat_count++;
|
||||
$array[] = self::transformLicenseSeat($seat, $seat_count);
|
||||
$array[] = self::transformLicenseSeat($seat);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformLicenseSeat(LicenseSeat $seat, $seat_count = 0)
|
||||
public function transformLicenseSeat(LicenseSeat $seat)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $seat->id,
|
||||
@@ -55,10 +54,6 @@ class LicenseSeatsTransformer
|
||||
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
|
||||
];
|
||||
|
||||
if ($seat_count != 0) {
|
||||
$array['name'] = trans('admin/licenses/general.seat_count', ['count' => $seat_count]);
|
||||
}
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => Gate::allows('checkout', License::class),
|
||||
'checkin' => Gate::allows('checkin', License::class),
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UploadedFilesTransformer
|
||||
@@ -26,23 +26,26 @@ class UploadedFilesTransformer
|
||||
{
|
||||
$snipeModel = $file->item_type;
|
||||
|
||||
|
||||
// This will be used later as we extend out this transformer to handle more types of uploads
|
||||
if ($file->item_type == Asset::class) {
|
||||
$file_url = route('show/assetfile', [$file->item_id, $file->id]);
|
||||
}
|
||||
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'icon' => Helper::filetype_icon($file->filename),
|
||||
'name' => e($file->filename),
|
||||
'item' => ($file->item_type) ? [
|
||||
'id' => (int) $file->item_id,
|
||||
'type' => strtolower(class_basename($file->item_type)),
|
||||
] : null,
|
||||
'filename' => e($file->filename),
|
||||
'url' => $file_url,
|
||||
'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
|
||||
'url' => $file->uploads_file_url(),
|
||||
'note' => ($file->note) ? e($file->note) : null,
|
||||
'created_by' => ($file->adminuser) ? [
|
||||
'id' => (int) $file->adminuser->id,
|
||||
'name'=> e($file->adminuser->present()->fullName),
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
|
||||
'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()),
|
||||
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
@@ -53,4 +56,5 @@ class UploadedFilesTransformer
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -71,12 +71,12 @@ class SlackSettingsForm extends Component
|
||||
|
||||
$this->setting = Setting::getSettings();
|
||||
$this->save_button = trans('general.save');
|
||||
$this->webhook_selected = $this->setting->webhook_selected;
|
||||
$this->webhook_name = $this->webhook_text[$this->setting->webhook_selected]["name"];
|
||||
$this->webhook_icon = $this->webhook_text[$this->setting->webhook_selected]["icon"];
|
||||
$this->webhook_placeholder = $this->webhook_text[$this->setting->webhook_selected]["placeholder"];
|
||||
$this->webhook_link = $this->webhook_text[$this->setting->webhook_selected]["link"];
|
||||
$this->webhook_test = $this->webhook_text[$this->setting->webhook_selected]["test"];
|
||||
$this->webhook_selected = $this->setting->webhook_selected ?? 'slack';
|
||||
$this->webhook_name = $this->webhook_text[$this->setting->webhook_selected]["name"] ?? $this->webhook_text['slack']["name"];
|
||||
$this->webhook_icon = $this->webhook_text[$this->setting->webhook_selected]["icon"] ?? $this->webhook_text['slack']["icon"];
|
||||
$this->webhook_placeholder = $this->webhook_text[$this->setting->webhook_selected]["placeholder"] ?? $this->webhook_text['slack']["placeholder"];
|
||||
$this->webhook_link = $this->webhook_text[$this->setting->webhook_selected]["link"] ?? $this->webhook_text['slack']["link"];
|
||||
$this->webhook_test = $this->webhook_text[$this->setting->webhook_selected]["test"] ?? $this->webhook_text['slack']["test"];
|
||||
$this->webhook_endpoint = $this->setting->webhook_endpoint;
|
||||
$this->webhook_channel = $this->setting->webhook_channel;
|
||||
$this->webhook_botname = $this->setting->webhook_botname;
|
||||
@@ -90,7 +90,7 @@ class SlackSettingsForm extends Component
|
||||
$this->isDisabled= '';
|
||||
}
|
||||
if($this->webhook_selected === 'microsoft' && $this->teams_webhook_deprecated) {
|
||||
session()->flash('warning', 'The selected Microsoft Teams webhook URL will be deprecated Jan 31st, 2025. Please use a workflow URL. Microsofts Documentation on creating a workflow can be found <a href="https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498" target="_blank"> here.</a>');
|
||||
session()->flash('warning', trans('admin/settings/message.webhook.ms_teams_deprecation'));
|
||||
}
|
||||
}
|
||||
public function updated($field) {
|
||||
@@ -191,6 +191,7 @@ class SlackSettingsForm extends Component
|
||||
$this->setting->webhook_endpoint = '';
|
||||
$this->setting->webhook_channel = '';
|
||||
$this->setting->webhook_botname = '';
|
||||
$this->setting->webhook_selected = '';
|
||||
|
||||
$this->setting->save();
|
||||
|
||||
|
||||
@@ -444,6 +444,62 @@ class Actionlog extends SnipeModel
|
||||
|
||||
}
|
||||
|
||||
public function uploads_file_url()
|
||||
{
|
||||
|
||||
switch ($this->item_type) {
|
||||
case Accessory::class:
|
||||
return route('show/accessoryfile', [$this->item_id, $this->id]);
|
||||
case Asset::class:
|
||||
return route('show/assetfile', [$this->item_id, $this->id]);
|
||||
case AssetModel::class:
|
||||
return route('show/modelfile', [$this->item_id, $this->id]);
|
||||
case Consumable::class:
|
||||
return route('show/locationsfile', [$this->item_id, $this->id]);
|
||||
case Component::class:
|
||||
return route('show/componentsfile', [$this->item_id, $this->id]);
|
||||
case License::class:
|
||||
return route('show/licensesfile', [$this->item_id, $this->id]);
|
||||
case Location::class:
|
||||
return route('show/locationsfile', [$this->item_id, $this->id]);
|
||||
case User::class:
|
||||
return route('show/userfile', [$this->item_id, $this->id]);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function uploads_file_path()
|
||||
{
|
||||
|
||||
switch ($this->item_type) {
|
||||
case Accessory::class:
|
||||
return 'private_uploads/accessories/'.$this->filename;
|
||||
case Asset::class:
|
||||
return 'private_uploads/assets/'.$this->filename;
|
||||
case AssetModel::class:
|
||||
return 'private_uploads/assetmodels/'.$this->filename;
|
||||
case Consumable::class:
|
||||
return 'private_uploads/consumables/'.$this->filename;
|
||||
case Component::class:
|
||||
return 'private_uploads/components/'.$this->filename;
|
||||
case License::class:
|
||||
return 'private_uploads/licenses/'.$this->filename;
|
||||
case Location::class:
|
||||
return 'private_uploads/locations/'.$this->filename;
|
||||
case User::class:
|
||||
return 'private_uploads/users/'.$this->filename;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Manually sets $this->source for determineActionSource()
|
||||
public function setActionSource($source = null): void
|
||||
{
|
||||
|
||||
@@ -656,6 +656,8 @@ class Asset extends Depreciable
|
||||
return Storage::disk('public')->url(app('assets_upload_path').e($this->image));
|
||||
} elseif ($this->model && ! empty($this->model->image)) {
|
||||
return Storage::disk('public')->url(app('models_upload_path').e($this->model->image));
|
||||
} elseif ($this->model->category && ! empty($this->model->category->image)) {
|
||||
return Storage::disk('public')->url(app('categories_upload_path').e($this->model->category->image));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -100,6 +100,14 @@ class Category extends SnipeModel
|
||||
public function isDeletable()
|
||||
{
|
||||
|
||||
// We have to check for models as well if the category type is asset
|
||||
if ($this->category_type == 'asset') {
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->itemCount() == 0)
|
||||
&& ($this->models_count == 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->itemCount() == 0)
|
||||
&& ($this->deleted_at == '');
|
||||
|
||||
@@ -113,7 +113,10 @@ class CustomFieldset extends Model
|
||||
$rule[] = 'unique_undeleted';
|
||||
}
|
||||
|
||||
array_push($rule, $field->attributes['format']);
|
||||
if ($field->attributes['format']!='') {
|
||||
array_push($rule, $field->attributes['format']);
|
||||
}
|
||||
|
||||
$rules[$field->db_column_name()] = $rule;
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
use Notifiable;
|
||||
use Presentable;
|
||||
use Searchable;
|
||||
use Loggable;
|
||||
|
||||
protected $hidden = ['password', 'remember_token', 'permissions', 'reset_password_code', 'persist_code'];
|
||||
protected $table = 'users';
|
||||
@@ -320,7 +321,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
if ($setting->name_display_format=='last_first') {
|
||||
if ($setting?->name_display_format == 'last_first') {
|
||||
return ($this->last_name) ? $this->last_name.' '.$this->first_name : $this->first_name;
|
||||
}
|
||||
return $this->last_name ? $this->first_name.' '.$this->last_name : $this->first_name;
|
||||
|
||||
@@ -298,6 +298,7 @@ class AssetPresenter extends Presenter
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.byod'),
|
||||
'class' => 'byod',
|
||||
'formatter' => 'trueFalseFormatter',
|
||||
|
||||
],
|
||||
|
||||
@@ -230,16 +230,7 @@ class LicensePresenter extends Presenter
|
||||
'switchable' => true,
|
||||
'title' => trans('general.id'),
|
||||
'visible' => false,
|
||||
],
|
||||
[
|
||||
'field' => 'name',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'sorter' => 'numericOnly',
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/licenses/general.seat'),
|
||||
'visible' => true,
|
||||
], [
|
||||
],[
|
||||
'field' => 'assigned_user',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
@@ -285,7 +276,7 @@ class LicensePresenter extends Presenter
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.date'),
|
||||
'title' => trans('general.updated_at'),
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
[
|
||||
|
||||
101
app/Presenters/UploadsPresenter.php
Normal file
101
app/Presenters/UploadsPresenter.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Presenters;
|
||||
|
||||
/**
|
||||
* Class AccessoryPresenter
|
||||
*/
|
||||
class UploadsPresenter extends Presenter
|
||||
{
|
||||
/**
|
||||
* Json Column Layout for bootstrap table
|
||||
* @return string
|
||||
*/
|
||||
public static function dataTableLayout($object)
|
||||
{
|
||||
|
||||
if ($object =='assets') {
|
||||
$object = 'hardware';
|
||||
}
|
||||
|
||||
$layout = [
|
||||
[
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.id'),
|
||||
'visible' => false,
|
||||
],
|
||||
[
|
||||
'field' => 'icon',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => false,
|
||||
'title' => trans('general.type'),
|
||||
'formatter' => 'iconFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'image',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.image'),
|
||||
'formatter' => 'inlineImageFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'filename',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.file_name'),
|
||||
'visible' => true,
|
||||
'formatter' => 'fileUploadNameFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'download',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.download'),
|
||||
'visible' => true,
|
||||
'formatter' => 'downloadOrOpenInNewWindowFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'note',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.notes'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => false,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'created_at',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.created_at'),
|
||||
'visible' => false,
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
], [
|
||||
'field' => 'available_actions',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'deleteUploadFormatter',
|
||||
],
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -67,6 +67,11 @@ class BreadcrumbsServiceProvider extends ServiceProvider
|
||||
->push(trans('general.create'), route('hardware.create'))
|
||||
);
|
||||
|
||||
Breadcrumbs::for('clone/hardware', fn (Trail $trail) =>
|
||||
$trail->parent('hardware.index', route('hardware.index'))
|
||||
->push(trans('admin/hardware/general.clone'), route('hardware.create'))
|
||||
);
|
||||
|
||||
Breadcrumbs::for('hardware.show', fn (Trail $trail, Asset $asset) =>
|
||||
$trail->parent('hardware.index', route('hardware.index'))
|
||||
->push($asset->present()->fullName(), route('hardware.show', $asset))
|
||||
@@ -378,6 +383,12 @@ class BreadcrumbsServiceProvider extends ServiceProvider
|
||||
->push(trans('general.create'), route('locations.create'))
|
||||
);
|
||||
|
||||
Breadcrumbs::for('clone/location', fn (Trail $trail) =>
|
||||
$trail->parent('locations.index', route('locations.index'))
|
||||
->push(trans('admin/locations/table.clone'), route('locations.create'))
|
||||
);
|
||||
|
||||
|
||||
Breadcrumbs::for('locations.show', fn (Trail $trail, Location $location) =>
|
||||
$trail->parent('locations.index', route('locations.index'))
|
||||
->push($location->name, route('locations.show', $location))
|
||||
@@ -539,6 +550,13 @@ class BreadcrumbsServiceProvider extends ServiceProvider
|
||||
->push(trans('general.create'), route('users.create'))
|
||||
);
|
||||
|
||||
Breadcrumbs::for('users.clone.show', fn (Trail $trail) =>
|
||||
$trail->parent('users.index', route('users.index'))
|
||||
->push(trans('admin/users/general.clone'), route('users.create'))
|
||||
);
|
||||
|
||||
|
||||
|
||||
Breadcrumbs::for('users.show', fn (Trail $trail, User $user) =>
|
||||
$trail->parent('users.index', route('users.index'))
|
||||
->push($user->getFullNameAttribute() ?? 'Missing Username!', route('users.show', $user))
|
||||
|
||||
12
composer.lock
generated
12
composer.lock
generated
@@ -3558,16 +3558,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/commonmark.git",
|
||||
"reference": "d990688c91cedfb69753ffc2512727ec646df2ad"
|
||||
"reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d990688c91cedfb69753ffc2512727ec646df2ad",
|
||||
"reference": "d990688c91cedfb69753ffc2512727ec646df2ad",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
|
||||
"reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3604,7 +3604,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.7-dev"
|
||||
"dev-main": "2.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -3661,7 +3661,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-29T14:10:59+00:00"
|
||||
"time": "2025-05-05T12:20:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/config",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v8.1.2',
|
||||
'full_app_version' => 'v8.1.2 - build 17914-g251851ec6',
|
||||
'build_version' => '17914',
|
||||
'app_version' => 'v8.1.3',
|
||||
'full_app_version' => 'v8.1.3 - build 18054-gd67933ab4',
|
||||
'build_version' => '18054',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'g251851ec6',
|
||||
'full_hash' => 'v8.1.2-32-g251851ec6',
|
||||
'hash_version' => 'gd67933ab4',
|
||||
'full_hash' => 'v8.1.3-133-gd67933ab4',
|
||||
'branch' => 'develop',
|
||||
);
|
||||
35
public/css/dist/skins/_all-skins.css
vendored
35
public/css/dist/skins/_all-skins.css
vendored
@@ -614,11 +614,6 @@ input[type=search] {
|
||||
.search-highlight:hover {
|
||||
background-color: var(--back-sub) !important;
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -1408,11 +1403,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -2378,11 +2368,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -2918,11 +2903,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -3654,11 +3634,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -4406,11 +4381,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -5357,11 +5327,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
35
public/css/dist/skins/_all-skins.min.css
vendored
35
public/css/dist/skins/_all-skins.min.css
vendored
@@ -614,11 +614,6 @@ input[type=search] {
|
||||
.search-highlight:hover {
|
||||
background-color: var(--back-sub) !important;
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -1408,11 +1403,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -2378,11 +2368,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -2918,11 +2903,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -3654,11 +3634,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -4406,11 +4381,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
@@ -5357,11 +5327,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-black-dark.css
vendored
5
public/css/dist/skins/skin-black-dark.css
vendored
@@ -396,11 +396,6 @@ input[type=search] {
|
||||
.search-highlight:hover {
|
||||
background-color: var(--back-sub) !important;
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
@@ -396,11 +396,6 @@ input[type=search] {
|
||||
.search-highlight:hover {
|
||||
background-color: var(--back-sub) !important;
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-blue-dark.css
vendored
5
public/css/dist/skins/skin-blue-dark.css
vendored
@@ -377,11 +377,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-blue-dark.min.css
vendored
5
public/css/dist/skins/skin-blue-dark.min.css
vendored
@@ -377,11 +377,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-green-dark.css
vendored
5
public/css/dist/skins/skin-green-dark.css
vendored
@@ -368,11 +368,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
@@ -368,11 +368,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-orange-dark.css
vendored
5
public/css/dist/skins/skin-orange-dark.css
vendored
@@ -378,11 +378,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
@@ -378,11 +378,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-purple-dark.css
vendored
5
public/css/dist/skins/skin-purple-dark.css
vendored
@@ -378,11 +378,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
@@ -378,11 +378,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-red-dark.css
vendored
5
public/css/dist/skins/skin-red-dark.css
vendored
@@ -377,11 +377,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-red-dark.min.css
vendored
5
public/css/dist/skins/skin-red-dark.min.css
vendored
@@ -377,11 +377,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
5
public/css/dist/skins/skin-yellow-dark.css
vendored
5
public/css/dist/skins/skin-yellow-dark.css
vendored
@@ -355,11 +355,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
@@ -355,11 +355,6 @@ input[type=search] {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.input-group,
|
||||
.input-group-addon {
|
||||
background-color: var(--back-sub) !important;
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable > tbody > tr > td > nobr > a > i.fa {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
2577
public/js/dist/bootstrap-table.js
vendored
2577
public/js/dist/bootstrap-table.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=19253af36b58ed3fb6770c7bb944f079",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=06c13e817cc022028b3f4a33c0ca303a",
|
||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=3955411f214d1aa011fd509b6c81fbea",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=78bfb1c7b5782df4fb0ac7e36f80f847",
|
||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=503d0b09e157a22f555e3670d1ec9bb5",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=2bfc7b71d951c5ac026dbc034f7373b1",
|
||||
"/css/build/app.css": "/css/build/app.css?id=4b4c2f1225d59efa7a22b76f7bbe39d8",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=5bbc74fa36ad7e85114e95c99f244421",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=f6b2e7fa795596ac4754500c9c30eacc",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=b70f0d0cf76a643b3fbf47a1dfdb1529",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=bcba8a437f59c2334f9bd32f668ff9bb",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=6fe68325d5356197672c27bc77cedcb4",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=25f255bc860b5472b201ef700fd638a8",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=093bd9d22c19a903b826bb4a1c819263",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=07e257b423e6a8f984e1d9bf0bb04fe0",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=723ac4a390d628c9253d5416494cdaf4",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=bb648ddce4bc923ac79205a78882248d",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=624d1d9b9bf141a593cfe835fdfc85cc",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=a82b065847bf3cd5d713c04ee8dc86c6",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=e4facb3da382b3c6f040f7d0b738643d",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=7aacfabbafd138c5af6420609f97820d",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=12e96a25d0dc3ee39e9d3ce97044fbbf",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
@@ -92,24 +92,24 @@
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=331c85bd61ffa93af09273d1bc2add5a",
|
||||
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=5e93ef0a1889bed3f92a705dc1e92c9b",
|
||||
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=c0f21fb7e62d6f0a0153f1cdbf26782a",
|
||||
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=3955411f214d1aa011fd509b6c81fbea",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=06c13e817cc022028b3f4a33c0ca303a",
|
||||
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=503d0b09e157a22f555e3670d1ec9bb5",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=78bfb1c7b5782df4fb0ac7e36f80f847",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=e4facb3da382b3c6f040f7d0b738643d",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=7aacfabbafd138c5af6420609f97820d",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=a82b065847bf3cd5d713c04ee8dc86c6",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=da6c7997d9de2f8329142399f0ce50da",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=bb648ddce4bc923ac79205a78882248d",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=624d1d9b9bf141a593cfe835fdfc85cc",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=07e257b423e6a8f984e1d9bf0bb04fe0",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=723ac4a390d628c9253d5416494cdaf4",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=25f255bc860b5472b201ef700fd638a8",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=093bd9d22c19a903b826bb4a1c819263",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=6fe68325d5356197672c27bc77cedcb4",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=b70f0d0cf76a643b3fbf47a1dfdb1529",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=bcba8a437f59c2334f9bd32f668ff9bb",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=5bbc74fa36ad7e85114e95c99f244421",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=f6b2e7fa795596ac4754500c9c30eacc",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=54d676a6ea8677dd48f6c4b3041292cf",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=89dffa552c6e3abe3a2aac6c9c7b466b",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=783d3a8076337744f0176d60e1041ea4",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=03bcd02b9ca5e53e1390ee840cd0a561",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=8c6d7286f667eeb62a0a28a09851a6c3"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'crwdns6501:0crwdne6501:0',
|
||||
'field' => 'crwdns1487:0crwdne1487:0',
|
||||
'about_fieldsets_title' => 'crwdns1488:0crwdne1488:0',
|
||||
'about_fieldsets_text' => 'crwdns13176:0crwdne13176:0',
|
||||
'about_fieldsets_text' => 'crwdns13226:0crwdne13226:0',
|
||||
'custom_format' => 'crwdns6505:0crwdne6505:0',
|
||||
'encrypt_field' => 'crwdns1792:0crwdne1792:0',
|
||||
'encrypt_field_help' => 'crwdns1683:0crwdne1683:0',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'crwdns6693:0crwdne6693:0',
|
||||
'signed_by_location_manager' => 'crwdns6695:0crwdne6695:0',
|
||||
'signed_by' => 'crwdns6697:0crwdne6697:0',
|
||||
'clone' => 'crwdns13240:0crwdne13240:0',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'crwdns6337:0crwdne6337:0',
|
||||
'eula_settings' => 'crwdns1296:0crwdne1296:0',
|
||||
'eula_markdown' => 'crwdns1261:0crwdne1261:0',
|
||||
'empty_row_count' => 'crwdns13236:0crwdne13236:0',
|
||||
'empty_row_count_help' => 'crwdns13238:0crwdne13238:0',
|
||||
'favicon' => 'crwdns5858:0crwdne5858:0',
|
||||
'favicon_format' => 'crwdns5860:0crwdne5860:0',
|
||||
'favicon_size' => 'crwdns5862:0crwdne5862:0',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'crwdns1465:0crwdne1465:0',
|
||||
'scope_locations_fmcs_support_text' => 'crwdns13049:0crwdne13049:0',
|
||||
'scope_locations_fmcs_support_help_text' => 'crwdns13188:0crwdne13188:0',
|
||||
'scope_locations_fmcs_check_button' => 'crwdns13234:0crwdne13234:0',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'crwdns13190:0crwdne13190:0',
|
||||
'show_in_model_list' => 'crwdns1990:0crwdne1990:0',
|
||||
'optional' => 'crwdns1298:0crwdne1298:0',
|
||||
|
||||
@@ -389,6 +389,8 @@ return [
|
||||
'new_license' => 'crwdns6249:0crwdne6249:0',
|
||||
'new_accessory' => 'crwdns6251:0crwdne6251:0',
|
||||
'new_consumable' => 'crwdns6253:0crwdne6253:0',
|
||||
'new_component' => 'crwdns13228:0crwdne13228:0',
|
||||
'new_user' => 'crwdns13230:0crwdne13230:0',
|
||||
'collapse' => 'crwdns6255:0crwdne6255:0',
|
||||
'assigned' => 'crwdns6257:0crwdne6257:0',
|
||||
'asset_count' => 'crwdns6259:0crwdne6259:0',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'crwdns12550:0crwdne12550:0',
|
||||
'ulid' => 'crwdns12552:0crwdne12552:0',
|
||||
'uuid' => 'crwdns12554:0crwdne12554:0',
|
||||
'fmcs_location' => 'crwdns13232:0crwdne13232:0',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'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.',
|
||||
'about_fieldsets_text' => 'Veldstelle stel jou in staat om groepe van persoonlike velde te skep wat gereeld hergebruik word vir spesifieke tipe bates.',
|
||||
'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.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Signed By (Finance Auditor):',
|
||||
'signed_by_location_manager' => 'Signed By (Location Manager):',
|
||||
'signed_by' => 'Signed Off By:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Enabled',
|
||||
'eula_settings' => 'EULA-instellings',
|
||||
'eula_markdown' => 'Hierdie EULA laat <a href="https://help.github.com/articles/github-flavored-markdown/">Github-geurde markdown</a> toe.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'Accepted filetypes are ico, png, and gif. Other image formats may not work in all browsers.',
|
||||
'favicon_size' => 'Favicons should be square images, 16x16 pixels.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Volledige Veelvuldige Maatskappye Ondersteuning',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Show in Model Dropdowns',
|
||||
'optional' => 'opsioneel',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'New License',
|
||||
'new_accessory' => 'New Accessory',
|
||||
'new_consumable' => 'New Consumable',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Collapse',
|
||||
'assigned' => 'Assigned',
|
||||
'asset_count' => 'Asset Count',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'Manage',
|
||||
'field' => 'Field',
|
||||
'about_fieldsets_title' => 'About Fieldsets',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used for specific asset model types.',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used used for specific asset model types.',
|
||||
'custom_format' => 'Custom Regex format...',
|
||||
'encrypt_field' => 'Encrypt the value of this field in the database',
|
||||
'encrypt_field_help' => 'WARNING: Encrypting a field makes it unsearchable.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Signed By (Finance Auditor):',
|
||||
'signed_by_location_manager' => 'Signed By (Location Manager):',
|
||||
'signed_by' => 'Signed Off By:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Enabled',
|
||||
'eula_settings' => 'EULA Settings',
|
||||
'eula_markdown' => 'This EULA allows <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'Accepted filetypes are ico, png, and gif. Other image formats may not work in all browsers.',
|
||||
'favicon_size' => 'Favicons should be square images, 16x16 pixels.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Show in Model Dropdowns',
|
||||
'optional' => 'optional',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'New License',
|
||||
'new_accessory' => 'New Accessory',
|
||||
'new_consumable' => 'New Consumable',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Collapse',
|
||||
'assigned' => 'Assigned',
|
||||
'asset_count' => 'Asset Count',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'إدارة',
|
||||
'field' => 'حقل',
|
||||
'about_fieldsets_title' => 'حول مجموعة الحقول',
|
||||
'about_fieldsets_text' => 'مجموعات الحقول تسمح لك بإنشاء مجموعات من الحقول المخصصة التي يعاد استخدامها في كثير من الأحيان لأنواع معينة من نماذج الأصول.',
|
||||
'about_fieldsets_text' => '(مجموعات الحقول) تسمح لك بإنشاء مجموعات من الحقول اللتي يمكن إعادة إستخدامها مع موديل محدد.',
|
||||
'custom_format' => 'تنسيق Regex المخصص...',
|
||||
'encrypt_field' => 'تشفير قيمة هذا الحقل في قاعدة البيانات',
|
||||
'encrypt_field_help' => 'تحذير: تشفير الحقل يجعله غير قابل للبحث.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'موقعة من قبل (مراجع الحسابات المالي):',
|
||||
'signed_by_location_manager' => 'توقيع بواسطة (مدير الموقع):',
|
||||
'signed_by' => 'تم توقيعه من قبل:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'تمكين',
|
||||
'eula_settings' => 'إعدادات اتفاقية ترخيص المستخدم النهائي',
|
||||
'eula_markdown' => 'تسمح اتفاقية ترخيص المستخدم هذه <a href="https://help.github.com/articles/github-flavored-markdown/">بتطبيق نمط الكتابة من Github</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'أنواع الملفات المقبولة هي رمز و png و gif. قد لا تعمل تنسيقات الصور الأخرى في كافة المستعرضات.',
|
||||
'favicon_size' => 'وينبغي أن تكون Favicons صور مربعة ، 16x16 بكسل.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'كامل دعم الشركات المتعددة',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'إظهار في القوائم المنسدلة للنماذج',
|
||||
'optional' => 'اختياري',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'ترخيص جديد',
|
||||
'new_accessory' => 'ملحق الجودة',
|
||||
'new_consumable' => 'مادة إستهلاكية جديدة',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'اخفاء',
|
||||
'assigned' => 'مسندة',
|
||||
'asset_count' => 'عدد الأصول',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'Управление',
|
||||
'field' => 'Поле',
|
||||
'about_fieldsets_title' => 'Относно Fieldsets',
|
||||
'about_fieldsets_text' => '"Група от полета" позволяват създаването на групи от персонализирани полета, които се използват и преизползват често за специфични типове модели на активи.',
|
||||
'about_fieldsets_text' => 'Fieldsets позволяват създаването на групи от персонализирани полета, които се използват и преизползват често за специфични типове модели на активи.',
|
||||
'custom_format' => 'Персонализиран формат...',
|
||||
'encrypt_field' => 'Шифроване на стойността на това поле в базата данни',
|
||||
'encrypt_field_help' => 'ВНИМАНИЕ: Шифроване на поле го прави невалидно за търсене.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Подписан от (счетоводител):',
|
||||
'signed_by_location_manager' => 'Подписан от (мениджър):',
|
||||
'signed_by' => 'Подписано от:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Активно',
|
||||
'eula_settings' => 'Настройки на EULA',
|
||||
'eula_markdown' => 'Съдържанието на EULA може да бъде форматирано с <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'Приетите файлови формати са ico, png, и gif. Другите формати на снимки може да не работят в всъчки браузъри.',
|
||||
'favicon_size' => 'Favicons трябва да бъдат квадратна снимка с размери, 16х16 пиксела.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Поддръжка на множество компании',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Показване в падащите менюта на моделите',
|
||||
'optional' => 'незадължително',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'Нов Лиценз',
|
||||
'new_accessory' => 'Нов аксесоар',
|
||||
'new_consumable' => 'Нов Консуматив',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Свий',
|
||||
'assigned' => 'Възложен',
|
||||
'asset_count' => 'Брой Активи',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'Manage',
|
||||
'field' => 'Field',
|
||||
'about_fieldsets_title' => 'About Fieldsets',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used for specific asset model types.',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used used for specific asset model types.',
|
||||
'custom_format' => 'Custom Regex format...',
|
||||
'encrypt_field' => 'Encrypt the value of this field in the database',
|
||||
'encrypt_field_help' => 'WARNING: Encrypting a field makes it unsearchable.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Signed By (Finance Auditor):',
|
||||
'signed_by_location_manager' => 'Signed By (Location Manager):',
|
||||
'signed_by' => 'Signed Off By:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Enabled',
|
||||
'eula_settings' => 'EULA Settings',
|
||||
'eula_markdown' => 'This EULA allows <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'Accepted filetypes are ico, png, and gif. Other image formats may not work in all browsers.',
|
||||
'favicon_size' => 'Favicons should be square images, 16x16 pixels.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Show in Model Dropdowns',
|
||||
'optional' => 'optional',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'New License',
|
||||
'new_accessory' => 'New Accessory',
|
||||
'new_consumable' => 'New Consumable',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Collapse',
|
||||
'assigned' => 'Assigned',
|
||||
'asset_count' => 'Asset Count',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'Manage',
|
||||
'field' => 'Field',
|
||||
'about_fieldsets_title' => 'About Fieldsets',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used for specific asset model types.',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used used for specific asset model types.',
|
||||
'custom_format' => 'Custom Regex format...',
|
||||
'encrypt_field' => 'Encrypt the value of this field in the database',
|
||||
'encrypt_field_help' => 'WARNING: Encrypting a field makes it unsearchable.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Signed By (Finance Auditor):',
|
||||
'signed_by_location_manager' => 'Signed By (Location Manager):',
|
||||
'signed_by' => 'Signed Off By:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Enabled',
|
||||
'eula_settings' => 'EULA Settings',
|
||||
'eula_markdown' => 'This EULA allows <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'Accepted filetypes are ico, png, and gif. Other image formats may not work in all browsers.',
|
||||
'favicon_size' => 'Favicons should be square images, 16x16 pixels.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Show in Model Dropdowns',
|
||||
'optional' => 'optional',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'New License',
|
||||
'new_accessory' => 'New Accessory',
|
||||
'new_consumable' => 'New Consumable',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Collapse',
|
||||
'assigned' => 'Assigned',
|
||||
'asset_count' => 'Asset Count',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Podepsáno (Finanční auditor):',
|
||||
'signed_by_location_manager' => 'Podepsáno (Manager):',
|
||||
'signed_by' => 'Odepsal:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Povoleno',
|
||||
'eula_settings' => 'Nastavení EULA',
|
||||
'eula_markdown' => 'Tato EULA umožňuje <a href="https://help.github.com/articles/github-flavored-markdown/">Github markdown</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicona',
|
||||
'favicon_format' => 'Povolené typy souborů jsou ico, png a gif. Ostatní formáty obrázků nemusí fungovat ve všech prohlížečích.',
|
||||
'favicon_size' => 'Favikony by měly být čtvercové obrázky, 16 x 16 pixelů.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Plná podpora více společností',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Zobrazit v rozbalovacích nabídkách modelu',
|
||||
'optional' => 'volitelný',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'Nová licence',
|
||||
'new_accessory' => 'Nové příslušenství',
|
||||
'new_consumable' => 'Nový spotřební materiál',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Sbalit',
|
||||
'assigned' => 'Přiřazené',
|
||||
'asset_count' => 'Počet aktiv',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => ':attribute není platnou URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@ return [
|
||||
'manage' => 'Manage',
|
||||
'field' => 'Meysydd',
|
||||
'about_fieldsets_title' => 'Amdan grwpiau meysydd',
|
||||
'about_fieldsets_text' => 'Fieldsets allow you to create groups of custom fields that are frequently re-used for specific asset model types.',
|
||||
'about_fieldsets_text' => 'Mae grwpiau meysydd yn caniatau i chi creu grwpiau o meysydd addasedig sydd yn cael ei defnyddio yn amal ar gyfer mathau penodol o asedau.',
|
||||
'custom_format' => 'Custom Regex format...',
|
||||
'encrypt_field' => 'Hamcryptio gwerth y maes yma yn y basdata',
|
||||
'encrypt_field_help' => 'RHYBUDD: Mae hamcryptio maes yn feddwl nid oes modd chwilio amdano.',
|
||||
|
||||
@@ -39,4 +39,5 @@ return [
|
||||
'signed_by_finance_auditor' => 'Signed By (Finance Auditor):',
|
||||
'signed_by_location_manager' => 'Signed By (Location Manager):',
|
||||
'signed_by' => 'Signed Off By:',
|
||||
'clone' => 'Clone Location',
|
||||
];
|
||||
|
||||
@@ -64,6 +64,8 @@ return [
|
||||
'enabled' => 'Enabled',
|
||||
'eula_settings' => 'Gosodiadau CTDT',
|
||||
'eula_markdown' => 'Mae\'r CTDT yma yn caniatau <a href="https://help.github.com/articles/github-flavored-markdown/">markdown GitHub</a>.',
|
||||
'empty_row_count' => 'Field Start Offset (Empty Rows)',
|
||||
'empty_row_count_help' => 'Fields will begin populating after this many empty rows are skipped at the top of the label.',
|
||||
'favicon' => 'Favicon',
|
||||
'favicon_format' => 'Mathau o ffeiliau a dderbynnir yw ico, png, a gif. Mae\'n bosib cewch trafferthion hefo rhai gwahanol mewn rhai porrwyr.',
|
||||
'favicon_size' => 'Dylith favicons bod yn delweddau sgwar 16x16 pixels.',
|
||||
@@ -152,6 +154,7 @@ return [
|
||||
'full_multiple_companies_support_text' => 'Cefnogaeth Llawn ar gyfer Nifer o Cwmniau',
|
||||
'scope_locations_fmcs_support_text' => 'Scope Locations with Full Multiple Companies Support',
|
||||
'scope_locations_fmcs_support_help_text' => 'Restrict locations to their selected company.',
|
||||
'scope_locations_fmcs_check_button' => 'Check Compatibility',
|
||||
'scope_locations_fmcs_support_disabled_text' => 'This option is disabled because you have conflicting locations set for :count or more items.',
|
||||
'show_in_model_list' => 'Dangos mewn dewislen modelau',
|
||||
'optional' => 'dewisol',
|
||||
|
||||
@@ -390,6 +390,8 @@ return [
|
||||
'new_license' => 'New License',
|
||||
'new_accessory' => 'New Accessory',
|
||||
'new_consumable' => 'New Consumable',
|
||||
'new_component' => 'New Component',
|
||||
'new_user' => 'New User',
|
||||
'collapse' => 'Collapse',
|
||||
'assigned' => 'Assigned',
|
||||
'asset_count' => 'Asset Count',
|
||||
|
||||
@@ -172,6 +172,7 @@ return [
|
||||
'url' => 'The :attribute field must be a valid URL.',
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
'fmcs_location' => 'Full multiple company support and location scoping is enabled in the Admin Settings, and the selected location and selected company are not compatible.',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user