Merge branch 'develop' into bug/sc-15034

# Conflicts:
#	database/factories/SettingFactory.php
This commit is contained in:
Marcus Moore 2023-04-17 17:22:55 -07:00
commit 2d56675ade
No known key found for this signature in database
1204 changed files with 12315 additions and 8239 deletions

View file

@ -1,18 +1,22 @@
frontend: ["*.js", "*.css", "*.vue", "*.scss", "*.less", "*.blade.*", "*livewire*"]
frontend: ["*.js", "*.css", "*.vue", "*.scss", "*.less", "*.blade.*", "resources/views/livewire/*"]
skins: ["*.js", "*.css", "*.scss", "*.less"]
css: ["*.css","*.scss", "*.less"]
backend: ["/app/*", "*.php"]
javascript: ["*.js", "package.json", "package.lock"]
backend: ["/app/*", "composer.json", "composer.lock"]
translations: ["/resources/lang"]
livewire: ["/app/Http/Livewire/*", "resources/views/livewire/*"]
backups: ["*backup*"]
restore: ["*restore*"]
saml: ["*saml*"]
scim: ["*scim*"]
custom fields: ["*fields*", "*fieldsets*"]
dependencies: ["composer.json"]
dependencies: ["composer.json", "composer.lock", "package.json", "package.lock"]
consumables: ["*consumables*"]
api: ["/app/Http/Controllers/api/*"]
api: ["/app/Http/Controllers/Api/*"]
notifications: ["/app/Notifications/*"]
importer: ["/app/Importer/*"]
importer: ["/app/Importer/*","/app/Http/Livewire/Importer.php", "resources/views/livewire/importer.php"]
cli / artisan: ["/app/Console/*"]
LDAP: ["*LDAP*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"]
LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"]
docker: ["*docker/*", "Dockerfile", "Dockerfile.alpine", "Dockerfile.fpm-alpine", ".dockerignore", ".env.docker"]
tests: ["/tests/*", "/stubs"]
config: .github

View file

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

View file

@ -1,34 +1,34 @@
FROM alpine:3.14.2
FROM alpine:3.17.3
# Apache + PHP
RUN apk add --no-cache \
apache2 \
php7 \
php7-common \
php7-apache2 \
php7-curl \
php7-ldap \
php7-mysqli \
php7-gd \
php7-xml \
php7-mbstring \
php7-zip \
php7-ctype \
php7-tokenizer \
php7-pdo_mysql \
php7-openssl \
php7-bcmath \
php7-phar \
php7-json \
php7-iconv \
php7-fileinfo \
php7-simplexml \
php7-session \
php7-dom \
php7-xmlwriter \
php7-xmlreader \
php7-sodium \
php7-redis \
php7-pecl-memcached \
php81 \
php81-common \
php81-apache2 \
php81-curl \
php81-ldap \
php81-mysqli \
php81-gd \
php81-xml \
php81-mbstring \
php81-zip \
php81-ctype \
php81-tokenizer \
php81-pdo_mysql \
php81-openssl \
php81-bcmath \
php81-phar \
php81-json \
php81-iconv \
php81-fileinfo \
php81-simplexml \
php81-session \
php81-dom \
php81-xmlwriter \
php81-xmlreader \
php81-sodium \
php81-redis \
php81-pecl-memcached \
curl \
wget \
vim \
@ -41,7 +41,7 @@ COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
# Where apache's PID lives
RUN mkdir -p /run/apache2 && chown apache:apache /run/apache2
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php7/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php81/php.ini
COPY docker/000-default-2.4.conf /etc/apache2/conf.d/default.conf
# Enable mod_rewrite

View file

@ -1,8 +1,8 @@
ARG ENVIRONMENT=production
ARG SNIPEIT_RELEASE=5.1.3
ARG PHP_VERSION=7.4.16
ARG PHP_ALPINE_VERSION=3.13
ARG COMPOSER_VERSION=2.0.11
ARG SNIPEIT_RELEASE=6.1.0
ARG PHP_VERSION=8.2
ARG PHP_ALPINE_VERSION=3.17
ARG COMPOSER_VERSION=2
# Cannot use arguments with 'COPY --from' workaround
# https://github.com/moby/moby/issues/34482#issuecomment-454716952
@ -52,7 +52,7 @@ RUN { \
# Install php extensions inside docker containers easily
# https://github.com/mlocati/docker-php-extension-installer
COPY --from=mlocati/php-extension-installer:1.2.19 /usr/bin/install-php-extensions /usr/local/bin/
COPY --from=mlocati/php-extension-installer:2.1.15 /usr/bin/install-php-extensions /usr/local/bin/
RUN set -eux; \
install-php-extensions \
bcmath \

View file

@ -10,7 +10,7 @@ use ArieTimmerman\Laravel\SCIMServer\Exceptions\SCIMException;
use Log;
use Throwable;
use JsonException;
use Carbon\Exceptions\InvalidFormatException;
class Handler extends ExceptionHandler
{
@ -30,6 +30,7 @@ class Handler extends ExceptionHandler
\League\OAuth2\Server\Exception\OAuthServerException::class,
JsonException::class,
SCIMException::class, //these generally don't need to be reported
InvalidFormatException::class,
];
/**
@ -69,21 +70,34 @@ class Handler extends ExceptionHandler
// 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);
return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid JSON'), 422);
}
// Handle SCIM exceptions
if ($e instanceof SCIMException) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'invalid SCIM Request'), 400);
return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid SCIM Request'), 400);
}
// Handle Ajax requests that fail because the model doesn't exist
// Handle standard 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) {
return redirect()->back()->withInput()->with('error', trans('validation.date', ['attribute' => 'date']));
}
// Handle API requests that fail
if ($request->ajax() || $request->wantsJson()) {
// 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) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('validation.date', ['attribute' => 'date'])), 200);
}
// Handle API requests that fail because the model doesn't exist
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
$className = last(explode('\\', $e->getModel()));
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)) {
$statusCode = $e->getStatusCode();
@ -103,6 +117,8 @@ class Handler extends ExceptionHandler
}
if ($this->isHttpException($e) && (isset($statusCode)) && ($statusCode == '404' )) {
return response()->view('layouts/basic', [
'content' => view('errors/404')

View file

@ -25,11 +25,16 @@ class AccessoryCheckoutController extends Controller
public function create($accessoryId)
{
// Check if the accessory exists
if (is_null($accessory = Accessory::find($accessoryId))) {
if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) {
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
// Make sure there is at least one available to checkout
if ($accessory->numRemaining() <= 0){
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable'));
}
if ($accessory->category) {
$this->authorize('checkout', $accessory);
@ -55,17 +60,23 @@ class AccessoryCheckoutController extends Controller
public function store(Request $request, $accessoryId)
{
// Check if the accessory exists
if (is_null($accessory = Accessory::find($accessoryId))) {
if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) {
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.user_not_found'));
}
$this->authorize('checkout', $accessory);
if (! $user = User::find($request->input('assigned_to'))) {
if (!$user = User::find($request->input('assigned_to'))) {
return redirect()->route('accessories.checkout.show', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist'));
}
// Make sure there is at least one available to checkout
if ($accessory->numRemaining() <= 0){
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable'));
}
// Update the accessory data
$accessory->assigned_to = e($request->input('assigned_to'));

View file

@ -80,12 +80,9 @@ class AccessoriesController extends Controller
$accessories->where('notes','=',$request->input('notes'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $accessories->count()) ? $accessories->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');

View file

@ -55,12 +55,9 @@ class AssetMaintenancesController extends Controller
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($maintenances) && ($request->get('offset') > $maintenances->count())) ? $maintenances->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $maintenances->count()) ? $maintenances->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$allowed_columns = [
'id',

View file

@ -78,12 +78,9 @@ class AssetModelsController extends Controller
$assetmodels->TextSearch($request->input('search'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($assetmodels) && ($request->get('offset') > $assetmodels->count())) ? $assetmodels->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $assetmodels->count()) ? $assetmodels->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') : 'models.created_at';

View file

@ -199,13 +199,9 @@ class AssetsController extends Controller
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($assets) && ($request->get('offset') > $assets->count())) ? $assets->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -67,8 +67,6 @@ class CategoriesController extends Controller
$categories = $categories->withCount('showableAssets as assets_count');
}
if ($request->filled('search')) {
$categories = $categories->TextSearch($request->input('search'));
}
@ -93,14 +91,9 @@ class CategoriesController extends Controller
$categories->where('checkin_email', '=', $request->input('checkin_email'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($categories) && ($request->get('offset') > $categories->count())) ? $categories->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $categories->count()) ? $categories->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') : 'assets_count';

View file

@ -48,12 +48,10 @@ class CompaniesController extends Controller
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($companies) && ($request->get('offset') > $companies->count())) ? $companies->count() : $request->get('offset', 0);
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';

View file

@ -12,6 +12,7 @@ use App\Http\Requests\ImageUploadRequest;
use App\Events\CheckoutableCheckedIn;
use App\Events\ComponentCheckedIn;
use App\Models\Asset;
use Illuminate\Support\Facades\Validator;
class ComponentsController extends Controller
{
@ -45,7 +46,7 @@ class ComponentsController extends Controller
$components = Company::scopeCompanyables(Component::select('components.*')
->with('company', 'location', 'category', 'assets'));
->with('company', 'location', 'category', 'assets', 'supplier'));
if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search'));
@ -63,6 +64,10 @@ class ComponentsController extends Controller
$components->where('category_id', '=', $request->input('category_id'));
}
if ($request->filled('supplier_id')) {
$components->where('supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('location_id')) {
$components->where('location_id', '=', $request->input('location_id'));
}
@ -71,13 +76,9 @@ class ComponentsController extends Controller
$components->where('notes','=',$request->input('notes'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($components) && ($request->get('offset') > $components->count())) ? $components->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $components->count()) ? $components->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = $request->input('sort');
@ -93,6 +94,9 @@ class ComponentsController extends Controller
case 'company':
$components = $components->OrderCompany($order);
break;
case 'supplier':
$components = $components->OrderSupplier($order);
break;
default:
$components = $components->orderBy($column_sort, $order);
break;
@ -225,20 +229,30 @@ class ComponentsController extends Controller
public function checkout(Request $request, $componentId)
{
// Check if the component exists
if (is_null($component = Component::find($componentId))) {
if (!$component = Component::find($componentId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist')));
}
$this->authorize('checkout', $component);
$validator = Validator::make($request->all(), [
'asset_id' => 'required|exists:assets,id',
'assigned_qty' => "required|numeric|min:1|digits_between:1,".$component->numRemaining(),
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', $validator->errors()));
}
// Make sure there is at least one available to checkout
if ($component->numRemaining() <= $request->get('assigned_qty')) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')])));
}
if ($component->numRemaining() >= $request->get('assigned_qty')) {
if (!$asset = Asset::find($request->input('assigned_to'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')));
}
// Update the accessory data
$asset = Asset::find($request->input('assigned_to'));
$component->assigned_to = $request->input('assigned_to');
$component->assets()->attach($component->id, [
@ -255,7 +269,7 @@ class ComponentsController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Not enough components remaining: '.$component->numRemaining().' remaining, '.$request->get('assigned_qty').' requested.'));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')])));
}
/**

View file

@ -75,6 +75,10 @@ class ConsumablesController extends Controller
$consumables->where('manufacturer_id', '=', $request->input('manufacturer_id'));
}
if ($request->filled('supplier_id')) {
$consumables->where('supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('location_id')) {
$consumables->where('location_id','=',$request->input('location_id'));
}
@ -84,12 +88,9 @@ class ConsumablesController extends Controller
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($consumables) && ($request->get('offset') > $consumables->count())) ? $consumables->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $consumables->count()) ? $consumables->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$allowed_columns = ['id', 'name', 'order_number', 'min_amt', 'purchase_date', 'purchase_cost', 'company', 'category', 'model_number', 'item_no', 'manufacturer', 'location', 'qty', 'image'];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@ -111,6 +112,9 @@ class ConsumablesController extends Controller
case 'company':
$consumables = $consumables->OrderCompany($order);
break;
case 'supplier':
$components = $consumables->OrderSupplier($order);
break;
default:
$consumables = $consumables->orderBy($column_sort, $order);
break;
@ -154,7 +158,7 @@ class ConsumablesController extends Controller
public function show($id)
{
$this->authorize('view', Consumable::class);
$consumable = Consumable::findOrFail($id);
$consumable = Consumable::with('users')->findOrFail($id);
return (new ConsumablesTransformer)->transformConsumable($consumable);
}
@ -253,33 +257,39 @@ class ConsumablesController extends Controller
public function checkout(Request $request, $id)
{
// Check if the consumable exists
if (is_null($consumable = Consumable::find($id))) {
if (!$consumable = Consumable::with('users')->find($id)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.does_not_exist')));
}
$this->authorize('checkout', $consumable);
if ($consumable->qty > 0) {
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable')));
\Log::debug('No enough remaining');
}
// Check if the user exists
$assigned_to = $request->input('assigned_to');
if (is_null($user = User::find($assigned_to))) {
// Return error message
return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found'));
}
// Check if the user exists - @TODO: this should probably be handled via validation, not here??
if (!$user = User::find($request->input('assigned_to'))) {
// Return error message
return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found'));
\Log::debug('No valid user');
}
// Update the consumable data
$consumable->assigned_to = e($assigned_to);
// Update the consumable data
$consumable->assigned_to = $request->input('assigned_to');
$consumable->users()->attach($consumable->id, [
'consumable_id' => $consumable->id,
'user_id' => $user->id,
'assigned_to' => $assigned_to,
'note' => $request->input('note'),
]);
$consumable->users()->attach($consumable->id,
[
'consumable_id' => $consumable->id,
'user_id' => $user->id,
'assigned_to' => $request->input('assigned_to'),
'note' => $request->input('note'),
]
);
// Log checkout event
$logaction = $consumable->logCheckout(e($request->input('note')), $user);
$logaction = $consumable->logCheckout($request->input('note'), $user);
$data['log_id'] = $logaction->id;
$data['eula'] = $consumable->getEula();
$data['first_name'] = $user->first_name;
@ -289,9 +299,7 @@ class ConsumablesController extends Controller
$data['require_acceptance'] = $consumable->requireAcceptance();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'No consumables remaining'));
}
/**

View file

@ -58,12 +58,9 @@ class DepartmentsController extends Controller
$departments->where('location_id', '=', $request->input('location_id'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($departments) && ($request->get('offset') > $departments->count())) ? $departments->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $departments->count()) ? $departments->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';

View file

@ -28,12 +28,9 @@ class DepreciationsController extends Controller
$depreciations = $depreciations->TextSearch($request->input('search'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($depreciations) && ($request->get('offset') > $depreciations->count())) ? $depreciations->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->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';

View file

@ -35,12 +35,9 @@ class GroupsController extends Controller
$groups->where('name', '=', $request->input('name'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($groups) && ($request->get('offset') > $groups->count())) ? $groups->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $groups->count()) ? $groups->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';

View file

@ -39,8 +39,10 @@ class LicenseSeatsController extends Controller
}
$total = $seats->count();
$offset = (($seats) && (request('offset') >= $total)) ? 0 : request('offset', 0);
$limit = request('limit', 50);
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $seats->count()) ? $seats->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$seats = $seats->skip($offset)->take($limit)->get();

View file

@ -94,12 +94,9 @@ class LicensesController extends Controller
$licenses->onlyTrashed();
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($licenses) && ($request->get('offset') > $licenses->count())) ? $licenses->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View file

@ -78,14 +78,15 @@ class LocationsController extends Controller
$locations->where('locations.country', '=', $request->input('country'));
}
$offset = (($locations) && (request('offset') > $locations->count())) ? $locations->count() : request('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $locations->count()) ? $locations->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';
switch ($request->input('sort')) {
case 'parent':
$locations->OrderParent($order);

View file

@ -57,12 +57,9 @@ class ManufacturersController extends Controller
$manufacturers->where('support_email', '=', $request->input('support_email'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($manufacturers) && ($request->get('offset') > $manufacturers->count())) ? $manufacturers->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->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';

View file

@ -29,8 +29,10 @@ class PredefinedKitsController extends Controller
$kits = $kits->TextSearch($request->input('search'));
}
$offset = $request->input('offset', 0);
$limit = $request->input('limit', 50);
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $kits->count()) ? $kits->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'desc' ? 'desc' : 'asc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'name';
$kits->orderBy($sort, $order);

View file

@ -54,15 +54,15 @@ class ReportsController extends Controller
'note',
];
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
$offset = request('offset', 0);
$total = $actionlogs->count();
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);

View file

@ -50,12 +50,9 @@ class StatuslabelsController extends Controller
}
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($statuslabels) && ($request->get('offset') > $statuslabels->count())) ? $statuslabels->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->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';

View file

@ -23,11 +23,30 @@ class SuppliersController extends Controller
public function index(Request $request)
{
$this->authorize('view', Supplier::class);
$allowed_columns = ['id', 'name', 'address', 'phone', 'contact', 'fax', 'email', 'image', 'assets_count', 'licenses_count', 'accessories_count', 'url'];
$allowed_columns = ['
id',
'name',
'address',
'phone',
'contact',
'fax',
'email',
'image',
'assets_count',
'licenses_count',
'accessories_count',
'components_count',
'consumables_count',
'url',
];
$suppliers = Supplier::select(
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes']
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count');
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes'])
->withCount('assets as assets_count')
->withCount('licenses as licenses_count')
->withCount('accessories as accessories_count')
->withCount('components as components_count')
->withCount('consumables as consumables_count');
if ($request->filled('search')) {
@ -74,12 +93,9 @@ class SuppliersController extends Controller
$suppliers->where('notes', '=', $request->input('notes'));
}
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($suppliers) && ($request->get('offset') > $suppliers->count())) ? $suppliers->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->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';

View file

@ -193,12 +193,9 @@ class UsersController extends Controller
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($users) && ($request->get('offset') > $users->count())) ? $users->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $users->count()) ? $users->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
switch ($request->input('sort')) {

View file

@ -92,7 +92,7 @@ class BulkAssetModelsController extends Controller
AssetModel::whereIn('id', $models_raw_array)->update($update_array);
return redirect()->route('models.index')
->with('success', trans('admin/models/message.bulkedit.success'));
->with('success', trans_choice('admin/models/message.bulkedit.success', count($models_raw_array), ['model_count' => count($models_raw_array)]));
}
return redirect()->route('models.index')

View file

@ -33,6 +33,11 @@ class ComponentCheckoutController extends Controller
}
$this->authorize('checkout', $component);
// Make sure there is at least one available to checkout
if ($component->numRemaining() <= 0){
return redirect()->route('components.index')->with('error', trans('admin/components/message.checkout.unavailable'));
}
return view('components/checkout', compact('component'));
}
@ -50,7 +55,7 @@ class ComponentCheckoutController extends Controller
public function store(Request $request, $componentId)
{
// Check if the component exists
if (is_null($component = Component::find($componentId))) {
if (!$component = Component::find($componentId)) {
// Redirect to the component management page with error
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
}
@ -58,9 +63,15 @@ class ComponentCheckoutController extends Controller
$this->authorize('checkout', $component);
$max_to_checkout = $component->numRemaining();
// Make sure there are at least the requested number of components available to checkout
if ($max_to_checkout < $request->get('assigned_qty')) {
return redirect()->back()->withInput()->with('error', trans('admin/components/message.checkout.unavailable', ['remaining' => $max_to_checkout, 'requested' => $request->get('assigned_qty')]));
}
$validator = Validator::make($request->all(), [
'asset_id' => 'required',
'assigned_qty' => "required|numeric|between:1,$max_to_checkout",
'asset_id' => 'required|exists:assets,id',
'assigned_qty' => "required|numeric|min:1|digits_between:1,$max_to_checkout",
]);
if ($validator->fails()) {
@ -69,24 +80,17 @@ class ComponentCheckoutController extends Controller
->withInput();
}
$admin_user = Auth::user();
$asset_id = e($request->input('asset_id'));
// Check if the user exists
if (is_null($asset = Asset::find($asset_id))) {
// Redirect to the component management page with error
return redirect()->route('components.index')->with('error', trans('admin/components/message.asset_does_not_exist'));
}
$asset = Asset::find($request->input('asset_id'));
// Update the component data
$component->asset_id = $asset_id;
$component->asset_id = $request->input('asset_id');
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'user_id' => $admin_user->id,
'user_id' => Auth::user(),
'created_at' => date('Y-m-d H:i:s'),
'assigned_qty' => $request->input('assigned_qty'),
'asset_id' => $asset_id,
'asset_id' => $request->input('asset_id'),
'note' => $request->input('note'),
]);

View file

@ -71,6 +71,7 @@ class ComponentsController extends Controller
$component = new Component();
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->supplier_id = $request->input('supplier_id');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number', null);
@ -145,6 +146,7 @@ class ComponentsController extends Controller
// Update the component data
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->supplier_id = $request->input('supplier_id');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number');

View file

@ -24,9 +24,16 @@ class ConsumableCheckoutController extends Controller
*/
public function create($consumableId)
{
if (is_null($consumable = Consumable::find($consumableId))) {
if (is_null($consumable = Consumable::with('users')->find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0){
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable'));
}
$this->authorize('checkout', $consumable);
return view('consumables/checkout', compact('consumable'));
@ -44,12 +51,18 @@ class ConsumableCheckoutController extends Controller
*/
public function store(Request $request, $consumableId)
{
if (is_null($consumable = Consumable::find($consumableId))) {
if (is_null($consumable = Consumable::with('users')->find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
}
$this->authorize('checkout', $consumable);
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable'));
}
$admin_user = Auth::user();
$assigned_to = e($request->input('assigned_to'));

View file

@ -68,6 +68,7 @@ class ConsumablesController extends Controller
$consumable = new Consumable();
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->supplier_id = $request->input('supplier_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');
@ -144,6 +145,7 @@ class ConsumablesController extends Controller
$consumable->name = $request->input('name');
$consumable->category_id = $request->input('category_id');
$consumable->supplier_id = $request->input('supplier_id');
$consumable->location_id = $request->input('location_id');
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$consumable->order_number = $request->input('order_number');

View file

@ -9,11 +9,11 @@
*
* **THIS DOCUMENTATION DOES NOT COVER INSTALLATION.** If you're here and you're not a
* developer, you're probably in the wrong place. Please see the
* [Installation documentation](http://docs.snipeitapp.com) for
* [Installation documentation](https://snipe-it.readme.io) for
* information on how to install Snipe-IT.
*
* To learn how to set up a development environment and get started developing for Snipe-IT,
* please see the [contributing documentation](http://docs.snipeitapp.com/contributing.html).
* please see the [contributing documentation](https://snipe-it.readme.io/docs/contributing-overview).
*
* Only the Snipe-IT specific controllers, models, helpers, service providers,
* etc have been included in this documentation (excluding vendors, Laravel core, etc)

View file

@ -84,7 +84,7 @@ class SettingsController extends Controller
}
$pageURL = $protocol.$host.$_SERVER['REQUEST_URI'];
$start_settings['url_config'] = url('/').'/setup';
$start_settings['url_config'] = config('app.url').'/setup';
$start_settings['url_valid'] = ($start_settings['url_config'] === $pageURL);
$start_settings['real_url'] = $pageURL;
$start_settings['php_version_min'] = true;

View file

@ -74,7 +74,6 @@ class UsersController extends Controller
$permissions = $this->filterDisplayable($permissions);
$user = new User;
$user->activated = 1;
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
->with('user', $user);

View file

@ -20,13 +20,13 @@ class CheckForSetup
if (Setting::setupCompleted()) {
if ($request->is('setup*')) {
return redirect(url('/'));
return redirect(config('app.url'));
} else {
return $next($request);
}
} else {
if (! ($request->is('setup*')) && ! ($request->is('.env')) && ! ($request->is('health'))) {
return redirect(url('/').'/setup');
return redirect(config('app.url').'/setup');
}
return $next($request);

View file

@ -37,6 +37,7 @@ class ComponentsTransformer
'id' => (int) $component->category->id,
'name' => e($component->category->name),
] : null,
'supplier' => ($component->supplier) ? ['id' => $component->supplier->id, 'name'=> e($component->supplier->name)] : null,
'order_number' => e($component->order_number),
'purchase_date' => Helper::getFormattedDateObject($component->purchase_date, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost),

View file

@ -31,6 +31,7 @@ class ConsumablesTransformer
'item_no' => e($consumable->item_no),
'location' => ($consumable->location) ? ['id' => (int) $consumable->location->id, 'name' => e($consumable->location->name)] : null,
'manufacturer' => ($consumable->manufacturer) ? ['id' => (int) $consumable->manufacturer->id, 'name' => e($consumable->manufacturer->name)] : null,
'supplier' => ($consumable->supplier) ? ['id' => $consumable->supplier->id, 'name'=> e($consumable->supplier->name)] : null,
'min_amt' => (int) $consumable->min_amt,
'model_number' => ($consumable->model_number != '') ? e($consumable->model_number) : null,
'remaining' => $consumable->numRemaining(),

View file

@ -41,6 +41,8 @@ class SuppliersTransformer
'assets_count' => (int) $supplier->assets_count,
'accessories_count' => (int) $supplier->accessories_count,
'licenses_count' => (int) $supplier->licenses_count,
'consumables_count' => (int) $supplier->consumables_count,
'components_count' => (int) $supplier->components_count,
'notes' => ($supplier->notes) ? e($supplier->notes) : null,
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),

View file

@ -330,7 +330,11 @@ class Accessory extends SnipeModel
/**
* Check how many items of an accessory remain
* Check how many items of an accessory remain.
*
* In order to use this model method, you MUST call withCount('users as users_count')
* on the eloquent query in the controller, otherwise $this->>users_count will be null and
* bad things happen.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]

View file

@ -33,7 +33,8 @@ class Component extends SnipeModel
'name' => 'required|min:3|max:255',
'qty' => 'required|integer|min:1',
'category_id' => 'required|integer|exists:categories,id',
'company_id' => 'integer|nullable',
'supplier_id' => 'nullable|integer|exists:suppliers,id',
'company_id' => 'integer|nullable|exists:companies,id',
'min_amt' => 'integer|min:0|nullable',
'purchase_date' => 'date_format:Y-m-d|nullable',
'purchase_cost' => 'numeric|nullable|gte:0',
@ -57,6 +58,7 @@ class Component extends SnipeModel
protected $fillable = [
'category_id',
'company_id',
'supplier_id',
'location_id',
'name',
'purchase_cost',
@ -86,6 +88,7 @@ class Component extends SnipeModel
'category' => ['name'],
'company' => ['name'],
'location' => ['name'],
'supplier' => ['name'],
];
@ -168,6 +171,18 @@ class Component extends SnipeModel
return $this->belongsTo(\App\Models\Category::class, 'category_id');
}
/**
* Establishes the item -> supplier relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
{
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
}
/**
* Establishes the component -> action logs relationship
*
@ -247,4 +262,17 @@ class Component extends SnipeModel
{
return $query->leftJoin('companies', 'components.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
}
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'components.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
}
}

View file

@ -27,6 +27,7 @@ class Consumable extends SnipeModel
'requestable' => 'boolean',
'category_id' => 'integer',
'company_id' => 'integer',
'supplier_id',
'qty' => 'integer',
'min_amt' => 'integer',
];
@ -95,6 +96,7 @@ class Consumable extends SnipeModel
'company' => ['name'],
'location' => ['name'],
'manufacturer' => ['name'],
'supplier' => ['name'],
];
@ -249,6 +251,18 @@ class Consumable extends SnipeModel
return $this->belongsToMany(\App\Models\User::class, 'consumables_users', 'consumable_id', 'assigned_to')->withPivot('user_id')->withTrashed()->withTimestamps();
}
/**
* Establishes the item -> supplier relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
{
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
}
/**
* Determine whether to send a checkin/checkout email based on
@ -376,4 +390,17 @@ class Consumable extends SnipeModel
{
return $query->leftJoin('companies', 'consumables.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
}
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'consumables.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
}
}

View file

@ -33,9 +33,9 @@ class License extends Depreciable
protected $table = 'licenses';
protected $casts = [
'purchase_date' => 'datetime',
'expiration_date' => 'datetime',
'termination_date' => 'datetime',
'purchase_date' => 'date',
'expiration_date' => 'date',
'termination_date' => 'date',
'category_id' => 'integer',
'company_id' => 'integer',
];
@ -49,9 +49,9 @@ class License extends Depreciable
'category_id' => 'required|exists:categories,id',
'company_id' => 'integer|nullable',
'purchase_cost'=> 'numeric|nullable|gte:0',
'purchase_date' => 'date_format:Y-m-d|nullable',
'expiration_date' => 'date_format:Y-m-d|nullable',
'termination_date' => 'date_format:Y-m-d|nullable',
'purchase_date' => 'date_format:Y-m-d|nullable|max:10',
'expiration_date' => 'date_format:Y-m-d|nullable|max:10',
'termination_date' => 'date_format:Y-m-d|nullable|max:10',
];
/**

View file

@ -267,7 +267,7 @@ class Location extends SnipeModel
foreach ($locations_with_children[$parent_id] as $location) {
$location->use_text = $prefix.' '.$location->name;
$location->use_image = ($location->image) ? url('/').'/uploads/locations/'.$location->image : null;
$location->use_image = ($location->image) ? config('app.url').'/uploads/locations/'.$location->image : null;
$results[] = $location;
//now append the children. (if we have any)
if (array_key_exists($location->id, $locations_with_children)) {

View file

@ -121,6 +121,30 @@ class Supplier extends SnipeModel
return $this->hasMany(\App\Models\Accessory::class, 'supplier_id');
}
/**
* Establishes the supplier -> component relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
{
return $this->hasMany(\App\Models\Component::class, 'supplier_id');
}
/**
* Establishes the supplier -> component relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumables()
{
return $this->hasMany(\App\Models\Consumable::class, 'supplier_id');
}
/**
* Establishes the supplier -> asset maintenances relationship
*

View file

@ -76,8 +76,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'start_date' => 'datetime:Y-m-d',
'end_date' => 'datetime:Y-m-d',
];
/**

View file

@ -24,7 +24,7 @@ class FirstAdminNotification extends Notification
$this->_data['last_name'] = $content['last_name'];
$this->_data['username'] = $content['username'];
$this->_data['password'] = $content['password'];
$this->_data['url'] = url('/');
$this->_data['url'] = config('app.url');
}
/**

View file

@ -24,7 +24,7 @@ class WelcomeNotification extends Notification
$this->_data['last_name'] = htmlspecialchars_decode($content['last_name']);
$this->_data['username'] = htmlspecialchars_decode($content['username']);
$this->_data['password'] = htmlspecialchars_decode($content['password']);
$this->_data['url'] = url('/');
$this->_data['url'] = config('app.url');
}
/**

View file

@ -210,7 +210,7 @@ class AssetModelPresenter extends Presenter
public function imageUrl()
{
if (! empty($this->image)) {
return '<img src="'.url('/').'/uploads/models/'.$this->image.'" alt="'.$this->name.'" height="50" width="50">';
return '<img src="'.config('app.url').'/uploads/models/'.$this->image.'" alt="'.$this->name.'" height="50" width="50">';
}
return '';
@ -223,7 +223,7 @@ class AssetModelPresenter extends Presenter
public function imageSrc()
{
if (! empty($this->image)) {
return url('/').'/uploads/models/'.$this->image;
return config('app.url').'/uploads/models/'.$this->image;
}
return '';

View file

@ -59,6 +59,15 @@ class ComponentPresenter extends Presenter
'title' => trans('general.category'),
'formatter' => 'categoriesLinkObjFormatter',
], [
'field' => 'supplier',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.supplier'),
'visible' => false,
'formatter' => 'suppliersLinkObjFormatter',
],
[
'field' => 'qty',
'searchable' => false,
'sortable' => true,

View file

@ -53,6 +53,14 @@ class ConsumablePresenter extends Presenter
'sortable' => true,
'title' => trans('general.category'),
'formatter' => 'categoriesLinkObjFormatter',
], [
'field' => 'supplier',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.supplier'),
'visible' => false,
'formatter' => 'suppliersLinkObjFormatter',
], [
'field' => 'model_number',
'searchable' => true,

View file

@ -406,7 +406,7 @@ class UserPresenter extends Presenter
}
// Set a fun, gender-neutral default icon
return url('/').'/img/default-sm.png';
return config('app.url').'/img/default-sm.png';
}
/**

View file

@ -29,6 +29,23 @@ class SettingsServiceProvider extends ServiceProvider
$view->with('snipeSettings', Setting::getSettings());
});
// Make sure the limit is actually set, is an integer and does not exceed system limits
\App::singleton('api_limit_value', function () {
$limit = config('app.max_results');
if ((abs(intval(request('limit'))) > 0) && (abs(request('limit')) <= config('app.max_results'))) {
$limit = abs(request('limit'));
}
\Log::debug('Max in env: '.config('app.max_results'));
\Log::debug('Original requested limit: '.request('limit'));
\Log::debug('Modified limit: '.$limit);
\Log::debug('------------------------------');
return $limit;
});
/**
* Set some common variables so that they're globally available.
* The paths should always be public (versus private uploads)

View file

@ -161,7 +161,7 @@ class Saml
//Let onelogin/php-saml know to use 'X-Forwarded-*' headers if it is from a trusted proxy
OneLogin_Saml2_Utils::setProxyVars(request()->isFromTrustedProxy());
data_set($settings, 'sp.entityId', url('/'));
data_set($settings, 'sp.entityId', config('app.url'));
data_set($settings, 'sp.assertionConsumerService.url', route('saml.acs'));
data_set($settings, 'sp.singleLogoutService.url', route('saml.sls'));
data_set($settings, 'sp.x509cert', $setting->saml_sp_x509cert);

View file

@ -43,10 +43,10 @@ return [
'secret' => env('STRIPE_SECRET'),
],
'stunning' => [
'enabled' => env('ENABLE_STUNNING', false),
'app_key' => env('STUNNING_APP_KEY'),
'stripe_id' => env('STUNNING_STRIPE_ID'),
'baremetrics' => [
'enabled' => env('ENABLE_BMPAY', false),
'app_key' => env('BMPAY_PUBLIC_KEY', null),
'stripe_id' => env('BMPAY_STRIPE_ID', null),
],
'google' => [

View file

@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v6.1.0-pre',
'full_app_version' => 'v6.1.0-pre - build 10030-gdcbd216e2',
'build_version' => '10030',
'app_version' => 'v6.1.0',
'full_app_version' => 'v6.1.0 - build 10161-ga8ca3ad2a',
'build_version' => '10161',
'prerelease_version' => '',
'hash_version' => 'gdcbd216e2',
'full_hash' => 'v6.1.0-pre-986-gdcbd216e2',
'hash_version' => 'ga8ca3ad2a',
'full_hash' => 'v6.1.0-127-ga8ca3ad2a',
'branch' => 'develop',
);

View file

@ -3,6 +3,7 @@
"files": [
{
"source" : "/resources/lang/en/**/*.php",
"translation" : "/resources/lang/%locale%/%original_file_name%"
# https://developer.crowdin.com/configuration-file/#placeholders
"translation" : "/resources/lang/%locale%/**/%original_file_name%"
}
]

View file

@ -7,6 +7,7 @@ use App\Models\Company;
use App\Models\Component;
use App\Models\Location;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Supplier;
class ComponentFactory extends Factory
{
@ -35,6 +36,7 @@ class ComponentFactory extends Factory
'purchase_cost' => $this->faker->randomFloat(2),
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
'company_id' => Company::factory(),
'supplier_id' => Supplier::factory(),
];
}

View file

@ -8,6 +8,7 @@ use App\Models\Consumable;
use App\Models\Manufacturer;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Supplier;
class ConsumableFactory extends Factory
{
@ -36,6 +37,7 @@ class ConsumableFactory extends Factory
'qty' => $this->faker->numberBetween(5, 10),
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
'company_id' => Company::factory(),
'supplier_id' => Supplier::factory(),
];
}

View file

@ -34,31 +34,4 @@ class SettingFactory extends Factory
'email_domain' => 'test.com',
];
}
public function withMultipleFullCompanySupport()
{
return $this->state(function () {
return [
'full_multiple_companies_support' => 1,
];
});
}
public function withWebhookEnabled()
{
return $this->state(fn() => [
'webhook_botname' => 'SnipeBot5000',
'webhook_endpoint' => 'https://hooks.slack.com/services/NZ59/Q446/672N',
'webhook_channel' => '#it',
]);
}
public function withWebhookDisabled()
{
return $this->state(fn() => [
'webhook_botname' => '',
'webhook_endpoint' => '',
'webhook_channel' => '',
]);
}
}

View file

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddSupplierToComponents extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('components', function (Blueprint $table) {
if (!Schema::hasColumn('components', 'supplier_id')) {
$table->integer('supplier_id')->after('user_id')->nullable()->default(null);
}
});
Schema::table('consumables', function (Blueprint $table) {
if (!Schema::hasColumn('consumables', 'supplier_id')) {
$table->integer('supplier_id')->after('user_id')->nullable()->default(null);
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('components', function (Blueprint $table) {
if (Schema::hasColumn('components', 'supplier_id')) {
$table->dropColumn('supplier_id');
}
});
Schema::table('consumables', function (Blueprint $table) {
if (Schema::hasColumn('consumables', 'supplier_id')) {
$table->dropColumn('supplier_id');
}
});
}
}

View file

@ -29,26 +29,26 @@ class AssetSeeder extends Seeder
$this->locationIds = Location::all()->pluck('id');
$this->supplierIds = Supplier::all()->pluck('id');
Asset::factory()->count(1000)->laptopMbp()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(2000)->laptopMbp()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(50)->laptopMbpPending()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(50)->laptopMbpArchived()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(50)->laptopAir()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(5)->laptopSurface()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(50)->laptopSurface()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(5)->laptopXps()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(5)->laptopSpectre()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(5)->laptopZenbook()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(3)->laptopYoga()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(50)->laptopZenbook()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(30)->laptopYoga()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(30)->desktopMacpro()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(30)->desktopLenovoI5()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(30)->desktopOptiplex()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(5)->confPolycom()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(2)->confPolycomcx()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(12)->tabletIpad()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(4)->tabletTab3()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(50)->confPolycom()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(20)->confPolycomcx()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(30)->tabletIpad()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(10)->tabletTab3()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(27)->phoneIphone11()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(40)->phoneIphone12()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(10)->ultrafine()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(10)->ultrasharp()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(20)->ultrafine()->state(new Sequence($this->getState()))->create();
Asset::factory()->count(20)->ultrasharp()->state(new Sequence($this->getState()))->create();
$del_files = Storage::files('assets');
foreach ($del_files as $del_file) { // iterate files

1186
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -34,11 +34,10 @@
"bootstrap-colorpicker": "^2.5.3",
"bootstrap-datepicker": "^1.9.0",
"bootstrap-less": "^3.3.8",
"bootstrap-table": "1.20.2",
"bootstrap-table": "1.21.3",
"chart.js": "^2.9.4",
"css-loader": "^4.0.0",
"ekko-lightbox": "^5.1.1",
"icheck": "^1.0.2",
"imagemin": "^8.0.1",
"jquery-form-validator": "^2.3.79",
"jquery-slimscroll": "^1.3.8",
@ -52,9 +51,9 @@
"papaparse": "^4.3.3",
"select2": "4.0.13",
"sheetjs": "^2.0.0",
"tableexport.jquery.plugin": "1.26.0",
"tableexport.jquery.plugin": "1.27.0",
"tether": "^1.4.0",
"vue-resource": "^1.5.2",
"webpack": "^5.76.0"
"webpack": "^5.76.2"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/dist/all.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -1,8 +1,8 @@
{
"/js/build/app.js": "/js/build/app.js?id=bcb572126085fb7637accdcff1e0c0c6",
"/js/build/app.js": "/js/build/app.js?id=7caeae38608edd96421f8ef59d33f5f6",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
"/css/build/overrides.css": "/css/build/overrides.css?id=d9175e3d9b9074397343dddebfe23888",
"/css/build/app.css": "/css/build/app.css?id=dcb8aa9f4501a370214a67442e88daf0",
"/css/build/overrides.css": "/css/build/overrides.css?id=ce23fa22306439befbf49e8e63adb4e0",
"/css/build/app.css": "/css/build/app.css?id=2e40bdf6f6d3d6d6954c391a0a91285e",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=dc383f8560a8d4adb51d44fb4043e03b",
"/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=f343f659ca1d45534d2c2c3cc30fb619",
@ -18,9 +18,7 @@
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
"/css/dist/all.css": "/css/dist/all.css?id=0314c741a636de602ec952468eb171f3",
"/css/blue.png": "/css/blue.png?id=e83a6c29e04fe851f2122815b2e4b150",
"/css/blue@2x.png": "/css/blue@2x.png?id=51135dd4d24f88f5de0b2414bd51dac5",
"/css/dist/all.css": "/css/dist/all.css?id=1faccd9b34013f9893ed467fa3ddcb39",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=2df05d4beaa48550d71234e8dca79141",
@ -31,10 +29,10 @@
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=6707d0247b0bca1b4964bab435e3c0d6",
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=a947172f4fde88e43b4c1a60b01db061",
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=bbc23038a6067c78310d3f19432a3ebf",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=ee4896df8b8f008ce73a9a0c2549aefd",
"/js/build/vendor.js": "/js/build/vendor.js?id=47ecbb4bb3b0e02315f391caadbdf971",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=14d9a2affec7b066d20fcba2e6e67ad2",
"/js/dist/all.js": "/js/dist/all.js?id=eb7becb7a5a2ebf0dae7926190d95832",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=418917c053841ab1aa1b78610a1825e0",
"/js/build/vendor.js": "/js/build/vendor.js?id=3843eca1b2e670b29c1e1cb57e1d7aa7",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=7a506bf59323cf5b5fe97f7080fc5ee0",
"/js/dist/all.js": "/js/dist/all.js?id=97b1034b75e3ac29a2eb9770d66c3370",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=c0d21166315b7c2cdd4819fa4a5e4d1e",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",

View file

@ -185,14 +185,6 @@ $(document).ready(function () {
}
});
/*
* iCheck checkbox plugin
*/
$('input[type="checkbox"].minimal, input[type="radio"].minimal').iCheck({
checkboxClass: 'icheckbox_minimal-blue',
radioClass: 'iradio_minimal-blue'
});
/*
@ -607,26 +599,14 @@ function htmlEntities(str) {
})(jQuery);
/**
* Universal Livewire Select2 and iCheck integration
* Universal Livewire Select2 integration
*
* How to use:
*
* 1. Set the class of your select2 elements to 'livewire-select2' and your icheck elements to 'livewire-icheck' (as appropriate).
* (For iCheck, you may still need to apply the other iCheck classes like 'minimal' or 'iCheck')
* 1. Set the class of your select2 elements to 'livewire-select2').
* 2. Name your element to match a property in your Livewire component
* 3. Add an attribute called 'data-livewire-component' that points to $_instance->id (via `{{ }}` if you're in a blade,
* or just $_instance->id if not).
* 4. For iCheck, you need to wrap the 'checkbox' element with wire:ignore - perhaps in the <label> if it wraps the
* <input> element, or just put a <span wire:ignore></span> around just the input element.
* 5. If you have dynamically shown/hidden checkboxes, you might need to initialize iCheck on them on component page-load.
* Just use $('.livewire-icheck').iCheck(), or for the minimal-style, use:
*
* $('input[type="checkbox"].minimal.livewire-icheck, input[type="radio"].minimal.livewire-icheck').iCheck({
* checkboxClass: 'icheckbox_minimal-blue',
* radioClass: 'iradio_minimal-blue'
* });
*
* (which is stolen from above here in this JS file)
*/
$(function () {
$('.livewire-select2').select2()
@ -643,16 +623,6 @@ $(function () {
window.livewire.hook('message.processed', function (el,component) {
$('.livewire-select2').select2();
//$('.livewire-icheck').iCheck(); //this seems to blow up pretty badly.
});
$(document).on('ifToggled', '.livewire-icheck', function (event) {
if(!event.target.name || !$(event.target).data('livewire-component')) {
console.error("You need to set both name (which should match a Livewire property) and data-livewire-component on your iCheck elements!")
console.error("For data-livewire-component, you probably want to use $_instance->id or {{ $_instance->id }}, as appropriate")
return false
}
window.livewire.find($(event.target).data('livewire-component')).set(event.target.name, event.target.checked)
})
})

View file

@ -666,9 +666,6 @@ th.css-accessory > .th-inner::before
border-radius: 0px;
}
.bs-checkbox input {
zoom: 1.5;
}
@media screen and (max-width: 511px){
.sidebar-menu{
margin-top:160px;
@ -691,3 +688,154 @@ th.css-accessory > .th-inner::before
white-space: nowrap;
text-overflow: ellipsis;
}
/** Form-stuff overrides for checkboxes and stuff **/
label.form-control {
display: grid;
grid-template-columns: 1.8em auto;
gap: 0.5em;
border: 0px;
padding-left: 0px;
background-color: inherit;
color: inherit;
font-size: inherit;
font-weight: inherit;
}
label.form-control--disabled {
color: #959495;
pointer-events:none;
cursor: not-allowed;
}
/** --------------------------------------- **/
/** Start checkbox styles to replace iCheck **/
/** --------------------------------------- **/
input[type="checkbox"] {
/* Add if not using autoprefixer */
-webkit-appearance: none;
appearance: none;
/* For iOS < 15 to remove gradient background */
background-color: #fff;
/* Not removed via appearance */
margin: 0;
font: inherit;
color: #959495;
width: 1.8em;
height: 1.8em;
border: 0.05em solid;
border-radius: 0em;
transform: translateY(-0.075em);
display: grid;
place-content: center;
/*Windows High Contrast Mode*/
}
/** This sets the display of a checkbox, and what the "fill" checkmark should look like */
input[type="checkbox"]::before {
/** If you want to use the non-checkbox, filled square, use this instead **/
content: "";
width: 1em;
height: 1em;
transform: scale(0);
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em rgb(211, 211, 211);
content: "";
width: 1em;
height: 1em;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
transform: scale(0);
transform-origin: bottom left;
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em #428bca;
/* Windows High Contrast Mode */
background-color: CanvasText;
}
/** This sets the size of the scale up for the shape we defined above **/
input[type="checkbox"]:checked::before {
transform: scale(1);
}
/** This sets the scale and color of the DISABLED but CHECKED checkbox */
input[type=checkbox]:disabled::before, input[type=radio]:disabled::before {
content: "";
width: 1em;
height: 1em;
transform: scale(1);
box-shadow: inset 1em 1em rgb(211, 211, 211);
}
/* This sets the scale and style of a DISABLED checkbox that is NOT checked */
input[type=checkbox]:disabled:not(:checked)::before, input[type=radio]:disabled:not(:checked)::before {
content: "";
transform: scale(0);
cursor: not-allowed;
pointer-events:none;
}
/** this is the color of the checkbox and content on a disabled, checked box **/
input[type=checkbox]:disabled, input[type=radio]:disabled {
--form-control-color: rgb(211, 211, 211);
color: #959495;
cursor: not-allowed;
pointer-events:none;
}
/** Radio styles to replace iCheck **/
input[type="radio"] {
appearance: none;
background-color: #fff;
margin: 0;
font: inherit;
color: #959495;
width: 1.8em;
height: 1.8em;
border: 0.05em solid;
border-radius: 50%;
transform: translateY(-0.075em);
display: grid;
place-content: center;
}
input[type="radio"]::before {
content: "";
width: 1em;
height: 1em;
border-radius: 50%;
transform: scale(0);
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em #428bca;
}
input[type="radio"]:checked::before {
transform: scale(1);
}
/**
* This addresses the column selector in bootstrap-table. Without these two lines, the
* checkbox and the <span></span> with the label text that BS tables generates will
* end up on two different lines and it looks assy.
*/
.dropdown-item-marker input[type=checkbox] {
font-size: 10px;
}
.bootstrap-table .fixed-table-toolbar li.dropdown-item-marker label {
font-weight: normal;
display: grid;
grid-template-columns: .1em auto;
gap: 1.5em;
}
/** --------------------------------------- **/
/** End checkbox styles to replace iCheck **/
/** --------------------------------------- **/

View file

@ -135,6 +135,18 @@ a {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--light-link);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--visited-link);
border-color: var(--light-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);

View file

@ -132,6 +132,32 @@ a {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--light-link);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--visited-link);
border-color: var(--light-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.tasks-menu > .dropdown-menu > li .menu{
background-color: var(--back-main);
}
.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress{
background-color: var(--background);
}
h2.task_menu{
color:var(--link);
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu{
background-color:var(--back-main);
color:var(--link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);

View file

@ -131,6 +131,32 @@ a {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--light-link);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--visited-link);
border-color: var(--light-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.tasks-menu > .dropdown-menu > li .menu{
background-color: var(--back-main);
}
.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress{
background-color: var(--background);
}
h2.task_menu{
color:var(--link);
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu{
background-color:var(--back-main);
color:var(--link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);
@ -288,10 +314,6 @@ input[type=text], input[type=search] {
.nav-tabs-custom, .nav-tabs-custom>.tab-content {
background-color: var(--back-main);
}
.navbar-nav>.tasks-menu>.dropdown-menu>li.header {
background-color: var(--back-main);
color: var(--header);
}
.open>.dropdown-toggle.btn-default {
background-color: var(--back-sub);
color: var(--header);
@ -327,7 +349,7 @@ input[type=text], input[type=search] {
color: var(--text-main);
}
.skin-green-dark .main-header .navbar .dropdown-menu li a {
color: var(--header);
color: var(--link);
}
.fixed-table-body thead th .th-inner, .skin-green-dark .sidebar-menu>li.active>a, .skin-green .sidebar-menu>li:hover>a, .sidebar-toggle:hover {
background-color: var(--header)!important;

View file

@ -119,6 +119,32 @@ li.dropdown-item-marker {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--light-link);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--visited-link);
border-color: var(--light-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.tasks-menu > .dropdown-menu > li .menu{
background-color: var(--back-main);
}
.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress{
background-color: var(--background);
}
h2.task_menu{
color:var(--link);
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu{
background-color:var(--back-main);
color:var(--link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);

View file

@ -132,6 +132,32 @@ a {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--light-link);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--button-default);
border-color: var(--light-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.tasks-menu > .dropdown-menu > li .menu{
background-color: var(--back-main);
}
.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress{
background-color: var(--background);
}
h2.task_menu{
color:var(--link);
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu{
background-color:var(--back-main);
color:var(--link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);
@ -299,7 +325,7 @@ input[type=text], input[type=search] {
}
.navbar-nav>.tasks-menu>.dropdown-menu>li.header {
background-color: var(--back-main);
color: var(--header);
color: var(--link);
}
.open>.dropdown-toggle.btn-default {
background-color: var(--back-sub);

View file

@ -134,7 +134,32 @@ a {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--light-link);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--visited-link);
border-color: var(--light-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.tasks-menu > .dropdown-menu > li .menu{
background-color: var(--back-main);
}
.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress{
background-color: var(--background);
}
h2.task_menu{
color:var(--link);
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu{
background-color:var(--back-main);
color:var(--link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);
background: -webkit-linear-gradient(top, var(--header) 0%,var(--header) 100%);
@ -209,7 +234,6 @@ body {
}
.btn-default{
background-color: var(--back-sub);
color: var(--link);
}
.btn-default dropdown-toggle {

View file

@ -125,7 +125,33 @@ a {
#ldapad_test_results.well.well-sm{
color: var(--back-main);
}
//pagination
.pagination > li >a{
color: var(--text-main);
background-color: var(--back-main);
}
.pagination > .active > a {
background-color: var(--button-default);
border-color: var(--light-link);
color:var(--nav-link);
}
.pagination > .active > a:hover{
background-color: var(--hover-link);
}
.tasks-menu > .dropdown-menu > li .menu{
background-color: var(--back-main);
}
.tasks-menu > .dropdown-menu > li .menu > li > a:hover .progress{
background-color: var(--background);
}
h2.task_menu{
color:var(--link);
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu{
background-color:var(--back-main);
color:var(--link);
}
.main-header .navbar, .main-header .logo {
background-color: var(--header);
background: -webkit-linear-gradient(top, var(--header) 0%,var(--header) 100%);

View file

@ -16,5 +16,6 @@ return array(
'update' => 'Opdatering bywerk',
'use_default_eula' => 'Gebruik eerder die <a href="#" data-toggle="modal" data-target="#eulaModal">primary standaard EULA</a>.',
'use_default_eula_disabled' => '<del>Gebruik die primêre standaardverlof in plaas daarvan.</del> Geen primêre standaard EULA is ingestel nie. Voeg asseblief een by Instellings.',
'clone' => 'Clone Accessory',
);

View file

@ -24,6 +24,7 @@ return array(
'checkout' => array(
'error' => 'Toebehore is nie nagegaan nie, probeer asseblief weer',
'success' => 'Toebehore suksesvol nagegaan.',
'unavailable' => 'Accessory is not available for checkout. Check quantity available',
'user_does_not_exist' => 'Die gebruiker is ongeldig. Probeer asseblief weer.'
),

View file

@ -1,14 +1,14 @@
<?php
return [
'asset_maintenance_type' => 'Onderhoudstipe',
'asset_maintenance_type' => 'Asset Maintenance Type',
'title' => 'Titel',
'start_date' => 'begin',
'completion_date' => 'voltooi',
'start_date' => 'Start Date',
'completion_date' => 'Completion Date',
'cost' => 'koste',
'is_warranty' => 'Garantieverbetering',
'asset_maintenance_time' => 'dae',
'asset_maintenance_time' => 'Asset Maintenance Time (in days)',
'notes' => 'notas',
'update' => 'Opdateer',
'create' => 'Skep'
'update' => 'Update Asset Maintenance',
'create' => 'Create Asset Maintenance'
];

View file

@ -3,7 +3,7 @@
return array(
'group_exists' => 'Groep bestaan reeds!',
'group_not_found' => 'Groep [: id] bestaan nie.',
'group_not_found' => 'Group ID :id does not exist.',
'group_name_required' => 'Die naam veld is nodig',
'success' => array(

View file

@ -2,8 +2,11 @@
return [
'bulk_delete' => 'Bevestig bates vir die verwydering van grootmaat',
'bulk_restore' => 'Confirm Bulk Restore Assets',
'bulk_delete_help' => 'Hersien die bates vir grootmaatverwydering hieronder. Sodra dit verwyder is, kan hierdie bates herstel word, maar hulle word nie meer geassosieer met enige gebruikers wat hulle tans toegewys is nie.',
'bulk_restore_help' => 'Review the assets for bulk restoration below. Once restored, these assets will not be associated with any users they were previously assigned to.',
'bulk_delete_warn' => 'Jy is op die punt om te verwyder: bate_count bates.',
'bulk_restore_warn' => 'You are about to restore :asset_count assets.',
'bulk_update' => 'Grootskaalse opdateringsbates',
'bulk_update_help' => 'Met hierdie vorm kan u verskeie bates gelyktydig bywerk. Vul slegs die velde in wat u moet verander. Enige velde wat leeg is, bly onveranderd.',
'bulk_update_warn' => 'You are about to edit the properties of a single asset.|You are about to edit the properties of :asset_count assets.',
@ -45,7 +48,7 @@ return [
'asset_location_update_default' => 'Update only default location',
'asset_not_deployable' => 'That asset status is not deployable. This asset cannot be checked out.',
'asset_deployable' => 'That status is deployable. This asset can be checked out.',
'processing_spinner' => 'Processing...',
'processing_spinner' => 'Processing... (This might take a bit of time on large files)',
'optional_infos' => 'Optional Information',
'order_details' => 'Order Related Information'
];

View file

@ -42,5 +42,6 @@ return [
'error_messages' => 'Error messages:',
'success_messages' => 'Success messages:',
'alert_details' => 'Please see below for details.',
'custom_export' => 'Custom Export'
'custom_export' => 'Custom Export',
'mfg_warranty_lookup' => ':manufacturer Warranty Status Lookup',
];

View file

@ -22,6 +22,8 @@ return [
'restore' => [
'error' => 'Bate is nie herstel nie, probeer asseblief weer',
'success' => 'Bate herstel suksesvol.',
'bulk_success' => 'Asset restored successfully.',
'nothing_updated' => 'No assets were selected, so nothing was restored.',
],
'audit' => [

View file

@ -6,7 +6,7 @@ return array(
'deleted' => 'This model has been deleted.',
'bulk_delete' => 'Bulk Delete Asset Models',
'bulk_delete_help' => 'Use the checkboxes below to confirm the deletion of the selected asset models. Asset models that have assets associated with them cannot be deleted until the assets are associated with a different model.',
'bulk_delete_warn' => 'You are about to delete :model_count asset models.',
'bulk_delete_warn' => 'You are about to delete one asset model.|You are about to delete :model_count asset models.',
'restore' => 'Herstel Model',
'requestable' => 'Gebruikers kan hierdie model aanvra',
'show_mac_address' => 'Wys MAC adres veld in bates in hierdie model',

View file

@ -16,7 +16,7 @@ return array(
'update' => array(
'error' => 'Model is nie opgedateer nie, probeer asseblief weer',
'success' => 'Model suksesvol opgedateer.'
'success' => 'Model suksesvol opgedateer.',
),
'delete' => array(
@ -32,12 +32,14 @@ return array(
'bulkedit' => array(
'error' => 'Geen velde is verander nie, so niks is opgedateer nie.',
'success' => 'Modelle opgedateer.'
'success' => 'Model successfully updated. |:model_count models successfully updated.',
'warn' => 'You are about to update the properies of the following model: |You are about to edit the properties of the following :model_count models:',
),
'bulkdelete' => array(
'error' => 'No models were selected, so nothing was deleted.',
'success' => ':success_count model(s) deleted!',
'success' => 'Model deleted!|:success_count models deleted!',
'success_partial' => ':success_count model(s) were deleted, however :fail_count were unable to be deleted because they still have assets associated with them.'
),

View file

@ -11,7 +11,7 @@ return [
'admin_cc_email_help' => 'If you would like to send a copy of checkin/checkout emails that are sent to users to an additional email account, enter it here. Otherwise leave this field blank.',
'is_ad' => 'Dit is \'n Active Directory-bediener',
'alerts' => 'Alerts',
'alert_title' => 'Update Alert Settings',
'alert_title' => 'Update Notification Settings',
'alert_email' => 'Stuur kennisgewings aan',
'alert_email_help' => 'Email addresses or distribution lists you want alerts to be sent to, comma separated',
'alerts_enabled' => 'Alerts aangeskakel',
@ -198,16 +198,21 @@ return [
'show_images_in_email' => 'Show images in emails',
'show_images_in_email_help' => 'Uncheck this box if your Snipe-IT installation is behind a VPN or closed network and users outside the network will not be able to load images served from this installation in their emails.',
'site_name' => 'Site Naam',
'integrations' => 'Integrations',
'slack' => 'Slack',
'slack_title' => 'Update Slack Settings',
'slack_help' => 'Slack settings',
'slack_botname' => 'Slack Botname',
'slack_channel' => 'Slack Channel',
'slack_endpoint' => 'Slack Endpoint',
'slack_integration' => 'Slack Settings',
'slack_integration_help' => 'Slack integration is optional, however the endpoint and channel are required if you wish to use it. To configure Slack integration, you must first <a href=":slack_link" target="_new" rel="noopener">create an incoming webhook</a> on your Slack account. Click on the <strong>Test Slack Integration</strong> button to confirm your settings are correct before saving. ',
'slack_integration_help_button' => 'Once you have saved your Slack information, a test button will appear.',
'slack_test_help' => 'Test whether your Slack integration is configured correctly. YOU MUST SAVE YOUR UPDATED SLACK SETTINGS FIRST.',
'general_webhook' => 'General Webhook',
'webhook' => ':app',
'webhook_presave' => 'Test to Save',
'webhook_title' => 'Update Webhook Settings',
'webhook_help' => 'Integration settings',
'webhook_botname' => ':app Botname',
'webhook_channel' => ':app Channel',
'webhook_endpoint' => ':app Endpoint',
'webhook_integration' => ':app Settings',
'webhook_test' =>'Test :app integration',
'webhook_integration_help' => ':app integration is optional, however the endpoint and channel are required if you wish to use it. To configure :app integration, you must first <a href=":webhook_link" target="_new" rel="noopener">create an incoming webhook</a> on your :app account. Click on the <strong>Test :app Integration</strong> button to confirm your settings are correct before saving. ',
'webhook_integration_help_button' => 'Once you have saved your :app information, a test button will appear.',
'webhook_test_help' => 'Test whether your :app integration is configured correctly. YOU MUST SAVE YOUR UPDATED :app SETTINGS FIRST.',
'snipe_version' => 'Snipe-IT-weergawe',
'support_footer' => 'Support Footer Links ',
'support_footer_help' => 'Specify who sees the links to the Snipe-IT Support info and Users Manual',
@ -302,7 +307,7 @@ return [
'localization_keywords' => 'localization, currency, local, locale, time zone, timezone, international, internatinalization, language, languages, translation',
'localization_help' => 'Language, date display',
'notifications' => 'Notifications',
'notifications_help' => 'Email alerts, audit settings',
'notifications_help' => 'Email Alerts & Audit Settings',
'asset_tags_help' => 'Incrementing and prefixes',
'labels' => 'Labels',
'labels_title' => 'Update Label Settings',

View file

@ -33,12 +33,12 @@ return [
'testing_authentication' => 'Testing LDAP Authentication...',
'authentication_success' => 'User authenticated against LDAP successfully!'
],
'slack' => [
'sending' => 'Sending Slack test message...',
'webhook' => [
'sending' => 'Sending :app test message...',
'success_pt1' => 'Success! Check the ',
'success_pt2' => ' channel for your test message, and be sure to click SAVE below to store your settings.',
'500' => '500 Server Error.',
'error' => 'Something went wrong. Slack responded with: :error_message',
'error' => 'Something went wrong. :app responded with: :error_message',
'error_misc' => 'Something went wrong. :( ',
]
];

View file

@ -19,6 +19,8 @@ return [
'print_assigned' => 'Print All Assigned',
'email_assigned' => 'Email List of All Assigned',
'user_notified' => 'User has been emailed a list of their currently assigned items.',
'auto_assign_label' => 'Include this user when auto-assigning eligible licenses',
'auto_assign_help' => 'Skip this user in auto assignment of licenses',
'software_user' => 'Sagteware Uitgesoek na: naam',
'send_email_help' => 'You must provide an email address for this user to send them credentials. Emailing credentials can only be done on user creation. Passwords are stored in a one-way hash and cannot be retrieved once saved.',
'view_user' => 'Sien gebruiker: naam',
@ -33,7 +35,6 @@ return [
'superadmin_permission_warning' => 'Only superadmins may grant a user superadmin access.',
'admin_permission_warning' => 'Only users with admins rights or greater may grant a user admin access.',
'remove_group_memberships' => 'Remove Group Memberships',
'warning_deletion' => 'WARNING:',
'warning_deletion_information' => 'You are about to checkin ALL items from the :count user(s) listed below. Super admin names are highlighted in red.',
'update_user_assets_status' => 'Update all assets for these users to this status',
'checkin_user_properties' => 'Check in all properties associated with these users',
@ -41,4 +42,13 @@ return [
'remote' => 'Remote',
'remote_help' => 'This can be useful if you need to filter by remote users who never or rarely come into your physical locations.',
'not_remote_label' => 'This is not a remote user',
'vip_label' => 'VIP user',
'vip_help' => 'This can be helpful to mark important people in your org if you would like to handle them in special ways.',
'create_user' => 'Create a user',
'create_user_page_explanation' => 'This is the account information you will use to access the site for the first time.',
'email_credentials' => 'Email credentials',
'email_credentials_text' => 'Email my credentials to the email address above',
'next_save_user' => 'Next: Save User',
'all_assigned_list_generation' => 'Generated on:',
'email_user_creds_on_create' => 'Email this user their credentials?',
];

View file

@ -45,7 +45,7 @@ return [
'bulk_edit' => 'Bulk Edit',
'bulk_delete' => 'Bulk Delete',
'bulk_actions' => 'Bulk Actions',
'bulk_checkin_delete' => 'Bulk Checkin Items from Users',
'bulk_checkin_delete' => 'Bulk Checkin / Delete Users',
'byod' => 'BYOD',
'byod_help' => 'This device is owned by the user',
'bystatus' => 'by Status',
@ -148,6 +148,7 @@ return [
'filetypes_accepted_help' => 'Accepted filetype is :types. Max upload size allowed is :size.|Accepted filetypes are :types. Max upload size allowed is :size.',
'filetypes_size_help' => 'Max upload size allowed is :size.',
'image_filetypes_help' => 'Accepted filetypes are jpg, webp, png, gif, and svg. Max upload size allowed is :size.',
'unaccepted_image_type' => 'This image file was not readable. Accepted filetypes are jpg, webp, png, gif, and svg. The mimetype of this file is: :mimetype.',
'import' => 'invoer',
'importing' => 'Importing',
'importing_help' => 'You can import assets, accessories, licenses, components, consumables, and users via CSV file. <br><br>The CSV should be comma-delimited and formatted with headers that match the ones in the <a href="https://snipe-it.readme.io/docs/importing" target="_new">sample CSVs in the documentation</a>.',
@ -157,6 +158,8 @@ return [
'asset_maintenances' => 'Asset Maintenances',
'item' => 'item',
'item_name' => 'Item Name',
'import_file' => 'import CSV file',
'import_type' => 'CSV import type',
'insufficient_permissions' => 'Onvoldoende toestemmings!',
'kits' => 'Predefined Kits',
'language' => 'Taal',
@ -227,6 +230,7 @@ return [
'requested_assets_menu' => 'Requested Assets',
'request_canceled' => 'Versoek gekanselleer',
'save' => 'Save',
'select_var' => 'Select :thing... ', // this will eventually replace all of our other selects
'select' => 'Kies',
'select_all' => 'Select All',
'search' => 'Soek',
@ -249,8 +253,8 @@ return [
'signature' => 'Handtekening',
'signed_off_by' => 'Signed Off By',
'skin' => 'Skin',
'slack_msg_note' => 'A slack message will be sent',
'slack_test_msg' => 'Oh hai! Looks like your Slack integration with Snipe-IT is working!',
'webhook_msg_note' => 'A notification will be sent via webhook',
'webhook_test_msg' => 'Oh hai! Looks like your :app integration with Snipe-IT is working!',
'some_features_disabled' => 'DEMO MODE: Sommige funksies is afgeskakel vir hierdie installasie.',
'site_name' => 'Site Naam',
'state' => 'staat',
@ -262,7 +266,6 @@ return [
'sure_to_delete' => 'Is jy seker jy wil verwyder',
'submit' => 'Indien',
'target' => 'teiken',
'toggle_navigation' => 'Toogle Navigation',
'time_and_date_display' => 'Tyd en datum vertoon',
'total_assets' => 'totale bates',
'total_licenses' => 'totale lisensies',
@ -384,7 +387,8 @@ return [
'bulk_soft_delete' =>'Also soft-delete these users. Their asset history will remain intact unless/until you purge deleted records in the Admin Settings.',
'bulk_checkin_delete_success' => 'Your selected users have been deleted and their items have been checked in.',
'bulk_checkin_success' => 'The items for the selected users have been checked in.',
'set_to_null' => 'Delete values for this asset|Delete values for all :asset_count assets ',
'set_to_null' => 'Delete values for this asset|Delete values for all :asset_count assets ',
'set_users_field_to_null' => 'Delete :field values for this user|Delete :field values for all :user_count users ',
'na_no_purchase_date' => 'N/A - No purchase date provided',
'assets_by_status' => 'Assets by Status',
'assets_by_status_type' => 'Assets by Status Type',
@ -403,7 +407,36 @@ return [
'toggle_navigation' => 'Toggle navigation',
'alerts' => 'Alerts',
'tasks_view_all' => 'View all tasks',
'true' => 'True',
'false' => 'False',
'integration_option' => 'Integration Option',
'log_does_not_exist' => 'No matching log record exists.',
'merge_users' => 'Merge Users',
'merge_information' => 'This will merge the :count users into a single user. Select the user you wish to merge the others into below, and the associated assets, licences, etc will be moved over to the selected user and the other users will be marked as deleted.',
'warning_merge_information' => 'This action CANNOT be undone and should ONLY be used when you need to merge users because of a bad import or sync. Be sure to run a backup first.',
'no_users_selected' => 'No users selected',
'not_enough_users_selected' => 'At least :count users must be selected',
'merge_success' => ':count users merged successfully into :into_username!',
'merged' => 'merged',
'merged_log_this_user_into' => 'Merged this user (ID :to_id - :to_username) into user ID :from_id (:from_username) ',
'merged_log_this_user_from' => 'Merged user ID :from_id (:from_username) into this user (ID :to_id - :to_username)',
'clear_and_save' => 'Clear & Save',
'update_existing_values' => 'Update Existing Values?',
'auto_incrementing_asset_tags_disabled_so_tags_required' => 'Generating auto-incrementing asset tags is disabled so all rows need to have the "Asset Tag" column populated.',
'auto_incrementing_asset_tags_enabled_so_now_assets_will_be_created' => 'Note: Generating auto-incrementing asset tags is enabled so assets will be created for rows that do not have "Asset Tag" populated. Rows that do have "Asset Tag" populated will be updated with the provided information.',
'send_welcome_email_to_users' => ' Send Welcome Email for new Users?',
'back_before_importing' => 'Backup before importing?',
'csv_header_field' => 'CSV Header Field',
'import_field' => 'Import Field',
'sample_value' => 'Sample Value',
'no_headers' => 'No Columns Found',
'error_in_import_file' => 'There was an error reading the CSV file: :error',
'percent_complete' => ':percent % Complete',
'errors_importing' => 'Some Errors occurred while importing: ',
'warning' => 'WARNING: :warning',
'success_redirecting' => '"Success... Redirecting.',
'setup_successful_migrations' => 'Your database tables have been created',
'setup_migration_output' => 'Migration output:',
'setup_migration_create_user' => 'Next: Create User',
'importer_generic_error' => 'Your file import is complete, but we did receive an error. This is usually caused by third-party API throttling from a notification webhook (such as Slack) and would not have interfered with the import itself, but you should confirm this.',
];

View file

@ -15,7 +15,7 @@ return [
'more_info_title' => 'More Info',
'audit_help' => 'Checking this box will edit the asset record to reflect this new location. Leaving it unchecked will simply note the location in the audit log.<br><br>Note that is this asset is checked out, it will not change the location of the person, asset or location it is checked out to.',
'audit_help' => 'Checking this box will edit the asset record to reflect this new location. Leaving it unchecked will simply note the location in the audit log.<br><br>Note that if this asset is checked out, it will not change the location of the person, asset or location it is checked out to.',
'assets' => 'Assets are items tracked by serial number or asset tag. They tend to be higher value items where identifying a specific item matters.',

View file

@ -16,5 +16,6 @@ return array(
'update' => 'Update Accessory',
'use_default_eula' => 'Use the <a href="#" data-toggle="modal" data-target="#eulaModal">primary default EULA</a> instead.',
'use_default_eula_disabled' => '<del>Use the primary default EULA instead.</del> No primary default EULA is set. Please add one in Settings.',
'clone' => 'Clone Accessory',
);

View file

@ -24,6 +24,7 @@ return array(
'checkout' => array(
'error' => 'Accessory was not checked out, please try again',
'success' => 'Accessory checked out successfully.',
'unavailable' => 'Accessory is not available for checkout. Check quantity available',
'user_does_not_exist' => 'That user is invalid. Please try again.'
),

View file

@ -1,14 +1,14 @@
<?php
return [
'asset_maintenance_type' => 'Maintenance Type',
'asset_maintenance_type' => 'Asset Maintenance Type',
'title' => 'Title',
'start_date' => 'Started',
'completion_date' => 'Completed',
'start_date' => 'Start Date',
'completion_date' => 'Completion Date',
'cost' => 'Cost',
'is_warranty' => 'Warranty Improvement',
'asset_maintenance_time' => 'Days',
'asset_maintenance_time' => 'Asset Maintenance Time (in days)',
'notes' => 'Notes',
'update' => 'Update',
'create' => 'Create'
'update' => 'Update Asset Maintenance',
'create' => 'Create Asset Maintenance'
];

View file

@ -3,7 +3,7 @@
return array(
'group_exists' => 'Group already exists!',
'group_not_found' => 'Group [:id] does not exist.',
'group_not_found' => 'Group ID :id does not exist.',
'group_name_required' => 'The name field is required',
'success' => array(

View file

@ -2,8 +2,11 @@
return [
'bulk_delete' => 'Confirm Bulk Delete Assets',
'bulk_restore' => 'Confirm Bulk Restore Assets',
'bulk_delete_help' => 'Review the assets for bulk deletion below. Once deleted, these assets can be restored, but they will no longer be associated with any users they are currently assigned to.',
'bulk_restore_help' => 'Review the assets for bulk restoration below. Once restored, these assets will not be associated with any users they were previously assigned to.',
'bulk_delete_warn' => 'You are about to delete :asset_count assets.',
'bulk_restore_warn' => 'You are about to restore :asset_count assets.',
'bulk_update' => 'Bulk Update Assets',
'bulk_update_help' => 'This form allows you to update multiple assets at once. Only fill in the fields you need to change. Any fields left blank will remain unchanged. ',
'bulk_update_warn' => 'You are about to edit the properties of a single asset.|You are about to edit the properties of :asset_count assets.',
@ -45,7 +48,7 @@ return [
'asset_location_update_default' => 'Update only default location',
'asset_not_deployable' => 'That asset status is not deployable. This asset cannot be checked out.',
'asset_deployable' => 'That status is deployable. This asset can be checked out.',
'processing_spinner' => 'Processing...',
'processing_spinner' => 'Processing... (This might take a bit of time on large files)',
'optional_infos' => 'Optional Information',
'order_details' => 'Order Related Information'
];

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