Merge branch 'develop' into fixes/custom-field-values
# Conflicts: # resources/views/livewire/custom-field-set-default-values-for-model.blade.php
This commit is contained in:
commit
6423df2133
1072 changed files with 28908 additions and 4702 deletions
|
@ -3181,6 +3181,42 @@
|
|||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Glukose1",
|
||||
"name": "Glukose1",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/167117705?v=4",
|
||||
"profile": "https://github.com/Glukose1",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Scarzy",
|
||||
"name": "Scarzy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1197791?v=4",
|
||||
"profile": "https://github.com/Scarzy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "setpill",
|
||||
"name": "setpill",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/37372069?v=4",
|
||||
"profile": "https://github.com/setpill",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "swift2512",
|
||||
"name": "swift2512",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3755203?v=4",
|
||||
"profile": "https://github.com/swift2512",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# --------------------------------------------
|
||||
# REQUIRED: DB SETUP
|
||||
# --------------------------------------------
|
||||
# https://mariadb.com/kb/en/mariadb-server-docker-official-image-environment-variables/
|
||||
|
||||
MYSQL_DATABASE=snipeit
|
||||
MYSQL_USER=snipeit
|
||||
MYSQL_PASSWORD=changeme1234
|
||||
|
|
|
@ -32,6 +32,8 @@ DB_PREFIX=null
|
|||
DB_DUMP_PATH='/usr/bin'
|
||||
DB_CHARSET=utf8mb4
|
||||
DB_COLLATION=utf8mb4_unicode_ci
|
||||
DB_SANITIZE_BY_DEFAULT=false
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SSL DATABASE SETTINGS
|
||||
|
|
43
.github/stale.yml
vendored
43
.github/stale.yml
vendored
|
@ -1,43 +0,0 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- :woman_technologist: ready for dev
|
||||
- :moneybag: bounty
|
||||
- :hand: bug
|
||||
- "🔐 security"
|
||||
- "👩💻 ready for dev"
|
||||
- "💰 bounty"
|
||||
- "✋ bug"
|
||||
|
||||
exemptMilestones: true
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
only: issues
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
unmarkComment: >
|
||||
Okay, it looks like this issue or feature request might still be important. We'll re-open
|
||||
it for now. Thank you for letting us know!
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Is this still relevant? We haven't heard from anyone in a bit. If so,
|
||||
please comment with any updates or additional detail.
|
||||
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Don't
|
||||
take it personally, we just need to keep a handle on things. Thank you
|
||||
for your contributions!
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not had
|
||||
recent activity. If you believe this is still an issue, please confirm that
|
||||
this issue is still happening in the most recent version of Snipe-IT and reply
|
||||
to this thread to re-open it.
|
39
.github/workflows/stale.yml
vendored
Normal file
39
.github/workflows/stale.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
name: 'Close stale issues'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# contents: write # only for delete-branch option
|
||||
issues: write
|
||||
# pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
debug-only: true
|
||||
operations-per-run: 100 # just while we're debugging
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 60
|
||||
days-before-close: 7
|
||||
exempt-all-milestones: true
|
||||
stale-issue-message: >
|
||||
Is this still relevant? We haven't heard from anyone in a bit. If so,
|
||||
please comment with any updates or additional detail.
|
||||
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Don't
|
||||
take it personally, we just need to keep a handle on things. Thank you
|
||||
for your contributions!
|
||||
close-issue-message: >
|
||||
This issue has been automatically closed because it has not had
|
||||
recent activity. If you believe this is still an issue, please confirm that
|
||||
this issue is still happening in the most recent version of Snipe-IT and reply
|
||||
to this thread to re-open it.
|
||||
# There doesn't seem to be a 'reopen issue message'?
|
||||
# Since there is no 'stale-pr-message' - PR's should not be stale'd
|
||||
stale-issue-label: stale
|
||||
exempt-issue-labels: >
|
||||
pinned,security,:woman_technologist: ready for dev,:moneybag: bounty,:hand: bug,🔐 security,👩💻 ready for dev,💰 bounty,✋ bug
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -47,6 +47,7 @@ storage/private_uploads/users/*
|
|||
tests/_data/scenarios
|
||||
tests/_output/*
|
||||
tests/_support/_generated/*
|
||||
tests/coverage/*
|
||||
/npm-debug.log
|
||||
/storage/oauth-private.key
|
||||
/storage/oauth-public.key
|
||||
|
|
|
@ -52,6 +52,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
|||
| [<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/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") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
|
|
@ -79,12 +79,12 @@ USER root
|
|||
|
||||
VOLUME ["/var/lib/snipeit"]
|
||||
|
||||
# Entrypoints
|
||||
COPY docker/entrypoint_alpine.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
# Startup script
|
||||
COPY docker/startup_alpine.sh /startup.sh
|
||||
RUN chmod +x /startup.sh
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
||||
CMD ["/entrypoint.sh"]
|
||||
CMD ["/startup.sh"]
|
||||
|
||||
EXPOSE 80
|
||||
|
|
|
@ -97,7 +97,7 @@ RUN set -eux; \
|
|||
VOLUME [ "/var/lib/snipeit" ]
|
||||
|
||||
COPY --chown=www-data:www-data docker/docker-secrets.env /var/www/html/.env
|
||||
COPY --chmod=655 docker/docker-entrypoint.sh /usr/local/bin/docker-snipeit-entrypoint
|
||||
COPY --chmod=655 docker/startup_alpine_fpm.sh /startup.sh
|
||||
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
|
||||
ENTRYPOINT [ "/usr/local/bin/docker-snipeit-entrypoint" ]
|
||||
CMD [ "/usr/local/bin/docker-php-entrypoint", "php-fpm" ]
|
||||
ENTRYPOINT [ "/startup.sh" ]
|
||||
CMD [ "/startup.sh", "php-fpm" ]
|
||||
|
|
|
@ -72,12 +72,13 @@ Since the release of the JSON REST API, several third-party developers have been
|
|||
- [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira)
|
||||
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
|
||||
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
|
||||
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it.
|
||||
- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT
|
||||
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-IT.
|
||||
- [MosyleSnipeSync](https://github.com/RodneyLeeBrands/MosyleSnipeSync) by [@Karpadiem](https://github.com/Karpadiem) - Python script to synchronize information between Mosyle and Snipe-IT.
|
||||
- [WWW::SnipeIT](https://github.com/SEDC/perl-www-snipeit) by [@SEDC](https://github.com/SEDC) - perl module for accessing the API
|
||||
- [UniFi to Snipe-IT](https://github.com/RodneyLeeBrands/UnifiSnipeSync) by [@karpadiem](https://github.com/karpadiem) - Python script that synchronizes UniFi devices with Snipe-IT.
|
||||
- [Kandji2Snipe](https://github.com/grokability/kandji2snipe) by [@briangoldstein](https://github.com/briangoldstein) - Python script that synchronizes Kandji with Snipe-IT.
|
||||
- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by @ReticentRobot - Windows agent for Snipe-IT
|
||||
- [SnipeAgent](https://github.com/ReticentRobot/SnipeAgent) by [@ReticentRobot](https://github.com/ReticentRobot) - Windows agent for Snipe-IT.
|
||||
- [Gate Pass Generator](https://github.com/cha7uraAE/snipe-it-gate-pass-system) by [@cha7uraAE](https://github.com/cha7uraAE) - A Streamlit application for generating gate passes based on hardware data from a Snipe-IT API.
|
||||
|
||||
-----
|
||||
|
||||
|
|
60
app/Console/Commands/RemoveExplicitEols.php
Normal file
60
app/Console/Commands/RemoveExplicitEols.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RemoveExplicitEols extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:remove-explicit-eols {--model_name= : The name of the asset model to update (use "all" to update all models)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Removes explicit EOLs on assets with selected model so they may inherit the asset model EOL';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$startTime = microtime(true);
|
||||
|
||||
if ($this->option('model_name') == 'all') {
|
||||
$assets = Asset::all();
|
||||
$this->updateAssets($assets);
|
||||
} else {
|
||||
$assetModel = AssetModel::where('name', '=', $this->option('model_name'))->first();
|
||||
|
||||
if ($assetModel) {
|
||||
$assets = Asset::where('model_id', '=', $assetModel->id)->get();
|
||||
$this->updateAssets($assets);
|
||||
} else {
|
||||
$this->error('Asset model not found');
|
||||
}
|
||||
}
|
||||
$endTime = microtime(true);
|
||||
$executionTime = ($endTime - $startTime);
|
||||
$this->info('Command executed in ' . round($executionTime, 2) . ' seconds.');
|
||||
}
|
||||
|
||||
private function updateAssets($assets)
|
||||
{
|
||||
foreach ($assets as $asset) {
|
||||
$asset->eol_explicit = 0;
|
||||
$asset->asset_eol_date = null;
|
||||
$asset->save();
|
||||
}
|
||||
|
||||
$this->info($assets->count() . ' Assets updated successfully');
|
||||
}
|
||||
}
|
|
@ -47,9 +47,10 @@ class SendAcceptanceReminder extends Command
|
|||
{
|
||||
$pending = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')
|
||||
->whereHas('checkoutable', function($query) {
|
||||
$query->where('archived', 0);
|
||||
$query->where('accepted_at', null)
|
||||
->where('declined_at', null);
|
||||
})
|
||||
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.adminuser'])
|
||||
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.admin'])
|
||||
->get();
|
||||
|
||||
$count = 0;
|
||||
|
|
190
app/Helpers/IconHelper.php
Normal file
190
app/Helpers/IconHelper.php
Normal file
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class IconHelper
|
||||
{
|
||||
|
||||
public static function icon($type) {
|
||||
switch ($type) {
|
||||
case 'checkout':
|
||||
return 'fa-solid fa-rotate-left';
|
||||
case 'checkin':
|
||||
return 'fa-solid fa-rotate-right';
|
||||
case 'edit':
|
||||
return 'fas fa-pencil-alt';
|
||||
case 'clone':
|
||||
return 'far fa-clone';
|
||||
case 'delete':
|
||||
return 'fas fa-trash';
|
||||
case 'create':
|
||||
return 'fa-solid fa-plus';
|
||||
case 'audit':
|
||||
return 'fa-solid fa-clipboard-check';
|
||||
case '2fa reset':
|
||||
return 'fa-solid fa-mobile-screen';
|
||||
case 'new-user':
|
||||
return 'fa-solid fa-user-plus';
|
||||
case 'merged-user':
|
||||
return 'fa-solid fa-people-arrows';
|
||||
case 'delete-user':
|
||||
return 'fa-solid fa-user-minus';
|
||||
case 'update-user':
|
||||
return 'fa-solid fa-user-pen';
|
||||
case 'user':
|
||||
return 'fa-solid fa-user';
|
||||
case 'users':
|
||||
return 'fas fa-users';
|
||||
case 'restore':
|
||||
return 'fa-solid fa-trash-arrow-up';
|
||||
case 'external-link':
|
||||
return 'fa fa-external-link';
|
||||
case 'email':
|
||||
return 'fa-regular fa-envelope';
|
||||
case 'phone':
|
||||
return 'fa-solid fa-phone';
|
||||
case 'long-arrow-right':
|
||||
return 'fas fa-long-arrow-alt-right';
|
||||
case 'download':
|
||||
return 'fas fa-download';
|
||||
case 'checkmark':
|
||||
return 'fas fa-check icon-white';
|
||||
case 'x':
|
||||
return 'fas fa-times';
|
||||
case 'logout':
|
||||
return 'fa fa-sign-out';
|
||||
case 'admin-settings':
|
||||
return 'fas fa-cogs';
|
||||
case 'settings':
|
||||
return 'fas fa-cog';
|
||||
case 'angle-left':
|
||||
return 'fas fa-angle-left';
|
||||
case 'warning':
|
||||
return 'fas fa-exclamation-triangle';
|
||||
case 'kits':
|
||||
return 'fas fa-object-group';
|
||||
case 'assets':
|
||||
case 'asset':
|
||||
return 'fas fa-barcode';
|
||||
case 'accessories':
|
||||
case 'accessory':
|
||||
return 'far fa-keyboard';
|
||||
case 'components':
|
||||
case 'component':
|
||||
return 'far fa-hdd';
|
||||
case 'consumables':
|
||||
case 'consumable':
|
||||
return 'fas fa-tint';
|
||||
case 'licenses':
|
||||
case 'license':
|
||||
return 'far fa-save';
|
||||
case 'requestable':
|
||||
return 'fas fa-laptop';
|
||||
case 'reports':
|
||||
return 'fas fa-chart-bar';
|
||||
case 'heart':
|
||||
return 'fas fa-heart';
|
||||
case 'circle':
|
||||
return 'fa-regular fa-circle';
|
||||
case 'circle-solid':
|
||||
return 'fa-solid fa-circle';
|
||||
case 'due':
|
||||
return 'fas fa-history';
|
||||
case 'import':
|
||||
return 'fas fa-cloud-upload-alt';
|
||||
case 'search':
|
||||
return 'fas fa-search';
|
||||
case 'alerts':
|
||||
return 'far fa-flag';
|
||||
case 'password':
|
||||
return 'fa-solid fa-key';
|
||||
case 'api-key':
|
||||
return 'fa-solid fa-user-secret';
|
||||
case 'nav-toggle':
|
||||
return 'fas fa-bars';
|
||||
case 'dashboard':
|
||||
return 'fas fa-tachometer-alt';
|
||||
case 'info-circle':
|
||||
return 'fas fa-info-circle';
|
||||
case 'caret-right':
|
||||
return 'fa fa-caret-right';
|
||||
case 'caret-up':
|
||||
return 'fa fa-caret-up';
|
||||
case 'caret-down':
|
||||
return 'fa fa-caret-down';
|
||||
case 'arrow-circle-right':
|
||||
return 'fa fa-arrow-circle-right';
|
||||
case 'minus':
|
||||
return 'fas fa-minus';
|
||||
case 'spinner':
|
||||
return 'fas fa-spinner fa-spin';
|
||||
case 'copy-clipboard':
|
||||
return 'fa-regular fa-clipboard';
|
||||
case 'paperclip':
|
||||
return 'fas fa-paperclip';
|
||||
case 'files':
|
||||
return 'fa-regular fa-file';
|
||||
case 'more-info':
|
||||
return 'far fa-life-ring';
|
||||
case 'calendar':
|
||||
return 'fas fa-calendar';
|
||||
case 'plus':
|
||||
return 'fas fa-plus';
|
||||
case 'history':
|
||||
return 'fas fa-history';
|
||||
case 'more-files':
|
||||
return 'fa-solid fa-laptop-file';
|
||||
case 'maintenances':
|
||||
return 'fas fa-wrench';
|
||||
case 'seats':
|
||||
return 'far fa-list-alt';
|
||||
case 'globe-us':
|
||||
return 'fas fa-globe-americas';
|
||||
case 'locked':
|
||||
return 'fas fa-lock';
|
||||
case 'unlocked':
|
||||
return 'fas fa-lock';
|
||||
case 'locations':
|
||||
return 'fas fa-map-marker-alt';
|
||||
case 'location':
|
||||
return 'fas fa-map-marker-alt';
|
||||
case 'superadmin':
|
||||
return 'fas fa-crown';
|
||||
case 'print':
|
||||
return 'fa-solid fa-print';
|
||||
case 'checkin-and-delete':
|
||||
return 'fa-solid fa-user-xmark';
|
||||
case 'branding':
|
||||
return 'fas fa-copyright';
|
||||
case 'general-settings':
|
||||
return 'fa-solid fa-list-check';
|
||||
case 'groups':
|
||||
return 'fa-solid fa-user-group';
|
||||
case 'bell':
|
||||
return 'fa-solid fa-bell';
|
||||
case 'hashtag':
|
||||
return 'fa-solid fa-hashtag';
|
||||
case 'asset-tags':
|
||||
return 'fas fa-list-ol';
|
||||
case 'labels':
|
||||
return 'fas fa-tags';
|
||||
case 'ldap':
|
||||
return 'fas fa-sitemap';
|
||||
case 'google':
|
||||
return 'fa-brands fa-google';
|
||||
case 'saml':
|
||||
return 'fas fa-sign-in-alt';
|
||||
case 'backups':
|
||||
return 'fas fa-file-archive';
|
||||
case 'logins':
|
||||
return 'fas fa-crosshairs';
|
||||
case 'oauth':
|
||||
return 'fas fa-user-secret';
|
||||
case 'employee_num' :
|
||||
return 'fa-regular fa-id-card';
|
||||
case 'department' :
|
||||
return 'fa-solid fa-building-user';
|
||||
|
||||
}
|
||||
}
|
||||
}
|
200
app/Http/Controllers/Api/AssetModelFilesController.php
Normal file
200
app/Http/Controllers/Api/AssetModelFilesController.php
Normal file
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetModelFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetModelId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assetmodels')) {
|
||||
Storage::makeDirectory('private_uploads/assetmodels', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
|
||||
|
||||
$assetModel->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function list($assetModelId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('view', $assetModel);
|
||||
|
||||
// Check that there are some uploads on this asset that can be listed
|
||||
if ($assetModel->uploads->count() > 0) {
|
||||
$files = array();
|
||||
foreach ($assetModel->uploads as $upload) {
|
||||
array_push($files, $upload);
|
||||
}
|
||||
// Give the list of files back to the user
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// There are no files.
|
||||
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('view', $assetModel);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assetmodels/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$rel_path = 'private_uploads/assetmodels';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
|
@ -78,6 +78,10 @@ class AssetModelsController extends Controller
|
|||
$assetmodels = $assetmodels->where('models.category_id', '=', $request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation_id')) {
|
||||
$assetmodels = $assetmodels->where('models.depreciation_id', '=', $request->input('depreciation_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$assetmodels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
|
|
@ -602,7 +602,7 @@ class AssetsController extends Controller
|
|||
if ($field->field_encrypted == '1') {
|
||||
Log::debug('This model field is encrypted in this fieldset.');
|
||||
|
||||
if (Gate::allows('admin')) {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
|
||||
// If input value is null, use custom field's default value
|
||||
if (($field_val == null) && ($request->has('model_id') != '')) {
|
||||
|
@ -695,7 +695,7 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
$field_val = Crypt::encrypt($field_val);
|
||||
} else {
|
||||
$problems_updating_encrypted_custom_fields = true;
|
||||
|
@ -928,7 +928,7 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
if ($request->has('status_id')) {
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->input('status_id');
|
||||
}
|
||||
|
||||
|
@ -978,7 +978,7 @@ class AssetsController extends Controller
|
|||
public function checkinByTag(Request $request, $tag = null) : JsonResponse
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
if(null == $tag && null !== ($request->input('asset_tag'))) {
|
||||
if (null == $tag && null !== ($request->input('asset_tag'))) {
|
||||
$tag = $request->input('asset_tag');
|
||||
}
|
||||
$asset = Asset::where('asset_tag', $tag)->first();
|
||||
|
|
|
@ -86,6 +86,9 @@ class ConsumablesController extends Controller
|
|||
case 'company':
|
||||
$consumables = $consumables->OrderCompany($order);
|
||||
break;
|
||||
case 'remaining':
|
||||
$consumables = $consumables->OrderRemaining($order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$consumables = $consumables->OrderSupplier($order);
|
||||
break;
|
||||
|
|
|
@ -20,9 +20,22 @@ class DepreciationsController extends Controller
|
|||
public function index(Request $request) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$allowed_columns = ['id','name','months','depreciation_min', 'depreciation_type','created_at'];
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'months',
|
||||
'depreciation_min',
|
||||
'depreciation_type',
|
||||
'created_at',
|
||||
'assets_count',
|
||||
'models_count',
|
||||
'licenses_count',
|
||||
];
|
||||
|
||||
$depreciations = Depreciation::select('id','name','months','depreciation_min','depreciation_type','user_id','created_at','updated_at');
|
||||
$depreciations = Depreciation::select('id','name','months','depreciation_min','depreciation_type','user_id','created_at','updated_at')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('models as models_count')
|
||||
->withCount('licenses as licenses_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
|
|
|
@ -27,7 +27,7 @@ class LicensesController extends Controller
|
|||
$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'));
|
||||
$licenses->where('licenses.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
|
|
@ -246,7 +246,7 @@ class PredefinedKitsController extends Controller
|
|||
|
||||
$relation = $kit->models();
|
||||
if ($relation->find($model_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, ['model' => 'Model already attached to kit']));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, ['model' => trans('admin/kits/general.model_already_attached')]));
|
||||
}
|
||||
$relation->attach($model_id, ['quantity' => $quantity]);
|
||||
|
||||
|
|
|
@ -427,13 +427,10 @@ class UsersController extends Controller
|
|||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
*/
|
||||
public function update(SaveUserRequest $request, $id) : JsonResponse
|
||||
public function update(SaveUserRequest $request, User $user): JsonResponse
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
if ($user = User::find($id)) {
|
||||
|
||||
|
||||
$this->authorize('update', $user);
|
||||
|
||||
/**
|
||||
|
@ -443,12 +440,10 @@ class UsersController extends Controller
|
|||
*
|
||||
*/
|
||||
|
||||
|
||||
if ((($id == 1) || ($id == 2)) && (config('app.lock_passwords'))) {
|
||||
if ((($user->id == 1) || ($user->id == 2)) && (config('app.lock_passwords'))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Permission denied. You cannot update user information via API on the demo.'));
|
||||
}
|
||||
|
||||
|
||||
$user->fill($request->all());
|
||||
|
||||
if ($user->id == $request->input('manager_id')) {
|
||||
|
@ -473,16 +468,13 @@ class UsersController extends Controller
|
|||
$user->permissions = $permissions_array;
|
||||
}
|
||||
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
|
||||
|
||||
|
||||
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
|
||||
|
||||
if ($user->save()) {
|
||||
|
||||
// Check if the request has groups passed and has a value, AND that the user us a superuser
|
||||
if (($request->has('groups')) && (auth()->user()->isSuperUser())) {
|
||||
|
||||
|
@ -496,20 +488,12 @@ class UsersController extends Controller
|
|||
|
||||
// Sync the groups since the user is a superuser and the groups pass validation
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found', compact('id'))));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
|
|
|
@ -202,6 +202,7 @@ class AssetModelsController extends Controller
|
|||
if ($model->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('models/'.$model->image);
|
||||
$model->update(['image' => null]);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
|
@ -233,7 +234,7 @@ class AssetModelsController extends Controller
|
|||
|
||||
if ($model->restore()) {
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = User::class;
|
||||
$logaction->item_type = AssetModel::class;
|
||||
$logaction->item_id = $model->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = auth()->id();
|
||||
|
|
|
@ -165,7 +165,7 @@ class AssetsController extends Controller
|
|||
if (($model) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
|
@ -388,7 +388,7 @@ class AssetsController extends Controller
|
|||
foreach ($model->fieldset->fields as $field) {
|
||||
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
|
@ -844,7 +844,7 @@ class AssetsController extends Controller
|
|||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
|
||||
return view('hardware/quickscan-checkin');
|
||||
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
|
||||
}
|
||||
|
||||
public function audit($id)
|
||||
|
|
|
@ -227,7 +227,8 @@ class BulkAssetsController extends Controller
|
|||
* its checkout status.
|
||||
*/
|
||||
|
||||
if (($request->filled('purchase_date'))
|
||||
if (($request->filled('name'))
|
||||
|| ($request->filled('purchase_date'))
|
||||
|| ($request->filled('expected_checkin'))
|
||||
|| ($request->filled('purchase_cost'))
|
||||
|| ($request->filled('supplier_id'))
|
||||
|
@ -239,6 +240,7 @@ class BulkAssetsController extends Controller
|
|||
|| ($request->filled('status_id'))
|
||||
|| ($request->filled('model_id'))
|
||||
|| ($request->filled('next_audit_date'))
|
||||
|| ($request->filled('null_name'))
|
||||
|| ($request->filled('null_purchase_date'))
|
||||
|| ($request->filled('null_expected_checkin_date'))
|
||||
|| ($request->filled('null_next_audit_date'))
|
||||
|
@ -251,13 +253,14 @@ class BulkAssetsController extends Controller
|
|||
$this->update_array = [];
|
||||
|
||||
/**
|
||||
* Leave out model_id and status here because we do math on that later. We have to do some extra
|
||||
* validation and checks on those two.
|
||||
* Leave out model_id and status here because we do math on that later. We have to do some
|
||||
* extra validation and checks on those two.
|
||||
*
|
||||
* It's tempting to make these match the request check above, but some of these values require
|
||||
* extra work to make sure the data makes sense.
|
||||
*/
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
$this->conditionallyAddItem('name')
|
||||
->conditionallyAddItem('purchase_date')
|
||||
->conditionallyAddItem('expected_checkin')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
|
@ -271,6 +274,11 @@ class BulkAssetsController extends Controller
|
|||
/**
|
||||
* Blank out fields that were requested to be blanked out via checkbox
|
||||
*/
|
||||
if ($request->input('null_name')=='1') {
|
||||
|
||||
$this->update_array['name'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_purchase_date')=='1') {
|
||||
$this->update_array['purchase_date'] = null;
|
||||
}
|
||||
|
|
|
@ -508,8 +508,8 @@ class LoginController extends Controller
|
|||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
'username' => 'required|not_array',
|
||||
'password' => 'required|not_array',
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -193,13 +193,20 @@ class DepreciationsController extends Controller
|
|||
*/
|
||||
public function show($id) : View | RedirectResponse
|
||||
{
|
||||
if (is_null($depreciation = Depreciation::find($id))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
$depreciation = Depreciation::withCount('assets as assets_count')
|
||||
->withCount('models as models_count')
|
||||
->withCount('licenses as licenses_count')
|
||||
->find($id);
|
||||
|
||||
$this->authorize('view', $depreciation);
|
||||
|
||||
if ($depreciation) {
|
||||
return view('depreciations/view', compact('depreciation'));
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,10 +62,10 @@ class CheckoutKitController extends Controller
|
|||
|
||||
$checkout_result = $this->kitService->checkout($request, $kit, $user);
|
||||
if (Arr::has($checkout_result, 'errors') && count($checkout_result['errors']) > 0) {
|
||||
return redirect()->back()->with('error', trans('general.checkout_error'))->with('error_messages', $checkout_result['errors']);
|
||||
return redirect()->back()->with('error', trans('admin/kits/general.checkout_error'))->with('error_messages', $checkout_result['errors']);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('general.checkout_success'))
|
||||
return redirect()->back()->with('success', trans('admin/kits/general.checkout_success'))
|
||||
->with('assets', Arr::get($checkout_result, 'assets', null))
|
||||
->with('accessories', Arr::get($checkout_result, 'accessories', null))
|
||||
->with('consumables', Arr::get($checkout_result, 'consumables', null));
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
|
@ -193,7 +194,13 @@ class LocationsController extends Controller
|
|||
*/
|
||||
public function show($id = null) : View | RedirectResponse
|
||||
{
|
||||
$location = Location::find($id);
|
||||
$location = Location::withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->withTrashed()
|
||||
->find($id);
|
||||
|
||||
if (isset($location->id)) {
|
||||
return view('locations/view', compact('location'));
|
||||
|
@ -249,6 +256,41 @@ class LocationsController extends Controller
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restore a given Asset Model (mark as un-deleted)
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $id
|
||||
*/
|
||||
public function postRestore($id) : RedirectResponse
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
|
||||
if ($location = Location::withTrashed()->find($id)) {
|
||||
|
||||
if ($location->deleted_at == '') {
|
||||
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.location')]));
|
||||
}
|
||||
|
||||
if ($location->restore()) {
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = Location::class;
|
||||
$logaction->item_id = $location->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = auth()->id();
|
||||
$logaction->logaction('restore');
|
||||
|
||||
return redirect()->route('locations.index')->with('success', trans('admin/locations/message.restore.success'));
|
||||
}
|
||||
|
||||
// Check validation
|
||||
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.location'), 'error' => $location->getErrors()->first()]));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
|
||||
|
||||
}
|
||||
public function print_all_assigned($id) : View | RedirectResponse
|
||||
{
|
||||
if ($location = Location::where('id', $id)->first()) {
|
||||
|
|
|
@ -50,6 +50,7 @@ class ProfileController extends Controller
|
|||
$user->skin = $request->input('skin');
|
||||
$user->phone = $request->input('phone');
|
||||
$user->enable_sounds = $request->input('enable_sounds', false);
|
||||
$user->enable_confetti = $request->input('enable_confetti', false);
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en-US');
|
||||
|
|
|
@ -637,6 +637,7 @@ class SettingsController extends Controller
|
|||
$setting->alert_threshold = $request->input('alert_threshold');
|
||||
$setting->audit_interval = $request->input('audit_interval');
|
||||
$setting->audit_warning_days = $request->input('audit_warning_days');
|
||||
$setting->due_checkin_days = $request->input('due_checkin_days');
|
||||
$setting->show_alerts_in_menu = $request->input('show_alerts_in_menu', '0');
|
||||
|
||||
if ($setting->save()) {
|
||||
|
@ -1203,7 +1204,7 @@ class SettingsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v6.0]
|
||||
*/
|
||||
public function postRestore($filename = null) : RedirectResponse
|
||||
public function postRestore(Request $request, $filename = null): RedirectResponse
|
||||
{
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
|
@ -1223,13 +1224,29 @@ class SettingsController extends Controller
|
|||
|
||||
Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);
|
||||
|
||||
// run the restore command
|
||||
Artisan::call('snipeit:restore',
|
||||
[
|
||||
$restore_params = [
|
||||
'--force' => true,
|
||||
'--no-progress' => true,
|
||||
'filename' => storage_path($path).'/'.$filename
|
||||
'filename' => storage_path($path) . '/' . $filename
|
||||
];
|
||||
|
||||
if ($request->input('clean')) {
|
||||
Log::debug("Attempting 'clean' - first, guessing prefix...");
|
||||
Artisan::call('snipeit:restore', [
|
||||
'--sanitize-guess-prefix' => true,
|
||||
'filename' => storage_path($path) . '/' . $filename
|
||||
]);
|
||||
$guess_prefix_output = Artisan::output();
|
||||
Log::debug("Sanitize output is: $guess_prefix_output");
|
||||
list($prefix, $_output) = explode("\n", $guess_prefix_output);
|
||||
Log::debug("prefix is: '$prefix'");
|
||||
$restore_params['--sanitize-with-prefix'] = $prefix;
|
||||
}
|
||||
|
||||
// run the restore command
|
||||
Artisan::call('snipeit:restore',
|
||||
$restore_params
|
||||
);
|
||||
|
||||
// If it's greater than 300, it probably worked
|
||||
$output = Artisan::output();
|
||||
|
|
|
@ -30,7 +30,7 @@ class BulkUsersController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.7]
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @return \Illuminate\Contracts\View\View | \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function edit(Request $request)
|
||||
|
@ -116,6 +116,9 @@ class BulkUsersController extends Controller
|
|||
->conditionallyAddItem('remote')
|
||||
->conditionallyAddItem('ldap_import')
|
||||
->conditionallyAddItem('activated')
|
||||
->conditionallyAddItem('start_date')
|
||||
->conditionallyAddItem('end_date')
|
||||
->conditionallyAddItem('city')
|
||||
->conditionallyAddItem('autoassign_licenses');
|
||||
|
||||
|
||||
|
@ -146,6 +149,13 @@ class BulkUsersController extends Controller
|
|||
$this->update_array['company_id'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_start_date')=='1') {
|
||||
$this->update_array['start_date'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_end_date')=='1') {
|
||||
$this->update_array['end_date'] = null;
|
||||
}
|
||||
|
||||
if (! $manager_conflict) {
|
||||
$this->conditionallyAddItem('manager_id');
|
||||
|
|
|
@ -186,7 +186,7 @@ class UsersController extends Controller
|
|||
{
|
||||
|
||||
$this->authorize('update', User::class);
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($id);
|
||||
$user = User::with(['assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc'])->withTrashed()->find($id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
|
@ -214,28 +214,24 @@ class UsersController extends Controller
|
|||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update(SaveUserRequest $request, $id = null)
|
||||
public function update(SaveUserRequest $request, User $user)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
// This is a janky hack to prevent people from changing admin demo user data on the public demo.
|
||||
// The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
|
||||
// Thanks, jerks. You are why we can't have nice things. - snipe
|
||||
|
||||
if ((($id == 1) || ($id == 2)) && (config('app.lock_passwords'))) {
|
||||
if ((($user->id == 1) || ($user->id == 2)) && (config('app.lock_passwords'))) {
|
||||
return redirect()->route('users.index')->with('error', trans('general.permission_denied_superuser_demo'));
|
||||
}
|
||||
|
||||
|
||||
// We need to reverse the UI specific logic for our
|
||||
// permissions here before we update the user.
|
||||
$permissions = $request->input('permissions', []);
|
||||
app('request')->request->set('permissions', $permissions);
|
||||
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($id);
|
||||
$user->load(['assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc'])->withTrashed();
|
||||
|
||||
// User is valid - continue...
|
||||
if ($user) {
|
||||
$this->authorize('update', $user);
|
||||
|
||||
// Figure out of this user was an admin before this edit
|
||||
|
@ -318,13 +314,7 @@ class UsersController extends Controller
|
|||
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
|
||||
->with('success', trans('admin/users/message.success.update'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -601,19 +591,15 @@ class UsersController extends Controller
|
|||
/**
|
||||
* Print inventory
|
||||
*
|
||||
* @author Aladin Alaily
|
||||
* @since [v1.8]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @author Aladin Alaily
|
||||
*/
|
||||
public function printInventory($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::where('id', $id)->withTrashed()->first();
|
||||
if ($user = User::where('id', $id)->withTrashed()->first()) {
|
||||
|
||||
|
||||
// Make sure they can view this particular user
|
||||
$this->authorize('view', $user);
|
||||
|
||||
$assets = Asset::where('assigned_to', $id)->where('assigned_type', User::class)->with('model', 'model.category')->get();
|
||||
$accessories = $user->accessories()->get();
|
||||
$consumables = $user->consumables()->get();
|
||||
|
@ -626,6 +612,10 @@ class UsersController extends Controller
|
|||
->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Emails user a list of assigned assets
|
||||
*
|
||||
|
|
|
@ -14,6 +14,7 @@ class Kernel extends HttpKernel
|
|||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\App\Http\Middleware\NoSessionStore::class,
|
||||
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
|
@ -21,6 +22,7 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\CheckForSetup::class,
|
||||
\App\Http\Middleware\CheckForDebug::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\App\Http\Middleware\SecurityHeaders::class,
|
||||
\App\Http\Middleware\PreventBackHistory::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
|
@ -48,6 +50,7 @@ class Kernel extends HttpKernel
|
|||
|
||||
'api' => [
|
||||
'auth:api',
|
||||
\App\Http\Middleware\CheckLocale::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Models\Setting;
|
|||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use App\Rules\UserCannotSwitchCompaniesIfItemsAssigned;
|
||||
|
||||
class SaveUserRequest extends FormRequest
|
||||
{
|
||||
|
@ -34,6 +35,7 @@ class SaveUserRequest extends FormRequest
|
|||
$rules = [
|
||||
'department_id' => 'nullable|exists:departments,id',
|
||||
'manager_id' => 'nullable|exists:users,id',
|
||||
'company_id' => ['nullable','exists:companies,id']
|
||||
];
|
||||
|
||||
switch ($this->method()) {
|
||||
|
@ -52,11 +54,13 @@ class SaveUserRequest extends FormRequest
|
|||
$rules['first_name'] = 'required|string|min:1';
|
||||
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('update').'|confirmed';
|
||||
$rules['company_id'] = [new UserCannotSwitchCompaniesIfItemsAssigned()];
|
||||
break;
|
||||
|
||||
// Save only what's passed
|
||||
case 'PATCH':
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('update');
|
||||
$rules['company_id'] = [new UserCannotSwitchCompaniesIfItemsAssigned()];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Requests;
|
|||
|
||||
use App\Http\Requests\Traits\MayContainCustomFields;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
|
@ -41,6 +42,12 @@ class UpdateAssetRequest extends ImageUploadRequest
|
|||
],
|
||||
);
|
||||
|
||||
// if the purchase cost is passed in as a string **and** the digit_separator is ',' (as is common in the EU)
|
||||
// then we tweak the purchase_cost rule to make it a string
|
||||
if (Setting::getSettings()->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) {
|
||||
$rules['purchase_cost'] = ['nullable', 'string'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,15 +11,17 @@ trait TwoColumnUniqueUndeletedTrait
|
|||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareTwoColumnUniqueUndeletedRule($parameters, $field)
|
||||
protected function prepareTwoColumnUniqueUndeletedRule($parameters)
|
||||
{
|
||||
$column = $parameters[0];
|
||||
$value = $this->{$parameters[0]};
|
||||
|
||||
// This is an existing model we're updating so ignore the current ID ($this->getKey())
|
||||
if ($this->exists) {
|
||||
return 'two_column_unique_undeleted:'.$this->table.','.$this->getKey().','.$column.','.$value;
|
||||
}
|
||||
|
||||
// This is a new record, so we can ignore the current ID
|
||||
return 'two_column_unique_undeleted:'.$this->table.',0,'.$column.','.$value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class AssetsTransformer
|
|||
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date, 'date'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($asset->deleted_at, 'datetime'),
|
||||
'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'),
|
||||
'age' => $asset->purchase_date ? $asset->purchase_date->diffForHumans() : '',
|
||||
'age' => $asset->purchase_date ? $asset->purchase_date->locale(app()->getLocale())->diffForHumans() : '',
|
||||
'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'),
|
||||
'last_checkin' => Helper::getFormattedDateObject($asset->last_checkin, 'datetime'),
|
||||
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
|
||||
|
|
|
@ -28,6 +28,9 @@ class DepreciationsTransformer
|
|||
'name' => e($depreciation->name),
|
||||
'months' => $depreciation->months.' '.trans('general.months'),
|
||||
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
|
||||
'assets_count' => $depreciation->assets_count,
|
||||
'models_count' => $depreciation->models_count,
|
||||
'licenses_count' => $depreciation->licenses_count,
|
||||
'created_at' => Helper::getFormattedDateObject($depreciation->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($depreciation->updated_at, 'datetime')
|
||||
];
|
||||
|
|
|
@ -71,8 +71,10 @@ class AssetImporter extends ItemImporter
|
|||
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
|
||||
if ($asset) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Asset '.$asset_tag.' already exists');
|
||||
return;
|
||||
$exists_error = trans('general.import_asset_tag_exists', ['asset_tag' => $asset_tag]);
|
||||
$this->log($exists_error);
|
||||
$this->addErrorToBag($asset, 'asset_tag', $exists_error);
|
||||
return $exists_error;
|
||||
}
|
||||
|
||||
$this->log('Updating Asset');
|
||||
|
|
|
@ -281,6 +281,13 @@ abstract class Importer
|
|||
}
|
||||
}
|
||||
|
||||
protected function addErrorToBag($item, $field, $error_message)
|
||||
{
|
||||
if ($this->errorCallback) {
|
||||
call_user_func($this->errorCallback, $item, $field, [$field => [$error_message]]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the user matching given data, or creates a new one if there is no match.
|
||||
* This is NOT used by the User Import, only for Asset/Accessory/etc where
|
||||
|
|
|
@ -196,39 +196,46 @@ class ItemImporter extends Importer
|
|||
{
|
||||
$condition = array();
|
||||
$asset_model_name = $this->findCsvMatch($row, 'asset_model');
|
||||
$asset_model_category = $this->findCsvMatch($row, 'category');
|
||||
$asset_modelNumber = $this->findCsvMatch($row, 'model_number');
|
||||
|
||||
// TODO: At the moment, this means we can't update the model number if the model name stays the same.
|
||||
if (! $this->shouldUpdateField($asset_model_name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((empty($asset_model_name)) && (! empty($asset_modelNumber))) {
|
||||
$asset_model_name = $asset_modelNumber;
|
||||
} elseif ((empty($asset_model_name)) && (empty($asset_modelNumber))) {
|
||||
$asset_model_name = 'Unknown';
|
||||
}
|
||||
|
||||
if ((!empty($asset_model_name)) && (empty($asset_modelNumber))) {
|
||||
$condition[] = ['name', '=', $asset_model_name];
|
||||
} elseif ((!empty($asset_model_name)) && (!empty($asset_modelNumber))) {
|
||||
$condition[] = ['name', '=', $asset_model_name];
|
||||
$condition[] = ['model_number', '=', $asset_modelNumber];
|
||||
$asset_model = AssetModel::select('id');
|
||||
|
||||
if (!empty($asset_model_name)) {
|
||||
$asset_model = $asset_model->where('name', '=', $asset_model_name);
|
||||
|
||||
if (!empty($asset_modelNumber)) {
|
||||
$asset_model = $asset_model->where('model_number', '=', $asset_modelNumber);
|
||||
}
|
||||
}
|
||||
|
||||
$editingModel = $this->updating;
|
||||
$asset_model = AssetModel::where($condition)->first();
|
||||
$asset_model = $asset_model->first();
|
||||
|
||||
if ($asset_model) {
|
||||
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching model already exists, returning it.');
|
||||
|
||||
return $asset_model->id;
|
||||
}
|
||||
|
||||
$this->log('Matching Model found, updating it.');
|
||||
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
|
||||
$item['name'] = $asset_model_name;
|
||||
$item['notes'] = $this->findCsvMatch($row, 'model_notes');
|
||||
|
||||
if(!empty($asset_modelNumber)){
|
||||
if (!empty($asset_modelNumber)){
|
||||
$item['model_number'] = $asset_modelNumber;
|
||||
}
|
||||
|
||||
|
@ -237,23 +244,29 @@ class ItemImporter extends Importer
|
|||
$this->log('Asset Model Updated');
|
||||
|
||||
return $asset_model->id;
|
||||
}
|
||||
$this->log('No Matching Model, Creating a new one');
|
||||
|
||||
}
|
||||
|
||||
$this->log('No Matching Model, Creating a new one');
|
||||
$asset_model = new AssetModel();
|
||||
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
|
||||
$item['name'] = $asset_model_name;
|
||||
$item['model_number'] = $asset_modelNumber;
|
||||
$item['notes'] = $this->findCsvMatch($row, 'model_notes');
|
||||
$item['category_id'] = $this->createOrFetchCategory($asset_model_category);
|
||||
|
||||
$asset_model->fill($item);
|
||||
//$asset_model = AssetModel::firstOrNew($item);
|
||||
$item = null;
|
||||
|
||||
|
||||
|
||||
if ($asset_model->save()) {
|
||||
$this->log('Asset Model '.$asset_model_name.' with model number '.$asset_modelNumber.' was created');
|
||||
|
||||
return $asset_model->id;
|
||||
}
|
||||
$this->log('Asset Model Errors: '.$asset_model->getErrors());
|
||||
$this->logError($asset_model, 'Asset Model "'.$asset_model_name.'"');
|
||||
|
||||
return null;
|
||||
|
|
|
@ -3,30 +3,25 @@
|
|||
namespace App\Livewire;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use Livewire\Component;
|
||||
|
||||
use App\Models\Import;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
|
||||
class Importer extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public $files;
|
||||
|
||||
public $progress; //upload progress - '-1' means don't show
|
||||
public $progress = -1; //upload progress - '-1' means don't show
|
||||
public $progress_message;
|
||||
public $progress_bar_class;
|
||||
public $progress_bar_class = 'progress-bar-warning';
|
||||
|
||||
public $message; //status/error message?
|
||||
public $message_type; //success/error?
|
||||
|
||||
//originally from ImporterFile
|
||||
public $import_errors; //
|
||||
public ?Import $activeFile = null;
|
||||
public $activeFileId;
|
||||
public $headerRow = [];
|
||||
public $typeOfImport;
|
||||
public $importTypes;
|
||||
public $columnOptions;
|
||||
public $statusType;
|
||||
|
@ -35,7 +30,6 @@ class Importer extends Component
|
|||
public $send_welcome;
|
||||
public $run_backup;
|
||||
public $field_map; // we need a separate variable for the field-mapping, because the keys in the normal array are too complicated for Livewire to understand
|
||||
public $file_id; // TODO: I can't figure out *why* we need this, but it really seems like we do. I can't seem to pull the id from the activeFile for some reason?
|
||||
|
||||
// Make these variables public - we set the properties in the constructor so we can localize them (versus the old static arrays)
|
||||
public $accessories_fields;
|
||||
|
@ -51,10 +45,8 @@ class Importer extends Component
|
|||
'files.*.file_path' => 'required|string',
|
||||
'files.*.created_at' => 'required|string',
|
||||
'files.*.filesize' => 'required|integer',
|
||||
'activeFile' => 'Import',
|
||||
'activeFile.import_type' => 'string',
|
||||
'activeFile.field_map' => 'array',
|
||||
'activeFile.header_row' => 'array',
|
||||
'headerRow' => 'array',
|
||||
'typeOfImport' => 'string',
|
||||
'field_map' => 'array'
|
||||
];
|
||||
|
||||
|
@ -68,15 +60,13 @@ class Importer extends Component
|
|||
{
|
||||
$tmp = array();
|
||||
if ($this->activeFile) {
|
||||
$tmp = array_combine($this->activeFile->header_row, $this->field_map);
|
||||
$tmp = array_combine($this->headerRow, $this->field_map);
|
||||
$tmp = array_filter($tmp);
|
||||
}
|
||||
return json_encode($tmp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function getColumns($type)
|
||||
{
|
||||
switch ($type) {
|
||||
|
@ -115,17 +105,15 @@ class Importer extends Component
|
|||
return $results;
|
||||
}
|
||||
|
||||
public function updating($name, $new_import_type)
|
||||
public function updatingTypeOfImport($type)
|
||||
{
|
||||
if ($name == "activeFile.import_type") {
|
||||
|
||||
// go through each header, find a matching field to try and map it to.
|
||||
foreach ($this->activeFile->header_row as $i => $header) {
|
||||
foreach ($this->headerRow as $i => $header) {
|
||||
// do we have something mapped already?
|
||||
if (array_key_exists($i, $this->field_map)) {
|
||||
// yes, we do. Is it valid for this type of import?
|
||||
// (e.g. the import type might have been changed...?)
|
||||
if (array_key_exists($this->field_map[$i], $this->columnOptions[$new_import_type])) {
|
||||
if (array_key_exists($this->field_map[$i], $this->columnOptions[$type])) {
|
||||
//yes, this key *is* valid. Continue on to the next field.
|
||||
continue;
|
||||
} else {
|
||||
|
@ -135,9 +123,9 @@ class Importer extends Component
|
|||
} // TODO - strictly speaking, this isn't necessary here I don't think.
|
||||
}
|
||||
// first, check for exact matches
|
||||
foreach ($this->columnOptions[$new_import_type] as $value => $text) {
|
||||
foreach ($this->columnOptions[$type] as $v => $text) {
|
||||
if (strcasecmp($text, $header) === 0) { // case-INSENSITIVe on purpose!
|
||||
$this->field_map[$i] = $value;
|
||||
$this->field_map[$i] = $v;
|
||||
continue 2; //don't bother with the alias check, go to the next header
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +136,7 @@ class Importer extends Component
|
|||
// Make *absolutely* sure that this key actually _exists_ in this import type -
|
||||
// you can trigger this by importing accessories with a 'Warranty' column (which don't exist
|
||||
// in "Accessories"!)
|
||||
if (array_key_exists($key, $this->columnOptions[$new_import_type])) {
|
||||
if (array_key_exists($key, $this->columnOptions[$type])) {
|
||||
$this->field_map[$i] = $key;
|
||||
continue 3; // bust out of both of these loops; as well as the surrounding one - e.g. move on to the next header
|
||||
}
|
||||
|
@ -159,18 +147,10 @@ class Importer extends Component
|
|||
$this->field_map[$i] = null; // Booooo :(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function boot() { // FIXME - delete or undelete.
|
||||
///////$this->activeFile = null; // I do *not* understand why I have to do this, but, well, whatever.
|
||||
}
|
||||
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->authorize('import');
|
||||
$this->progress = -1; // '-1' means 'don't show the progressbar'
|
||||
$this->progress_bar_class = 'progress-bar-warning';
|
||||
$this->importTypes = [
|
||||
'asset' => trans('general.assets'),
|
||||
'accessory' => trans('general.accessories'),
|
||||
|
@ -374,6 +354,12 @@ class Importer extends Component
|
|||
'model name',
|
||||
'model',
|
||||
],
|
||||
'eol_date' =>
|
||||
[
|
||||
'eol',
|
||||
'eol date',
|
||||
'asset eol date',
|
||||
],
|
||||
'gravatar' =>
|
||||
[
|
||||
'gravatar',
|
||||
|
@ -504,19 +490,16 @@ class Importer extends Component
|
|||
];
|
||||
|
||||
$this->columnOptions[''] = $this->getColumns(''); //blank mode? I don't know what this is supposed to mean
|
||||
foreach($this->importTypes AS $type => $name) {
|
||||
foreach ($this->importTypes as $type => $name) {
|
||||
$this->columnOptions[$type] = $this->getColumns($type);
|
||||
}
|
||||
if ($this->activeFile) {
|
||||
$this->field_map = $this->activeFile->field_map ? array_values($this->activeFile->field_map) : [];
|
||||
}
|
||||
}
|
||||
|
||||
public function selectFile($id)
|
||||
{
|
||||
$this->clearMessage();
|
||||
|
||||
$this->activeFile = Import::find($id);
|
||||
$this->activeFileId = $id;
|
||||
|
||||
if (!$this->activeFile) {
|
||||
$this->message = trans('admin/hardware/message.import.file_missing');
|
||||
|
@ -525,15 +508,17 @@ class Importer extends Component
|
|||
return;
|
||||
}
|
||||
|
||||
$this->headerRow = $this->activeFile->header_row;
|
||||
$this->typeOfImport = $this->activeFile->import_type;
|
||||
|
||||
$this->field_map = null;
|
||||
foreach($this->activeFile->header_row as $element) {
|
||||
if(isset($this->activeFile->field_map[$element])) {
|
||||
foreach ($this->headerRow as $element) {
|
||||
if (isset($this->activeFile->field_map[$element])) {
|
||||
$this->field_map[] = $this->activeFile->field_map[$element];
|
||||
} else {
|
||||
$this->field_map[] = null; // re-inject the 'nulls' if a file was imported with some 'Do Not Import' settings
|
||||
}
|
||||
}
|
||||
$this->file_id = $id;
|
||||
$this->import_errors = null;
|
||||
$this->statusText = null;
|
||||
|
||||
|
@ -541,22 +526,34 @@ class Importer extends Component
|
|||
|
||||
public function destroy($id)
|
||||
{
|
||||
// TODO: why don't we just do File::find($id)? This seems dumb.
|
||||
foreach($this->files as $file) {
|
||||
if ($id == $file->id) {
|
||||
if (Storage::delete('private_uploads/imports/'.$file->file_path)) {
|
||||
$file->delete();
|
||||
$this->authorize('import');
|
||||
|
||||
$import = Import::find($id);
|
||||
|
||||
// Check that the import wasn't deleted after while page was already loaded...
|
||||
// @todo: next up...handle the file being missing for other interactions...
|
||||
// for example having an import open in two tabs, deleting it, and then changing
|
||||
// the import type in the other tab. The error message below wouldn't display in that case.
|
||||
if (!$import) {
|
||||
$this->message = trans('admin/hardware/message.import.file_already_deleted');
|
||||
$this->message_type = 'danger';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Storage::delete('private_uploads/imports/' . $import->file_path)) {
|
||||
$import->delete();
|
||||
$this->message = trans('admin/hardware/message.import.file_delete_success');
|
||||
$this->message_type = 'success';
|
||||
|
||||
unset($this->files);
|
||||
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
|
||||
$this->message = trans('admin/hardware/message.import.file_delete_error');
|
||||
$this->message_type = 'danger';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function clearMessage()
|
||||
{
|
||||
|
@ -564,9 +561,20 @@ class Importer extends Component
|
|||
$this->message_type = null;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function files()
|
||||
{
|
||||
return Import::orderBy('id', 'desc')->get();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function activeFile()
|
||||
{
|
||||
return Import::find($this->activeFileId);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->files = Import::orderBy('id','desc')->get(); //HACK - slows down renders.
|
||||
return view('livewire.importer')
|
||||
->extends('layouts.default')
|
||||
->section('content');
|
||||
|
|
|
@ -1315,7 +1315,7 @@ class Asset extends Depreciable
|
|||
|
||||
public function scopeDueForCheckin($query, $settings)
|
||||
{
|
||||
$interval = $settings->audit_warning_days ?? 0;
|
||||
$interval = $settings->due_checkin_days ?? 0;
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval)->format('Y-m-d');
|
||||
|
||||
|
@ -1561,7 +1561,7 @@ class Asset extends Depreciable
|
|||
$leftJoin->on('assets_dept_users.id', '=', 'assets.assigned_to')
|
||||
->where('assets.assigned_type', '=', User::class);
|
||||
})->where(function ($query) use ($search) {
|
||||
$query->where('assets_dept_users.department_id', '=', $search);
|
||||
$query->whereIn('assets_dept_users.department_id', $search);
|
||||
|
||||
})->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
|
||||
}
|
||||
|
@ -1811,7 +1811,9 @@ class Asset extends Depreciable
|
|||
public function scopeInCategory($query, $category_id)
|
||||
{
|
||||
return $query->join('models as category_models', 'assets.model_id', '=', 'category_models.id')
|
||||
->join('categories', 'category_models.category_id', '=', 'categories.id')->where('category_models.category_id', '=', $category_id);
|
||||
->join('categories', 'category_models.category_id', '=', 'categories.id')
|
||||
->whereIn('category_models.category_id', (!is_array($category_id) ? explode(',',$category_id): $category_id));
|
||||
//->whereIn('category_models.category_id', $category_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1825,7 +1827,7 @@ class Asset extends Depreciable
|
|||
public function scopeByManufacturer($query, $manufacturer_id)
|
||||
{
|
||||
return $query->join('models', 'assets.model_id', '=', 'models.id')
|
||||
->join('manufacturers', 'models.manufacturer_id', '=', 'manufacturers.id')->where('models.manufacturer_id', '=', $manufacturer_id);
|
||||
->join('manufacturers', 'models.manufacturer_id', '=', 'manufacturers.id')->whereIn('models.manufacturer_id', (!is_array($manufacturer_id) ? explode(',',$manufacturer_id): $manufacturer_id));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Gate;
|
|||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use \App\Presenters\AssetModelPresenter;
|
||||
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
|
||||
|
||||
/**
|
||||
* Model for Asset Models. Asset Models contain higher level
|
||||
|
@ -21,21 +22,8 @@ class AssetModel extends SnipeModel
|
|||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
protected $presenter = AssetModelPresenter::class;
|
||||
use Loggable, Requestable, Presentable;
|
||||
|
||||
protected $table = 'models';
|
||||
protected $hidden = ['user_id', 'deleted_at'];
|
||||
|
||||
// Declare the rules for the model validation
|
||||
protected $rules = [
|
||||
'name' => 'required|min:1|max:255',
|
||||
'model_number' => 'max:255|nullable',
|
||||
'min_amt' => 'integer|min:0|nullable',
|
||||
'category_id' => 'required|integer|exists:categories,id',
|
||||
'manufacturer_id' => 'integer|exists:manufacturers,id|nullable',
|
||||
'eol' => 'integer:min:0|max:240|nullable',
|
||||
];
|
||||
use TwoColumnUniqueUndeletedTrait;
|
||||
|
||||
/**
|
||||
* Whether the model should inject its identifier to the unique
|
||||
|
@ -44,8 +32,26 @@ class AssetModel extends SnipeModel
|
|||
*
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
protected $injectUniqueIdentifier = true;
|
||||
use ValidatingTrait;
|
||||
protected $table = 'models';
|
||||
protected $hidden = ['user_id', 'deleted_at'];
|
||||
protected $presenter = AssetModelPresenter::class;
|
||||
|
||||
// Declare the rules for the model validation
|
||||
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'string|required|min:1|max:255|two_column_unique_undeleted:model_number',
|
||||
'model_number' => 'string|max:255|nullable|two_column_unique_undeleted:name',
|
||||
'min_amt' => 'integer|min:0|nullable',
|
||||
'category_id' => 'required|integer|exists:categories,id',
|
||||
'manufacturer_id' => 'integer|exists:manufacturers,id|nullable',
|
||||
'eol' => 'integer:min:0|max:240|nullable',
|
||||
];
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
@ -73,7 +79,12 @@ class AssetModel extends SnipeModel
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'model_number', 'notes', 'eol'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'model_number',
|
||||
'notes',
|
||||
'eol'
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
@ -86,6 +97,9 @@ class AssetModel extends SnipeModel
|
|||
'manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the model -> assets relationship
|
||||
*
|
||||
|
|
|
@ -425,6 +425,20 @@ class Consumable extends SnipeModel
|
|||
return $query->leftJoin('companies', 'consumables.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on remaining
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param string $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderRemaining($query, $order)
|
||||
{
|
||||
$order_by = 'consumables.qty - consumables_users_count ' . $order;
|
||||
return $query->orderByRaw($order_by);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on supplier
|
||||
*
|
||||
|
|
|
@ -75,4 +75,17 @@ class Depreciation extends SnipeModel
|
|||
{
|
||||
return $this->hasMany(\App\Models\License::class, 'depreciation_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the depreciation -> assets relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v5.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function assets()
|
||||
{
|
||||
return $this->hasManyThrough(\App\Models\Asset::class, \App\Models\AssetModel::class, 'depreciation_id', 'model_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class Location extends SnipeModel
|
|||
'country' => 'min:2|max:191|nullable',
|
||||
'zip' => 'max:10|nullable',
|
||||
'manager_id' => 'exists:users,id|nullable',
|
||||
'parent_id' => 'non_circular:locations,id',
|
||||
'parent_id' => 'nullable|exists:locations,id|non_circular:locations,id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
@ -74,7 +74,10 @@ class Setting extends Model
|
|||
'login_remote_user_header_name' => 'string|nullable',
|
||||
'thumbnail_max_h' => 'numeric|max:500|min:25',
|
||||
'pwd_secure_min' => 'numeric|required|min:8',
|
||||
'alert_threshold' => 'numeric|nullable',
|
||||
'alert_interval' => 'numeric|nullable',
|
||||
'audit_warning_days' => 'numeric|nullable',
|
||||
'due_checkin_days' => 'numeric|nullable',
|
||||
'audit_interval' => 'numeric|nullable',
|
||||
'custom_forgot_pass_url' => 'url|nullable',
|
||||
'privacy_policy_link' => 'nullable|url',
|
||||
|
|
|
@ -21,6 +21,11 @@ class SnipeModel extends Model
|
|||
*/
|
||||
public function setPurchaseCostAttribute($value)
|
||||
{
|
||||
if (is_float($value)) {
|
||||
//value is *already* a floating-point number. Just assign it directly
|
||||
$this->attributes['purchase_cost'] = $value;
|
||||
return;
|
||||
}
|
||||
$value = Helper::ParseCurrency($value);
|
||||
|
||||
if ($value == 0) {
|
||||
|
|
|
@ -579,6 +579,6 @@ class AssetPresenter extends Presenter
|
|||
|
||||
public function glyph()
|
||||
{
|
||||
return '<i class="fas fa-barcode" aria-hidden="true"></i>';
|
||||
return '<x-icon type="assets" />';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,40 +65,46 @@ class CompanyPresenter extends Presenter
|
|||
'field' => 'users_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => '<span class="hidden-xs"><i class="fas fa-users"></i></span><span class="hidden-md hidden-lg">'.trans('general.users').'</span></th>',
|
||||
'title' => trans('general.users'),
|
||||
'visible' => true,
|
||||
'class' => 'css-users',
|
||||
|
||||
], [
|
||||
'field' => 'assets_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => '<span class="hidden-xs"><i class="fas fa-barcode" aria-hidden="true"></i></span><span class="hidden-md hidden-lg">'.trans('general.assets').'</span>',
|
||||
'title' => trans('general.assets'),
|
||||
'visible' => true,
|
||||
'class' => 'css-barcode',
|
||||
|
||||
], [
|
||||
'field' => 'licenses_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.licenses'),
|
||||
'visible' => true,
|
||||
'title' => ' <span class="hidden-xs"><i class="far fa-save"></i></span><span class="hidden-md hidden-lg">'.trans('general.licenses').'</span>',
|
||||
'class' => 'css-license',
|
||||
], [
|
||||
'field' => 'accessories_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.accessories'),
|
||||
'visible' => true,
|
||||
'title' => ' <span class="hidden-xs"><i class="far fa-keyboard"></i></span><span class="hidden-md hidden-lg">'.trans('general.accessories').'</span>',
|
||||
'class' => 'css-accessory',
|
||||
], [
|
||||
'field' => 'consumables_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.consumables'),
|
||||
'visible' => true,
|
||||
'title' => ' <span class="hidden-xs"><i class="fas fa-tint"></i></span><span class="hidden-md hidden-lg">'.trans('general.consumables').'</span>',
|
||||
'class' => 'css-consumable',
|
||||
], [
|
||||
'field' => 'components_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.components'),
|
||||
'visible' => true,
|
||||
'title' => ' <span class="hidden-xs"><i class="far fa-hdd"></i></span><span class="hidden-md hidden-lg">'.trans('general.components').'</span>',
|
||||
'class' => 'css-component',
|
||||
], [
|
||||
'field' => 'updated_at',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -75,13 +75,13 @@ class ConsumablePresenter extends Presenter
|
|||
], [
|
||||
'field' => 'qty',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/components/general.total'),
|
||||
'visible' => true,
|
||||
], [
|
||||
'field' => 'remaining',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/components/general.remaining'),
|
||||
'visible' => true,
|
||||
], [
|
||||
|
|
|
@ -46,6 +46,26 @@ class DepreciationPresenter extends Presenter
|
|||
"title" => trans('admin/depreciations/table.depreciation_min'),
|
||||
"visible" => true,
|
||||
],
|
||||
[
|
||||
'field' => 'assets_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.assets'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'models_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.asset_models'),
|
||||
'visible' => true,
|
||||
], [
|
||||
'field' => 'licenses_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.licenses'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'actions',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -394,6 +394,6 @@ class DepreciationReportPresenter extends Presenter
|
|||
|
||||
public function glyph()
|
||||
{
|
||||
return '<i class="fas fa-barcode" aria-hidden="true"></i>';
|
||||
return '<x-icon type="reports" class="text-orange" />';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ class LocationPresenter extends Presenter
|
|||
|
||||
public function glyph()
|
||||
{
|
||||
return '<i class="fas fa-map-marker-alt" aria-hidden="true"></i>';
|
||||
return '<x-icon type="locations" />';
|
||||
}
|
||||
|
||||
public function fullName()
|
||||
|
|
|
@ -94,36 +94,36 @@ class ManufacturerPresenter extends Presenter
|
|||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => ' <span class="hidden-md hidden-lg">Assets</span>'
|
||||
.'<span class="hidden-xs"><i class="fas fa-barcode fa-lg"></i></span>',
|
||||
'title' => trans('general.assets'),
|
||||
'visible' => true,
|
||||
'class' => 'css-barcode',
|
||||
],
|
||||
[
|
||||
'field' => 'licenses_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => ' <span class="hidden-md hidden-lg">Licenses</span>'
|
||||
.'<span class="hidden-xs"><i class="far fa-save fa-lg"></i></span>',
|
||||
'title' => trans('general.licenses'),
|
||||
'visible' => true,
|
||||
'class' => 'css-license',
|
||||
],
|
||||
[
|
||||
'field' => 'consumables_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => ' <span class="hidden-md hidden-lg">Consumables</span>'
|
||||
.'<span class="hidden-xs"><i class="fas fa-tint fa-lg"></i></span>',
|
||||
'title' => trans('general.consumables'),
|
||||
'visible' => true,
|
||||
'class' => 'css-consumable',
|
||||
],
|
||||
[
|
||||
'field' => 'accessories_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => ' <span class="hidden-md hidden-lg">Accessories</span>'
|
||||
.'<span class="hidden-xs"><i class="far fa-keyboard fa-lg"></i></span>',
|
||||
'title' => trans('general.accessories'),
|
||||
'visible' => true,
|
||||
'class' => 'css-accessory',
|
||||
],
|
||||
[
|
||||
'field' => 'created_at',
|
||||
|
|
|
@ -492,6 +492,6 @@ class UserPresenter extends Presenter
|
|||
|
||||
public function glyph()
|
||||
{
|
||||
return '<i class="fas fa-user" aria-hidden="true"></i>';
|
||||
return '<x-icon type="user"/>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@ use App\Models\CustomField;
|
|||
use App\Models\Department;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
|
@ -91,18 +88,26 @@ class ValidationServiceProvider extends ServiceProvider
|
|||
*
|
||||
* $parameters[0] - the name of the first table we're looking at
|
||||
* $parameters[1] - the ID (this will be 0 on new creations)
|
||||
* $parameters[2] - the name of the second table we're looking at
|
||||
* $parameters[2] - the name of the second field we're looking at
|
||||
* $parameters[3] - the value that the request is passing for the second table we're
|
||||
* checking for uniqueness across
|
||||
*
|
||||
*/
|
||||
Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) {
|
||||
|
||||
if (count($parameters)) {
|
||||
|
||||
$count = DB::table($parameters[0])
|
||||
->select('id')->where($attribute, '=', $value)
|
||||
->whereNull('deleted_at')
|
||||
->where('id', '!=', $parameters[1])
|
||||
->where($parameters[2], $parameters[3])->count();
|
||||
->select('id')
|
||||
->where($attribute, '=', $value)
|
||||
->where('id', '!=', $parameters[1]);
|
||||
|
||||
if ($parameters[3]!='') {
|
||||
$count = $count->where($parameters[2], $parameters[3]);
|
||||
}
|
||||
|
||||
$count = $count->whereNull('deleted_at')
|
||||
->count();
|
||||
|
||||
return $count < 1;
|
||||
}
|
||||
|
|
23
app/Rules/UserCannotSwitchCompaniesIfItemsAssigned.php
Normal file
23
app/Rules/UserCannotSwitchCompaniesIfItemsAssigned.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class UserCannotSwitchCompaniesIfItemsAssigned implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$user = User::find(request()->route('user')->id);
|
||||
if (($value) && ($user->allAssignedCount() > 0) && (Setting::getSettings()->full_multiple_companies_support)) {
|
||||
$fail(trans('admin/users/message.error.multi_company_items_assigned'));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -78,7 +78,7 @@
|
|||
"fakerphp/faker": "^1.16",
|
||||
"larastan/larastan": "^2.9",
|
||||
"mockery/mockery": "^1.4",
|
||||
"nunomaduro/phpinsights": "^2.7",
|
||||
"nunomaduro/phpinsights": "^2.11",
|
||||
"php-mock/php-mock-phpunit": "^2.10",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
|
@ -120,7 +120,9 @@
|
|||
],
|
||||
"post-create-project-cmd": [
|
||||
"php artisan key:generate"
|
||||
]
|
||||
],
|
||||
"coverage:herd:clover": "herd coverage vendor/bin/phpunit --coverage-clover tests/coverage/clover.xml",
|
||||
"coverage:herd:html": "herd coverage vendor/bin/phpunit --coverage-html tests/coverage/html"
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
|
|
2
composer.lock
generated
2
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "35c741a2d3300848d758b187554b5b17",
|
||||
"content-hash": "3819ab4ef72eb77fabe494c0e746b83b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
|
|
|
@ -372,7 +372,9 @@ return [
|
|||
'Google2FA' => PragmaRX\Google2FALaravel\Facade::class,
|
||||
'Image' => Intervention\Image\ImageServiceProvider::class,
|
||||
'Carbon' => Carbon\Carbon::class,
|
||||
'Helper' => App\Helpers\Helper::class, // makes it much easier to use 'Helper::blah' in blades (which is where we usually use this)
|
||||
'Helper' => App\Helpers\Helper::class,
|
||||
// makes it much easier to use 'Helper::blah' in blades (which is where we usually use this)
|
||||
'Icon' => App\Helpers\IconHelper::class,
|
||||
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
|
||||
|
||||
|
||||
|
|
|
@ -237,4 +237,6 @@ return [
|
|||
],
|
||||
],
|
||||
|
||||
'sanitize_by_default' => env('DB_SANITIZE_BY_DEFAULT', false),
|
||||
|
||||
];
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
return array (
|
||||
'app_version' => 'v7.0.10',
|
||||
'full_app_version' => 'v7.0.10 - build 14684-gc2bcc2e2d',
|
||||
'build_version' => '14684',
|
||||
'app_version' => 'v7.0.11',
|
||||
'full_app_version' => 'v7.0.11 - build 15044-g46ed07642',
|
||||
'build_version' => '15044',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'gc2bcc2e2d',
|
||||
'full_hash' => 'v7.0.10-311-gc2bcc2e2d',
|
||||
'branch' => 'master',
|
||||
'hash_version' => 'g46ed07642',
|
||||
'full_hash' => 'v7.0.11-133-g46ed07642',
|
||||
'branch' => 'develop',
|
||||
);
|
|
@ -296,6 +296,11 @@ class UserFactory extends Factory
|
|||
return $this->appendPermission(['reports.view' => '1']);
|
||||
}
|
||||
|
||||
public function canImport()
|
||||
{
|
||||
return $this->appendPermission(['import' => '1']);
|
||||
}
|
||||
|
||||
private function appendPermission(array $permission)
|
||||
{
|
||||
return $this->state(function ($currentState) use ($permission) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?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('users', function (Blueprint $table) {
|
||||
$table->boolean('enable_confetti')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('enable_confetti');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
<?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->integer('due_checkin_days')->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropColumn('due_checkin_days');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,3 +1,5 @@
|
|||
# Compose file to spin up a local Snipe-IT for development.
|
||||
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
@ -8,43 +10,39 @@ services:
|
|||
container_name: snipeit
|
||||
ports:
|
||||
- "8000:80"
|
||||
volumes:
|
||||
- ./storage/logs:/var/www/html/storage/logs
|
||||
depends_on:
|
||||
- mariadb
|
||||
- redis
|
||||
redis:
|
||||
# The default needs to be stated.
|
||||
condition: service_started
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
restart: true
|
||||
env_file:
|
||||
- .env.docker
|
||||
networks:
|
||||
- snipeit-backend
|
||||
- .env.dev.docker
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.6.4-focal
|
||||
image: mariadb:11.5.2
|
||||
volumes:
|
||||
- db:/var/lib/mysql
|
||||
env_file:
|
||||
- .env.docker
|
||||
networks:
|
||||
- snipeit-backend
|
||||
- .env.dev.docker
|
||||
ports:
|
||||
- "3306:3306"
|
||||
healthcheck:
|
||||
# https://mariadb.com/kb/en/using-healthcheck-sh/#compose-file-example
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:6.2.5-buster
|
||||
networks:
|
||||
- snipeit-backend
|
||||
image: redis:7.4.0
|
||||
|
||||
mailhog:
|
||||
image: mailhog/mailhog:v1.0.1
|
||||
ports:
|
||||
# - 1025:1025
|
||||
- "8025:8025"
|
||||
networks:
|
||||
- snipeit-backend
|
||||
|
||||
|
||||
volumes:
|
||||
db: {}
|
||||
|
||||
networks:
|
||||
snipeit-backend: {}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# Compose file for production.
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
storage:
|
||||
|
||||
services:
|
||||
app:
|
||||
image: snipe/snipe-it:${APP_VERSION:-v6.4.1}
|
||||
restart: always
|
||||
image: snipe/snipe-it:${APP_VERSION:-v7.0.11}
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- storage:/var/lib/snipeit
|
||||
ports:
|
||||
|
@ -18,8 +20,8 @@ services:
|
|||
- .env
|
||||
|
||||
db:
|
||||
image: mariadb:10.6.4-focal
|
||||
restart: always
|
||||
image: mariadb:11.5.2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
environment:
|
||||
|
@ -28,7 +30,8 @@ services:
|
|||
MYSQL_PASSWORD: ${DB_PASSWORD}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
|
||||
# https://mariadb.com/kb/en/using-healthcheck-sh/#compose-file-example
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
|
|
|
@ -1,7 +1,48 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Cribbed from nextcloud docker official repo
|
||||
# https://github.com/nextcloud/docker/blob/master/docker-entrypoint.sh
|
||||
# usage: file_env VAR [DEFAULT]
|
||||
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
|
||||
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
|
||||
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
|
||||
file_env() {
|
||||
local var="$1"
|
||||
local fileVar="${var}_FILE"
|
||||
local def="${2:-}"
|
||||
local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//")
|
||||
local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//")
|
||||
if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then
|
||||
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "${varValue}" ]; then
|
||||
export "$var"="${varValue}"
|
||||
elif [ -n "${fileVarValue}" ]; then
|
||||
export "$var"="$(cat "${fileVarValue}")"
|
||||
elif [ -n "${def}" ]; then
|
||||
export "$var"="$def"
|
||||
fi
|
||||
unset "$fileVar"
|
||||
}
|
||||
|
||||
# Add docker secrets support for the variables below:
|
||||
file_env APP_KEY
|
||||
file_env DB_HOST
|
||||
file_env DB_PORT
|
||||
file_env DB_DATABASE
|
||||
file_env DB_USERNAME
|
||||
file_env DB_PASSWORD
|
||||
file_env REDIS_HOST
|
||||
file_env REDIS_PASSWORD
|
||||
file_env REDIS_PORT
|
||||
file_env MAIL_HOST
|
||||
file_env MAIL_PORT
|
||||
file_env MAIL_USERNAME
|
||||
file_env MAIL_PASSWORD
|
||||
|
||||
# fix key if needed
|
||||
if [ -z "$APP_KEY" ]
|
||||
if [ -z "$APP_KEY" -a -z "$APP_KEY_FILE" ]
|
||||
then
|
||||
echo "Please re-run this container with an environment variable \$APP_KEY"
|
||||
echo "An example APP_KEY you could use is: "
|
||||
|
|
|
@ -1,7 +1,48 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Cribbed from nextcloud docker official repo
|
||||
# https://github.com/nextcloud/docker/blob/master/docker-entrypoint.sh
|
||||
# usage: file_env VAR [DEFAULT]
|
||||
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
|
||||
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
|
||||
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
|
||||
file_env() {
|
||||
local var="$1"
|
||||
local fileVar="${var}_FILE"
|
||||
local def="${2:-}"
|
||||
local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//")
|
||||
local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//")
|
||||
if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then
|
||||
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "${varValue}" ]; then
|
||||
export "$var"="${varValue}"
|
||||
elif [ -n "${fileVarValue}" ]; then
|
||||
export "$var"="$(cat "${fileVarValue}")"
|
||||
elif [ -n "${def}" ]; then
|
||||
export "$var"="$def"
|
||||
fi
|
||||
unset "$fileVar"
|
||||
}
|
||||
|
||||
# Add docker secrets support for the variables below:
|
||||
file_env APP_KEY
|
||||
file_env DB_HOST
|
||||
file_env DB_PORT
|
||||
file_env DB_DATABASE
|
||||
file_env DB_USERNAME
|
||||
file_env DB_PASSWORD
|
||||
file_env REDIS_HOST
|
||||
file_env REDIS_PASSWORD
|
||||
file_env REDIS_PORT
|
||||
file_env MAIL_HOST
|
||||
file_env MAIL_PORT
|
||||
file_env MAIL_USERNAME
|
||||
file_env MAIL_PASSWORD
|
||||
|
||||
# fix key if needed
|
||||
if [ -z "$APP_KEY" ]
|
||||
if [ -z "$APP_KEY" -a -z "$APP_KEY_FILE" ]
|
||||
then
|
||||
echo "Please re-run this container with an environment variable \$APP_KEY"
|
||||
echo "An example APP_KEY you could use is: "
|
63
package-lock.json
generated
63
package-lock.json
generated
|
@ -15,14 +15,15 @@
|
|||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-datepicker": "^1.10.0",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
"bootstrap-table": "1.23.0",
|
||||
"bootstrap-table": "1.23.2",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"chart.js": "^2.9.4",
|
||||
"clipboard": "^2.0.11",
|
||||
"css-loader": "^5.0.0",
|
||||
"ekko-lightbox": "^5.1.1",
|
||||
"imagemin": "^8.0.1",
|
||||
"jquery-slimscroll": "^1.3.8",
|
||||
"jquery-ui": "^1.13.3",
|
||||
"jquery-ui": "^1.14.0",
|
||||
"jquery-validation": "^1.21.0",
|
||||
"jquery.iframe-transport": "^1.0.0",
|
||||
"jspdf-autotable": "^3.8.2",
|
||||
|
@ -36,7 +37,7 @@
|
|||
"signature_pad": "^4.2.0",
|
||||
"tableexport.jquery.plugin": "1.30.0",
|
||||
"tether": "^1.4.0",
|
||||
"webpack": "^5.92.0"
|
||||
"webpack": "^5.94.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"all-contributors-cli": "^6.26.1",
|
||||
|
@ -2104,25 +2105,10 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.10",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint-scope": {
|
||||
"version": "3.7.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"license": "MIT"
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.21",
|
||||
|
@ -3692,9 +3678,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bootstrap-table": {
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.0.tgz",
|
||||
"integrity": "sha512-fAIhu2CAqMsZWkzeFxXyh0yQA2DMBdB0tCdr1iF6bKr3c/Hf79cw5PykNt7NdtqLz/a0p192S8EKyT5lG4yrpw==",
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.23.2.tgz",
|
||||
"integrity": "sha512-1IFiWFZzbKlleXgYEHdwHkX6rxlQMEx2N1tA8rJK/j08pI+NjIGnxFeXUL26yQLQ0U135eis/BX3OV1+anY25g==",
|
||||
"peerDependencies": {
|
||||
"jquery": "3"
|
||||
}
|
||||
|
@ -4098,6 +4084,15 @@
|
|||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/canvas-confetti": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz",
|
||||
"integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==",
|
||||
"funding": {
|
||||
"type": "donate",
|
||||
"url": "https://www.paypal.me/kirilvatev"
|
||||
}
|
||||
},
|
||||
"node_modules/canvg": {
|
||||
"version": "3.0.10",
|
||||
"license": "MIT",
|
||||
|
@ -5300,9 +5295,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.17.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz",
|
||||
"integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==",
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
|
||||
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
|
@ -7057,10 +7052,11 @@
|
|||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/jquery-ui": {
|
||||
"version": "1.13.3",
|
||||
"license": "MIT",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.0.tgz",
|
||||
"integrity": "sha512-mPfYKBoRCf0MzaT2cyW5i3IuZ7PfTITaasO5OFLAQxrHuI+ZxruPa+4/K1OMNT8oElLWGtIxc9aRbyw20BKr8g==",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.8.0 <4.0.0"
|
||||
"jquery": ">=1.12.0 <5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery-validation": {
|
||||
|
@ -10869,11 +10865,10 @@
|
|||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.92.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz",
|
||||
"integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==",
|
||||
"version": "5.94.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
||||
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.5",
|
||||
"@webassemblyjs/ast": "^1.12.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.12.1",
|
||||
|
@ -10882,7 +10877,7 @@
|
|||
"acorn-import-attributes": "^1.9.5",
|
||||
"browserslist": "^4.21.10",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.17.0",
|
||||
"enhanced-resolve": "^5.17.1",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
|
|
|
@ -35,14 +35,15 @@
|
|||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-datepicker": "^1.10.0",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
"bootstrap-table": "1.23.0",
|
||||
"bootstrap-table": "1.23.2",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"chart.js": "^2.9.4",
|
||||
"clipboard": "^2.0.11",
|
||||
"css-loader": "^5.0.0",
|
||||
"ekko-lightbox": "^5.1.1",
|
||||
"imagemin": "^8.0.1",
|
||||
"jquery-slimscroll": "^1.3.8",
|
||||
"jquery-ui": "^1.13.3",
|
||||
"jquery-ui": "^1.14.0",
|
||||
"jquery-validation": "^1.21.0",
|
||||
"jquery.iframe-transport": "^1.0.0",
|
||||
"jspdf-autotable": "^3.8.2",
|
||||
|
@ -56,6 +57,6 @@
|
|||
"signature_pad": "^4.2.0",
|
||||
"tableexport.jquery.plugin": "1.30.0",
|
||||
"tether": "^1.4.0",
|
||||
"webpack": "^5.92.0"
|
||||
"webpack": "^5.94.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -697,6 +697,9 @@ body {
|
|||
font-size: 14px;
|
||||
white-space: normal;
|
||||
}
|
||||
.modal-warning .modal-help {
|
||||
color: #fff8af;
|
||||
}
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
@ -828,19 +831,36 @@ body {
|
|||
}
|
||||
.select2-selection--multiple {
|
||||
border-color: #d2d6de !important;
|
||||
height: 34px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.select2-selection__choice {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.select2-search select2-search--inline {
|
||||
height: 35px !important;
|
||||
float: left;
|
||||
margin: 0;
|
||||
}
|
||||
.select2-selection__rendered needsclick {
|
||||
color: red;
|
||||
}
|
||||
.select2-results__option {
|
||||
padding: 5px;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
margin: 0px;
|
||||
}
|
||||
img.navbar-brand-img,
|
||||
.navbar-brand > img {
|
||||
float: left;
|
||||
padding: 5px 5px 5px 0;
|
||||
max-height: 50px;
|
||||
}
|
||||
.input-daterange {
|
||||
border-radius: 0px;
|
||||
.input-daterange,
|
||||
.input-daterange input:first-child,
|
||||
.input-daterange input:last-child {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.btn.bg-maroon,
|
||||
.btn.bg-purple {
|
||||
|
@ -1006,6 +1026,7 @@ th.css-consumable > .th-inner,
|
|||
th.css-envelope > .th-inner,
|
||||
th.css-users > .th-inner,
|
||||
th.css-location > .th-inner,
|
||||
th.css-component > .th-inner,
|
||||
th.css-accessory > .th-inner {
|
||||
font-size: 0px;
|
||||
line-height: 0.75 !important;
|
||||
|
@ -1021,6 +1042,7 @@ th.css-consumable > .th-inner::before,
|
|||
th.css-envelope > .th-inner::before,
|
||||
th.css-users > .th-inner::before,
|
||||
th.css-location > .th-inner::before,
|
||||
th.css-component > .th-inner::before,
|
||||
th.css-accessory > .th-inner::before {
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
|
@ -1074,6 +1096,11 @@ th.css-location > .th-inner::before {
|
|||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
th.css-component > .th-inner::before {
|
||||
content: "\f0a0";
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 500;
|
||||
}
|
||||
.small-box .inner {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
|
@ -1122,15 +1149,37 @@ th.css-location > .th-inner::before {
|
|||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 992px) {
|
||||
.info-stack-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.col-md-3.col-xs-12.col-sm-push-9.info-stack {
|
||||
left: auto;
|
||||
order: 1;
|
||||
}
|
||||
.col-md-9.col-xs-12.col-sm-pull-3.info-stack {
|
||||
right: auto;
|
||||
order: 2;
|
||||
}
|
||||
.info-stack-container > .col-md-9.col-xs-12.col-sm-pull-3.info-stack > .row-new-striped > .row > .col-sm-2 {
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1318px) and (min-width: 1200px) {
|
||||
.box {
|
||||
.admin.box {
|
||||
height: 170px;
|
||||
}
|
||||
}
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
@media screen and (max-width: 1494px) and (min-width: 1200px) {
|
||||
.dashboard.small-box {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 188px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
/** Form-stuff overrides for checkboxes and stuff **/
|
||||
label.form-control {
|
||||
|
|
|
@ -330,6 +330,9 @@ body {
|
|||
font-size: 14px;
|
||||
white-space: normal;
|
||||
}
|
||||
.modal-warning .modal-help {
|
||||
color: #fff8af;
|
||||
}
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
@ -461,19 +464,36 @@ body {
|
|||
}
|
||||
.select2-selection--multiple {
|
||||
border-color: #d2d6de !important;
|
||||
height: 34px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.select2-selection__choice {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.select2-search select2-search--inline {
|
||||
height: 35px !important;
|
||||
float: left;
|
||||
margin: 0;
|
||||
}
|
||||
.select2-selection__rendered needsclick {
|
||||
color: red;
|
||||
}
|
||||
.select2-results__option {
|
||||
padding: 5px;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
margin: 0px;
|
||||
}
|
||||
img.navbar-brand-img,
|
||||
.navbar-brand > img {
|
||||
float: left;
|
||||
padding: 5px 5px 5px 0;
|
||||
max-height: 50px;
|
||||
}
|
||||
.input-daterange {
|
||||
border-radius: 0px;
|
||||
.input-daterange,
|
||||
.input-daterange input:first-child,
|
||||
.input-daterange input:last-child {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.btn.bg-maroon,
|
||||
.btn.bg-purple {
|
||||
|
@ -639,6 +659,7 @@ th.css-consumable > .th-inner,
|
|||
th.css-envelope > .th-inner,
|
||||
th.css-users > .th-inner,
|
||||
th.css-location > .th-inner,
|
||||
th.css-component > .th-inner,
|
||||
th.css-accessory > .th-inner {
|
||||
font-size: 0px;
|
||||
line-height: 0.75 !important;
|
||||
|
@ -654,6 +675,7 @@ th.css-consumable > .th-inner::before,
|
|||
th.css-envelope > .th-inner::before,
|
||||
th.css-users > .th-inner::before,
|
||||
th.css-location > .th-inner::before,
|
||||
th.css-component > .th-inner::before,
|
||||
th.css-accessory > .th-inner::before {
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
|
@ -707,6 +729,11 @@ th.css-location > .th-inner::before {
|
|||
font-size: 19px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
th.css-component > .th-inner::before {
|
||||
content: "\f0a0";
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 500;
|
||||
}
|
||||
.small-box .inner {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
|
@ -755,15 +782,37 @@ th.css-location > .th-inner::before {
|
|||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 992px) {
|
||||
.info-stack-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.col-md-3.col-xs-12.col-sm-push-9.info-stack {
|
||||
left: auto;
|
||||
order: 1;
|
||||
}
|
||||
.col-md-9.col-xs-12.col-sm-pull-3.info-stack {
|
||||
right: auto;
|
||||
order: 2;
|
||||
}
|
||||
.info-stack-container > .col-md-9.col-xs-12.col-sm-pull-3.info-stack > .row-new-striped > .row > .col-sm-2 {
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1318px) and (min-width: 1200px) {
|
||||
.box {
|
||||
.admin.box {
|
||||
height: 170px;
|
||||
}
|
||||
}
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
@media screen and (max-width: 1494px) and (min-width: 1200px) {
|
||||
.dashboard.small-box {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 188px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
/** Form-stuff overrides for checkboxes and stuff **/
|
||||
label.form-control {
|
||||
|
|
124
public/css/dist/all.css
vendored
124
public/css/dist/all.css
vendored
File diff suppressed because one or more lines are too long
2
public/css/dist/bootstrap-table.css
vendored
2
public/css/dist/bootstrap-table.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1618
public/js/dist/all.js
vendored
1618
public/js/dist/all.js
vendored
File diff suppressed because it is too large
Load diff
4
public/js/dist/bootstrap-table-en-US.min.js
vendored
4
public/js/dist/bootstrap-table-en-US.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
19515
public/js/dist/bootstrap-table.js
vendored
19515
public/js/dist/bootstrap-table.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=da3f7fee4a180ba924f6a3920c94eb71",
|
||||
"/js/build/app.js": "/js/build/app.js?id=5e9ac5c1a7e089f056fb1dba566193a6",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f0b08873a06bb54daeee176a9459f4a9",
|
||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=f4397c717b99fce41a633ca6edd5d1f4",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=a759aa24710e294392877c5139fda40e",
|
||||
"/css/build/app.css": "/css/build/app.css?id=b3b3df70f679f45e15a6bcd28a8e87cc",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=ebd921b0b5dca37487551bcc7dc934c5",
|
||||
"/css/build/app.css": "/css/build/app.css?id=2b1b6164d02342fcd4cd303fef52e895",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=393aaa7b368b0670fc42434c8cca7dc7",
|
||||
|
@ -19,7 +19,7 @@
|
|||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=7b8e04041af3dfe3de25d73107bfda91",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=c1cd73524bd82ddb8a4d7e8d1a504506",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
||||
|
@ -90,8 +90,8 @@
|
|||
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=541cafc702f56f57de95f3d1f792f428",
|
||||
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=51ade19e1b10d7a0031b18568a2b01d5",
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=1cc408d68a27c3757b4460bbc542433e",
|
||||
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=27eb00f47f9bae70cd630d184b7969f1",
|
||||
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=57bdb4770b2924f5efeda100caf3c9b7",
|
||||
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=467938d6a524df8e62c4fb8ae5e7f3f1",
|
||||
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=d4ef3db8dc9f809258218c187de5ee2a",
|
||||
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=f4397c717b99fce41a633ca6edd5d1f4",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=f0b08873a06bb54daeee176a9459f4a9",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
|
@ -108,8 +108,8 @@
|
|||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=393aaa7b368b0670fc42434c8cca7dc7",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=8abbb6aea625ec64cd7ebdad77ebf6e5",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=c1c24b883f48dc3d16b817aed0b457cc",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=859e11e4e6b05c84e4b7302de29bac5e",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=ea6fb4f01f01c2194310403dafc1658f"
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=393d720a0f9aba560094fbc8d3b0c0f0",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=5269eb5a6beb74f03387c78938cf17b2",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=6660df122e24940d42d03c06775fec7b",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=e0a4b1a80b09333a460973137f39eab4"
|
||||
}
|
||||
|
|
BIN
public/sounds/lock.mp3
Normal file
BIN
public/sounds/lock.mp3
Normal file
Binary file not shown.
|
@ -82,47 +82,31 @@ pieOptions = {
|
|||
|
||||
var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
||||
|
||||
(function($, settings) {
|
||||
var Components = {};
|
||||
Components.modals = {};
|
||||
$(function () {
|
||||
|
||||
// confirm restore modal
|
||||
Components.modals.confirmRestore = function() {
|
||||
var $el = $('table');
|
||||
|
||||
var events = {
|
||||
'click': function(evnt) {
|
||||
// confirm restore modal
|
||||
|
||||
$el.on('click', '.restore-asset', function (evnt) {
|
||||
var $context = $(this);
|
||||
var $restoreConfirmModal = $('#restoreConfirmModal');
|
||||
var href = $context.attr('href');
|
||||
var message = $context.attr('data-content');
|
||||
var title = $context.attr('data-title');
|
||||
|
||||
$('#restoreConfirmModalLabel').text(title);
|
||||
$('#confirmModalLabel').text(title);
|
||||
$restoreConfirmModal.find('.modal-body').text(message);
|
||||
$('#restoreForm').attr('action', href);
|
||||
$restoreConfirmModal.modal({
|
||||
show: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var render = function() {
|
||||
$el.on('click', '.restore-asset', events['click']);
|
||||
};
|
||||
|
||||
return {
|
||||
render: render
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
// confirm delete modal
|
||||
Components.modals.confirmDelete = function() {
|
||||
var $el = $('table');
|
||||
|
||||
var events = {
|
||||
'click': function(evnt) {
|
||||
$el.on('click', '.delete-asset', function (evnt) {
|
||||
var $context = $(this);
|
||||
var $dataConfirmModal = $('#dataConfirmModal');
|
||||
var href = $context.attr('href');
|
||||
|
@ -136,30 +120,7 @@ var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
|||
show: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var render = function() {
|
||||
$el.on('click', '.delete-asset', events['click']);
|
||||
};
|
||||
|
||||
return {
|
||||
render: render
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Application start point
|
||||
* Component definition stays out of load event, execution only happens.
|
||||
*/
|
||||
$(function() {
|
||||
new Components.modals.confirmRestore().render();
|
||||
new Components.modals.confirmDelete().render();
|
||||
});
|
||||
}(jQuery, window.snipeit.settings));
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
/*
|
||||
* Slideout help menu
|
||||
|
|
|
@ -358,6 +358,10 @@ body {
|
|||
white-space: normal;
|
||||
}
|
||||
|
||||
.modal-warning .modal-help {
|
||||
color: #fff8af;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
@ -496,13 +500,27 @@ body {
|
|||
|
||||
.select2-selection--multiple {
|
||||
border-color: #d2d6de !important;
|
||||
height: 34px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.select2-selection__choice {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.select2-search select2-search--inline {
|
||||
height: 35px !important;
|
||||
float: left;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.select2-results__option {
|
||||
padding: 5px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
img.navbar-brand-img, .navbar-brand>img {
|
||||
float: left;
|
||||
|
@ -510,8 +528,8 @@ img.navbar-brand-img, .navbar-brand>img {
|
|||
max-height: 50px;
|
||||
}
|
||||
|
||||
.input-daterange {
|
||||
border-radius: 0px;
|
||||
.input-daterange, .input-daterange input:first-child, .input-daterange input:last-child {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.btn.bg-maroon, .btn.bg-purple{
|
||||
|
@ -718,6 +736,7 @@ th.css-consumable > .th-inner,
|
|||
th.css-envelope > .th-inner,
|
||||
th.css-users > .th-inner,
|
||||
th.css-location > .th-inner,
|
||||
th.css-component > .th-inner,
|
||||
th.css-accessory > .th-inner
|
||||
{
|
||||
font-size: 0px;
|
||||
|
@ -736,6 +755,7 @@ th.css-consumable > .th-inner::before,
|
|||
th.css-envelope > .th-inner::before,
|
||||
th.css-users > .th-inner::before,
|
||||
th.css-location > .th-inner::before,
|
||||
th.css-component > .th-inner::before,
|
||||
th.css-accessory > .th-inner::before
|
||||
|
||||
{
|
||||
|
@ -791,6 +811,11 @@ th.css-location > .th-inner::before {
|
|||
content: "\f3c5"; font-family: "Font Awesome 5 Free"; font-size: 19px; margin-bottom: 0px;
|
||||
}
|
||||
|
||||
th.css-component > .th-inner::before
|
||||
{
|
||||
content: "\f0a0"; font-family: "Font Awesome 5 Free"; font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
.small-box .inner {
|
||||
padding-left: 15px;
|
||||
|
@ -844,19 +869,39 @@ th.css-location > .th-inner::before {
|
|||
margin-top:50px
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 992px){
|
||||
.info-stack-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.col-md-3.col-xs-12.col-sm-push-9.info-stack{
|
||||
left:auto;
|
||||
order:1;
|
||||
}
|
||||
.col-md-9.col-xs-12.col-sm-pull-3.info-stack{
|
||||
right:auto;
|
||||
order:2;
|
||||
}
|
||||
.info-stack-container > .col-md-9.col-xs-12.col-sm-pull-3.info-stack > .row-new-striped > .row > .col-sm-2{
|
||||
width:auto;
|
||||
float:none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1318px) and (min-width: 1200px){
|
||||
.box{
|
||||
.admin.box{
|
||||
height:170px;
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
@media screen and (max-width: 1494px) and (min-width: 1200px){
|
||||
.dashboard.small-box{
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 188px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Form-stuff overrides for checkboxes and stuff **/
|
||||
|
||||
label.form-control {
|
||||
|
|
|
@ -12,4 +12,6 @@ return array(
|
|||
'api_reference' => 'crwdns12270:0crwdne12270:0',
|
||||
'profile_updated' => 'crwdns12202:0crwdne12202:0',
|
||||
'no_tokens' => 'crwdns12318:0crwdne12318:0',
|
||||
'enable_sounds' => 'crwdns12658:0crwdne12658:0',
|
||||
'enable_confetti' => 'crwdns12664:0crwdne12664:0',
|
||||
);
|
||||
|
|
|
@ -58,6 +58,7 @@ return [
|
|||
'file_delete_success' => 'crwdns1698:0crwdne1698:0',
|
||||
'file_delete_error' => 'crwdns1699:0crwdne1699:0',
|
||||
'file_missing' => 'crwdns11835:0crwdne11835:0',
|
||||
'file_already_deleted' => 'crwdns12694:0crwdne12694:0',
|
||||
'header_row_has_malformed_characters' => 'crwdns11229:0crwdne11229:0',
|
||||
'content_row_has_malformed_characters' => 'crwdns11231:0crwdne11231:0',
|
||||
],
|
||||
|
|
|
@ -47,4 +47,5 @@ return [
|
|||
'kit_deleted' => 'crwdns6659:0crwdne6659:0',
|
||||
'kit_model_updated' => 'crwdns6661:0crwdne6661:0',
|
||||
'kit_model_detached' => 'crwdns6663:0crwdne6663:0',
|
||||
'model_already_attached' => 'crwdns12684:0crwdne12684:0',
|
||||
];
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
return array(
|
||||
|
||||
'does_not_exist' => 'crwdns650:0crwdne650:0',
|
||||
'assoc_users' => 'crwdns12272:0crwdne12272:0',
|
||||
'assoc_users' => 'crwdns12666:0crwdne12666:0',
|
||||
'assoc_assets' => 'crwdns1404:0crwdne1404:0',
|
||||
'assoc_child_loc' => 'crwdns1405:0crwdne1405:0',
|
||||
'assigned_assets' => 'crwdns11179:0crwdne11179:0',
|
||||
'current_location' => 'crwdns11181:0crwdne11181:0',
|
||||
'open_map' => 'crwdns12696:0crwdne12696:0',
|
||||
|
||||
|
||||
'create' => array(
|
||||
|
@ -20,6 +21,11 @@ return array(
|
|||
'success' => 'crwdns655:0crwdne655:0'
|
||||
),
|
||||
|
||||
'restore' => array(
|
||||
'error' => 'crwdns12698:0crwdne12698:0',
|
||||
'success' => 'crwdns12700:0crwdne12700:0'
|
||||
),
|
||||
|
||||
'delete' => array(
|
||||
'confirm' => 'crwdns656:0crwdne656:0',
|
||||
'error' => 'crwdns657:0crwdne657:0',
|
||||
|
|
|
@ -7,7 +7,7 @@ return array(
|
|||
'no_association' => 'crwdns11693:0crwdne11693:0',
|
||||
'no_association_fix' => 'crwdns11235:0crwdne11235:0',
|
||||
'assoc_users' => 'crwdns672:0crwdne672:0',
|
||||
'invalid_category_type' => 'crwdns12302:0crwdne12302:0',
|
||||
'invalid_category_type' => 'crwdns12678:0crwdne12678:0',
|
||||
|
||||
'create' => array(
|
||||
'error' => 'crwdns673:0crwdne673:0',
|
||||
|
|
|
@ -218,6 +218,8 @@ return [
|
|||
'webhook_integration_help' => 'crwdns11403:0crwdne11403:0',
|
||||
'webhook_integration_help_button' => 'crwdns11405:0crwdne11405:0',
|
||||
'webhook_test_help' => 'crwdns11407:0crwdne11407:0',
|
||||
'shortcuts_enabled' => 'crwdns12654:0crwdne12654:0',
|
||||
'shortcuts_help_text' => 'crwdns12656:0crwdne12656:0',
|
||||
'snipe_version' => 'crwdns1266:0crwdne1266:0',
|
||||
'support_footer' => 'crwdns1991:0crwdne1991:0',
|
||||
'support_footer_help' => 'crwdns1992:0crwdne1992:0',
|
||||
|
@ -379,5 +381,7 @@ return [
|
|||
'default_avatar_help' => 'crwdns12590:0crwdne12590:0',
|
||||
'restore_default_avatar' => 'crwdns12592:0crwdne12592:0',
|
||||
'restore_default_avatar_help' => 'crwdns12594:0crwdne12594:0',
|
||||
'due_checkin_days' => 'crwdns12680:0crwdne12680:0',
|
||||
'due_checkin_days_help' => 'crwdns12682:0crwdne12682:0',
|
||||
|
||||
];
|
||||
|
|
|
@ -14,6 +14,9 @@ return [
|
|||
'restore_warning' => 'crwdns6709:0crwdne6709:0',
|
||||
'restore_confirm' => 'crwdns6711:0crwdne6711:0'
|
||||
],
|
||||
'restore' => [
|
||||
'success' => 'crwdns12668:0crwdne12668:0'
|
||||
],
|
||||
'purge' => [
|
||||
'error' => 'crwdns1615:0crwdne1615:0',
|
||||
'validation_failed' => 'crwdns1616:0crwdne1616:0',
|
||||
|
|
|
@ -26,7 +26,7 @@ return [
|
|||
'clone' => 'crwdns12598:0crwdne12598:0',
|
||||
'edit' => 'crwdns12600:0crwdne12600:0',
|
||||
'delete' => 'crwdns12602:0crwdne12602:0',
|
||||
'restore' => 'crwdns12604:0crwdne12604:0',
|
||||
'restore' => 'crwdns12646:0crwdne12646:0',
|
||||
'create' => 'crwdns12606:0crwdne12606:0',
|
||||
'checkout' => 'crwdns12608:0crwdne12608:0',
|
||||
'checkin' => 'crwdns12610:0crwdne12610:0',
|
||||
|
|
|
@ -77,7 +77,7 @@ return [
|
|||
'consumables' => 'crwdns1327:0crwdne1327:0',
|
||||
'country' => 'crwdns1039:0crwdne1039:0',
|
||||
'could_not_restore' => 'crwdns11894:0:item_type:crwdne11894:0',
|
||||
'not_deleted' => 'crwdns11896:0crwdne11896:0',
|
||||
'not_deleted' => 'crwdns12670:0crwdne12670:0',
|
||||
'create' => 'crwdns1040:0crwdne1040:0',
|
||||
'created' => 'crwdns1773:0crwdne1773:0',
|
||||
'created_asset' => 'crwdns1041:0crwdne1041:0',
|
||||
|
@ -98,7 +98,7 @@ return [
|
|||
'debug_warning_text' => 'crwdns1828:0crwdne1828:0',
|
||||
'delete' => 'crwdns1046:0crwdne1046:0',
|
||||
'delete_confirm' => 'crwdns2020:0crwdne2020:0',
|
||||
'delete_confirm_no_undo' => 'crwdns11599:0crwdne11599:0',
|
||||
'delete_confirm_no_undo' => 'crwdns12672:0crwdne12672:0',
|
||||
'deleted' => 'crwdns1047:0crwdne1047:0',
|
||||
'delete_seats' => 'crwdns1430:0crwdne1430:0',
|
||||
'deletion_failed' => 'crwdns6117:0crwdne6117:0',
|
||||
|
@ -134,7 +134,7 @@ return [
|
|||
'lastname_firstinitial' => 'crwdns5952:0crwdne5952:0',
|
||||
'firstinitial.lastname' => 'crwdns5954:0crwdne5954:0',
|
||||
'firstnamelastinitial' => 'crwdns5956:0crwdne5956:0',
|
||||
'lastnamefirstname' => 'crwdns12266:0crwdne12266:0',
|
||||
'lastnamefirstname' => 'crwdns12620:0crwdne12620:0',
|
||||
'first_name' => 'crwdns1053:0crwdne1053:0',
|
||||
'first_name_format' => 'crwdns1647:0crwdne1647:0',
|
||||
'files' => 'crwdns1996:0crwdne1996:0',
|
||||
|
@ -156,9 +156,9 @@ return [
|
|||
'image_delete' => 'crwdns1057:0crwdne1057:0',
|
||||
'include_deleted' => 'crwdns10518:0crwdne10518:0',
|
||||
'image_upload' => 'crwdns1058:0crwdne1058:0',
|
||||
'filetypes_accepted_help' => 'crwdns6129:0crwdne6129:0',
|
||||
'filetypes_size_help' => 'crwdns6131:0crwdne6131:0',
|
||||
'image_filetypes_help' => 'crwdns12284:0crwdne12284:0',
|
||||
'filetypes_accepted_help' => 'crwdns12622:0crwdne12622:0',
|
||||
'filetypes_size_help' => 'crwdns12624:0crwdne12624:0',
|
||||
'image_filetypes_help' => 'crwdns12674:0crwdne12674:0',
|
||||
'unaccepted_image_type' => 'crwdns11365:0crwdne11365:0',
|
||||
'import' => 'crwdns1411:0crwdne1411:0',
|
||||
'import_this_file' => 'crwdns11922:0crwdne11922:0',
|
||||
|
@ -183,7 +183,7 @@ return [
|
|||
'licenses_available' => 'crwdns12172:0crwdne12172:0',
|
||||
'licenses' => 'crwdns1062:0crwdne1062:0',
|
||||
'list_all' => 'crwdns1063:0crwdne1063:0',
|
||||
'loading' => 'crwdns6135:0crwdne6135:0',
|
||||
'loading' => 'crwdns12628:0crwdne12628:0',
|
||||
'lock_passwords' => 'crwdns5974:0crwdne5974:0',
|
||||
'feature_disabled' => 'crwdns1774:0crwdne1774:0',
|
||||
'location' => 'crwdns1064:0crwdne1064:0',
|
||||
|
@ -193,7 +193,7 @@ return [
|
|||
'logout' => 'crwdns1066:0crwdne1066:0',
|
||||
'lookup_by_tag' => 'crwdns1648:0crwdne1648:0',
|
||||
'maintenances' => 'crwdns1998:0crwdne1998:0',
|
||||
'manage_api_keys' => 'crwdns6137:0crwdne6137:0',
|
||||
'manage_api_keys' => 'crwdns12630:0crwdne12630:0',
|
||||
'manufacturer' => 'crwdns1067:0crwdne1067:0',
|
||||
'manufacturers' => 'crwdns1068:0crwdne1068:0',
|
||||
'markdown' => 'crwdns1574:0crwdne1574:0',
|
||||
|
@ -254,7 +254,7 @@ return [
|
|||
'select_all' => 'crwdns6155:0crwdne6155:0',
|
||||
'search' => 'crwdns1290:0crwdne1290:0',
|
||||
'select_category' => 'crwdns1663:0crwdne1663:0',
|
||||
'select_datasource' => 'crwdns12166:0crwdne12166:0',
|
||||
'select_datasource' => 'crwdns12632:0crwdne12632:0',
|
||||
'select_department' => 'crwdns1880:0crwdne1880:0',
|
||||
'select_depreciation' => 'crwdns1282:0crwdne1282:0',
|
||||
'select_location' => 'crwdns1283:0crwdne1283:0',
|
||||
|
@ -274,11 +274,12 @@ return [
|
|||
'signed_off_by' => 'crwdns6784:0crwdne6784:0',
|
||||
'skin' => 'crwdns2002:0crwdne2002:0',
|
||||
'webhook_msg_note' => 'crwdns11483:0crwdne11483:0',
|
||||
'webhook_test_msg' => 'crwdns11371:0crwdne11371:0',
|
||||
'webhook_test_msg' => 'crwdns12634:0crwdne12634:0',
|
||||
'some_features_disabled' => 'crwdns1669:0crwdne1669:0',
|
||||
'site_name' => 'crwdns1086:0crwdne1086:0',
|
||||
'state' => 'crwdns1087:0crwdne1087:0',
|
||||
'status_labels' => 'crwdns1088:0crwdne1088:0',
|
||||
'status_label' => 'crwdns12662:0crwdne12662:0',
|
||||
'status' => 'crwdns1089:0crwdne1089:0',
|
||||
'accept_eula' => 'crwdns6786:0crwdne6786:0',
|
||||
'supplier' => 'crwdns1833:0crwdne1833:0',
|
||||
|
@ -339,16 +340,16 @@ return [
|
|||
'view_all' => 'crwdns6165:0crwdne6165:0',
|
||||
'hide_deleted' => 'crwdns6167:0crwdne6167:0',
|
||||
'email' => 'crwdns6169:0crwdne6169:0',
|
||||
'do_not_change' => 'crwdns6171:0crwdne6171:0',
|
||||
'bug_report' => 'crwdns6173:0crwdne6173:0',
|
||||
'do_not_change' => 'crwdns12636:0crwdne12636:0',
|
||||
'bug_report' => 'crwdns12638:0crwdne12638:0',
|
||||
'user_manual' => 'crwdns6175:0crwdne6175:0',
|
||||
'setup_step_1' => 'crwdns6177:0crwdne6177:0',
|
||||
'setup_step_2' => 'crwdns6179:0crwdne6179:0',
|
||||
'setup_step_3' => 'crwdns6181:0crwdne6181:0',
|
||||
'setup_step_4' => 'crwdns6183:0crwdne6183:0',
|
||||
'setup_config_check' => 'crwdns6185:0crwdne6185:0',
|
||||
'setup_create_database' => 'crwdns6187:0crwdne6187:0',
|
||||
'setup_create_admin' => 'crwdns6189:0crwdne6189:0',
|
||||
'setup_create_database' => 'crwdns12640:0crwdne12640:0',
|
||||
'setup_create_admin' => 'crwdns12676:0crwdne12676:0',
|
||||
'setup_done' => 'crwdns6191:0crwdne6191:0',
|
||||
'bulk_edit_about_to' => 'crwdns6193:0crwdne6193:0',
|
||||
'checked_out' => 'crwdns6195:0crwdne6195:0',
|
||||
|
@ -424,7 +425,7 @@ return [
|
|||
'assets_by_status_type' => 'crwdns10544:0crwdne10544:0',
|
||||
'pie_chart_type' => 'crwdns10546:0crwdne10546:0',
|
||||
'hello_name' => 'crwdns10548:0crwdne10548:0',
|
||||
'unaccepted_profile_warning' => 'crwdns10550:0crwdne10550:0',
|
||||
'unaccepted_profile_warning' => 'crwdns12686:0crwdne12686:0',
|
||||
'start_date' => 'crwdns11168:0crwdne11168:0',
|
||||
'end_date' => 'crwdns11170:0crwdne11170:0',
|
||||
'alt_uploaded_image_thumbnail' => 'crwdns11172:0crwdne11172:0',
|
||||
|
@ -557,5 +558,8 @@ return [
|
|||
'expires' => 'crwdns12310:0crwdne12310:0',
|
||||
'map_fields'=> 'crwdns12572:0crwdne12572:0',
|
||||
'remaining_var' => 'crwdns12612:0crwdne12612:0',
|
||||
'assets_in_var' => 'crwdns12688:0crwdne12688:0',
|
||||
'label' => 'crwdns12690:0crwdne12690:0',
|
||||
'import_asset_tag_exists' => 'crwdns12692:0crwdne12692:0',
|
||||
|
||||
];
|
||||
|
|
|
@ -40,7 +40,9 @@ return [
|
|||
'ms-MY'=> 'crwdns11986:0crwdne11986:0',
|
||||
'mi-NZ'=> 'crwdns11988:0crwdne11988:0',
|
||||
'mn-MN'=> 'crwdns11990:0crwdne11990:0',
|
||||
'no-NO'=> 'crwdns11992:0crwdne11992:0',
|
||||
//'no-NO'=> 'Norwegian',
|
||||
'nb-NO'=> 'crwdns12644:0crwdne12644:0',
|
||||
//'nn-NO'=> 'Norwegian Nynorsk',
|
||||
'fa-IR'=> 'crwdns11994:0crwdne11994:0',
|
||||
'pl-PL'=> 'crwdns11996:0crwdne11996:0',
|
||||
'pt-PT'=> 'crwdns10634:0crwdne10634:0',
|
||||
|
|
|
@ -125,6 +125,8 @@ return [
|
|||
'symbols' => 'crwdns12504:0crwdne12504:0',
|
||||
'uncompromised' => 'crwdns12506:0crwdne12506:0',
|
||||
],
|
||||
'percent' => 'crwdns12660:0crwdne12660:0',
|
||||
|
||||
'present' => 'crwdns1936:0crwdne1936:0',
|
||||
'present_if' => 'crwdns12508:0crwdne12508:0',
|
||||
'present_unless' => 'crwdns12510:0crwdne12510:0',
|
||||
|
@ -188,6 +190,8 @@ return [
|
|||
'hashed_pass' => 'crwdns1946:0crwdne1946:0',
|
||||
'dumbpwd' => 'crwdns1947:0crwdne1947:0',
|
||||
'statuslabel_type' => 'crwdns1948:0crwdne1948:0',
|
||||
'custom_field_not_found' => 'crwdns12650:0crwdne12650:0',
|
||||
'custom_field_not_found_on_model' => 'crwdns12652:0crwdne12652:0',
|
||||
|
||||
// date_format validation with slightly less stupid messages. It duplicates a lot, but it gets the job done :(
|
||||
// We use this because the default error message for date_format is reflects php Y-m-d, which non-PHP
|
||||
|
|
|
@ -12,4 +12,6 @@ return array(
|
|||
'api_reference' => 'Please check the <a href="https://snipe-it.readme.io/reference" target="_blank">API reference</a> to find specific API endpoints and additional API documentation.',
|
||||
'profile_updated' => 'Account successfully updated',
|
||||
'no_tokens' => 'You have not created any personal access tokens.',
|
||||
'enable_sounds' => 'Enable sound effects',
|
||||
'enable_confetti' => 'Enable confetti effects',
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue