diff --git a/.all-contributorsrc b/.all-contributorsrc index ec5db5021..68aec8532 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -3181,6 +3181,15 @@ "contributions": [ "code" ] + }, + { + "login": "Glukose1", + "name": "Glukose1", + "avatar_url": "https://avatars.githubusercontent.com/u/167117705?v=4", + "profile": "https://github.com/Glukose1", + "contributions": [ + "code" + ] } ] } diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 0e4efc974..6359814b4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -52,6 +52,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken | [bilias](https://github.com/bilias)[π»](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [coach1988](https://github.com/coach1988)[π»](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [MrM](https://github.com/mauro-miatello)[π»](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [koiakoia](https://github.com/koiakoia)[π»](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [Mustafa Online](https://github.com/mustafa-online)[π»](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [franceslui](https://github.com/franceslui)[π»](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [Q4kK](https://github.com/Q4kK)[π»](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | | [squintfox](https://github.com/squintfox)[π»](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [Jeff Clay](https://github.com/jeffclay)[π»](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [Phil J R](https://github.com/PP-JN-RL)[π»](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [i_virus](https://www.corelight.com/)[π»](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [Paul Grime](https://github.com/gitgrimbo)[π»](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [Lee Porte](https://leeporte.co.uk)[π»](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [BRYAN ](https://github.com/bryanlopezinc)[π»](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [β οΈ](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | | [U-H-T](https://github.com/U-H-T)[π»](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [Matt Tyree](https://github.com/Tyree)[π](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [Florent Bervas](http://spoontux.net)[π»](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [Daniel Albertsen](https://ditscheri.com)[π»](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [r-xyz](https://github.com/r-xyz)[π»](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [Steven Mainor](https://github.com/DrekiDegga)[π»](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [arne-kroeger](https://github.com/arne-kroeger)[π»](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | +| [Glukose1](https://github.com/Glukose1)[π»](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/README.md b/README.md index 6d2c45ac7..0086c7b32 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,13 @@ Since the release of the JSON REST API, several third-party developers have been - [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira) - [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag. - [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit). -- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it. -- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT +- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-IT. +- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT. - [WWW::SnipeIT](https://github.com/SEDC/perl-www-snipeit) by [@SEDC](https://github.com/SEDC) - perl module for accessing the API - [UniFi to Snipe-IT](https://github.com/RodneyLeeBrands/UnifiSnipeSync) by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT. - [Kandji2Snipe](https://github.com/grokability/kandji2snipe) by [@briangoldstein](https://github.com/briangoldstein) - Python script that synchronizes Kandji with Snipe-IT. -- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by @ReticentRobot - Windows agent for Snipe-IT +- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by [@ReticentRobot](https://github.com/ReticentRobot) - Windows agent for Snipe-IT. +- [Gate Pass Generator](https://github.com/cha7uraAE/snipe-it-gate-pass-system) by [@cha7uraAE](https://github.com/cha7uraAE) - A Streamlit application for generating gate passes based on hardware data from a Snipe-IT API. ----- diff --git a/app/Http/Controllers/Api/PredefinedKitsController.php b/app/Http/Controllers/Api/PredefinedKitsController.php index 2bc118db3..26ccb5035 100644 --- a/app/Http/Controllers/Api/PredefinedKitsController.php +++ b/app/Http/Controllers/Api/PredefinedKitsController.php @@ -246,7 +246,7 @@ class PredefinedKitsController extends Controller $relation = $kit->models(); if ($relation->find($model_id)) { - return response()->json(Helper::formatStandardApiResponse('error', null, ['model' => 'Model already attached to kit'])); + return response()->json(Helper::formatStandardApiResponse('error', null, ['model' => trans('admin/kits/general.model_already_attached')])); } $relation->attach($model_id, ['quantity' => $quantity]); diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 9ac976b43..e7b10877c 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -508,8 +508,8 @@ class LoginController extends Controller protected function validator(array $data) { return Validator::make($data, [ - 'username' => 'required', - 'password' => 'required', + 'username' => 'required|not_array', + 'password' => 'required|not_array', ]); } diff --git a/app/Http/Controllers/Kits/CheckoutKitController.php b/app/Http/Controllers/Kits/CheckoutKitController.php index c75e4ea8f..bf4f64a8d 100644 --- a/app/Http/Controllers/Kits/CheckoutKitController.php +++ b/app/Http/Controllers/Kits/CheckoutKitController.php @@ -62,10 +62,10 @@ class CheckoutKitController extends Controller $checkout_result = $this->kitService->checkout($request, $kit, $user); if (Arr::has($checkout_result, 'errors') && count($checkout_result['errors']) > 0) { - return redirect()->back()->with('error', trans('general.checkout_error'))->with('error_messages', $checkout_result['errors']); + return redirect()->back()->with('error', trans('admin/kits/general.checkout_error'))->with('error_messages', $checkout_result['errors']); } - return redirect()->back()->with('success', trans('general.checkout_success')) + return redirect()->back()->with('success', trans('admin/kits/general.checkout_success')) ->with('assets', Arr::get($checkout_result, 'assets', null)) ->with('accessories', Arr::get($checkout_result, 'accessories', null)) ->with('consumables', Arr::get($checkout_result, 'consumables', null)); diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index e58647d72..0ab052596 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -637,6 +637,7 @@ class SettingsController extends Controller $setting->alert_threshold = $request->input('alert_threshold'); $setting->audit_interval = $request->input('audit_interval'); $setting->audit_warning_days = $request->input('audit_warning_days'); + $setting->due_checkin_days = $request->input('due_checkin_days'); $setting->show_alerts_in_menu = $request->input('show_alerts_in_menu', '0'); if ($setting->save()) { diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 73358454d..79027547b 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -48,6 +48,7 @@ class Kernel extends HttpKernel 'api' => [ 'auth:api', + \App\Http\Middleware\CheckLocale::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; diff --git a/app/Http/Traits/TwoColumnUniqueUndeletedTrait.php b/app/Http/Traits/TwoColumnUniqueUndeletedTrait.php index 4aae02bfb..0cf0edb45 100644 --- a/app/Http/Traits/TwoColumnUniqueUndeletedTrait.php +++ b/app/Http/Traits/TwoColumnUniqueUndeletedTrait.php @@ -11,15 +11,17 @@ trait TwoColumnUniqueUndeletedTrait * @param string $field * @return string */ - protected function prepareTwoColumnUniqueUndeletedRule($parameters, $field) + protected function prepareTwoColumnUniqueUndeletedRule($parameters) { $column = $parameters[0]; $value = $this->{$parameters[0]}; + // This is an existing model we're updating so ignore the current ID ($this->getKey()) if ($this->exists) { return 'two_column_unique_undeleted:'.$this->table.','.$this->getKey().','.$column.','.$value; } + // This is a new record, so we can ignore the current ID return 'two_column_unique_undeleted:'.$this->table.',0,'.$column.','.$value; } } diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index d3a19929e..17693fccf 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -86,7 +86,7 @@ class AssetsTransformer 'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date, 'date'), 'deleted_at' => Helper::getFormattedDateObject($asset->deleted_at, 'datetime'), 'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'), - 'age' => $asset->purchase_date ? $asset->purchase_date->diffForHumans() : '', + 'age' => $asset->purchase_date ? $asset->purchase_date->locale(app()->getLocale())->diffForHumans() : '', 'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'), 'last_checkin' => Helper::getFormattedDateObject($asset->last_checkin, 'datetime'), 'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'), diff --git a/app/Importer/AssetImporter.php b/app/Importer/AssetImporter.php index 4bb887bcd..781a6311f 100644 --- a/app/Importer/AssetImporter.php +++ b/app/Importer/AssetImporter.php @@ -71,8 +71,10 @@ class AssetImporter extends ItemImporter $asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first(); if ($asset) { if (! $this->updating) { - $this->log('A matching Asset '.$asset_tag.' already exists'); - return; + $exists_error = trans('general.import_asset_tag_exists', ['asset_tag' => $asset_tag]); + $this->log($exists_error); + $this->addErrorToBag($asset, 'asset_tag', $exists_error); + return $exists_error; } $this->log('Updating Asset'); diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php index 973872350..c2214ef37 100644 --- a/app/Importer/Importer.php +++ b/app/Importer/Importer.php @@ -281,6 +281,13 @@ abstract class Importer } } + protected function addErrorToBag($item, $field, $error_message) + { + if ($this->errorCallback) { + call_user_func($this->errorCallback, $item, $field, [$field => [$error_message]]); + } + } + /** * Finds the user matching given data, or creates a new one if there is no match. * This is NOT used by the User Import, only for Asset/Accessory/etc where diff --git a/app/Importer/ItemImporter.php b/app/Importer/ItemImporter.php index ee680413d..29197ca5d 100644 --- a/app/Importer/ItemImporter.php +++ b/app/Importer/ItemImporter.php @@ -196,64 +196,77 @@ class ItemImporter extends Importer { $condition = array(); $asset_model_name = $this->findCsvMatch($row, 'asset_model'); + $asset_model_category = $this->findCsvMatch($row, 'category'); $asset_modelNumber = $this->findCsvMatch($row, 'model_number'); + // TODO: At the moment, this means we can't update the model number if the model name stays the same. if (! $this->shouldUpdateField($asset_model_name)) { return; } + if ((empty($asset_model_name)) && (! empty($asset_modelNumber))) { $asset_model_name = $asset_modelNumber; } elseif ((empty($asset_model_name)) && (empty($asset_modelNumber))) { $asset_model_name = 'Unknown'; } - if ((!empty($asset_model_name)) && (empty($asset_modelNumber))) { - $condition[] = ['name', '=', $asset_model_name]; - } elseif ((!empty($asset_model_name)) && (!empty($asset_modelNumber))) { - $condition[] = ['name', '=', $asset_model_name]; - $condition[] = ['model_number', '=', $asset_modelNumber]; + $asset_model = AssetModel::select('id'); + + if (!empty($asset_model_name)) { + $asset_model = $asset_model->where('name', '=', $asset_model_name); + + if (!empty($asset_modelNumber)) { + $asset_model = $asset_model->where('model_number', '=', $asset_modelNumber); + } } $editingModel = $this->updating; - $asset_model = AssetModel::where($condition)->first(); + $asset_model = $asset_model->first(); if ($asset_model) { + if (! $this->updating) { $this->log('A matching model already exists, returning it.'); - return $asset_model->id; } + $this->log('Matching Model found, updating it.'); $item = $this->sanitizeItemForStoring($asset_model, $editingModel); $item['name'] = $asset_model_name; $item['notes'] = $this->findCsvMatch($row, 'model_notes'); - if(!empty($asset_modelNumber)){ + if (!empty($asset_modelNumber)){ $item['model_number'] = $asset_modelNumber; } $asset_model->update($item); $asset_model->save(); $this->log('Asset Model Updated'); - + return $asset_model->id; - } - $this->log('No Matching Model, Creating a new one'); + } + + $this->log('No Matching Model, Creating a new one'); $asset_model = new AssetModel(); $item = $this->sanitizeItemForStoring($asset_model, $editingModel); $item['name'] = $asset_model_name; $item['model_number'] = $asset_modelNumber; $item['notes'] = $this->findCsvMatch($row, 'model_notes'); + $item['category_id'] = $this->createOrFetchCategory($asset_model_category); $asset_model->fill($item); + //$asset_model = AssetModel::firstOrNew($item); $item = null; + + if ($asset_model->save()) { $this->log('Asset Model '.$asset_model_name.' with model number '.$asset_modelNumber.' was created'); return $asset_model->id; } + $this->log('Asset Model Errors: '.$asset_model->getErrors()); $this->logError($asset_model, 'Asset Model "'.$asset_model_name.'"'); return null; diff --git a/app/Livewire/Importer.php b/app/Livewire/Importer.php index 99fb73b2a..3c6f7990e 100644 --- a/app/Livewire/Importer.php +++ b/app/Livewire/Importer.php @@ -354,6 +354,12 @@ class Importer extends Component 'model name', 'model', ], + 'eol_date' => + [ + 'eol', + 'eol date', + 'asset eol date', + ], 'gravatar' => [ 'gravatar', diff --git a/app/Models/Asset.php b/app/Models/Asset.php index b3bf126bd..98d7275b1 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -1315,7 +1315,7 @@ class Asset extends Depreciable public function scopeDueForCheckin($query, $settings) { - $interval = $settings->audit_warning_days ?? 0; + $interval = $settings->due_checkin_days ?? 0; $today = Carbon::now(); $interval_date = $today->copy()->addDays($interval)->format('Y-m-d'); diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php index 7a6662091..3c023507d 100755 --- a/app/Models/AssetModel.php +++ b/app/Models/AssetModel.php @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; use Watson\Validating\ValidatingTrait; use \App\Presenters\AssetModelPresenter; +use App\Http\Traits\TwoColumnUniqueUndeletedTrait; /** * Model for Asset Models. Asset Models contain higher level @@ -21,21 +22,8 @@ class AssetModel extends SnipeModel { use HasFactory; use SoftDeletes; - protected $presenter = AssetModelPresenter::class; use Loggable, Requestable, Presentable; - - protected $table = 'models'; - protected $hidden = ['user_id', 'deleted_at']; - - // Declare the rules for the model validation - protected $rules = [ - 'name' => 'string|required|min:1|max:255|unique:models,name', - 'model_number' => 'string|max:255|nullable', - 'min_amt' => 'integer|min:0|nullable', - 'category_id' => 'required|integer|exists:categories,id', - 'manufacturer_id' => 'integer|exists:manufacturers,id|nullable', - 'eol' => 'integer:min:0|max:240|nullable', - ]; + use TwoColumnUniqueUndeletedTrait; /** * Whether the model should inject its identifier to the unique @@ -44,8 +32,26 @@ class AssetModel extends SnipeModel * * @var bool */ + protected $injectUniqueIdentifier = true; use ValidatingTrait; + protected $table = 'models'; + protected $hidden = ['user_id', 'deleted_at']; + protected $presenter = AssetModelPresenter::class; + + // Declare the rules for the model validation + + + protected $rules = [ + 'name' => 'string|required|min:1|max:255|two_column_unique_undeleted:model_number', + 'model_number' => 'string|max:255|nullable|two_column_unique_undeleted:name', + 'min_amt' => 'integer|min:0|nullable', + 'category_id' => 'required|integer|exists:categories,id', + 'manufacturer_id' => 'integer|exists:manufacturers,id|nullable', + 'eol' => 'integer:min:0|max:240|nullable', + ]; + + /** * The attributes that are mass assignable. @@ -86,6 +92,9 @@ class AssetModel extends SnipeModel 'manufacturer' => ['name'], ]; + + + /** * Establishes the model -> assets relationship * diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 1a25735e0..d775be81c 100755 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -74,7 +74,10 @@ class Setting extends Model 'login_remote_user_header_name' => 'string|nullable', 'thumbnail_max_h' => 'numeric|max:500|min:25', 'pwd_secure_min' => 'numeric|required|min:8', + 'alert_threshold' => 'numeric|nullable', + 'alert_interval' => 'numeric|nullable', 'audit_warning_days' => 'numeric|nullable', + 'due_checkin_days' => 'numeric|nullable', 'audit_interval' => 'numeric|nullable', 'custom_forgot_pass_url' => 'url|nullable', 'privacy_policy_link' => 'nullable|url', diff --git a/app/Providers/ValidationServiceProvider.php b/app/Providers/ValidationServiceProvider.php index 041aaad98..1f3abca8a 100644 --- a/app/Providers/ValidationServiceProvider.php +++ b/app/Providers/ValidationServiceProvider.php @@ -6,10 +6,7 @@ use App\Models\CustomField; use App\Models\Department; use App\Models\Setting; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Crypt; -use Illuminate\Support\Facades\Log; use Illuminate\Support\ServiceProvider; -use Illuminate\Validation\Rule; use Illuminate\Support\Facades\Validator; /** @@ -91,18 +88,26 @@ class ValidationServiceProvider extends ServiceProvider * * $parameters[0] - the name of the first table we're looking at * $parameters[1] - the ID (this will be 0 on new creations) - * $parameters[2] - the name of the second table we're looking at + * $parameters[2] - the name of the second field we're looking at * $parameters[3] - the value that the request is passing for the second table we're * checking for uniqueness across * */ Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) { + if (count($parameters)) { + $count = DB::table($parameters[0]) - ->select('id')->where($attribute, '=', $value) - ->whereNull('deleted_at') - ->where('id', '!=', $parameters[1]) - ->where($parameters[2], $parameters[3])->count(); + ->select('id') + ->where($attribute, '=', $value) + ->where('id', '!=', $parameters[1]); + + if ($parameters[3]!='') { + $count = $count->where($parameters[2], $parameters[3]); + } + + $count = $count->whereNull('deleted_at') + ->count(); return $count < 1; } diff --git a/database/migrations/2024_08_16_104137_add_due_checkin_days_to_settings.php b/database/migrations/2024_08_16_104137_add_due_checkin_days_to_settings.php new file mode 100644 index 000000000..09314b88f --- /dev/null +++ b/database/migrations/2024_08_16_104137_add_due_checkin_days_to_settings.php @@ -0,0 +1,28 @@ +integer('due_checkin_days')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('due_checkin_days'); + }); + } +}; diff --git a/docker/entrypoint_alpine.sh b/docker/entrypoint_alpine.sh index c1a75b0cb..d9d6c8a9a 100644 --- a/docker/entrypoint_alpine.sh +++ b/docker/entrypoint_alpine.sh @@ -1,7 +1,48 @@ #!/bin/sh +# Cribbed from nextcloud docker official repo +# https://github.com/nextcloud/docker/blob/master/docker-entrypoint.sh +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + if [ -n "${varValue}" ]; then + export "$var"="${varValue}" + elif [ -n "${fileVarValue}" ]; then + export "$var"="$(cat "${fileVarValue}")" + elif [ -n "${def}" ]; then + export "$var"="$def" + fi + unset "$fileVar" +} + +# Add docker secrets support for the variables below: +file_env APP_KEY +file_env DB_HOST +file_env DB_PORT +file_env DB_DATABASE +file_env DB_USERNAME +file_env DB_PASSWORD +file_env REDIS_HOST +file_env REDIS_PASSWORD +file_env REDIS_PORT +file_env MAIL_HOST +file_env MAIL_PORT +file_env MAIL_USERNAME +file_env MAIL_PASSWORD + # fix key if needed -if [ -z "$APP_KEY" ] +if [ -z "$APP_KEY" -a -z "$APP_KEY_FILE" ] then echo "Please re-run this container with an environment variable \$APP_KEY" echo "An example APP_KEY you could use is: " diff --git a/docker/startup.sh b/docker/startup.sh index 62002a2ba..2f6be7b0f 100644 --- a/docker/startup.sh +++ b/docker/startup.sh @@ -1,7 +1,48 @@ #!/bin/bash +# Cribbed from nextcloud docker official repo +# https://github.com/nextcloud/docker/blob/master/docker-entrypoint.sh +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + if [ -n "${varValue}" ]; then + export "$var"="${varValue}" + elif [ -n "${fileVarValue}" ]; then + export "$var"="$(cat "${fileVarValue}")" + elif [ -n "${def}" ]; then + export "$var"="$def" + fi + unset "$fileVar" +} + +# Add docker secrets support for the variables below: +file_env APP_KEY +file_env DB_HOST +file_env DB_PORT +file_env DB_DATABASE +file_env DB_USERNAME +file_env DB_PASSWORD +file_env REDIS_HOST +file_env REDIS_PASSWORD +file_env REDIS_PORT +file_env MAIL_HOST +file_env MAIL_PORT +file_env MAIL_USERNAME +file_env MAIL_PASSWORD + # fix key if needed -if [ -z "$APP_KEY" ] +if [ -z "$APP_KEY" -a -z "$APP_KEY_FILE" ] then echo "Please re-run this container with an environment variable \$APP_KEY" echo "An example APP_KEY you could use is: " diff --git a/public/css/build/app.css b/public/css/build/app.css index bcc317f06..f70346905 100644 --- a/public/css/build/app.css +++ b/public/css/build/app.css @@ -1123,14 +1123,18 @@ th.css-location > .th-inner::before { } } @media screen and (max-width: 1318px) and (min-width: 1200px) { - .box { + .admin.box { height: 170px; } } -.ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +@media screen and (max-width: 1494px) and (min-width: 1200px) { + .dashboard.small-box { + white-space: nowrap; + text-overflow: ellipsis; + max-width: 188px; + display: block; + overflow: hidden; + } } /** Form-stuff overrides for checkboxes and stuff **/ label.form-control { diff --git a/public/css/build/overrides.css b/public/css/build/overrides.css index 7b9e7d1b9..d8169245b 100644 --- a/public/css/build/overrides.css +++ b/public/css/build/overrides.css @@ -756,14 +756,18 @@ th.css-location > .th-inner::before { } } @media screen and (max-width: 1318px) and (min-width: 1200px) { - .box { + .admin.box { height: 170px; } } -.ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +@media screen and (max-width: 1494px) and (min-width: 1200px) { + .dashboard.small-box { + white-space: nowrap; + text-overflow: ellipsis; + max-width: 188px; + display: block; + overflow: hidden; + } } /** Form-stuff overrides for checkboxes and stuff **/ label.form-control { diff --git a/public/css/dist/all.css b/public/css/dist/all.css index 47beba874..8957c0099 100644 --- a/public/css/dist/all.css +++ b/public/css/dist/all.css @@ -22095,14 +22095,18 @@ th.css-location > .th-inner::before { } } @media screen and (max-width: 1318px) and (min-width: 1200px) { - .box { + .admin.box { height: 170px; } } -.ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +@media screen and (max-width: 1494px) and (min-width: 1200px) { + .dashboard.small-box { + white-space: nowrap; + text-overflow: ellipsis; + max-width: 188px; + display: block; + overflow: hidden; + } } /** Form-stuff overrides for checkboxes and stuff **/ label.form-control { @@ -23519,14 +23523,18 @@ th.css-location > .th-inner::before { } } @media screen and (max-width: 1318px) and (min-width: 1200px) { - .box { + .admin.box { height: 170px; } } -.ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +@media screen and (max-width: 1494px) and (min-width: 1200px) { + .dashboard.small-box { + white-space: nowrap; + text-overflow: ellipsis; + max-width: 188px; + display: block; + overflow: hidden; + } } /** Form-stuff overrides for checkboxes and stuff **/ label.form-control { diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 568d8a0bb..547cbd7f5 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -2,8 +2,8 @@ "/js/build/app.js": "/js/build/app.js?id=5030f4cb69d0d0b87b7fe6ba1b9eece9", "/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f0b08873a06bb54daeee176a9459f4a9", "/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=f4397c717b99fce41a633ca6edd5d1f4", - "/css/build/overrides.css": "/css/build/overrides.css?id=a759aa24710e294392877c5139fda40e", - "/css/build/app.css": "/css/build/app.css?id=b3b3df70f679f45e15a6bcd28a8e87cc", + "/css/build/overrides.css": "/css/build/overrides.css?id=e83697e14b9cd1478ea2bda8fb50f30c", + "/css/build/app.css": "/css/build/app.css?id=ff82b7edca60f7276504bde91568f6d1", "/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9", "/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690", "/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=393aaa7b368b0670fc42434c8cca7dc7", @@ -19,7 +19,7 @@ "/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374", "/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899", "/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb", - "/css/dist/all.css": "/css/dist/all.css?id=7b8e04041af3dfe3de25d73107bfda91", + "/css/dist/all.css": "/css/dist/all.css?id=053e0f8c49e8cf1b903c80c8f67639eb", "/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", "/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde", diff --git a/resources/assets/less/overrides.less b/resources/assets/less/overrides.less index 3717dd477..a6a2347d4 100644 --- a/resources/assets/less/overrides.less +++ b/resources/assets/less/overrides.less @@ -845,18 +845,20 @@ th.css-location > .th-inner::before { } } @media screen and (max-width: 1318px) and (min-width: 1200px){ - .box{ + .admin.box{ height:170px; } } - -.ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +@media screen and (max-width: 1494px) and (min-width: 1200px){ + .dashboard.small-box{ + white-space: nowrap; + text-overflow: ellipsis; + max-width: 188px; + display: block; + overflow: hidden; + } } - /** Form-stuff overrides for checkboxes and stuff **/ label.form-control { diff --git a/resources/lang/en-US/admin/kits/general.php b/resources/lang/en-US/admin/kits/general.php index f57fb645c..2b5c2d9cd 100644 --- a/resources/lang/en-US/admin/kits/general.php +++ b/resources/lang/en-US/admin/kits/general.php @@ -47,4 +47,5 @@ return [ 'kit_deleted' => 'Kit was successfully deleted', 'kit_model_updated' => 'Model was successfully updated', 'kit_model_detached' => 'Model was successfully detached', + 'model_already_attached' => 'Model already attached to kit', ]; diff --git a/resources/lang/en-US/admin/settings/general.php b/resources/lang/en-US/admin/settings/general.php index 31165cf3f..7ce360eda 100644 --- a/resources/lang/en-US/admin/settings/general.php +++ b/resources/lang/en-US/admin/settings/general.php @@ -381,5 +381,7 @@ return [ 'default_avatar_help' => 'This image will be displayed as a profile if a user does not have a profile photo.', 'restore_default_avatar' => 'Restore original system default avatar', 'restore_default_avatar_help' => '', + 'due_checkin_days' => 'Due For Checkin Warning', + 'due_checkin_days_help' => 'How many days before the expected checkin of an asset should it be listed in the "Due for checkin" page?', ]; diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index 42ca4cb96..763438790 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -426,7 +426,7 @@ return [ 'assets_by_status_type' => 'Assets by Status Type', 'pie_chart_type' => 'Dashboard Pie Chart Type', 'hello_name' => 'Hello, :name!', - 'unaccepted_profile_warning' => 'You have :count items requiring acceptance. Click here to accept or decline them', + 'unaccepted_profile_warning' => 'You have one item requiring acceptance. Click here to accept or decline it | You have :count items requiring acceptance. Click here to accept or decline them', 'start_date' => 'Start Date', 'end_date' => 'End Date', 'alt_uploaded_image_thumbnail' => 'Uploaded thumbnail', @@ -559,5 +559,8 @@ return [ 'expires' => 'Expires', 'map_fields'=> 'Map :item_type Field', 'remaining_var' => ':count Remaining', + 'assets_in_var' => 'Assets in :name :type', + 'label' => 'Label', + 'import_asset_tag_exists' => 'An asset with the asset tag :asset_tag already exists and an update was not requested. No change was made.', ]; diff --git a/resources/macros/macros.php b/resources/macros/macros.php index 584c7df84..7fefba2aa 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -84,8 +84,9 @@ Form::macro('time_display_format', function ($name = 'time_display_format', $sel 'H:i', ]; + $datetime = date("y-m-d").' 14:00:00'; foreach ($formats as $format) { - $time_display_formats[$format] = Carbon::now()->format($format); + $time_display_formats[$format] = Carbon::parse($datetime)->format($format); } $select = ''; foreach ($time_display_formats as $format => $time_display_format) { diff --git a/resources/views/account/view-assets.blade.php b/resources/views/account/view-assets.blade.php index 6fee34652..a4ffa16ec 100755 --- a/resources/views/account/view-assets.blade.php +++ b/resources/views/account/view-assets.blade.php @@ -17,7 +17,7 @@ - {{ trans('general.unaccepted_profile_warning', array('count' => $acceptances)) }} + {{ trans_choice('general.unaccepted_profile_warning', $acceptances, ['count' => $acceptances]) }} diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 16c67ff2b..298d55f4e 100755 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -57,16 +57,22 @@ - {{ trans('admin/users/table.username') }} + + + {{ trans('admin/users/table.username') }} + {!! $errors->first('username', ' :message') !!} - {{ trans('admin/users/table.password') }} + + + {{ trans('admin/users/table.password') }} + {!! $errors->first('password', ' :message') !!} - + {{ trans('auth/general.remember_me') }} diff --git a/resources/views/categories/view.blade.php b/resources/views/categories/view.blade.php index 9b361c46c..72a02bf92 100644 --- a/resources/views/categories/view.blade.php +++ b/resources/views/categories/view.blade.php @@ -3,8 +3,7 @@ {{-- Page title --}} @section('title') - {{ $category->name }} - {{ ucwords($category_type_route) }} +{{ trans('general.assets_in_var', ['name'=> $category->name, 'type' => trans('general.category')]) }} @parent @stop diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 40255f88b..233912207 100755 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -31,7 +31,7 @@ - + {{ number_format(\App\Models\Asset::AssetsForShow()->count()) }} {{ strtolower(trans('general.assets')) }} @@ -49,7 +49,7 @@ - + {{ number_format($counts['license']) }} {{ strtolower(trans('general.licenses')) }} @@ -68,7 +68,7 @@ - + {{ number_format($counts['accessory']) }} {{ strtolower(trans('general.accessories')) }} @@ -87,7 +87,7 @@ - + {{ number_format($counts['consumable']) }} {{ strtolower(trans('general.consumables')) }} @@ -104,7 +104,7 @@ - + {{ number_format($counts['component']) }} {{ strtolower(trans('general.components')) }} @@ -122,7 +122,7 @@ - + {{ number_format($counts['user']) }} {{ strtolower(trans('general.people')) }} diff --git a/resources/views/hardware/checkin-due.blade.php b/resources/views/hardware/checkin-due.blade.php index 6ed7cf42c..79ea7553a 100644 --- a/resources/views/hardware/checkin-due.blade.php +++ b/resources/views/hardware/checkin-due.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') - {{ trans_choice('general.checkin_due_days', $settings->audit_warning_days, ['days' => $settings->audit_warning_days]) }} + {{ trans_choice('general.checkin_due_days', $settings->due_checkin_days, ['days' => $settings->due_checkin_days]) }} @stop {{-- Page content --}} diff --git a/resources/views/hardware/checkin.blade.php b/resources/views/hardware/checkin.blade.php index efd050266..12a0627ae 100755 --- a/resources/views/hardware/checkin.blade.php +++ b/resources/views/hardware/checkin.blade.php @@ -132,7 +132,7 @@ :button_label="trans('general.checkin')" :disabled_select="!$asset->model" :options="[ - 'index' => trans('admin/hardware/form.redirect_to_all', ['type' => 'assets']), + 'index' => trans('admin/hardware/form.redirect_to_all', ['type' => trans('general.assets')]), 'item' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset')]), ]" /> diff --git a/resources/views/hardware/checkout.blade.php b/resources/views/hardware/checkout.blade.php index f91e06036..17b971a5b 100755 --- a/resources/views/hardware/checkout.blade.php +++ b/resources/views/hardware/checkout.blade.php @@ -180,7 +180,7 @@ :button_label="trans('general.checkout')" :disabled_select="!$asset->model" :options="[ - 'index' => trans('admin/hardware/form.redirect_to_all', ['type' => 'assets']), + 'index' => trans('admin/hardware/form.redirect_to_all', ['type' => trans('general.assets')]), 'item' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset')]), 'target' => trans('admin/hardware/form.redirect_to_checked_out_to'), diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index a4adb8355..4961cfce2 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -1030,7 +1030,7 @@ - {{ trans('general.labels') }} + {{ trans('general.label') }} diff --git a/resources/views/livewire/importer.blade.php b/resources/views/livewire/importer.blade.php index 38a2d6028..e69a38b18 100644 --- a/resources/views/livewire/importer.blade.php +++ b/resources/views/livewire/importer.blade.php @@ -33,6 +33,7 @@ {{ trans('general.item') }} + Field {{ trans('general.error') }} @@ -41,8 +42,8 @@ @foreach($error_bag as $field => $error_list) {{ $key }} + {{ $field }} - {{ $field }}: {{ implode(", ",$error_list) }} diff --git a/resources/views/settings/alerts.blade.php b/resources/views/settings/alerts.blade.php index 0d434040a..870692f1f 100644 --- a/resources/views/settings/alerts.blade.php +++ b/resources/views/settings/alerts.blade.php @@ -139,14 +139,29 @@ {{ Form::text('audit_warning_days', old('audit_warning_days', $setting->audit_warning_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} {{ trans('general.days') }} + + + {!! $errors->first('audit_warning_days', ':message') !!} + {{ trans('admin/settings/general.audit_warning_days_help') }} + + + + + + + {{ Form::label('due_checkin_days', trans('admin/settings/general.due_checkin_days')) }} + + + {{ Form::text('due_checkin_days', old('due_checkin_days', $setting->due_checkin_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} + {{ trans('general.days') }} - {!! $errors->first('audit_warning_days', ':message') !!} - {{ trans('admin/settings/general.audit_warning_days_help') }} + {!! $errors->first('due_checkin_days', ':message') !!} + {{ trans('admin/settings/general.due_checkin_days_help') }} diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php index 3b805f921..ac7095814 100755 --- a/resources/views/settings/index.blade.php +++ b/resources/views/settings/index.blade.php @@ -57,7 +57,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -107,7 +107,7 @@ - + @@ -124,7 +124,7 @@ - + @@ -142,7 +142,7 @@ - + @@ -158,7 +158,7 @@ - + @@ -173,7 +173,7 @@ - + @@ -188,7 +188,7 @@ - + @@ -203,7 +203,7 @@ - + @@ -219,7 +219,7 @@ - + @@ -234,7 +234,7 @@ - + @@ -249,7 +249,7 @@ - + @@ -264,7 +264,7 @@ - + @@ -280,7 +280,7 @@ - + @@ -295,7 +295,7 @@ - + @@ -311,7 +311,7 @@ @if (config('app.debug')=== true) - + @@ -329,7 +329,7 @@ - + diff --git a/resources/views/settings/labels.blade.php b/resources/views/settings/labels.blade.php index 65d7e6c84..058446e74 100644 --- a/resources/views/settings/labels.blade.php +++ b/resources/views/settings/labels.blade.php @@ -147,14 +147,13 @@ @php $select1DValues = [ - 'default' => trans('admin/settings/general.default').' [ '.$setting->alt_barcode.' ]', - 'none' => trans('admin/settings/general.none'), 'C128' => 'C128', 'C39' => 'C39', 'EAN5' => 'EAN5', 'EAN13' => 'EAN13', 'UPCA' => 'UPCA', - 'UPCE' => 'UPCE' + 'UPCE' => 'UPCE', + 'none' => trans('admin/settings/general.none'), ]; @endphp {{ Form::select('label2_1d_type', $select1DValues, old('label2_1d_type', $setting->label2_1d_type), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_1d_type' ]) }} @@ -179,11 +178,10 @@ @php $select2DValues = [ - 'default' => trans('admin/settings/general.default').' [ '.$setting->barcode_type.' ]', - 'none' => trans('admin/settings/general.none'), 'QRCODE' => 'QRCODE', 'DATAMATRIX' => 'DATAMATRIX', 'PDF417' => 'PDF417', + 'none' => trans('admin/settings/general.none'), ]; @endphp {{ Form::select('label2_2d_type', $select2DValues, old('label2_2d_type', $setting->label2_2d_type), [ 'class'=>'select2 col-md-4', 'aria-label'=>'label2_2d_type' ]) }} diff --git a/resources/views/settings/logins.blade.php b/resources/views/settings/logins.blade.php index aedef4f84..959392b63 100644 --- a/resources/views/settings/logins.blade.php +++ b/resources/views/settings/logins.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') - Attempted Logins + {{ trans('admin/settings/general.login') }} @parent @stop diff --git a/resources/views/users/print.blade.php b/resources/views/users/print.blade.php index a9f977a3a..26ed2e812 100644 --- a/resources/views/users/print.blade.php +++ b/resources/views/users/print.blade.php @@ -373,7 +373,7 @@ - + EULA @@ -395,8 +395,8 @@ - Name - Signature + {{ trans('general.name') }} + {{ trans('general.signature') }} {{ trans('general.date') }} @@ -407,8 +407,8 @@ - Name - Signature + {{ trans('general.name') }} + {{ trans('general.signature') }} {{ trans('general.date') }} @@ -419,6 +419,10 @@ + + + +
{{ strtolower(trans('general.assets')) }}
{{ strtolower(trans('general.licenses')) }}
{{ strtolower(trans('general.accessories')) }}
{{ strtolower(trans('general.consumables')) }}
{{ strtolower(trans('general.components')) }}
{{ strtolower(trans('general.people')) }}
{{ trans('admin/settings/general.audit_warning_days_help') }}
{{ trans('admin/settings/general.due_checkin_days_help') }}