mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-24 21:24:13 -08:00
Merge branch 'develop' into livewire-importer-improvements
# Conflicts: # resources/views/livewire/importer.blade.php
This commit is contained in:
commit
6521c02526
|
@ -3145,6 +3145,42 @@
|
|||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dbakan",
|
||||
"name": "Daniel Albertsen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4498077?v=4",
|
||||
"profile": "https://ditscheri.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "r-xyz",
|
||||
"name": "r-xyz",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/100710244?v=4",
|
||||
"profile": "https://github.com/r-xyz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DrekiDegga",
|
||||
"name": "Steven Mainor",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/47491036?v=4",
|
||||
"profile": "https://github.com/DrekiDegga",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arne-kroeger",
|
||||
"name": "arne-kroeger",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/65785975?v=4",
|
||||
"profile": "https://github.com/arne-kroeger",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
|
|||
APP_URL=http://localhost:8000
|
||||
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - TZ identifier
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE=en-US
|
||||
MAX_RESULTS=500
|
||||
|
||||
# --------------------------------------------
|
||||
|
|
|
@ -6,7 +6,7 @@ APP_DEBUG=false
|
|||
APP_KEY=base64:hTUIUh9CP6dQx+6EjSlfWTgbaMaaRvlpEwk45vp+xmk=
|
||||
APP_URL=http://127.0.0.1:8000
|
||||
APP_TIMEZONE='US/Eastern'
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE=en-US
|
||||
APP_LOCKED=false
|
||||
MAX_RESULTS=200
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ SESSION_LIFETIME=12000
|
|||
EXPIRE_ON_CLOSE=false
|
||||
ENCRYPT=false
|
||||
COOKIE_NAME=snipeit_session
|
||||
PASSPORT_COOKIE_NAME='snipeit_passport_token'
|
||||
COOKIE_DOMAIN=null
|
||||
SECURE_COOKIES=false
|
||||
API_TOKEN_EXPIRATION_YEARS=15
|
||||
|
@ -183,6 +184,7 @@ REPORT_TIME_LIMIT=12000
|
|||
REQUIRE_SAML=false
|
||||
API_THROTTLE_PER_MINUTE=120
|
||||
CSV_ESCAPE_FORMULAS=true
|
||||
LIVEWIRE_URL_PREFIX=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: HASHING
|
||||
|
|
2
.github/workflows/codacy-analysis.yml
vendored
2
.github/workflows/codacy-analysis.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
|||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@v4.4.1
|
||||
uses: codacy/codacy-analysis-cli-action@v4.4.5
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -67,3 +67,4 @@ _ide_helper_models.php
|
|||
/.phplint-cache
|
||||
storage/ldap_client_tls.cert
|
||||
storage/ldap_client_tls.key
|
||||
/storage/framework/testing
|
||||
|
|
|
@ -51,7 +51,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
|||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
FROM alpine:3.18.6
|
||||
FROM alpine:3.19
|
||||
# Apache + PHP
|
||||
RUN apk add --no-cache \
|
||||
apache2 \
|
||||
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 \
|
||||
php81-exif \
|
||||
php82 \
|
||||
php82-common \
|
||||
php82-apache2 \
|
||||
php82-curl \
|
||||
php82-ldap \
|
||||
php82-mysqli \
|
||||
php82-gd \
|
||||
php82-xml \
|
||||
php82-mbstring \
|
||||
php82-zip \
|
||||
php82-ctype \
|
||||
php82-tokenizer \
|
||||
php82-pdo_mysql \
|
||||
php82-openssl \
|
||||
php82-bcmath \
|
||||
php82-phar \
|
||||
php82-json \
|
||||
php82-iconv \
|
||||
php82-fileinfo \
|
||||
php82-simplexml \
|
||||
php82-session \
|
||||
php82-dom \
|
||||
php82-xmlwriter \
|
||||
php82-xmlreader \
|
||||
php82-sodium \
|
||||
php82-redis \
|
||||
php82-pecl-memcached \
|
||||
php82-exif \
|
||||
curl \
|
||||
wget \
|
||||
vim \
|
||||
|
@ -42,7 +42,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/php81/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php82/php.ini
|
||||
COPY docker/000-default-2.4.conf /etc/apache2/conf.d/default.conf
|
||||
|
||||
# Enable mod_rewrite
|
||||
|
|
|
@ -20,7 +20,7 @@ APP_DEBUG=true
|
|||
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
|
||||
APP_URL=http://localhost:8000
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
APP_LOCALE=en-US
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: DATABASE SETTINGS
|
||||
|
|
|
@ -251,6 +251,7 @@ class LdapSync extends Command
|
|||
// Creating a new user.
|
||||
$user = new User;
|
||||
$user->password = $user->noPassword();
|
||||
$user->locale = app()->getLocale();
|
||||
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
|
||||
$item['createorupdate'] = 'created';
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
|
@ -15,6 +16,8 @@ use App\Models\Statuslabel;
|
|||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Purge extends Command
|
||||
{
|
||||
|
@ -141,6 +144,20 @@ class Purge extends Command
|
|||
$this->info($users->count().' users purged.');
|
||||
$user_assoc = 0;
|
||||
foreach ($users as $user) {
|
||||
|
||||
$rel_path = 'private_uploads/users';
|
||||
$filenames = Actionlog::where('action_type', 'uploaded')
|
||||
->where('item_id', $user->id)
|
||||
->pluck('filename');
|
||||
foreach($filenames as $filename) {
|
||||
try {
|
||||
if (Storage::exists($rel_path . '/' . $filename)) {
|
||||
Storage::delete($rel_path . '/' . $filename);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::info('An error occurred while deleting files: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->info('- User "'.$user->username.'" deleted.');
|
||||
$user_assoc += $user->userlog()->count();
|
||||
$user->userlog()->forceDelete();
|
||||
|
|
|
@ -73,6 +73,7 @@ class ResetDemoSettings extends Command
|
|||
$settings->saml_forcelogin = '0';
|
||||
$settings->saml_slo = null;
|
||||
$settings->saml_custom_settings = null;
|
||||
$settings->default_avatar = 'default.png';
|
||||
|
||||
|
||||
$settings->save();
|
||||
|
|
|
@ -30,8 +30,11 @@ class SQLStreamer {
|
|||
public function parse_sql(string $line): string {
|
||||
// take into account the 'start of line or not' setting as an instance variable?
|
||||
// 'continuation' lines for a permitted statement are PERMITTED.
|
||||
// remove *only* line-feeds & carriage-returns; helpful for regexes against lines from
|
||||
// Windows dumps
|
||||
$line = trim($line, "\r\n");
|
||||
if($this->statement_is_permitted && $line[0] === ' ') {
|
||||
return $line;
|
||||
return $line . "\n"; //re-add the newline
|
||||
}
|
||||
|
||||
$table_regex = '`?([a-zA-Z0-9_]+)`?';
|
||||
|
@ -42,8 +45,12 @@ class SQLStreamer {
|
|||
"/^(INSERT INTO )$table_regex(.*)$/" => false,
|
||||
"/^UNLOCK TABLES/" => false,
|
||||
// "/^\\) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;/" => false, // FIXME not sure what to do here?
|
||||
"/^\\)[a-zA-Z0-9_= ]*;$/" => false
|
||||
// ^^^^^^ that bit should *exit* the 'perimitted' black
|
||||
"/^\\)[a-zA-Z0-9_= ]*;$/" => false,
|
||||
// ^^^^^^ that bit should *exit* the 'permitted' block
|
||||
"/^\\(.*\\)[,;]$/" => false, //older MySQL dump style with one set of values per line
|
||||
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
|
||||
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
|
||||
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
|
||||
];
|
||||
|
||||
foreach($allowed_statements as $statement => $statechange) {
|
||||
|
@ -67,7 +74,7 @@ class SQLStreamer {
|
|||
}
|
||||
//how do we *replace* the tablename?
|
||||
// print "RETURNING LINE: $line";
|
||||
return $line;
|
||||
return $line . "\n"; //re-add newline
|
||||
}
|
||||
}
|
||||
// all that is not allowed is denied.
|
||||
|
@ -85,7 +92,7 @@ class SQLStreamer {
|
|||
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
|
||||
|
||||
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; //TODO - move to statics?
|
||||
//can't use 'users' because the 'accessories_users' table?
|
||||
//can't use 'users' because the 'accessories_checkout' table?
|
||||
// can't use 'assets' because 'ver1_components_assets'
|
||||
foreach($check_tables as $check_table => $_ignore) {
|
||||
foreach ($parser->tablenames as $tablename => $_count) {
|
||||
|
@ -164,7 +171,8 @@ class RestoreFromBackup extends Command
|
|||
{filename : The zip file to be migrated}
|
||||
{--no-progress : Don\'t show a progress bar}
|
||||
{--sanitize-guess-prefix : Guess and output the table-prefix needed to "sanitize" the SQL}
|
||||
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}';
|
||||
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}
|
||||
{--sql-stdout-only : ONLY "Sanitize" the SQL and print it to stdout - useful for debugging - probably requires --sanitize-with-prefix= }';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
|
@ -365,6 +373,15 @@ class RestoreFromBackup extends Command
|
|||
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
|
||||
}
|
||||
|
||||
// If we're doing --sql-stdout-only, handle that now so we don't have to open pipes to mysql and all of that silliness
|
||||
if ($this->option('sql-stdout-only')) {
|
||||
$sql_importer = new SQLStreamer($sql_contents, STDOUT, $this->option('sanitize-with-prefix'));
|
||||
$bytes_read = $sql_importer->line_aware_piping();
|
||||
return $this->warn("$bytes_read total bytes read");
|
||||
//TODO - it'd be nice to dump this message to STDERR so that STDOUT is just pure SQL,
|
||||
// which would be good for redirecting to a file, and not having to trim the last line off of it
|
||||
}
|
||||
|
||||
//how to invoke the restore?
|
||||
$pipes = [];
|
||||
|
||||
|
@ -466,6 +483,9 @@ class RestoreFromBackup extends Command
|
|||
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
|
||||
$fp = $za->getStream($ugly_file_name);
|
||||
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
||||
if (!is_dir($file_details['dest'])) {
|
||||
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
|
||||
}
|
||||
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
|
||||
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
|
||||
fwrite($migrated_file, $buffer);
|
||||
|
|
105
app/Console/Commands/SendAcceptanceReminder.php
Normal file
105
app/Console/Commands/SendAcceptanceReminder.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use App\Notifications\UnacceptedAssetReminderNotification;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class SendAcceptanceReminder extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:acceptance-reminder';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This will resend users with unaccepted assets a reminder to accept or decline them.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$pending = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')
|
||||
->whereHas('checkoutable', function($query) {
|
||||
$query->where('archived', 0);
|
||||
})
|
||||
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.adminuser'])
|
||||
->get();
|
||||
|
||||
$count = 0;
|
||||
$unacceptedAssetGroups = $pending
|
||||
->filter(function($acceptance) {
|
||||
return $acceptance->checkoutable_type == 'App\Models\Asset';
|
||||
})
|
||||
->map(function($acceptance) {
|
||||
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
|
||||
})
|
||||
->groupBy(function($item) {
|
||||
return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : '';
|
||||
});
|
||||
|
||||
$no_mail_address = [];
|
||||
|
||||
foreach($unacceptedAssetGroups as $unacceptedAssetGroup) {
|
||||
$item_count = $unacceptedAssetGroup->count();
|
||||
foreach ($unacceptedAssetGroup as $unacceptedAsset) {
|
||||
// if ($unacceptedAsset['acceptance']->assignedTo->email == ''){
|
||||
// $no_mail_address[] = $unacceptedAsset['checkoutable']->assignedTo->present()->fullName;
|
||||
// }
|
||||
if ($unacceptedAsset['acceptance']->assignedTo) {
|
||||
|
||||
if (!$unacceptedAsset['acceptance']->assignedTo->locale) {
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
$unacceptedAsset['acceptance']->assignedTo,
|
||||
new UnacceptedAssetReminderNotification($unacceptedAsset['assetItem'], $count)
|
||||
);
|
||||
} else {
|
||||
Notification::send(
|
||||
$unacceptedAsset['acceptance']->assignedTo,
|
||||
new UnacceptedAssetReminderNotification($unacceptedAsset, $item_count)
|
||||
);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($no_mail_address)) {
|
||||
foreach($no_mail_address as $user) {
|
||||
return $user.' has no email.';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$this->info($count.' users notified.');
|
||||
}
|
||||
}
|
|
@ -62,8 +62,9 @@ class Helper
|
|||
'mn' => 'mn-MN', // Mongolian
|
||||
'ms' => 'ms-MY', // Malay
|
||||
'nl' => 'nl-NL', // Dutch
|
||||
'no' => 'no-NO', // Norwegian
|
||||
'no' => 'nb-NO', // Norwegian Bokmål
|
||||
'pl' => 'pl-PL', // Polish
|
||||
'pt' => 'pt-PT', // Portuguese
|
||||
'ro' => 'ro-RO', // Romanian
|
||||
'ru' => 'ru-RU', // Russian
|
||||
'sk' => 'sk-SK', // Slovak
|
||||
|
@ -720,7 +721,7 @@ class Helper
|
|||
{
|
||||
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
||||
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
||||
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
|
||||
$accessories = Accessory::withCount('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
|
||||
$components = Component::whereNotNull('min_amt')->get();
|
||||
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
||||
$licenses = License::where('min_amt', '>', 0)->get();
|
||||
|
@ -748,7 +749,7 @@ class Helper
|
|||
}
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
$avail = $accessory->qty - $accessory->users_count;
|
||||
$avail = $accessory->qty - $accessory->checkouts_count;
|
||||
if ($avail < ($accessory->min_amt) + $alert_threshold) {
|
||||
if ($accessory->qty > 0) {
|
||||
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
||||
|
@ -913,13 +914,22 @@ class Helper
|
|||
$rules = $class::rules();
|
||||
foreach ($rules as $rule_name => $rule) {
|
||||
if ($rule_name == $field) {
|
||||
if (strpos($rule, 'required') === false) {
|
||||
return false;
|
||||
if (is_array($rule)) {
|
||||
if (in_array('required', $rule)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
if (strpos($rule, 'required') === false) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1440,7 +1450,6 @@ class Helper
|
|||
|
||||
foreach (self::$language_map as $legacy => $new) {
|
||||
if ($language_code == $legacy) {
|
||||
Log::debug('Current language is '.$legacy.', using '.$new.' instead');
|
||||
return $new;
|
||||
}
|
||||
}
|
||||
|
@ -1451,6 +1460,7 @@ class Helper
|
|||
|
||||
public static function mapBackToLegacyLocale($new_locale = null)
|
||||
{
|
||||
|
||||
if (strlen($new_locale) <= 4) {
|
||||
return $new_locale; //"new locale" apparently wasn't quite so new
|
||||
}
|
||||
|
@ -1458,42 +1468,73 @@ class Helper
|
|||
// This does a *reverse* search against our new language map array - given the value, find the *key* for it
|
||||
$legacy_locale = array_search($new_locale, self::$language_map);
|
||||
|
||||
if($legacy_locale !== false) {
|
||||
if ($legacy_locale !== false) {
|
||||
return $legacy_locale;
|
||||
}
|
||||
return $new_locale; // better that you have some weird locale that doesn't fit into our mappings anywhere than 'void'
|
||||
}
|
||||
|
||||
public static function determineLanguageDirection() {
|
||||
return in_array(app()->getLocale(),
|
||||
[
|
||||
'ar-SA',
|
||||
'fa-IR',
|
||||
'he-IL'
|
||||
]) ? 'rtl' : 'ltr';
|
||||
}
|
||||
|
||||
static public function getRedirectOption($request, $id, $table, $asset_id = null)
|
||||
|
||||
static public function getRedirectOption($request, $id, $table, $item_id = null)
|
||||
{
|
||||
|
||||
$redirect_option = Session::get('redirect_option');
|
||||
$checkout_to_type = Session::get('checkout_to_type');
|
||||
|
||||
//return to index
|
||||
if ($redirect_option == '0') {
|
||||
// return to index
|
||||
if ($redirect_option == 'index') {
|
||||
switch ($table) {
|
||||
case "Assets":
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
return route('hardware.index');
|
||||
case "Users":
|
||||
return route('users.index');
|
||||
case "Licenses":
|
||||
return route('licenses.index');
|
||||
case "Accessories":
|
||||
return route('accessories.index');
|
||||
case "Components":
|
||||
return route('components.index');
|
||||
case "Consumables":
|
||||
return route('consumables.index');
|
||||
}
|
||||
}
|
||||
//return to thing being assigned
|
||||
if ($redirect_option == '1') {
|
||||
|
||||
// return to thing being assigned
|
||||
if ($redirect_option == 'item') {
|
||||
switch ($table) {
|
||||
case "Assets":
|
||||
return redirect()->route('hardware.show', $id ? $id : $asset_id)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
return route('hardware.show', $id ?? $item_id);
|
||||
case "Users":
|
||||
return route('users.show', $id ?? $item_id);
|
||||
case "Licenses":
|
||||
return route('licenses.show', $id ?? $item_id);
|
||||
case "Accessories":
|
||||
return route('accessories.show', $id ?? $item_id);
|
||||
case "Components":
|
||||
return route('components.show', $id ?? $item_id);
|
||||
case "Consumables":
|
||||
return route('consumables.show', $id ?? $item_id);
|
||||
}
|
||||
}
|
||||
//return to thing being assigned to
|
||||
if ($redirect_option == '2') {
|
||||
|
||||
// return to assignment target
|
||||
if ($redirect_option == 'target') {
|
||||
switch ($checkout_to_type) {
|
||||
case 'user':
|
||||
return redirect()->route('users.show', $request->assigned_user)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
return route('users.show', ['user' => $request->assigned_user]);
|
||||
case 'location':
|
||||
return redirect()->route('locations.show', $request->assigned_location)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
return route('locations.show', ['location' => $request->assigned_location]);
|
||||
case 'asset':
|
||||
return redirect()->route('hardware.show', $request->assigned_asset)->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
return route('hardware.show', ['hardware' => $request->assigned_asset]);
|
||||
}
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
|
||||
|
|
|
@ -79,10 +79,11 @@ class AccessoriesController extends Controller
|
|||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
// Was the accessory created?
|
||||
if ($accessory->save()) {
|
||||
// Redirect to the new accessory page
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.create.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
|
||||
|
@ -143,12 +144,12 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
|
||||
{
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) {
|
||||
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
"qty" => "required|numeric|min:$accessory->users_count"
|
||||
"qty" => "required|numeric|min:$accessory->checkouts_count"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
|
@ -176,9 +177,10 @@ class AccessoriesController extends Controller
|
|||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
// Was the accessory updated?
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($accessory->save()) {
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
|
||||
}
|
||||
} else {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
|
@ -231,7 +233,7 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function show($accessoryID = null) : View | RedirectResponse
|
||||
{
|
||||
$accessory = Accessory::withCount('users as users_count')->find($accessoryID);
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
|
||||
$this->authorize('view', $accessory);
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
namespace App\Http\Controllers\Accessories;
|
||||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
@ -23,7 +25,7 @@ class AccessoryCheckinController extends Controller
|
|||
*/
|
||||
public function create($accessoryUserId = null, $backto = null) : View | RedirectResponse
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_user = DB::table('accessories_checkout')->find($accessoryUserId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
|
@ -38,16 +40,16 @@ class AccessoryCheckinController extends Controller
|
|||
*
|
||||
* @uses Accessory::checkin_email() to determine if an email can and should be sent
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param null $accessoryUserId
|
||||
* @param null $accessoryCheckoutId
|
||||
* @param string $backto
|
||||
*/
|
||||
public function store(Request $request, $accessoryUserId = null, $backto = null) : RedirectResponse
|
||||
public function store(Request $request, $accessoryCheckoutId = null, $backto = null) : RedirectResponse
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryCheckoutId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
|
@ -58,12 +60,12 @@ class AccessoryCheckinController extends Controller
|
|||
}
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
$return_to = e($accessory_user->assigned_to);
|
||||
if ($accessory_checkout->delete()) {
|
||||
event(new CheckoutableCheckedIn($accessory, $accessory_checkout->assignedTo, auth()->user(), $request->input('note'), $checkin_at));
|
||||
|
||||
event(new CheckoutableCheckedIn($accessory, User::find($return_to), auth()->user(), $request->input('note'), $checkin_at));
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
return redirect()->route('accessories.show', $accessory->id)->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
}
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));
|
||||
|
|
|
@ -3,18 +3,24 @@
|
|||
namespace App\Http\Controllers\Accessories;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \Illuminate\Http\RedirectResponse;
|
||||
|
||||
class AccessoryCheckoutController extends Controller
|
||||
{
|
||||
|
||||
use CheckInOutRequest;
|
||||
|
||||
/**
|
||||
* Return the form to checkout an Accessory to a user.
|
||||
*
|
||||
|
@ -24,7 +30,7 @@ class AccessoryCheckoutController extends Controller
|
|||
public function create($id) : View | RedirectResponse
|
||||
{
|
||||
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($id)) {
|
||||
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($id)) {
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
|
@ -57,44 +63,38 @@ class AccessoryCheckoutController extends Controller
|
|||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param int $accessoryId
|
||||
* @param Accessory $accessory
|
||||
*/
|
||||
public function store(Request $request, $accessoryId) : RedirectResponse
|
||||
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
|
||||
{
|
||||
// Check if the accessory exists
|
||||
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'))) {
|
||||
return redirect()->route('accessories.checkout.show', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist'));
|
||||
$target = $this->determineCheckoutTarget();
|
||||
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
AccessoryCheckout::create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
}
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
|
||||
// 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'));
|
||||
}
|
||||
// Set this as user since we only allow checkout to user for this item type
|
||||
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
|
||||
$request->request->add(['assigned_user' => $target->id]);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->assigned_to = e($request->input('assigned_to'));
|
||||
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->get('assigned_to'),
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
|
||||
|
||||
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
|
||||
|
||||
// Redirect to the new accessory page
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
|
||||
->with('success', trans('admin/accessories/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ namespace App\Http\Controllers\Api;
|
|||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||
use App\Http\Requests\StoreAccessoryRequest;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Accessory;
|
||||
|
@ -15,10 +18,12 @@ use Carbon\Carbon;
|
|||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
use App\Models\AccessoryCheckout;
|
||||
|
||||
class AccessoriesController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -46,13 +51,13 @@ class AccessoriesController extends Controller
|
|||
'min_amt',
|
||||
'company_id',
|
||||
'notes',
|
||||
'users_count',
|
||||
'checkouts_count',
|
||||
'qty',
|
||||
];
|
||||
|
||||
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier')
|
||||
->withCount('users as users_count');
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier')
|
||||
->withCount('checkouts as checkouts_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
|
@ -121,12 +126,12 @@ class AccessoriesController extends Controller
|
|||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
public function store(StoreAccessoryRequest $request)
|
||||
{
|
||||
$this->authorize('create', Accessory::class);
|
||||
$accessory = new Accessory;
|
||||
|
@ -144,15 +149,15 @@ class AccessoriesController extends Controller
|
|||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessory = Accessory::withCount('users as users_count')->findOrFail($id);
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
|
||||
|
||||
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
||||
}
|
||||
|
@ -161,10 +166,10 @@ class AccessoriesController extends Controller
|
|||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function accessory_detail($id)
|
||||
{
|
||||
|
@ -195,28 +200,23 @@ class AccessoriesController extends Controller
|
|||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$accessory_users = $accessory->users;
|
||||
$total = $accessory_users->count();
|
||||
$accessory_checkouts = $accessory->checkouts;
|
||||
$total = $accessory_checkouts->count();
|
||||
|
||||
if ($total < $offset) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$accessory_users = $accessory->users()->skip($offset)->take($limit)->get();
|
||||
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessory_users = $accessory->users()
|
||||
->where(function ($query) use ($request) {
|
||||
$search_str = '%' . $request->input('search') . '%';
|
||||
$query->where('first_name', 'like', $search_str)
|
||||
->orWhere('last_name', 'like', $search_str)
|
||||
->orWhere('note', 'like', $search_str);
|
||||
})
|
||||
|
||||
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
|
||||
->get();
|
||||
$total = $accessory_users->count();
|
||||
$total = $accessory_checkouts->count();
|
||||
}
|
||||
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total);
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
|
||||
}
|
||||
|
||||
|
||||
|
@ -273,43 +273,31 @@ class AccessoriesController extends Controller
|
|||
* If Slack is enabled and/or asset acceptance is enabled, it will also
|
||||
* trigger a Slack message and send an email.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function checkout(Request $request, $accessoryId)
|
||||
public function checkout(AccessoryCheckoutRequest $request, Accessory $accessory)
|
||||
{
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
$target = $this->determineCheckoutTarget();
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
|
||||
if ($accessory->numRemaining() > 0) {
|
||||
|
||||
if (! $user = User::find($request->input('assigned_to'))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkout.user_does_not_exist')));
|
||||
}
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->assigned_to = $request->input('assigned_to');
|
||||
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
AccessoryCheckout::create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->get('assigned_to'),
|
||||
'note' => $request->get('note'),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'No accessories remaining'));
|
||||
// Set this value to be able to pass the qty through to the event
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
|
||||
|
||||
}
|
||||
|
||||
|
@ -326,19 +314,19 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function checkin(Request $request, $accessoryUserId = null)
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryUserId))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_user->assigned_to), $request->input('note'));
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
if (! is_null($accessory_user->assigned_to)) {
|
||||
$user = User::find($accessory_user->assigned_to);
|
||||
if ($accessory_checkout->delete()) {
|
||||
if (! is_null($accessory_checkout->assigned_to)) {
|
||||
$user = User::find($accessory_checkout->assigned_to);
|
||||
}
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
|||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Http\Requests\StoreAssetRequest;
|
||||
use App\Http\Requests\UpdateAssetRequest;
|
||||
use App\Http\Traits\MigratesLegacyAssetLocations;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\LicenseSeat;
|
||||
|
@ -651,36 +652,35 @@ class AssetsController extends Controller
|
|||
* Accepts a POST request to update an asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $id) : JsonResponse
|
||||
public function update(UpdateAssetRequest $request, Asset $asset): JsonResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
$asset->fill($request->validated());
|
||||
|
||||
if ($asset = Asset::find($id)) {
|
||||
$asset->fill($request->all());
|
||||
if ($request->has('model_id')) {
|
||||
$asset->model()->associate(AssetModel::find($request->validated()['model_id']));
|
||||
}
|
||||
if ($request->has('company_id')) {
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->validated()['company_id']);
|
||||
}
|
||||
if ($request->has('rtd_location_id') && !$request->has('location_id')) {
|
||||
$asset->location_id = $request->validated()['rtd_location_id'];
|
||||
}
|
||||
if ($request->input('last_audit_date')) {
|
||||
$asset->last_audit_date = Carbon::parse($request->input('last_audit_date'))->startOfDay()->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
($request->filled('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : '';
|
||||
($request->filled('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = AssetModel::find($asset->model_id);
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = $asset->model;
|
||||
|
||||
// Update custom fields
|
||||
$problems_updating_encrypted_custom_fields = false;
|
||||
|
@ -706,15 +706,13 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $id)
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
|
@ -728,17 +726,13 @@ class AssetsController extends Controller
|
|||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
}
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class LicensesController extends Controller
|
|||
{
|
||||
$this->authorize('view', License::class);
|
||||
|
||||
$licenses = License::with('company', 'manufacturer', 'supplier','category')->withCount('freeSeats as free_seats_count');
|
||||
$licenses = License::with('company', 'manufacturer', 'supplier','category', 'adminuser')->withCount('freeSeats as free_seats_count');
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$licenses->where('company_id', '=', $request->input('company_id'));
|
||||
|
@ -70,6 +70,9 @@ class LicensesController extends Controller
|
|||
$licenses->where('depreciation_id', '=', $request->input('depreciation_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('user_id')) {
|
||||
$licenses->where('user_id', '=', $request->input('user_id'));
|
||||
}
|
||||
|
||||
if (($request->filled('maintained')) && ($request->input('maintained')=='true')) {
|
||||
$licenses->where('maintained','=',1);
|
||||
|
@ -113,6 +116,9 @@ class LicensesController extends Controller
|
|||
case 'company':
|
||||
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
|
||||
break;
|
||||
case 'created_by':
|
||||
$licenses = $licenses->OrderCreatedBy($order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns =
|
||||
[
|
||||
|
|
|
@ -5,8 +5,10 @@ namespace App\Http\Controllers\Api;
|
|||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\LocationsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
@ -222,6 +224,15 @@ class LocationsController extends Controller
|
|||
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
|
||||
}
|
||||
|
||||
public function assets(Request $request, Location $location) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', $location);
|
||||
$assets = Asset::where('assigned_to', '=', $location->id)->where('assigned_type', '=', Location::class)->with('model', 'model.category', 'assetstatus', 'location', 'company', 'defaultLoc');
|
||||
$assets = $assets->get();
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
|
@ -237,6 +248,7 @@ class LocationsController extends Controller
|
|||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->findOrFail($id);
|
||||
|
||||
if (! $location->isDeletable()) {
|
||||
|
|
|
@ -11,7 +11,6 @@ use App\Models\Asset;
|
|||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \Illuminate\Http\RedirectResponse;
|
||||
|
@ -83,7 +82,6 @@ class AssetCheckinController extends Controller
|
|||
}
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
//$asset->last_checkout = null;
|
||||
$asset->last_checkin = now();
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->accepted = null;
|
||||
|
@ -128,12 +126,12 @@ class AssetCheckinController extends Controller
|
|||
$acceptance->delete();
|
||||
});
|
||||
|
||||
Session::put('redirect_option', $request->get('redirect_option'));
|
||||
// Was the asset updated?
|
||||
session()->put('redirect_option', $request->get('redirect_option'));
|
||||
|
||||
if ($asset->save()) {
|
||||
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
return Helper::getRedirectOption($asset, $assetId, 'Assets');
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());
|
||||
|
|
|
@ -109,10 +109,11 @@ class AssetCheckoutController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
Session::put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
|
||||
return Helper::getRedirectOption($request, $assetId, 'Assets');
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
|
||||
->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error').$asset->getErrors());
|
||||
|
|
|
@ -204,9 +204,13 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
|
||||
if ($success) {
|
||||
return redirect()->route('hardware.index')
|
||||
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => e($asset->asset_tag)]));
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
|
||||
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)]));
|
||||
|
||||
|
||||
}
|
||||
|
@ -289,6 +293,7 @@ class AssetsController extends Controller
|
|||
*/
|
||||
public function update(ImageUploadRequest $request, $assetId = null) : RedirectResponse
|
||||
{
|
||||
|
||||
// Check if the asset exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
// Redirect to the asset management page with error
|
||||
|
@ -331,7 +336,7 @@ class AssetsController extends Controller
|
|||
|
||||
$status = Statuslabel::find($asset->status_id);
|
||||
|
||||
if($status->archived){
|
||||
if ($status && $status->archived) {
|
||||
$asset->assigned_to = null;
|
||||
}
|
||||
|
||||
|
@ -350,14 +355,26 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
// Update the asset data
|
||||
$asset_tag = $request->input('asset_tags');
|
||||
|
||||
$serial = $request->input('serials');
|
||||
$asset->serial = $request->input('serials');
|
||||
|
||||
if (is_array($request->input('serials'))) {
|
||||
$asset->serial = $serial[1];
|
||||
}
|
||||
|
||||
$asset->name = $request->input('name');
|
||||
$asset->serial = $serial[1];
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
||||
$asset->model_id = $request->input('model_id');
|
||||
$asset->order_number = $request->input('order_number');
|
||||
$asset->asset_tag = $asset_tag[1];
|
||||
|
||||
$asset_tags = $request->input('asset_tags');
|
||||
$asset->asset_tag = $request->input('asset_tags');
|
||||
|
||||
if (is_array($request->input('asset_tags'))) {
|
||||
$asset->asset_tag = $asset_tags[1];
|
||||
}
|
||||
|
||||
$asset->notes = $request->input('notes');
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
|
@ -369,6 +386,7 @@ class AssetsController extends Controller
|
|||
$model = AssetModel::find($request->get('model_id'));
|
||||
if (($model) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
|
@ -387,9 +405,10 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
if ($asset->save()) {
|
||||
return redirect()->route('hardware.show', $assetId)
|
||||
return redirect()->to(Helper::getRedirectOption($request, $assetId, 'Assets'))
|
||||
->with('success', trans('admin/hardware/message.update.success'));
|
||||
}
|
||||
|
||||
|
@ -459,9 +478,16 @@ class AssetsController extends Controller
|
|||
$tag = $tag ? $tag : $request->get('assetTag');
|
||||
$topsearch = ($request->get('topsearch') == 'true');
|
||||
|
||||
if (! $asset = Asset::where('asset_tag', '=', $tag)->first()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
// Search for an exact and unique asset tag match
|
||||
$assets = Asset::where('asset_tag', '=', $tag);
|
||||
|
||||
// If not a unique result, redirect to the index view
|
||||
if ($assets->count() != 1) {
|
||||
return redirect()->route('hardware.index')
|
||||
->with('search', $tag)
|
||||
->with('warning', trans('admin/hardware/message.does_not_exist_var', [ 'asset_tag' => $tag ]));
|
||||
}
|
||||
$asset = $assets->first();
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
||||
|
@ -475,7 +501,7 @@ class AssetsController extends Controller
|
|||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function getQrCode($assetId = null) : Response | BinaryFileResponse
|
||||
public function getQrCode($assetId = null) : Response | BinaryFileResponse | string | bool
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
|
@ -502,6 +528,7 @@ class AssetsController extends Controller
|
|||
|
||||
return 'That asset is invalid';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -575,26 +602,20 @@ class AssetsController extends Controller
|
|||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getClone($assetId = null)
|
||||
public function getClone(Asset $asset)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset_to_clone = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('create', $asset_to_clone);
|
||||
|
||||
$asset = clone $asset_to_clone;
|
||||
$asset->id = null;
|
||||
$asset->asset_tag = '';
|
||||
$asset->serial = '';
|
||||
$asset->assigned_to = '';
|
||||
$this->authorize('create', $asset);
|
||||
$cloned = clone $asset;
|
||||
$cloned->id = null;
|
||||
$cloned->asset_tag = '';
|
||||
$cloned->serial = '';
|
||||
$cloned->assigned_to = '';
|
||||
$cloned->deleted_at = '';
|
||||
|
||||
return view('hardware/edit')
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('statuslabel_types', Helper::statusTypeList())
|
||||
->with('item', $asset);
|
||||
->with('item', $cloned);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -92,7 +92,9 @@ class BulkAssetsController extends Controller
|
|||
// This handles all of the pivot sorting below (versus the assets.* fields in the allowed_columns array)
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.id';
|
||||
|
||||
$assets = Asset::with('assignedTo', 'location', 'model')->whereIn('assets.id', $asset_ids);
|
||||
$assets = Asset::with('assignedTo', 'location', 'model')
|
||||
->whereIn('assets.id', $asset_ids)
|
||||
->withTrashed();
|
||||
|
||||
$assets = $assets->get();
|
||||
|
||||
|
@ -483,12 +485,7 @@ class BulkAssetsController extends Controller
|
|||
if ($request->filled('ids')) {
|
||||
$assets = Asset::find($request->get('ids'));
|
||||
foreach ($assets as $asset) {
|
||||
$update_array['deleted_at'] = date('Y-m-d H:i:s');
|
||||
$update_array['assigned_to'] = null;
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update($update_array);
|
||||
$asset->delete();
|
||||
} // endforeach
|
||||
|
||||
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.delete.success'));
|
||||
|
|
|
@ -87,7 +87,7 @@ class ResetPasswordController extends Controller
|
|||
'password.not_in' => trans('validation.disallow_same_pwd_as_user_fields'),
|
||||
];
|
||||
|
||||
$request->validate($this->rules(), $request->all(), $this->validationErrorMessages());
|
||||
$request->validate($this->rules());
|
||||
|
||||
Log::debug('Checking if '.$request->input('username').' exists');
|
||||
// Check to see if the user even exists - we'll treat the response the same to prevent user sniffing
|
||||
|
|
|
@ -20,7 +20,7 @@ trait CheckInOutRequest
|
|||
return Location::findOrFail(request('assigned_location'));
|
||||
case 'asset':
|
||||
return Asset::findOrFail(request('assigned_asset'));
|
||||
case 'user':
|
||||
default:
|
||||
return User::findOrFail(request('assigned_user'));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Components;
|
|||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Events\ComponentCheckedIn;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Component;
|
||||
|
@ -96,12 +97,10 @@ class ComponentCheckinController extends Controller
|
|||
$asset = Asset::find($component_assets->asset_id);
|
||||
|
||||
event(new CheckoutableCheckedIn($component, $asset, auth()->user(), $request->input('note'), Carbon::now()));
|
||||
if ($backto == 'asset'){
|
||||
return redirect()->route('hardware.show', $asset->id)->with('success',
|
||||
trans('admin/components/message.checkin.success'));
|
||||
}
|
||||
|
||||
return redirect()->route('components.index')->with('success',
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success',
|
||||
trans('admin/components/message.checkin.success'));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,11 @@ namespace App\Http\Controllers\Components;
|
|||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Events\ComponentCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Component;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
|
@ -93,14 +95,18 @@ class ComponentCheckoutController extends Controller
|
|||
->withInput();
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
// Check if the asset exists
|
||||
$asset = Asset::find($request->input('asset_id'));
|
||||
|
||||
if ((Setting::getSettings()->full_multiple_companies_support) && $component->company_id !== $asset->company_id) {
|
||||
return redirect()->route('components.checkout.show', $componentId)->with('error', trans('general.error_user_company'));
|
||||
}
|
||||
|
||||
// Update the component data
|
||||
$component->asset_id = $request->input('asset_id');
|
||||
$component->assets()->attach($component->id, [
|
||||
'component_id' => $component->id,
|
||||
'user_id' => auth()->user(),
|
||||
'user_id' => auth()->user()->id,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'assigned_qty' => $request->input('assigned_qty'),
|
||||
'asset_id' => $request->input('asset_id'),
|
||||
|
@ -109,6 +115,11 @@ class ComponentCheckoutController extends Controller
|
|||
|
||||
event(new CheckoutableCheckedOut($component, $asset, auth()->user(), $request->input('note')));
|
||||
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
|
||||
$request->request->add(['checkout_to_type' => 'asset']);
|
||||
$request->request->add(['assigned_asset' => $asset->id]);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,8 +86,10 @@ class ComponentsController extends Controller
|
|||
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($component->save()) {
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($component->getErrors());
|
||||
|
@ -160,8 +162,10 @@ class ComponentsController extends Controller
|
|||
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($component->save()) {
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($component->getErrors());
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Controllers\Consumables;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
|
@ -33,7 +34,7 @@ class ConsumableCheckoutController extends Controller
|
|||
// 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'));
|
||||
->with('error', trans('admin/consumables/message.checkout.unavailable', ['requested' => 1, 'remaining' => $consumable->numRemaining()]));
|
||||
}
|
||||
|
||||
// Return the checkout view
|
||||
|
@ -76,7 +77,7 @@ class ConsumableCheckoutController extends Controller
|
|||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($consumable->numRemaining() <= 0 || $quantity > $consumable->numRemaining()) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable'));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable', ['requested' => $quantity, 'remaining' => $consumable->numRemaining() ]));
|
||||
}
|
||||
|
||||
$admin_user = auth()->user();
|
||||
|
@ -101,7 +102,13 @@ class ConsumableCheckoutController extends Controller
|
|||
}
|
||||
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
|
||||
|
||||
$request->request->add(['checkout_to_type' => 'user']);
|
||||
$request->request->add(['assigned_user' => $user->id]);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
|
||||
// Redirect to the new consumable page
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,8 +87,10 @@ class ConsumablesController extends Controller
|
|||
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
|
||||
|
@ -160,8 +162,10 @@ class ConsumablesController extends Controller
|
|||
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.update.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
|
||||
|
@ -200,7 +204,7 @@ class ConsumablesController extends Controller
|
|||
*/
|
||||
public function show($consumableId = null)
|
||||
{
|
||||
$consumable = Consumable::find($consumableId);
|
||||
$consumable = Consumable::withCount('users as users_consumables')->find($consumableId);
|
||||
$this->authorize($consumable);
|
||||
if (isset($consumable->id)) {
|
||||
return view('consumables/view', compact('consumable'));
|
||||
|
@ -209,4 +213,16 @@ class ConsumablesController extends Controller
|
|||
return redirect()->route('consumables.index')
|
||||
->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
}
|
||||
|
||||
public function clone(Consumable $consumable) : View
|
||||
{
|
||||
$this->authorize('create', $consumable);
|
||||
$consumable_to_close = $consumable;
|
||||
$consumable = clone $consumable_to_close;
|
||||
$consumable->id = null;
|
||||
$consumable->image = null;
|
||||
$consumable->user_id = null;
|
||||
|
||||
return view('consumables/edit')->with('item', $consumable);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,7 +211,7 @@ class CustomFieldsetsController extends Controller
|
|||
return redirect()->route('fieldsets.show', [$id])->with('success', trans('admin/custom_fields/message.field.create.assoc_success'));
|
||||
}
|
||||
|
||||
return redirect()->route('fieldsets.show', [$id])->with('error', 'No field selected.');
|
||||
return redirect()->route('fieldsets.show', [$id])->with('error', trans('admin/custom_fields/message.field.none_selected'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Controllers\Licenses;
|
||||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
|
@ -100,15 +101,15 @@ class LicenseCheckinController extends Controller
|
|||
$licenseSeat->asset_id = null;
|
||||
$licenseSeat->notes = $request->input('notes');
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $request->input('notes')));
|
||||
|
||||
if ($backTo == 'user') {
|
||||
return redirect()->route('users.show', $return_to->id)->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
}
|
||||
|
||||
return redirect()->route('licenses.show', $licenseSeat->license_id)->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
}
|
||||
|
||||
// Redirect to the license page with error
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Controllers\Licenses;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\LicenseCheckoutRequest;
|
||||
use App\Models\Accessory;
|
||||
|
@ -81,10 +82,27 @@ class LicenseCheckoutController extends Controller
|
|||
|
||||
|
||||
$checkoutMethod = 'checkoutTo'.ucwords(request('checkout_to_type'));
|
||||
if ($this->$checkoutMethod($licenseSeat)) {
|
||||
return redirect()->route('licenses.index')->with('success', trans('admin/licenses/message.checkout.success'));
|
||||
|
||||
if ($request->filled('asset_id')) {
|
||||
|
||||
$checkoutTarget = $this->checkoutToAsset($licenseSeat);
|
||||
$request->request->add(['assigned_asset' => $checkoutTarget->id]);
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'asset']);
|
||||
|
||||
} elseif ($request->filled('assigned_to')) {
|
||||
$checkoutTarget = $this->checkoutToUser($licenseSeat);
|
||||
$request->request->add(['assigned_user' => $checkoutTarget->id]);
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'user']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($checkoutTarget) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkout.success'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
return redirect()->route('licenses.index')->with('error', trans('Something went wrong handling this checkout.'));
|
||||
}
|
||||
|
||||
|
@ -94,14 +112,14 @@ class LicenseCheckoutController extends Controller
|
|||
|
||||
if (! $licenseSeat) {
|
||||
if ($seatId) {
|
||||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', 'This Seat is not available for checkout.'));
|
||||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.unavailable')));
|
||||
}
|
||||
|
||||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', 'There are no available seats for this license.'));
|
||||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats')));
|
||||
}
|
||||
|
||||
if (! $licenseSeat->license->is($license)) {
|
||||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', 'The license seat provided does not match the license.'));
|
||||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.mismatch')));
|
||||
}
|
||||
|
||||
return $licenseSeat;
|
||||
|
@ -120,8 +138,7 @@ class LicenseCheckoutController extends Controller
|
|||
}
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedOut($licenseSeat, $target, auth()->user(), request('notes')));
|
||||
|
||||
return true;
|
||||
return $target;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -137,8 +154,7 @@ class LicenseCheckoutController extends Controller
|
|||
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedOut($licenseSeat, $target, auth()->user(), request('notes')));
|
||||
|
||||
return true;
|
||||
return $target;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -102,8 +102,10 @@ class LicensesController extends Controller
|
|||
$license->user_id = Auth::id();
|
||||
$license->min_amt = $request->input('min_amt');
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($license->save()) {
|
||||
return redirect()->route('licenses.index')->with('success', trans('admin/licenses/message.create.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($license->getErrors());
|
||||
|
@ -180,8 +182,10 @@ class LicensesController extends Controller
|
|||
$license->category_id = $request->input('category_id');
|
||||
$license->min_amt = $request->input('min_amt');
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($license->save()) {
|
||||
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.update.success'));
|
||||
}
|
||||
// If we can't adjust the number of seats, the error is flashed to the session by the event handler in License.php
|
||||
return redirect()->back()->withInput()->withErrors($license->getErrors());
|
||||
|
|
|
@ -378,7 +378,7 @@ class ReportsController extends Controller
|
|||
|
||||
|
||||
$csv = implode("\n", $rows);
|
||||
$response = Response::make($csv, 200);
|
||||
$response = response()->make($csv, 200);
|
||||
$response->header('Content-Type', 'text/csv');
|
||||
$response->header('Content-disposition', 'attachment;filename=report.csv');
|
||||
|
||||
|
@ -1069,7 +1069,7 @@ class ReportsController extends Controller
|
|||
|
||||
// spit out a csv
|
||||
$csv = implode("\n", $rows);
|
||||
$response = Response::make($csv, 200);
|
||||
$response = response()->make($csv, 200);
|
||||
$response->header('Content-Type', 'text/csv');
|
||||
$response->header('Content-disposition', 'attachment;filename=report.csv');
|
||||
|
||||
|
@ -1249,7 +1249,7 @@ class ReportsController extends Controller
|
|||
|
||||
// spit out a csv
|
||||
$csv = implode("\n", $rows);
|
||||
$response = Response::make($csv, 200);
|
||||
$response = response()->make($csv, 200);
|
||||
$response->header('Content-Type', 'text/csv');
|
||||
$response->header('Content-disposition', 'attachment;filename=report.csv');
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ use App\Models\Asset;
|
|||
use App\Models\User;
|
||||
use App\Notifications\FirstAdminNotification;
|
||||
use App\Notifications\MailTest;
|
||||
use Illuminate\Http\Client\HttpClientException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
@ -129,11 +128,11 @@ class SettingsController extends Controller
|
|||
protected function dotEnvFileIsExposed() : bool
|
||||
{
|
||||
try {
|
||||
return Http::timeout(10)
|
||||
return Http::withoutVerifying()->timeout(10)
|
||||
->accept('*/*')
|
||||
->get(URL::to('.env'))
|
||||
->successful();
|
||||
} catch (HttpClientException $e) {
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e->getMessage());
|
||||
return true;
|
||||
}
|
||||
|
@ -325,6 +324,7 @@ class SettingsController extends Controller
|
|||
|
||||
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
|
||||
$setting->unique_serial = $request->input('unique_serial', '0');
|
||||
$setting->shortcuts_enabled = $request->input('shortcuts_enabled', '0');
|
||||
$setting->show_images_in_email = $request->input('show_images_in_email', '0');
|
||||
$setting->show_archived_in_list = $request->input('show_archived_in_list', '0');
|
||||
$setting->dashboard_message = $request->input('dashboard_message');
|
||||
|
@ -414,10 +414,7 @@ class SettingsController extends Controller
|
|||
$setting = $request->handleImages($setting, 600, 'logo', '', 'logo');
|
||||
|
||||
if ($request->input('clear_logo') == '1') {
|
||||
|
||||
if (($setting->logo) && (Storage::exists($setting->logo))) {
|
||||
Storage::disk('public')->delete($setting->logo);
|
||||
}
|
||||
$setting = $request->deleteExistingImage($setting, '', 'logo');
|
||||
$setting->logo = null;
|
||||
$setting->brand = 1;
|
||||
}
|
||||
|
@ -425,43 +422,38 @@ class SettingsController extends Controller
|
|||
// Email logo upload
|
||||
$setting = $request->handleImages($setting, 600, 'email_logo', '', 'email_logo');
|
||||
if ($request->input('clear_email_logo') == '1') {
|
||||
|
||||
if (($setting->email_logo) && (Storage::exists($setting->email_logo))) {
|
||||
Storage::disk('public')->delete($setting->email_logo);
|
||||
}
|
||||
$setting = $request->deleteExistingImage($setting, '', 'email_logo');
|
||||
$setting->email_logo = null;
|
||||
// If they are uploading an image, validate it and upload it
|
||||
}
|
||||
|
||||
// Label logo upload
|
||||
$setting = $request->handleImages($setting, 600, 'label_logo', '', 'label_logo');
|
||||
if ($request->input('clear_label_logo') == '1') {
|
||||
|
||||
if (($setting->label_logo) && (Storage::exists($setting->label_logo))) {
|
||||
Storage::disk('public')->delete($setting->label_logo);
|
||||
}
|
||||
if ($request->input('clear_label_logo') == '1') {
|
||||
$setting = $request->deleteExistingImage($setting, '', 'label_logo');
|
||||
$setting->label_logo = null;
|
||||
}
|
||||
|
||||
// Favicon upload
|
||||
$setting = $request->handleImages($setting, 100, 'favicon', '', 'favicon');
|
||||
if ('1' == $request->input('clear_favicon')) {
|
||||
|
||||
if (($setting->favicon) && (Storage::exists($setting->favicon))) {
|
||||
Storage::disk('public')->delete($setting->favicon);
|
||||
}
|
||||
$setting = $request->deleteExistingImage($setting, '', 'favicon');
|
||||
$setting->favicon = null;
|
||||
}
|
||||
|
||||
// Default avatar upload
|
||||
$setting = $request->handleImages($setting, 500, 'default_avatar', 'avatars', 'default_avatar');
|
||||
if ($request->input('clear_default_avatar') == '1') {
|
||||
|
||||
if (($setting->default_avatar) && (Storage::exists('avatars/'.$setting->default_avatar))) {
|
||||
Storage::disk('public')->delete('avatars/'.$setting->default_avatar);
|
||||
if ($request->input('clear_default_avatar') == '1') {
|
||||
// Don't delete the file, just update the field if this is the default
|
||||
if ($setting->default_avatar!='default.png') {
|
||||
$setting = $request->deleteExistingImage($setting, 'avatars', 'default_avatar');
|
||||
}
|
||||
$setting->default_avatar = null;
|
||||
}
|
||||
|
||||
if ($request->input('restore_default_avatar') == '1') {
|
||||
$setting->default_avatar = 'default.png';
|
||||
}
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
|
@ -1264,7 +1256,7 @@ class SettingsController extends Controller
|
|||
DB::table('users')->update(['remember_token' => null]);
|
||||
Auth::logout();
|
||||
|
||||
return redirect()->route('login')->with('success', 'Your system has been restored. Please login again.');
|
||||
return redirect()->route('login')->with('success', trans('admin/settings/message.restore.success'));
|
||||
} else {
|
||||
return redirect()->route('settings.backups.index')->with('error', trans('admin/settings/message.backup.file_not_found'));
|
||||
}
|
||||
|
|
|
@ -218,8 +218,8 @@ class BulkUsersController extends Controller
|
|||
}
|
||||
|
||||
$users = User::whereIn('id', $user_raw_array)->get();
|
||||
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', \App\Models\User::class)->get();
|
||||
$accessories = DB::table('accessories_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', User::class)->get();
|
||||
$accessories = DB::table('accessories_checkout')->where('assigned_type', User::class)->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$consumables = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
|
||||
|
|
|
@ -133,6 +133,8 @@ class UsersController extends Controller
|
|||
// we have to invoke the
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
|
@ -152,7 +154,7 @@ class UsersController extends Controller
|
|||
$user->notify(new WelcomeNotification($data));
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.create'));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))->with('success', trans('admin/users/message.success.create'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
|
@ -309,10 +311,11 @@ class UsersController extends Controller
|
|||
|
||||
// Handle uploaded avatar
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($user->save()) {
|
||||
// Redirect to the user page
|
||||
return redirect()->route('users.index')
|
||||
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
|
||||
->with('success', trans('admin/users/message.success.update'));
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,12 @@ class Kernel extends HttpKernel
|
|||
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
|
||||
\App\Http\Middleware\AssetCountForSidebar::class,
|
||||
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'auth:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class CheckLocale
|
|||
|
||||
}
|
||||
|
||||
\App::setLocale(Helper::mapLegacyLocale($language));
|
||||
app()->setLocale(Helper::mapLegacyLocale($language));
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,5 +20,5 @@ class EncryptCookies extends BaseEncrypter
|
|||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $serialize = true;
|
||||
protected static $serialize = false;
|
||||
}
|
||||
|
|
76
app/Http/Requests/AccessoryCheckoutRequest.php
Normal file
76
app/Http/Requests/AccessoryCheckoutRequest.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class AccessoryCheckoutRequest extends ImageUploadRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('checkout', new Accessory);
|
||||
}
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
|
||||
if ($this->accessory) {
|
||||
|
||||
$this->diff = ($this->accessory->numRemaining() - $this->checkout_qty);
|
||||
$this->merge([
|
||||
'checkout_qty' => $this->checkout_qty ?? 1,
|
||||
'number_remaining_after_checkout' => (int) ($this->accessory->numRemaining() - $this->checkout_qty),
|
||||
'number_currently_remaining' => (int) $this->accessory->numRemaining(),
|
||||
'checkout_difference' => (int) $this->diff,
|
||||
]);
|
||||
|
||||
\Log::debug('---------------------------------------------');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
|
||||
return array_merge(
|
||||
[
|
||||
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
|
||||
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
|
||||
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
|
||||
|
||||
'number_remaining_after_checkout' => [
|
||||
'min:0',
|
||||
'required',
|
||||
'integer',
|
||||
],
|
||||
|
||||
'checkout_qty' => [
|
||||
'integer',
|
||||
'lte:number_currently_remaining',
|
||||
'min:1',
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
$messages = [
|
||||
'checkout_qty.lte' => trans_choice('admin/accessories/message.checkout.checkout_qty.lte', $this->number_currently_remaining, [
|
||||
'number_currently_remaining' => $this->number_currently_remaining,
|
||||
'checkout_qty' => $this->checkout_qty,
|
||||
]),
|
||||
];
|
||||
return $messages;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ use Illuminate\Http\UploadedFile;
|
|||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\Exception\NotReadableException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class ImageUploadRequest extends Request
|
||||
{
|
||||
|
@ -123,7 +124,7 @@ class ImageUploadRequest extends Request
|
|||
|
||||
} catch(NotReadableException $e) {
|
||||
Log::debug($e);
|
||||
$validator = \Validator::make([], []);
|
||||
$validator = Validator::make([], []);
|
||||
$validator->errors()->add($form_fieldname, trans('general.unaccepted_image_type', ['mimetype' => $image->getClientMimeType()]));
|
||||
|
||||
throw new \Illuminate\Validation\ValidationException($validator);
|
||||
|
@ -135,28 +136,28 @@ class ImageUploadRequest extends Request
|
|||
}
|
||||
|
||||
// Remove Current image if exists
|
||||
if (($item->{$form_fieldname}!='') && (Storage::disk('public')->exists($path.'/'.$item->{$db_fieldname}))) {
|
||||
try {
|
||||
Storage::disk('public')->delete($path.'/'.$item->{$form_fieldname});
|
||||
} catch (\Exception $e) {
|
||||
Log::debug('Could not delete old file. '.$path.'/'.$file_name.' does not exist?');
|
||||
}
|
||||
}
|
||||
|
||||
$item = $this->deleteExistingImage($item, $path, $db_fieldname);
|
||||
$item->{$db_fieldname} = $file_name;
|
||||
}
|
||||
|
||||
|
||||
// If the user isn't uploading anything new but wants to delete their old image, do so
|
||||
} elseif ($this->input('image_delete') == '1') {
|
||||
Log::debug('Deleting image');
|
||||
$item = $this->deleteExistingImage($item, $path, $db_fieldname);
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function deleteExistingImage($item, $path = null, $db_fieldname = 'image') {
|
||||
|
||||
if ($item->{$db_fieldname}!='') {
|
||||
try {
|
||||
Storage::disk('public')->delete($path.'/'.$item->{$db_fieldname});
|
||||
$item->{$db_fieldname} = null;
|
||||
$item->{$db_fieldname} = null;
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $item;
|
||||
|
|
56
app/Http/Requests/StoreAccessoryRequest.php
Normal file
56
app/Http/Requests/StoreAccessoryRequest.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreAccessoryRequest extends ImageUploadRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', new Accessory);
|
||||
}
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
|
||||
if ($this->category_id) {
|
||||
if ($category = Category::find($this->category_id)) {
|
||||
$this->merge([
|
||||
'category_type' => $category->category_type ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
['category_type' => 'in:accessory'],
|
||||
parent::rules(),
|
||||
);
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
$messages = ['category_type.in' => trans('admin/accessories/message.invalid_category_type')];
|
||||
return $messages;
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
{
|
||||
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Traits\MayContainCustomFields;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
|
@ -11,6 +12,7 @@ use Illuminate\Support\Facades\Gate;
|
|||
|
||||
class StoreAssetRequest extends ImageUploadRequest
|
||||
{
|
||||
use MayContainCustomFields;
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
|
|
40
app/Http/Requests/Traits/MayContainCustomFields.php
Normal file
40
app/Http/Requests/Traits/MayContainCustomFields.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Traits;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\CustomField;
|
||||
|
||||
trait MayContainCustomFields
|
||||
{
|
||||
// this gets called automatically on a form request
|
||||
public function withValidator($validator)
|
||||
{
|
||||
// find the model
|
||||
if ($this->method() == 'POST') {
|
||||
$asset_model = AssetModel::find($this->model_id);
|
||||
}
|
||||
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
|
||||
// this is dependent on the asset update request PR
|
||||
$asset_model = $this->asset;
|
||||
}
|
||||
// collect the custom fields in the request
|
||||
$validator->after(function ($validator) use ($asset_model) {
|
||||
$request_fields = $this->collect()->keys()->filter(function ($attributes) {
|
||||
return str_starts_with($attributes, '_snipeit_');
|
||||
});
|
||||
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
|
||||
if (count($request_fields) > 0) {
|
||||
$request_fields->diff($asset_model->fieldset->fields->pluck('db_column'))
|
||||
->each(function ($request_field_name) use ($request_fields, $validator) {
|
||||
if (CustomField::where('db_column', $request_field_name)->exists()) {
|
||||
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found_on_model'));
|
||||
} else {
|
||||
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
44
app/Http/Requests/UpdateAssetRequest.php
Normal file
44
app/Http/Requests/UpdateAssetRequest.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateAssetRequest extends ImageUploadRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Gate::allows('update', $this->asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = array_merge(
|
||||
parent::rules(),
|
||||
(new Asset)->getRules(),
|
||||
// this is to overwrite rulesets that include required, and rewrite unique_undeleted
|
||||
[
|
||||
'model_id' => ['integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
|
||||
'status_id' => ['integer', 'exists:status_labels,id'],
|
||||
'asset_tag' => [
|
||||
'min:1', 'max:255', 'not_array',
|
||||
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed()
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ class AccessoriesTransformer
|
|||
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||
'remaining_qty' => (int) $accessory->numRemaining(),
|
||||
'users_count' => $accessory->users_count,
|
||||
'checkouts_count' => $accessory->checkouts_count,
|
||||
|
||||
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
|
||||
|
@ -66,27 +66,42 @@ class AccessoriesTransformer
|
|||
return $array;
|
||||
}
|
||||
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_users, $total)
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach ($accessory_users as $user) {
|
||||
foreach ($accessory_checkouts as $checkout) {
|
||||
$array[] = [
|
||||
|
||||
'assigned_pivot_id' => $user->pivot->id,
|
||||
'id' => (int) $user->id,
|
||||
'username' => e($user->username),
|
||||
'name' => e($user->getFullNameAttribute()),
|
||||
'first_name'=> e($user->first_name),
|
||||
'last_name'=> e($user->last_name),
|
||||
'employee_number' => e($user->employee_num),
|
||||
'checkout_notes' => e($user->pivot->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($user->pivot->created_at, 'datetime'),
|
||||
'type' => 'user',
|
||||
'id' => $checkout->id,
|
||||
'assigned_to' => $this->transformAssignedTo($checkout),
|
||||
'checkout_notes' => e($checkout->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
|
||||
'available_actions' => ['checkin' => true],
|
||||
];
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformAssignedTo($accessoryCheckout)
|
||||
{
|
||||
if ($accessoryCheckout->checkedOutToUser()) {
|
||||
return [
|
||||
'id' => (int) $accessoryCheckout->assigned->id,
|
||||
'username' => e($accessoryCheckout->assigned->username),
|
||||
'name' => e($accessoryCheckout->assigned->getFullNameAttribute()),
|
||||
'first_name'=> e($accessoryCheckout->assigned->first_name),
|
||||
'last_name'=> ($accessoryCheckout->assigned->last_name) ? e($accessoryCheckout->assigned->last_name) : null,
|
||||
'email'=> ($accessoryCheckout->assigned->email) ? e($accessoryCheckout->assigned->email) : null,
|
||||
'employee_number' => ($accessoryCheckout->assigned->employee_num) ? e($accessoryCheckout->assigned->employee_num) : null,
|
||||
'type' => 'user',
|
||||
];
|
||||
}
|
||||
|
||||
return $accessoryCheckout->assigned ? [
|
||||
'id' => $accessoryCheckout->assigned->id,
|
||||
'name' => e($accessoryCheckout->assigned->display_name),
|
||||
'type' => $accessoryCheckout->assignedType(),
|
||||
] : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,11 +205,11 @@ class ActionlogsTransformer
|
|||
|
||||
|
||||
|
||||
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
|
||||
public function transformCheckedoutActionlog (Collection $accessories_checkout, $total)
|
||||
{
|
||||
|
||||
$array = array();
|
||||
foreach ($accessories_users as $user) {
|
||||
foreach ($accessories_checkout as $user) {
|
||||
$array[] = (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
|
@ -256,7 +256,7 @@ class ActionlogsTransformer
|
|||
|
||||
$clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $oldRtdName : '';
|
||||
$clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $newRtdName : '';
|
||||
$clean_meta['Default Location'] = $clean_meta['rtd_location_id'];
|
||||
$clean_meta[trans('admin/hardware/form.default_location')] = $clean_meta['rtd_location_id'];
|
||||
unset($clean_meta['rtd_location_id']);
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ class ActionlogsTransformer
|
|||
|
||||
$clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ". $oldLocationName : '';
|
||||
$clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ". $newLocationName : '';
|
||||
$clean_meta['Current Location'] = $clean_meta['location_id'];
|
||||
$clean_meta[trans('admin/locations/message.current_location')] = $clean_meta['location_id'];
|
||||
unset($clean_meta['location_id']);
|
||||
}
|
||||
|
||||
|
@ -287,7 +287,7 @@ class ActionlogsTransformer
|
|||
$clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$oldModelName;
|
||||
$clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$newModelName; /** model is required at asset creation */
|
||||
|
||||
$clean_meta['Model'] = $clean_meta['model_id'];
|
||||
$clean_meta[trans('admin/hardware/form.model')] = $clean_meta['model_id'];
|
||||
unset($clean_meta['model_id']);
|
||||
}
|
||||
if(array_key_exists('company_id', $clean_meta)) {
|
||||
|
@ -300,7 +300,7 @@ class ActionlogsTransformer
|
|||
|
||||
$clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."] ". $oldCompanyName : trans('general.unassigned');
|
||||
$clean_meta['company_id']['new'] = $clean_meta['company_id']['new'] ? "[id: ".$clean_meta['company_id']['new']."] ". $newCompanyName : trans('general.unassigned');
|
||||
$clean_meta['Company'] = $clean_meta['company_id'];
|
||||
$clean_meta[trans('general.company')] = $clean_meta['company_id'];
|
||||
unset($clean_meta['company_id']);
|
||||
}
|
||||
if(array_key_exists('supplier_id', $clean_meta)) {
|
||||
|
@ -313,7 +313,7 @@ class ActionlogsTransformer
|
|||
|
||||
$clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ". $oldSupplierName : trans('general.unassigned');
|
||||
$clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ". $newSupplierName : trans('general.unassigned');
|
||||
$clean_meta['Supplier'] = $clean_meta['supplier_id'];
|
||||
$clean_meta[trans('general.supplier')] = $clean_meta['supplier_id'];
|
||||
unset($clean_meta['supplier_id']);
|
||||
}
|
||||
if(array_key_exists('status_id', $clean_meta)) {
|
||||
|
@ -326,11 +326,11 @@ class ActionlogsTransformer
|
|||
|
||||
$clean_meta['status_id']['old'] = $clean_meta['status_id']['old'] ? "[id: ".$clean_meta['status_id']['old']."] ". $oldStatusName : trans('general.unassigned');
|
||||
$clean_meta['status_id']['new'] = $clean_meta['status_id']['new'] ? "[id: ".$clean_meta['status_id']['new']."] ". $newStatusName : trans('general.unassigned');
|
||||
$clean_meta['Status'] = $clean_meta['status_id'];
|
||||
$clean_meta[trans('general.status_label')] = $clean_meta['status_id'];
|
||||
unset($clean_meta['status_id']);
|
||||
}
|
||||
if(array_key_exists('asset_eol_date', $clean_meta)) {
|
||||
$clean_meta['EOL date'] = $clean_meta['asset_eol_date'];
|
||||
$clean_meta[trans('admin/hardware/form.eol_date')] = $clean_meta['asset_eol_date'];
|
||||
unset($clean_meta['asset_eol_date']);
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ class ConsumablesTransformer
|
|||
'checkin' => Gate::allows('checkin', Consumable::class),
|
||||
'update' => Gate::allows('update', Consumable::class),
|
||||
'delete' => Gate::allows('delete', Consumable::class),
|
||||
'clone' => (Gate::allows('create', Consumable::class) && ($consumable->deleted_at == '')),
|
||||
];
|
||||
$array += $permissions_array;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ class LicenseSeatsTransformer
|
|||
];
|
||||
|
||||
if ($seat_count != 0) {
|
||||
$array['name'] = 'Seat '.$seat_count;
|
||||
$array['name'] = trans('admin/licenses/general.seat_count', ['count' => $seat_count]);
|
||||
}
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
|
|
|
@ -45,6 +45,10 @@ class LicensesTransformer
|
|||
'maintained' => ($license->maintained == 1) ? true : false,
|
||||
'supplier' => ($license->supplier) ? ['id' => (int) $license->supplier->id, 'name'=> e($license->supplier->name)] : null,
|
||||
'category' => ($license->category) ? ['id' => (int) $license->category->id, 'name'=> e($license->category->name)] : null,
|
||||
'created_by' => ($license->adminuser) ? [
|
||||
'id' => (int) $license->adminuser->id,
|
||||
'name'=> e($license->adminuser->present()->fullName()),
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($license->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($license->updated_at, 'datetime'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($license->deleted_at, 'datetime'),
|
||||
|
|
|
@ -3,14 +3,10 @@
|
|||
namespace App\Importer;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\User;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class AssetImporter extends ItemImporter
|
||||
{
|
||||
|
|
|
@ -67,6 +67,7 @@ class UserImporter extends ItemImporter
|
|||
$this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0;
|
||||
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0;
|
||||
|
||||
$this->handleEmptyStringsForDates();
|
||||
|
||||
$user_department = trim($this->findCsvMatch($row, 'department'));
|
||||
if ($this->shouldUpdateField($user_department)) {
|
||||
|
@ -179,4 +180,22 @@ class UserImporter extends ItemImporter
|
|||
{
|
||||
$this->send_welcome = $send;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the findCsvMatch() method will set '' for columns that are present but empty,
|
||||
* we need to set those empty strings to null to avoid passing bad data to the database
|
||||
* (ie ending up with 0000-00-00 instead of the intended null).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handleEmptyStringsForDates(): void
|
||||
{
|
||||
if ($this->item['start_date'] === '') {
|
||||
$this->item['start_date'] = null;
|
||||
}
|
||||
|
||||
if ($this->item['end_date'] === '') {
|
||||
$this->item['end_date'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ class Accessory extends SnipeModel
|
|||
'company_id' => 'integer|nullable',
|
||||
'min_amt' => 'integer|min:0|nullable',
|
||||
'purchase_cost' => 'numeric|nullable|gte:0',
|
||||
'purchase_date' => 'date_format:Y-m-d|nullable',
|
||||
'purchase_date' => 'date_format:Y-m-d|nullable',
|
||||
];
|
||||
|
||||
|
||||
|
@ -253,9 +253,10 @@ class Accessory extends SnipeModel
|
|||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function users()
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->withPivot('id', 'created_at', 'note')->withTrashed();
|
||||
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
|
||||
->with('assignedTo');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,7 +268,9 @@ class Accessory extends SnipeModel
|
|||
*/
|
||||
public function hasUsers()
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->count();
|
||||
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
|
||||
->where('assigned_type', User::class)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -329,11 +332,24 @@ class Accessory extends SnipeModel
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check how many items within an accessory are checked out
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0]
|
||||
* @return int
|
||||
*/
|
||||
public function numCheckedOut()
|
||||
{
|
||||
return $this->checkouts_count ?? $this->checkouts()->count();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* In order to use this model method, you MUST call withCount('checkouts as checkouts_count')
|
||||
* on the eloquent query in the controller, otherwise $this->checkouts_count will be null and
|
||||
* bad things happen.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
|
@ -342,11 +358,11 @@ class Accessory extends SnipeModel
|
|||
*/
|
||||
public function numRemaining()
|
||||
{
|
||||
$checkedout = $this->users_count;
|
||||
$checkedout = $this->numCheckedOut();
|
||||
$total = $this->qty;
|
||||
$remaining = $total - $checkedout;
|
||||
|
||||
return (int) $remaining;
|
||||
return $remaining;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,12 +373,12 @@ class Accessory extends SnipeModel
|
|||
*/
|
||||
public function declinedCheckout(User $declinedBy, $signature)
|
||||
{
|
||||
if (is_null($accessory_user = \DB::table('accessories_users')->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
|
||||
if (is_null($accessory_checkout = AccessoryCheckout::userAssigned()->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$accessory_user->limit(1)->delete();
|
||||
$accessory_checkout->limit(1)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
148
app/Models/AccessoryCheckout.php
Executable file
148
app/Models/AccessoryCheckout.php
Executable file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
/**
|
||||
* Model for Accessories.
|
||||
*
|
||||
* @version v1.0
|
||||
*/
|
||||
class AccessoryCheckout extends Model
|
||||
{
|
||||
use Searchable;
|
||||
|
||||
protected $fillable = ['user_id', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
|
||||
protected $table = 'accessories_checkout';
|
||||
|
||||
/**
|
||||
* Establishes the accessory checkout -> accessory relationship
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0.9]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function accessory()
|
||||
{
|
||||
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the accessory checkout -> user relationship
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0.9]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(\App\Models\User::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target this asset is checked out to
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function assignedTo()
|
||||
{
|
||||
return $this->morphTo('assigned', 'assigned_type', 'assigned_to')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lowercased name of the type of target the asset is assigned to
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return string
|
||||
*/
|
||||
public function assignedType()
|
||||
{
|
||||
return $this->assigned_type ? strtolower(class_basename($this->assigned_type)) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the accessory is checked out to a user
|
||||
*
|
||||
* Even though we allow allow for checkout to things beyond users
|
||||
* this method is an easy way of seeing if we are checked out to a user.
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0]
|
||||
*/
|
||||
public function checkedOutToUser(): bool
|
||||
{
|
||||
return $this->assignedType() === Asset::USER;
|
||||
}
|
||||
|
||||
public function scopeUserAssigned(Builder $query): void
|
||||
{
|
||||
$query->where('assigned_type', '=', User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run additional, advanced searches.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $terms The search terms
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function advancedTextSearch(Builder $query, array $terms)
|
||||
{
|
||||
|
||||
$userQuery = User::where(function ($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('first_name', 'like', $search_str)
|
||||
->orWhere('last_name', 'like', $search_str)
|
||||
->orWhere('note', 'like', $search_str);
|
||||
}
|
||||
})->select('id');
|
||||
|
||||
$locationQuery = Location::where(function ($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('name', 'like', $search_str);
|
||||
}
|
||||
})->select('id');
|
||||
|
||||
$assetQuery = Asset::where(function ($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('name', 'like', $search_str);
|
||||
}
|
||||
})->select('id');
|
||||
|
||||
$query->where(function ($query) use ($userQuery) {
|
||||
$query->where('assigned_type', User::class)
|
||||
->whereIn('assigned_to', $userQuery);
|
||||
})->orWhere(function($query) use ($locationQuery) {
|
||||
$query->where('assigned_type', Location::class)
|
||||
->whereIn('assigned_to', $locationQuery);
|
||||
})->orWhere(function($query) use ($assetQuery) {
|
||||
$query->where('assigned_type', Asset::class)
|
||||
->whereIn('assigned_to', $assetQuery);
|
||||
})->orWhere(function($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('note', 'like', $search_str);
|
||||
}
|
||||
});
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -97,35 +97,38 @@ class Asset extends Depreciable
|
|||
];
|
||||
|
||||
protected $rules = [
|
||||
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array',
|
||||
'name' => 'nullable|max:255',
|
||||
'company_id' => 'nullable|integer|exists:companies,id',
|
||||
'warranty_months' => 'nullable|numeric|digits_between:0,240',
|
||||
'last_checkout' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
'last_checkin' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
'expected_checkin' => 'nullable|date',
|
||||
'last_audit_date' => 'nullable|date_format:Y-m-d H:i:s',
|
||||
// 'next_audit_date' => 'nullable|date|after:last_audit_date',
|
||||
'next_audit_date' => 'nullable|date',
|
||||
'location_id' => 'nullable|exists:locations,id',
|
||||
'rtd_location_id' => 'nullable|exists:locations,id',
|
||||
'purchase_date' => 'nullable|date|date_format:Y-m-d',
|
||||
'serial' => 'nullable|unique_undeleted:assets,serial',
|
||||
'purchase_cost' => 'nullable|numeric|gte:0',
|
||||
'supplier_id' => 'nullable|exists:suppliers,id',
|
||||
'asset_eol_date' => 'nullable|date',
|
||||
'eol_explicit' => 'nullable|boolean',
|
||||
'byod' => 'nullable|boolean',
|
||||
'order_number' => 'nullable|string|max:191',
|
||||
'notes' => 'nullable|string|max:65535',
|
||||
'assigned_to' => 'nullable|integer',
|
||||
'requestable' => 'nullable|boolean',
|
||||
'model_id' => ['required', 'integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
|
||||
'status_id' => ['required', 'integer', 'exists:status_labels,id'],
|
||||
'asset_tag' => ['required', 'min:1', 'max:255', 'unique_undeleted:assets,asset_tag', 'not_array'],
|
||||
'name' => ['nullable', 'max:255'],
|
||||
'company_id' => ['nullable', 'integer', 'exists:companies,id'],
|
||||
'warranty_months' => ['nullable', 'numeric', 'digits_between:0,240'],
|
||||
'last_checkout' => ['nullable', 'date_format:Y-m-d H:i:s'],
|
||||
'last_checkin' => ['nullable', 'date_format:Y-m-d H:i:s'],
|
||||
'expected_checkin' => ['nullable', 'date'],
|
||||
'last_audit_date' => ['nullable', 'date_format:Y-m-d H:i:s'],
|
||||
'next_audit_date' => ['nullable', 'date'],
|
||||
//'after:last_audit_date'],
|
||||
'location_id' => ['nullable', 'exists:locations,id'],
|
||||
'rtd_location_id' => ['nullable', 'exists:locations,id'],
|
||||
'purchase_date' => ['nullable', 'date', 'date_format:Y-m-d'],
|
||||
'serial' => ['nullable', 'unique_undeleted:assets,serial'],
|
||||
'purchase_cost' => ['nullable', 'numeric', 'gte:0'],
|
||||
'supplier_id' => ['nullable', 'exists:suppliers,id'],
|
||||
'asset_eol_date' => ['nullable', 'date'],
|
||||
'eol_explicit' => ['nullable', 'boolean'],
|
||||
'byod' => ['nullable', 'boolean'],
|
||||
'order_number' => ['nullable', 'string', 'max:191'],
|
||||
'notes' => ['nullable', 'string', 'max:65535'],
|
||||
'assigned_to' => ['nullable', 'integer'],
|
||||
'requestable' => ['nullable', 'boolean'],
|
||||
'assigned_user' => ['nullable', 'exists:users,id,deleted_at,NULL'],
|
||||
'assigned_location' => ['nullable', 'exists:locations,id,deleted_at,NULL'],
|
||||
'assigned_asset' => ['nullable', 'exists:assets,id,deleted_at,NULL']
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
|
|
|
@ -205,7 +205,11 @@ class Component extends SnipeModel
|
|||
public function numCheckedOut()
|
||||
{
|
||||
$checkedout = 0;
|
||||
foreach ($this->assets as $checkout) {
|
||||
|
||||
// In case there are elements checked out to assets that belong to a different company
|
||||
// than this asset and full multiple company support is on we'll remove the global scope,
|
||||
// so they are included in the count.
|
||||
foreach ($this->assets()->withoutGlobalScope(new CompanyableScope)->get() as $checkout) {
|
||||
$checkedout += $checkout->pivot->assigned_qty;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use EasySlugger\Utf8Slugger;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Schema;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
class CustomField extends Model
|
||||
{
|
||||
|
|
|
@ -158,17 +158,20 @@ class Depreciable extends SnipeModel
|
|||
|
||||
public function time_until_depreciated()
|
||||
{
|
||||
// @link http://www.php.net/manual/en/class.datetime.php
|
||||
$d1 = new \DateTime();
|
||||
$d2 = $this->depreciated_date();
|
||||
if ($this->depreciated_date()) {
|
||||
// @link http://www.php.net/manual/en/class.datetime.php
|
||||
$d1 = new \DateTime();
|
||||
$d2 = $this->depreciated_date();
|
||||
|
||||
// @link http://www.php.net/manual/en/class.dateinterval.php
|
||||
$interval = $d1->diff($d2);
|
||||
if (! $interval->invert) {
|
||||
return $interval;
|
||||
} else {
|
||||
return new \DateInterval('PT0S'); //null interval (zero seconds from now)
|
||||
// @link http://www.php.net/manual/en/class.dateinterval.php
|
||||
$interval = $d1->diff($d2);
|
||||
if (! $interval->invert) {
|
||||
return $interval;
|
||||
} else {
|
||||
return new \DateInterval('PT0S'); //null interval (zero seconds from now)
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function depreciated_date()
|
||||
|
|
|
@ -229,6 +229,7 @@ class Ldap extends Model
|
|||
$item['department'] = $ldapattributes[$ldap_result_dept][0] ?? '';
|
||||
$item['manager'] = $ldapattributes[$ldap_result_manager][0] ?? '';
|
||||
$item['location'] = $ldapattributes[$ldap_result_location][0] ?? '';
|
||||
$item['locale'] = app()->getLocale();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
@ -239,7 +240,7 @@ class Ldap extends Model
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $ldapatttibutes
|
||||
* @return array|bool
|
||||
* @return User | bool
|
||||
*/
|
||||
public static function createUserFromLdap($ldapatttibutes, $password)
|
||||
{
|
||||
|
@ -252,6 +253,7 @@ class Ldap extends Model
|
|||
$user->last_name = $item['lastname'];
|
||||
$user->username = $item['username'];
|
||||
$user->email = $item['email'];
|
||||
$user->locale = $item['locale'];
|
||||
$user->password = $user->noPassword();
|
||||
|
||||
if (Setting::getSettings()->ldap_pw_sync == '1') {
|
||||
|
|
|
@ -50,7 +50,7 @@ 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|max:10',
|
||||
'purchase_date' => 'date_format:Y-m-d|nullable|max:10|required_with:depreciation_id',
|
||||
'expiration_date' => 'date_format:Y-m-d|nullable|max:10',
|
||||
'termination_date' => 'date_format:Y-m-d|nullable|max:10',
|
||||
'min_amt' => 'numeric|nullable|gte:0',
|
||||
|
@ -736,4 +736,17 @@ class License extends Depreciable
|
|||
return $query->leftJoin('companies as companies', 'licenses.company_id', '=', 'companies.id')->select('licenses.*')
|
||||
->orderBy('companies.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on the user that created it
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderCreatedBy($query, $order)
|
||||
{
|
||||
return $query->leftJoin('users as users_sort', 'licenses.user_id', '=', 'users_sort.id')->select('licenses.*')->orderBy('users_sort.first_name', $order)->orderBy('users_sort.last_name', $order);
|
||||
}
|
||||
}
|
|
@ -108,10 +108,11 @@ class Location extends SnipeModel
|
|||
{
|
||||
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->assets_count === 0)
|
||||
&& ($this->assigned_assets_count === 0)
|
||||
&& ($this->children_count === 0)
|
||||
&& ($this->users_count === 0);
|
||||
&& ($this->assets_count == 0)
|
||||
&& ($this->assigned_assets_count == 0)
|
||||
&& ($this->children_count == 0)
|
||||
&& ($this->accessories_count == 0)
|
||||
&& ($this->users_count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,9 +22,9 @@ class Manufacturer extends SnipeModel
|
|||
// Declare the rules for the form validation
|
||||
protected $rules = [
|
||||
'name' => 'required|min:2|max:255|unique:manufacturers,name,NULL,id,deleted_at,NULL',
|
||||
'url' => 'url|nullable',
|
||||
'url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://',
|
||||
'support_email' => 'email|nullable',
|
||||
'support_url' => 'nullable|url',
|
||||
'support_url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://',
|
||||
'warranty_lookup_url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://'
|
||||
];
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use Illuminate\Support\Collection;
|
|||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
|
|
@ -331,7 +331,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
|||
*/
|
||||
public function accessories()
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_users', 'assigned_to', 'accessory_id')
|
||||
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_checkout', 'assigned_to', 'accessory_id')
|
||||
->withPivot('id', 'created_at', 'note')->withTrashed()->orderBy('accessory_id');
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ class CheckoutAccessoryNotification extends Notification
|
|||
$this->item = $accessory;
|
||||
$this->admin = $checkedOutBy;
|
||||
$this->note = $note;
|
||||
$this->checkout_qty = $accessory->checkout_qty;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
$this->settings = Setting::getSettings();
|
||||
|
@ -107,7 +108,7 @@ class CheckoutAccessoryNotification extends Notification
|
|||
->from($botname)
|
||||
->to($channel)
|
||||
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
|
||||
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
|
||||
$attachment->title(htmlspecialchars_decode($this->checkout_qty.' x '.$item->present()->name), $item->present()->viewUrl())
|
||||
->fields($fields)
|
||||
->content($note);
|
||||
});
|
||||
|
@ -127,6 +128,7 @@ class CheckoutAccessoryNotification extends Notification
|
|||
->addStartGroupToSection('activityText')
|
||||
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
|
||||
->fact(trans('mail.assigned_to'), $target->present()->name)
|
||||
->fact(trans('general.qty'), $this->checkout_qty)
|
||||
->fact(trans('mail.checkedout_from'), $item->location->name ? $item->location->name : '')
|
||||
->fact(trans('mail.Accessory_Checkout_Notification') . " by ", $admin->present()->fullName())
|
||||
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
|
||||
|
@ -184,6 +186,7 @@ class CheckoutAccessoryNotification extends Notification
|
|||
'eula' => $eula,
|
||||
'req_accept' => $req_accept,
|
||||
'accept_url' => $accept_url,
|
||||
'checkout_qty' => $this->checkout_qty,
|
||||
])
|
||||
->subject(trans('mail.Confirm_accessory_delivery'));
|
||||
}
|
||||
|
|
73
app/Notifications/UnacceptedAssetReminderNotification.php
Normal file
73
app/Notifications/UnacceptedAssetReminderNotification.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class UnacceptedAssetReminderNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($checkout_info, $count)
|
||||
{
|
||||
$this->count = $count;
|
||||
$this->target = $checkout_info['acceptance']->assignedTo;
|
||||
$this->acceptance = $checkout_info['acceptance'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via()
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail()
|
||||
{
|
||||
$accept_url = route('account.accept');
|
||||
$message = (new MailMessage)->markdown('notifications.markdown.asset-reminder',
|
||||
[
|
||||
'count' => $this->count,
|
||||
'assigned_to' => $this->target->present()->fullName,
|
||||
'link' => route('account.accept'),
|
||||
'accept_url' => $accept_url,
|
||||
])
|
||||
->subject(trans('mail.unaccepted_asset_reminder'));
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
|
@ -90,7 +90,7 @@ class AccessoryPresenter extends Presenter
|
|||
'visible' => false,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
],[
|
||||
'field' => 'users_count',
|
||||
'field' => 'checkouts_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
|
|
|
@ -556,13 +556,12 @@ class AssetPresenter extends Presenter
|
|||
}
|
||||
|
||||
/**
|
||||
* Used to take user created warranty URL and dynamically fill in the needed values per asset
|
||||
* Used to take user created URL and dynamically fill in the needed values per asset
|
||||
* @return string
|
||||
*/
|
||||
public function dynamicWarrantyUrl()
|
||||
public function dynamicUrl($dynamic_url)
|
||||
{
|
||||
$warranty_lookup_url = $this->model->model->manufacturer->warranty_lookup_url;
|
||||
$url = (str_replace('{LOCALE}',\App\Models\Setting::getSettings()->locale, $warranty_lookup_url));
|
||||
$url = (str_replace('{LOCALE}',\App\Models\Setting::getSettings()->locale, $dynamic_url));
|
||||
$url = (str_replace('{SERIAL}', urlencode($this->model->serial), $url));
|
||||
$url = (str_replace('{MODEL_NAME}', urlencode($this->model->model->name), $url));
|
||||
$url = (str_replace('{MODEL_NUMBER}', urlencode($this->model->model->model_number), $url));
|
||||
|
|
|
@ -158,6 +158,13 @@ class LicensePresenter extends Presenter
|
|||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.order_number'),
|
||||
], [
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.admin'),
|
||||
'visible' => false,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'created_at',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -445,26 +445,30 @@ class UserPresenter extends Presenter
|
|||
return Storage::disk('public')->url('avatars/'.e($this->avatar));
|
||||
}
|
||||
|
||||
// If there is a default avatar
|
||||
if (Setting::getSettings()->default_avatar!= '') {
|
||||
|
||||
// If the default is system default
|
||||
if (Setting::getSettings()->default_avatar == 'default.png') {
|
||||
return Storage::disk('public')->url('default.png');
|
||||
}
|
||||
|
||||
// If there is a custom default avatar
|
||||
if (Setting::getSettings()->default_avatar != '') {
|
||||
return Storage::disk('public')->url('avatars/'.e(Setting::getSettings()->default_avatar));
|
||||
}
|
||||
|
||||
// Fall back to Gravatar if the settings allow loading remote scripts
|
||||
if (Setting::getSettings()->load_remote == '1') {
|
||||
if ($this->model->gravatar != '') {
|
||||
// If there is no default and no custom avatar, check for gravatar
|
||||
if ((Setting::getSettings()->load_remote == '1') && (Setting::getSettings()->default_avatar == '')) {
|
||||
|
||||
if ($this->model->gravatar != '') {
|
||||
$gravatar = md5(strtolower(trim($this->model->gravatar)));
|
||||
return '//gravatar.com/avatar/'.$gravatar;
|
||||
|
||||
} elseif ($this->email != '') {
|
||||
|
||||
$gravatar = md5(strtolower(trim($this->email)));
|
||||
return '//gravatar.com/avatar/'.$gravatar;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,11 +87,11 @@ class AuthServiceProvider extends ServiceProvider
|
|||
]);
|
||||
|
||||
$this->registerPolicies();
|
||||
//Passport::routes(); //this is no longer required in newer passport versions
|
||||
Passport::tokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
|
||||
Passport::refreshTokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
|
||||
Passport::personalAccessTokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
|
||||
Passport::withCookieSerialization();
|
||||
|
||||
Passport::cookie(config('passport.cookie_name'));
|
||||
|
||||
|
||||
/**
|
||||
|
|
25
app/Providers/BladeServiceProvider.php
Normal file
25
app/Providers/BladeServiceProvider.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class BladeServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Blade::anonymousComponentPath(__DIR__ . '/../../resources/views/blade');
|
||||
}
|
||||
}
|
32
app/Providers/LivewireServiceProvider.php
Normal file
32
app/Providers/LivewireServiceProvider.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Livewire;
|
||||
|
||||
class LivewireServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Livewire::setUpdateRoute(function ($handle) {
|
||||
return Route::post('/' . config('livewire.url_prefix') . '/livewire/update', $handle);
|
||||
});
|
||||
|
||||
Livewire::setScriptRoute(function ($handle) {
|
||||
return Route::get('/' . config('livewire.url_prefix') . '/livewire/livewire.js', $handle);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Crypt;
|
|||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Validator;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
* This service provider handles a few custom validation rules.
|
||||
|
@ -66,7 +66,6 @@ class ValidationServiceProvider extends ServiceProvider
|
|||
* `unique_undeleted:table,fieldname` in your rules out of the box
|
||||
*/
|
||||
Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) {
|
||||
|
||||
if (count($parameters)) {
|
||||
|
||||
// This is a bit of a shim, but serial doesn't have any other rules around it other than that it's nullable
|
||||
|
|
|
@ -107,7 +107,7 @@ class Label implements View
|
|||
|
||||
if ($settings->alt_barcode_enabled) {
|
||||
if ($template->getSupport1DBarcode()) {
|
||||
$barcode1DType = $settings->alt_barcode;
|
||||
$barcode1DType = $settings->label2_1d_type;
|
||||
if ($barcode1DType != 'none') {
|
||||
$assetData->put('barcode1d', (object)[
|
||||
'type' => $barcode1DType,
|
||||
|
|
|
@ -311,8 +311,10 @@ return [
|
|||
App\Providers\ValidationServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Custom service provider
|
||||
* Custom Service Providers...
|
||||
*/
|
||||
App\Providers\BladeServiceProvider::class,
|
||||
App\Providers\LivewireServiceProvider::class,
|
||||
App\Providers\MacroServiceProvider::class,
|
||||
App\Providers\SamlServiceProvider::class,
|
||||
|
||||
|
@ -426,5 +428,5 @@ return [
|
|||
*/
|
||||
|
||||
'escape_formulas' => env('CSV_ESCAPE_FORMULAS', true),
|
||||
|
||||
|
||||
];
|
||||
|
|
|
@ -139,7 +139,7 @@ return [
|
|||
'to' => env('MAIL_BACKUP_NOTIFICATION_ADDRESS', null),
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'address' => env('MAIL_FROM_ADDR', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
],
|
||||
|
|
|
@ -157,4 +157,21 @@ return [
|
|||
*/
|
||||
|
||||
'pagination_theme' => 'tailwind',
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| URL Prefix
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| By default, Livewire sends network requests to {app.com}/livewire/update
|
||||
| while javascript assets are served via {app.com}/livewire/livewire.js
|
||||
| If you need to adjust the prefix of those urls you can do it below.
|
||||
|
|
||||
| Defining a prefix will result in the following url
|
||||
| {app.com}/{prefix}/livewire/{update|livewire.js}
|
||||
| Note: do not include the leading or trailing /
|
||||
|
|
||||
*/
|
||||
|
||||
'url_prefix' => env('LIVEWIRE_URL_PREFIX', null),
|
||||
];
|
||||
|
|
|
@ -14,4 +14,5 @@ return [
|
|||
'private_key' => env('PASSPORT_PRIVATE_KEY'),
|
||||
'public_key' => env('PASSPORT_PUBLIC_KEY'),
|
||||
'expiration_years' => env('API_TOKEN_EXPIRATION_YEARS', 20),
|
||||
'cookie_name' => env('PASSPORT_COOKIE_NAME', 'snipeit_passport_token'),
|
||||
];
|
||||
|
|
|
@ -44,12 +44,6 @@ return [
|
|||
'secret' => env('STRIPE_SECRET'),
|
||||
],
|
||||
|
||||
'baremetrics' => [
|
||||
'enabled' => env('ENABLE_BMPAY', false),
|
||||
'app_key' => env('BMPAY_PUBLIC_KEY', null),
|
||||
'stripe_id' => env('BMPAY_STRIPE_ID', null),
|
||||
],
|
||||
|
||||
'google' => [
|
||||
'maps_api_key' => env('GOOGLE_MAPS_API'),
|
||||
],
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
return array (
|
||||
'app_version' => 'v7.0.9',
|
||||
'full_app_version' => 'v7.0.9 - build 14371-g1a541ce22',
|
||||
'build_version' => '14371',
|
||||
'app_version' => 'v7.0.10',
|
||||
'full_app_version' => 'v7.0.10 - build 14684-gc2bcc2e2d',
|
||||
'build_version' => '14684',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'g1a541ce22',
|
||||
'full_hash' => 'v7.0.9-119-g1a541ce22',
|
||||
'branch' => 'develop',
|
||||
'hash_version' => 'gc2bcc2e2d',
|
||||
'full_hash' => 'v7.0.10-311-gc2bcc2e2d',
|
||||
'branch' => 'master',
|
||||
);
|
|
@ -3,6 +3,7 @@
|
|||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Category;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
|
@ -125,11 +126,12 @@ class AccessoryFactory extends Factory
|
|||
})->afterCreating(function ($accessory) {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => now(),
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => $user->id,
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_type' => User::class,
|
||||
'note' => '',
|
||||
]);
|
||||
});
|
||||
|
@ -145,11 +147,12 @@ class AccessoryFactory extends Factory
|
|||
public function checkedOutToUser(User $user = null)
|
||||
{
|
||||
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => 1,
|
||||
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
||||
'assigned_type' => User::class,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class AssetFactory extends Factory
|
|||
'assigned_type' => null,
|
||||
'next_audit_date' => null,
|
||||
'last_checkout' => null,
|
||||
'asset_eol_date' => null
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -354,6 +355,17 @@ class AssetFactory extends Factory
|
|||
return $this->state(['requestable' => false]);
|
||||
}
|
||||
|
||||
public function noPurchaseOrEolDate()
|
||||
{
|
||||
return $this->afterCreating(function (Asset $asset) {
|
||||
$asset->update([
|
||||
'purchase_date' => null,
|
||||
'asset_eol_date' => null
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function hasEncryptedCustomField(CustomField $field = null)
|
||||
{
|
||||
return $this->state(function () use ($field) {
|
||||
|
@ -372,7 +384,6 @@ class AssetFactory extends Factory
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This allows bypassing model level validation if you want to purposefully
|
||||
* create an asset in an invalid state. Validation is turned back on
|
||||
|
|
|
@ -2,10 +2,15 @@
|
|||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use App\Models\Supplier;
|
||||
|
||||
|
@ -97,5 +102,16 @@ class ComponentFactory extends Factory
|
|||
});
|
||||
}
|
||||
|
||||
public function checkedOutToAsset(Asset $asset = null)
|
||||
{
|
||||
return $this->afterCreating(function (Component $component) use ($asset) {
|
||||
$component->assets()->attach($component->id, [
|
||||
'component_id' => $component->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => 1,
|
||||
'asset_id' => $asset->id ?? Asset::factory()->create()->id,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,13 @@ class LocationFactory extends Factory
|
|||
'image' => rand(1, 9).'.jpg',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// one of these can eventuall go away - left temporarily for conflict resolution
|
||||
public function deleted(): self
|
||||
{
|
||||
return $this->state(['deleted_at' => $this->faker->dateTime()]);
|
||||
}
|
||||
|
||||
public function deletedLocation()
|
||||
{
|
||||
return $this->state(function () {
|
||||
|
|
|
@ -314,4 +314,9 @@ class UserFactory extends Factory
|
|||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function deleted(): self
|
||||
{
|
||||
return $this->state(['deleted_at' => $this->faker->dateTime()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Setting::where('locale', 'no-NO')->update(['locale' => 'nb-NO']);
|
||||
User::where('locale', 'no-NO')->update(['locale' => 'nb-NO']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
|
||||
}
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('accessories_users')) {
|
||||
Schema::rename('accessories_users', 'accessories_checkout');
|
||||
|
||||
Schema::table('accessories_checkout', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('accessories_checkout', 'assigned_type')) {
|
||||
$table->string('assigned_type')->nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DB::update('update '.DB::getTablePrefix().'accessories_checkout set assigned_type = \'App\\\\Models\\\\User\' where assigned_type is null', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasTable('accessories_checkout')) {
|
||||
Schema::table('accessories_checkout', function (Blueprint $table) {
|
||||
$table->dropColumn('assigned_type');
|
||||
});
|
||||
|
||||
Schema::rename('accessories_checkout', 'accessories_users');
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->boolean('shortcuts_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropColumn('shortcuts_enabled');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -16,7 +16,7 @@ class AccessorySeeder extends Seeder
|
|||
public function run()
|
||||
{
|
||||
Accessory::truncate();
|
||||
DB::table('accessories_users')->truncate();
|
||||
DB::table('accessories_checkout')->truncate();
|
||||
|
||||
if (! Location::count()) {
|
||||
$this->call(LocationSeeder::class);
|
||||
|
|
|
@ -36,6 +36,7 @@ class SettingsSeeder extends Seeder
|
|||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = 'on';
|
||||
$settings->pwd_secure_min = '8';
|
||||
$settings->default_avatar = 'default.png';
|
||||
$settings->save();
|
||||
|
||||
if ($user = User::where('username', '=', 'admin')->first()) {
|
||||
|
|
|
@ -39,6 +39,14 @@ chown -R apache:root /var/lib/snipeit/data/*
|
|||
chown -R apache:root /var/lib/snipeit/dumps
|
||||
chown -R apache:root /var/lib/snipeit/keys
|
||||
|
||||
# Fix php settings
|
||||
if [ ! -z "${PHP_UPLOAD_LIMIT}" ]
|
||||
then
|
||||
echo "Changing upload limit to ${PHP_UPLOAD_LIMIT}"
|
||||
sed -i "s/^upload_max_filesize.*/upload_max_filesize = ${PHP_UPLOAD_LIMIT}M/" /etc/php*/php.ini
|
||||
sed -i "s/^post_max_size.*/post_max_size = ${PHP_UPLOAD_LIMIT}M/" /etc/php*/php.ini
|
||||
fi
|
||||
|
||||
# If the Oauth DB files are not present copy the vendor files over to the db migrations
|
||||
if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ]
|
||||
then
|
||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -15,7 +15,7 @@
|
|||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-datepicker": "^1.10.0",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
"bootstrap-table": "1.22.5",
|
||||
"bootstrap-table": "1.23.0",
|
||||
"chart.js": "^2.9.4",
|
||||
"clipboard": "^2.0.11",
|
||||
"css-loader": "^5.0.0",
|
||||
|
@ -3692,9 +3692,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bootstrap-table": {
|
||||
"version": "1.22.5",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.22.5.tgz",
|
||||
"integrity": "sha512-iaQBfZzNuMRVughNYdonPGvgL6A7xfsruqYKaSuDuUWqQDTt8WvTBVwV61XiDv2aks7RaAQoZhoi2jo9nF6U7w==",
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.0.tgz",
|
||||
"integrity": "sha512-fAIhu2CAqMsZWkzeFxXyh0yQA2DMBdB0tCdr1iF6bKr3c/Hf79cw5PykNt7NdtqLz/a0p192S8EKyT5lG4yrpw==",
|
||||
"peerDependencies": {
|
||||
"jquery": "3"
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-datepicker": "^1.10.0",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
"bootstrap-table": "1.22.5",
|
||||
"bootstrap-table": "1.23.0",
|
||||
"chart.js": "^2.9.4",
|
||||
"clipboard": "^2.0.11",
|
||||
"css-loader": "^5.0.0",
|
||||
|
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue