Compare commits

..

91 Commits

Author SHA1 Message Date
snipe
47c7061af5 Merge branch 'develop' 2017-10-23 20:11:01 -07:00
snipe
3799ab87ed Fixed search query for default sorting 2017-10-23 20:10:37 -07:00
snipe
6232a1f941 Merge branch 'develop' 2017-10-23 19:51:29 -07:00
snipe
f5ce580593 Bumped version 2017-10-23 19:51:14 -07:00
snipe
3afa5f7cda Merge branch 'develop' 2017-10-23 19:50:09 -07:00
snipe
17b271918f Fix date picker for custom fields 2017-10-23 19:47:43 -07:00
snipe
3922569d7f Merge branch 'develop' 2017-10-23 19:14:13 -07:00
Nicolai Essig
3a302fe2d7 ref #2737 prevent assets with "rtd_location_id" null values to be removed on location sort (#4283) 2017-10-23 18:28:06 -07:00
snipe
249f86bb21 Merge branch 'develop' 2017-10-23 17:50:58 -07:00
snipe
0951a756cc Updated passport to 3.0
Re: https://stackoverflow.com/a/45029309/200021
via @robertpearce
2017-10-23 17:35:31 -07:00
snipe
fc644925ea Fixes #4291 - adds phone to user listing 2017-10-23 14:21:51 -07:00
snipe
59c0b63ad0 Merge branch 'develop' 2017-10-20 20:22:42 -07:00
snipe
c0f8b3773c Temp fix for markdown stuff 2017-10-20 20:22:14 -07:00
snipe
fd210c6439 Fixes #4267 - email notifications showing model name as number 2017-10-20 18:58:11 -07:00
snipe
f7e23cf7c8 Fixes #4272 - adds serial to assigned assets view 2017-10-20 18:51:14 -07:00
snipe
387351d7ed Merge branch 'master' of github.com:snipe/snipe-it 2017-10-20 17:44:35 -07:00
snipe
6438d30afc Merge branch 'develop' 2017-10-20 17:44:28 -07:00
snipe
b4e1d37b16 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-20 17:44:04 -07:00
Brady Wetherington
8ac57d0121 Need to prefix status_id with assets. for uniqueness (#4279) 2017-10-20 17:37:46 -07:00
Kasey
d3b51715dc adding --force to php artistan snipeit:legacy-recrypt (#4232) 2017-10-20 17:21:21 -07:00
snipe
b215924b1a Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-20 17:18:13 -07:00
Brady Wetherington
25d3d66880 Add performance-improving indexes (#4278) 2017-10-20 17:17:11 -07:00
Brady Wetherington
189574377a Add 'where' clause to hasManyThrough relationship. (#4276) 2017-10-20 16:58:39 -07:00
snipe
341ddec9d8 Adds built-in mail notification vendor templates 2017-10-20 16:52:12 -07:00
Nicolai Essig
abcce78944 use translation for "All" in sidebar menu (#4268) 2017-10-20 00:20:33 -07:00
snipe
22e13cd4d2 Allow sorting on asset counts, disable delete button if the user has items checked out to them 2017-10-19 17:15:21 -07:00
snipe
5c105b52b6 Merge branch 'develop' 2017-10-19 16:45:22 -07:00
snipe
f757da1a98 Bumped hash 2017-10-19 16:45:04 -07:00
snipe
c1f8db37d9 Added note about saving before testing LDAP 2017-10-19 16:35:59 -07:00
snipe
cfc215a013 Merge branch 'develop' 2017-10-19 16:28:26 -07:00
snipe
4215a3257b Fixes #1044 - adds suppliers and image to accessories (#4266)
* Ignore accesories uploads

* API: Allow searching accessories by supplier id

* Adds suppliers and image upload to accessories

* Allow sorting by counts for suppliers

* Validate supplier image uploads

* Remove purchase_date from protected accessory array, it was converting it to datetime in datepicker
2017-10-19 16:25:24 -07:00
snipe
1f247ff541 Don’t let the user checkout an asset to itself
(We should consolidate that AssetCheckoutRequest for the API)
2017-10-19 15:51:55 -07:00
snipe
2fc46746e2 Adds translation string placeholders for new LDAP functionality 2017-10-19 12:22:54 -07:00
snipe
e185dc68af Fixes #4240 - allows admins to use custom password reset URL 2017-10-19 12:22:27 -07:00
snipe
54000ff69f Allows sorting by number of assets, etc in category 2017-10-19 11:48:09 -07:00
snipe
f6a38e848a Allows sorting by whether or not the category requires acceptance 2017-10-19 11:44:42 -07:00
snipe
7e8d670f81 Disable delete buttons if there are assets, etc 2017-10-19 11:41:35 -07:00
snipe
287b150b7f Show disabled delete button if thing can’t be deleted 2017-10-19 11:29:58 -07:00
snipe
b379656d55 Adds more consistent visual display of status label types 2017-10-19 11:06:55 -07:00
snipe
2e11a983c8 Nicer card display of status type explanations 2017-10-19 10:52:30 -07:00
snipe
a9753eb646 Include asset count in status labels overview 2017-10-19 10:48:15 -07:00
snipe
707c4db881 API: Check there are no assets associated before allowing delete 2017-10-19 10:39:08 -07:00
snipe
d1de34394e Removed stupid count method 2017-10-19 10:37:30 -07:00
snipe
e5719441bc Merge branch 'develop' 2017-10-19 08:36:05 -07:00
snipe
7153013fb0 Fake sending the test email if the app is in demo mode 2017-10-19 08:33:46 -07:00
snipe
55500f77fb Merge branch 'develop' 2017-10-19 08:20:44 -07:00
snipe
2b826c3adc Merge branch 'features/mail_test_button' into develop 2017-10-19 08:19:24 -07:00
snipe
cd193ce8bb Fixes #4036 - adds test email button to general settings 2017-10-19 08:18:56 -07:00
snipe
cb50142ba3 Update @thakilla as a contributor 2017-10-19 06:16:03 -07:00
snipe
81aaed92ce Change default session name 2017-10-19 06:08:58 -07:00
Nicolai Essig
4bc551db82 ref #4042 scale barcode with label size (#4258) 2017-10-19 06:08:01 -07:00
snipe
471d665408 Add @thakilla as a contributor 2017-10-19 06:07:42 -07:00
Robin Temme
068308ef56 Change changepassword menu icon to fixed width (#4262) 2017-10-19 06:04:02 -07:00
Nicolai Essig
1e65c7bf9a load custom css also on login page (#4260) 2017-10-19 06:01:41 -07:00
snipe
ae567c08db Fixes incorrect language reference for consumables on checkout if consumable doesn’t exist 2017-10-19 03:44:01 -07:00
snipe
0d25a4868a Merge branch 'develop' 2017-10-19 03:37:52 -07:00
snipe
13586be6b0 Fixed load error if license is invalid 2017-10-19 03:37:27 -07:00
snipe
44c649c3c8 Fixes #4256 - double encoding on user bulk checkin and delete blade 2017-10-19 03:17:55 -07:00
snipe
1a7bfb75fa Merge branch 'develop' 2017-10-19 02:22:39 -07:00
snipe
2b803a6a6c Fixes #4257 - use admin url when editing groups 2017-10-19 02:22:05 -07:00
snipe
92e4bdc02e Merge branch 'develop' 2017-10-19 02:09:06 -07:00
snipe
4d3d19ca2b Fixes older route reference in consumables 2017-10-19 02:07:31 -07:00
snipe
9c06912efd Small tweaks to prevent Chrome autofill 2017-10-19 01:59:13 -07:00
snipe
c5893b4445 Fixes #4249 - display deployed location in listing 2017-10-19 01:30:40 -07:00
snipe
b90933bb8b Fixes #4139 - fixed route for deleting file 2017-10-19 01:11:24 -07:00
snipe
fe9a90854d Adds d.m.Y as date format, per #2423 2017-10-18 10:30:25 -07:00
snipe
3b012f2827 Some advanced search query tweaks 2017-10-18 10:07:35 -07:00
snipe
e31dadc99a Merge branch 'develop' 2017-10-18 09:28:00 -07:00
snipe
5519e2d4ae Fixes custom fields sorting on asset listings
I need a silkwood shower :(
2017-10-18 09:27:34 -07:00
snipe
6285a8b5b2 Merge branch 'develop' 2017-10-18 08:54:35 -07:00
snipe
a3139c6fc6 Fix accessories route for invalid accessory ID 2017-10-18 08:53:25 -07:00
snipe
389ba9059f Merge branch 'develop' 2017-10-18 08:45:22 -07:00
snipe
c0e50be03e Duh. Helps if you actually assign the array first. 2017-10-18 08:45:05 -07:00
snipe
447833c996 Only try to process model bulk editing if at least one model was selected 2017-10-18 08:15:54 -07:00
snipe
809e310565 Recrypt the LDAP password properly
Older installs should add a line to their .env:

`LEGACY_CIPHER=rijndael-256`
2017-10-18 08:15:23 -07:00
snipe
38dd03b5ad Merge branch 'develop' 2017-10-18 07:16:19 -07:00
snipe
68f6385eba Fixes 500 in bulk checkout if no asset is selected 2017-10-18 07:15:16 -07:00
snipe
bd376a4992 Possible fix for #4227 2017-10-18 07:02:18 -07:00
snipe
97f65ceac0 Merge branch 'develop' 2017-10-18 06:25:01 -07:00
snipe
9e39abcc32 Merge branch 'develop' of github.com:snipe/snipe-it into develop 2017-10-18 06:24:41 -07:00
snipe
5cd2857d5d Use footer sumformatter for cost totals 2017-10-18 06:24:36 -07:00
snipe
585fcfb7d4 Use maintenances report API to populate the maintenances report 2017-10-18 05:47:47 -07:00
snipe
d9135a8aac Disallow deleting suppliers with associated assets, licenses or maintenances 2017-10-18 05:47:20 -07:00
snipe
081a64223a Add @TonisOrmisson as a contributor 2017-10-18 05:45:14 -07:00
Tõnis Ormisson
a4eeff01f0 FIXED upgrade Recrypt not working with changed cipher (#4245)
* FIX legacy cipher change

* FIX Recrypt Custom fields column names

* FIX ReCrypt Clean un-needed code
2017-10-18 05:43:54 -07:00
snipe
eedbe468ac Fix licenses not saving supplier id on edit 2017-10-18 05:43:15 -07:00
snipe
463d9513e2 Merge branch 'develop' 2017-10-18 04:49:16 -07:00
snipe
ed4aa7dec2 Account for deleted suppliers in asset maintenances report
This should all be reworked via the API though anyway
2017-10-18 04:48:52 -07:00
snipe
d6bbe15a76 Added upload state back to log test 2017-10-18 04:04:05 -07:00
snipe
ea0a0a4a69 Merge branch 'develop' 2017-10-18 02:35:43 -07:00
snipe
34442362ca Fixes bad route for new groups 2017-10-18 02:35:30 -07:00
105 changed files with 1705 additions and 637 deletions

View File

@@ -782,6 +782,24 @@
"contributions": [
"doc"
]
},
{
"login": "TonisOrmisson",
"name": "Tõnis Ormisson",
"avatar_url": "https://avatars1.githubusercontent.com/u/6357451?v=4",
"profile": "http://andmemasin.eu",
"contributions": [
"code"
]
},
{
"login": "thakilla",
"name": "Nicolai Essig",
"avatar_url": "https://avatars0.githubusercontent.com/u/449411?v=4",
"profile": "http://www.nicolai-essig.de",
"contributions": [
"code"
]
}
]
}

1
.gitignore vendored
View File

@@ -27,6 +27,7 @@ public/uploads/logo.png
public/uploads/logo.svg
public/uploads/models/*
public/uploads/suppliers/*
public/uploads/accessories/*
public/uploads/users/*
storage/app/private_uploads/users/*
storage/debugbar/

View File

@@ -1,5 +1,5 @@
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=develop)](https://travis-ci.org/snipe/snipe-it) [![Stories in Ready](https://badge.waffle.io/snipe/snipe-it.png?label=ready+for+dev&title=Ready+for+development)](http://waffle.io/snipe/snipe-it) [![Maintenance](https://img.shields.io/maintenance/yes/2017.svg)]() [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeyhead.svg?style=social)](https://twitter.com/snipeyhead) [![Zenhub](https://img.shields.io/badge/Shipping_faster_with-ZenHub-5e60ba.svg)](https://zenhub.io) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-84-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-86-orange.svg?style=flat-square)](#contributors)
## Snipe-IT - Open Source Asset Management System
@@ -68,6 +68,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars2.githubusercontent.com/u/857740?v=3" width="110px;"/><br /><sub>Gil Rutkowski</sub>](http://FlashingCursor.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=flashingcursor "Code") | [<img src="https://avatars3.githubusercontent.com/u/129360?v=3" width="110px;"/><br /><sub>Desmond Morris</sub>](http://www.desmondmorris.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=desmondmorris "Code") | [<img src="https://avatars2.githubusercontent.com/u/52936?v=3" width="110px;"/><br /><sub>Nick Peelman</sub>](http://peelman.us)<br />[💻](https://github.com/snipe/snipe-it/commits?author=peelman "Code") | [<img src="https://avatars0.githubusercontent.com/u/53161?v=3" width="110px;"/><br /><sub>Abraham Vegh</sub>](https://abrahamvegh.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=abrahamvegh "Code") | [<img src="https://avatars0.githubusercontent.com/u/2818680?v=3" width="110px;"/><br /><sub>Mohamed Rashid</sub>](https://github.com/rashivkp)<br />[📖](https://github.com/snipe/snipe-it/commits?author=rashivkp "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1509456?v=3" width="110px;"/><br /><sub>Kasey</sub>](http://hinchk.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=HinchK "Code") | [<img src="https://avatars2.githubusercontent.com/u/10522541?v=3" width="110px;"/><br /><sub>Brett</sub>](https://github.com/BrettFagerlund)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=BrettFagerlund "Tests") |
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -16,7 +16,8 @@ class RecryptFromMcrypt extends Command
*
* @var string
*/
protected $signature = 'snipeit:legacy-recrypt';
protected $signature = 'snipeit:legacy-recrypt
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
/**
* The console command description.
@@ -48,6 +49,7 @@ class RecryptFromMcrypt extends Command
// If not, we can try to use the current APP_KEY if looks like it's old
$legacy_key = env('LEGACY_APP_KEY');
$key_parts = explode(':', $legacy_key);
$legacy_cipher = env('LEGACY_CIPHER');
$errors = array();
if (!$legacy_key) {
@@ -60,6 +62,7 @@ class RecryptFromMcrypt extends Command
if (strlen($legacy_key) == 32) {
$legacy_length_check = true;
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1])==44)) {
$legacy_key = base64_decode($key_parts[1],true);
$legacy_length_check = true;
} else {
$legacy_length_check = false;
@@ -79,7 +82,9 @@ class RecryptFromMcrypt extends Command
$this->error('================================!!!! WARNING !!!!================================');
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
if ($this->confirm("Are you SURE you wish to continue?")) {
$force = ($this->option('force')) ? true : false;
if ($force || ($this->confirm("Are you SURE you wish to continue?"))) {
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
@@ -91,13 +96,21 @@ class RecryptFromMcrypt extends Command
}
$mcrypter = new McryptEncrypter($legacy_key);
if ($legacy_cipher){
$mcrypter = new McryptEncrypter($legacy_key,$legacy_cipher);
}else{
$mcrypter = new McryptEncrypter($legacy_key);
}
$settings = Setting::getSettings();
if ($settings->ldap_password=='') {
if ($settings->ldap_pword=='') {
$this->comment('INFO: No LDAP password found. Skipping... ');
} else {
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
$settings->ldap_pword = \Crypt::encrypt($decrypted_ldap_pword);
$settings->save();
}
/** @var CustomField[] $custom_fields */
$custom_fields = CustomField::where('field_encrypted','=', 1)->get();
$this->comment('INFO: Retrieving encrypted custom fields...');
@@ -110,32 +123,22 @@ class RecryptFromMcrypt extends Command
// Get all assets with a value in any of the fields that were encrypted
/** @var Asset[] $assets */
$assets = $query->get();
$bar = $this->output->createProgressBar(count($assets));
foreach ($custom_fields as $encrypted_field) {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($encrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
}
$bar->advance();
}
foreach ($assets as $asset) {
foreach ($custom_fields as $encrypted_field) {
$columnName = $encrypted_field->db_column;
// Make sure the value isn't null
if ($asset->{$encrypted_field}!='') {
if ($asset->{$columnName}!='') {
// Try to decrypt the payload using the legacy app key
try {
$decrypted_field = $mcrypter->decrypt($asset->{$encrypted_field});
$asset->{$encrypted_field} = \Crypt::encrypt($decrypted_field);
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
$asset->{$columnName} = \Crypt::encrypt($decrypted_field);
$this->comment($decrypted_field);
} catch (\Exception $e) {
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();

View File

@@ -7,6 +7,6 @@ class CheckoutNotAllowed extends Exception
{
public function __toString()
{
"A checkout is not allowed under these circumstances";
return "A checkout is not allowed under these circumstances";
}
}

View File

@@ -18,6 +18,8 @@ use Illuminate\Http\Request;
use Slack;
use Str;
use View;
use Image;
use App\Http\Requests\ImageUploadRequest;
/** This controller handles all actions related to Accessories for
* the Snipe-IT Asset Management application.
@@ -57,6 +59,7 @@ class AccessoriesController extends Controller
->with('item', new Accessory)
->with('category_list', Helper::categoryList('accessory'))
->with('company_list', Helper::companyList())
->with('supplier_list', Helper::suppliersList())
->with('location_list', Helper::locationsList())
->with('manufacturer_list', Helper::manufacturerList());
}
@@ -68,7 +71,7 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return Redirect
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
$this->authorize(Accessory::class);
// create a new model instance
@@ -87,6 +90,28 @@ class AccessoriesController extends Controller
$accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost'));
$accessory->qty = request('qty');
$accessory->user_id = Auth::user()->id;
$accessory->supplier_id = request('supplier_id');
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
$path = public_path('/uploads/accessories');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 250, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
$accessory->image = $file_name;
}
}
// Was the accessory created?
if ($accessory->save()) {
@@ -116,6 +141,7 @@ class AccessoriesController extends Controller
->with('category_list', Helper::categoryList('accessory'))
->with('company_list', Helper::companyList())
->with('location_list', Helper::locationsList())
->with('supplier_list', Helper::suppliersList())
->with('manufacturer_list', Helper::manufacturerList());
}
@@ -127,7 +153,7 @@ class AccessoriesController extends Controller
* @param int $accessoryId
* @return Redirect
*/
public function update(Request $request, $accessoryId = null)
public function update(ImageUploadRequest $request, $accessoryId = null)
{
if (is_null($accessory = Accessory::find($accessoryId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
@@ -144,11 +170,38 @@ class AccessoriesController extends Controller
$accessory->manufacturer_id = request('manufacturer_id');
$accessory->order_number = request('order_number');
$accessory->model_number = request('model_number');
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost');
$accessory->purchase_date = request('purchase_date');
$accessory->purchase_cost = request('purchase_cost');
$accessory->qty = request('qty');
$accessory->supplier_id = request('supplier_id');
// Was the accessory updated?
if ($request->hasFile('image')) {
if (!config('app.lock_passwords')) {
$image = $request->file('image');
$ext = $image->getClientOriginalExtension();
$file_name = "accessory-".str_random(18).'.'.$ext;
$path = public_path('/uploads/accessories');
if ($image->getClientOriginalExtension()!='svg') {
Image::make($image->getRealPath())->resize(null, 250, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path.'/'.$file_name);
} else {
$image->move($path, $file_name);
}
if (($accessory->image) && (file_exists($path.'/'.$accessory->image))) {
unlink($path.'/'.$accessory->image);
}
$accessory->image = $file_name;
}
}
// Was the accessory updated?
if ($accessory->save()) {
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
}
@@ -197,11 +250,7 @@ class AccessoriesController extends Controller
if (isset($accessory->id)) {
return view('accessories/view', compact('accessory'));
}
// Prepare the error message
$error = trans('admin/accessories/message.does_not_exist', compact('id'));
// Redirect to the user management page
return redirect()->route('accessories')->with('error', $error);
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', compact('id')));
}
/**

View File

@@ -38,6 +38,10 @@ class AccessoriesController extends Controller
$accessories->where('manufacturer_id','=',$request->input('manufacturer_id'));
}
if ($request->has('supplier_id')) {
$accessories->where('supplier_id','=',$request->input('supplier_id'));
}
$offset = $request->input('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';

View File

@@ -86,19 +86,19 @@ class AssetsController extends Controller
$assets = Company::scopeCompanyables(Asset::select('assets.*'))->with(
'assetloc', 'assetstatus', 'defaultLoc', 'assetlog', 'company',
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
// If we should search on everything
if (($request->has('search')) && (count($filter) == 0)) {
if (count($filter) > 0) {
$assets->ByFilter($filter);
} elseif ($request->has('search')) {
$assets->TextSearch($request->input('search'));
// otherwise loop through the filters and search strictly on them
} else {
if (count($filter) > 0) {
$assets->ByFilter($filter);
}
}
// These are used by the API to query against specific ID numbers
if ($request->has('status_id')) {
$assets->where('status_id', '=', $request->input('status_id'));
$assets->where('assets.status_id', '=', $request->input('status_id'));
}
if ($request->has('model_id')) {
@@ -196,11 +196,17 @@ class AssetsController extends Controller
}
// This is kinda gross, but we need to do this because the Bootstrap Tables
// API passes custom field ordering as custom_fields.fieldname, and we have to strip
// that out to let the default sorter below order them correctly on the assets table.
$sort_override = str_replace('custom_fields.','', $request->input('sort')) ;
// This handles all of the pivot sorting (versus the assets.* fields in the allowed_columns array)
$column_sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets.created_at';
switch ($request->input('sort')) {
// This handles all of the pivot sorting (versus the assets.* fields
// in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
@@ -451,16 +457,28 @@ class AssetsController extends Controller
$this->authorize('checkout', $asset);
$error_payload = [];
$error_payload['asset'] = [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
];
if ($request->has('user_id')) {
$target = User::find($request->input('user_id'));
$error_payload['target_id'] = $request->input('user_id');
$error_payload['target_type'] = User::class;
// Don't let the user check an asset out to itself
} elseif ($request->has('asset_id')) {
$target = Asset::find($request->input('asset_id'));
$target = Asset::where('id','!=',$asset_id)->find($request->input('asset_id'));
$error_payload['target_id'] = $request->input('asset_id');
$error_payload['target_type'] = Asset::class;
} elseif ($request->has('location_id')) {
$target = Location::find($request->input('location_id'));
$error_payload['target_id'] = $request->input('location_id');
$error_payload['target_type'] = Location::class;
}
if (!isset($target)) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], 'No valid checkout target specified for asset '.e($asset->asset_tag).'.'));
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'No valid checkout target specified for asset '.e($asset->asset_tag).'.'));
}
$checkout_at = request('checkout_at', date("Y-m-d H:i:s"));

View File

@@ -20,7 +20,7 @@ class CategoriesController extends Controller
public function index(Request $request)
{
$this->authorize('view', Category::class);
$allowed_columns = ['id', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email'];
$allowed_columns = ['id', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count'];
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email'])
->withCount('assets', 'accessories', 'consumables', 'components');
@@ -32,7 +32,7 @@ class CategoriesController extends Controller
$offset = $request->input('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
$categories->orderBy($sort, $order);
$total = $categories->count();

View File

@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Models\Ldap;
use Validator;
use App\Models\Setting;
use Mail;
class SettingsController extends Controller
{
@@ -160,4 +161,31 @@ class SettingsController extends Controller
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
if (!config('app.lock_passwords')) {
try {
Mail::send('emails.test', [], function ($m) {
$m->to(config('mail.from.address'), config('mail.from.name'));
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.test_email'));
});
return response()->json(['message' => 'Mail sent to '.config('mail.from.address')], 200);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
return response()->json(['message' => 'Mail would have been sent, but this application is in demo mode! '], 200);
}
}

View File

@@ -22,7 +22,7 @@ class StatuslabelsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Statuslabel::class);
$allowed_columns = ['id','name','created_at'];
$allowed_columns = ['id','name','created_at', 'assets_count'];
$statuslabels = Statuslabel::withCount('assets');
@@ -137,8 +137,14 @@ class StatuslabelsController extends Controller
$this->authorize('delete', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$this->authorize('delete', $statuslabel);
$statuslabel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
// Check that there are no assets associated
if ($statuslabel->assets()->count() == 0) {
$statuslabel->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/statuslabels/message.assoc_assets')));
}

View File

@@ -20,11 +20,11 @@ class SuppliersController extends Controller
public function index(Request $request)
{
$this->authorize('view', Supplier::class);
$allowed_columns = ['id','name','address','phone','contact','fax','email'];
$allowed_columns = ['id','name','address','phone','contact','fax','email','image','assets_count','licenses_count', 'accessories_count'];
$suppliers = Supplier::select(
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at')
)->withCount('assets')->withCount('licenses')->whereNull('deleted_at');
)->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at');
if ($request->has('search')) {
@@ -113,8 +113,22 @@ class SuppliersController extends Controller
public function destroy($id)
{
$this->authorize('delete', Supplier::class);
$supplier = Supplier::findOrFail($id);
$supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances','assets', 'licenses')->findOrFail($id);
$this->authorize('delete', $supplier);
if ($supplier->assets_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
}
if ($supplier->asset_maintenances_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count])));
}
if ($supplier->licenses_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
}
$supplier->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));

View File

@@ -31,6 +31,7 @@ class UsersController extends Controller
'users.two_factor_enrolled',
'users.jobtitle',
'users.email',
'users.phone',
'users.username',
'users.location_id',
'users.manager_id',
@@ -92,7 +93,8 @@ class UsersController extends Controller
[
'last_name','first_name','email','jobtitle','username','employee_num',
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
'two_factor_enrolled','two_factor_optin','last_login'
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
'consumables_count', 'accessories_count', 'phone'
];
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';

View File

@@ -376,13 +376,16 @@ class AssetModelsController extends Controller
*/
public function postBulkEdit(Request $request)
{
$models_raw_array = Input::get('ids');
$models = AssetModel::whereIn('id', $models_raw_array)->get();
$nochange = ['NC' => 'No Change'];
$fieldset_list = $nochange + Helper::customFieldsetList();
$depreciation_list = $nochange + Helper::depreciationList();
$category_list = $nochange + Helper::categoryList('asset');
$manufacturer_list = $nochange + Helper::manufacturerList();
if (is_array($models_raw_array)) {
$models = AssetModel::whereIn('id', $models_raw_array)->get();
$nochange = ['NC' => 'No Change'];
$fieldset_list = $nochange + Helper::customFieldsetList();
$depreciation_list = $nochange + Helper::depreciationList();
$category_list = $nochange + Helper::categoryList('asset');
$manufacturer_list = $nochange + Helper::manufacturerList();
return view('models/bulk-edit', compact('models'))
@@ -390,6 +393,10 @@ class AssetModelsController extends Controller
->with('category_list', $category_list)
->with('fieldset_list', $fieldset_list)
->with('depreciation_list', $depreciation_list);
}
return redirect()->route('models.index')
->with('error', 'You must select at least one model to edit.');
}

View File

@@ -458,7 +458,7 @@ class AssetsController extends Controller
if (request('assigned_user')) {
$target = User::find(request('assigned_user'));
} elseif (request('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
$target = Asset::where('id','!=',$assetId)->find(request('assigned_asset'));
} elseif (request('assigned_location')) {
$target = Location::find(request('assigned_location'));
}
@@ -675,8 +675,11 @@ class AssetsController extends Controller
$header = ['Content-type' => 'image/png'];
return response()->file($barcode_file, $header);
} else {
// Calculate barcode width in pixel based on label width (inch)
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 96.000000000001;
$barcode = new \Com\Tecnick\Barcode\Barcode();
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,300,50);
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,($barcode_width < 300 ? $barcode_width : 300),50);
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
@@ -1179,6 +1182,9 @@ class AssetsController extends Controller
$user = User::find(e(Input::get('assigned_to')));
$admin = Auth::user();
if (!is_array(Input::get('selected_assets'))) {
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
}
$asset_ids = array_filter(Input::get('selected_assets'));
if ((Input::has('checkout_at')) && (Input::get('checkout_at')!= date("Y-m-d"))) {

View File

@@ -194,7 +194,7 @@ class ConsumablesController extends Controller
if (isset($consumable->id)) {
return view('consumables/view', compact('consumable'));
}
return redirect()->route('consumables')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
}
/**
@@ -209,7 +209,7 @@ class ConsumablesController extends Controller
public function getCheckout($consumableId)
{
if (is_null($consumable = Consumable::find($consumableId))) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
}
$this->authorize('checkout', $consumable);
return view('consumables/checkout', compact('consumable'))->with('users_list', Helper::usersList());

View File

@@ -187,6 +187,7 @@ class LicensesController extends Controller
$license->termination_date = $request->input('termination_date');
$license->seats = e($request->input('seats'));
$license->manufacturer_id = $request->input('manufacturer_id');
$license->supplier_id = $request->input('supplier_id');
if ($license->save()) {
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
@@ -440,17 +441,15 @@ class LicensesController extends Controller
public function show($licenseId = null)
{
$license = License::find($licenseId);
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
$license = License::with('assignedusers', 'licenseSeats.user', 'licenseSeats.asset')->find($licenseId);
if (isset($license->id)) {
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
if ($license) {
$this->authorize('view', $license);
return view('licenses/view', compact('license'));
}
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
return redirect()->route('licenses.index')->with('error', $error);
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
}
public function getClone($licenseId = null)
{

View File

@@ -21,6 +21,7 @@ use App\Models\User;
use App\Http\Requests\SetupUserRequest;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\SettingsLdapRequest;
use App\Helpers\Helper;
/**
* This controller handles all actions related to Settings for
@@ -136,28 +137,6 @@ class SettingsController extends Controller
->with('section', 'Pre-Flight Check');
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
try {
Mail::send('emails.test', [], function ($m) {
$m->to(config('mail.from.address'), config('mail.from.name'));
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.test_email'));
});
return 'success';
} catch (Exception $e) {
return 'error';
}
}
/**
* Save the first admin user from Setup.
@@ -847,6 +826,7 @@ class SettingsController extends Controller
$setting->is_ad = $request->input('is_ad', '0');
$setting->ldap_tls = $request->input('ldap_tls', '0');
$setting->ldap_pw_sync = $request->input('ldap_pw_sync', '0');
$setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url');
if ($setting->save()) {
return redirect()->route('settings.index')
@@ -1024,4 +1004,28 @@ class SettingsController extends Controller
public function api() {
return view('settings.api');
}
/**
* Test the email configuration
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function ajaxTestEmail()
{
try {
Mail::send('emails.test', [], function ($m) {
$m->to(config('mail.from.address'), config('mail.from.name'));
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.test_email'));
});
return response()->json(Helper::formatStandardApiResponse('success', null, 'Maiol sent!'));
} catch (Exception $e) {
return response()->json(Helper::formatStandardApiResponse('success', null, $e->getMessage()));
}
}
}

View File

@@ -200,17 +200,15 @@ class StatuslabelsController extends Controller
{
// Check if the Statuslabel exists
if (is_null($statuslabel = Statuslabel::find($statuslabelId))) {
// Redirect to the blogs management page
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.not_found'));
}
if ($statuslabel->has_assets() == 0) {
// Check that there are no assets associated
if ($statuslabel->assets()->count() == 0) {
$statuslabel->delete();
// Redirect to the statuslabels management page
return redirect()->route('statuslabels.index')->with('success', trans('admin/statuslabels/message.delete.success'));
}
// Redirect to the asset management page
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.assoc_assets'));
}

View File

@@ -13,6 +13,7 @@ use Str;
use View;
use Auth;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -56,7 +57,7 @@ class SuppliersController extends Controller
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(ImageUploadRequest $request)
{
// Create a new supplier
$supplier = new Supplier;
@@ -135,7 +136,7 @@ class SuppliersController extends Controller
* @param int $supplierId
* @return \Illuminate\Http\RedirectResponse
*/
public function update($supplierId = null, Request $request)
public function update($supplierId = null, ImageUploadRequest $request)
{
// Check if the supplier exists
if (is_null($supplier = Supplier::find($supplierId))) {
@@ -158,18 +159,17 @@ class SuppliersController extends Controller
$supplier->url = $supplier->addhttp(request('url'));
$supplier->notes = request('notes');
if (Input::file('image')) {
$image = $request->file('image');
$file_name = str_random(25).".".$image->getClientOriginalExtension();
$file_name = 'suppliers-'.str_random(25).".".$image->getClientOriginalExtension();
$path = public_path('uploads/suppliers/'.$file_name);
Image::make($image->getRealPath())->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
$supplier->image = $file_name;
}
if (request('image_delete') == 1 && $request->file('image') == "") {
} elseif (request('image_delete') == 1) {
$supplier->image = null;
}
@@ -189,23 +189,29 @@ class SuppliersController extends Controller
*/
public function destroy($supplierId)
{
// Check if the supplier exists
if (is_null($supplier = Supplier::find($supplierId))) {
// Redirect to the suppliers page
if (is_null($supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances','assets','licenses')->find($supplierId))) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
}
if ($supplier->num_assets() == 0) {
// Delete the supplier
$supplier->delete();
// Redirect to the suppliers management page
return redirect()->route('suppliers.index')->with(
'success',
trans('admin/suppliers/message.delete.success')
);
if ($supplier->assets_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count]));
}
// Redirect to the asset management page
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.assoc_users'));
if ($supplier->asset_maintenances_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count]));
}
if ($supplier->licenses_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count]));
}
$supplier->delete();
return redirect()->route('suppliers.index')->with('success',
trans('admin/suppliers/message.delete.success')
);
}

View File

@@ -23,10 +23,13 @@ class AssetCheckoutRequest extends Request
*/
public function rules()
{
return [
$rules = [
"assigned_user" => 'required_without_all:assigned_asset,assigned_location',
"assigned_asset" => 'required_without_all:assigned_user,assigned_location',
"assigned_asset" => 'required_without_all:assigned_user,assigned_location|different:'.$this->id,
"assigned_location" => 'required_without_all:assigned_user,assigned_asset',
];
return $rules;
}
}

View File

@@ -25,6 +25,7 @@ class AccessoriesTransformer
'name' => e($accessory->name),
'company' => ($accessory->company) ? ['id' => $accessory->company->id,'name'=> e($accessory->company->name)] : null,
'manufacturer' => ($accessory->manufacturer) ? ['id' => $accessory->manufacturer->id,'name'=> e($accessory->manufacturer->name)] : null,
'supplier' => ($accessory->supplier) ? ['id' => $accessory->supplier->id,'name'=> e($accessory->supplier->name)] : null,
'model_number' => ($accessory->model_number) ? e($accessory->model_number) : null,
'category' => ($accessory->category) ? ['id' => $accessory->category->id,'name'=> e($accessory->category->name)] : null,
'location' => ($accessory->location) ? ['id' => $accessory->location->id,'name'=> e($accessory->location->name)] : null,
@@ -35,6 +36,7 @@ class AccessoriesTransformer
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
'remaining_qty' => $accessory->numRemaining(),
'image' => ($accessory->image) ? url('/').'/uploads/accessories/'.e($accessory->image) : null,
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),

View File

@@ -54,9 +54,9 @@ class AssetsTransformer
'id' => (int) $asset->company->id,
'name'=> e($asset->company->name)
] : null,
'location' => ($asset->assetloc) ? [
'id' => (int) $asset->assetloc->id,
'name'=> e($asset->assetloc->name)
'location' => ($asset->assetLoc) ? [
'id' => (int) $asset->assetLoc->id,
'name'=> e($asset->assetLoc->name)
] : null,
'rtd_location' => ($asset->defaultLoc) ? [
'id' => (int) $asset->defaultLoc->id,

View File

@@ -39,7 +39,7 @@ class CategoriesTransformer
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', Category::class) ? true : false,
'delete' => Gate::allows('delete', Category::class) ? true : false,
'delete' => (Gate::allows('delete', Category::class) && ($category->assets_count == 0) && ($category->accessories_count == 0) && ($category->consumables_count == 0) && ($category->components_count == 0)) ? true : false,
];
$array += $permissions_array;

View File

@@ -26,6 +26,7 @@ class StatuslabelsTransformer
'type' => $statuslabel->getStatuslabelType(),
'color' => ($statuslabel->color) ? e($statuslabel->color) : null,
'show_in_nav' => ($statuslabel->show_in_nav=='1') ? true : false,
'assets_count' => (int) $statuslabel->assets_count,
'notes' => e($statuslabel->notes),
'created_at' => Helper::getFormattedDateObject($statuslabel->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($statuslabel->updated_at, 'datetime'),
@@ -33,7 +34,7 @@ class StatuslabelsTransformer
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', Statuslabel::class) ? true : false,
'delete' => Gate::allows('delete', Statuslabel::class) ? true : false,
'delete' => (Gate::allows('delete', Statuslabel::class) && ($statuslabel->assets_count == 0)) ? true : false,
];
$array += $permissions_array;

View File

@@ -36,8 +36,9 @@ class SuppliersTransformer
'email' => ($supplier->email) ? e($supplier->email) : null,
'contact' => ($supplier->contact) ? e($supplier->contact) : null,
'assets_count' => (int) $supplier->assets_count,
'accessories_count' => (int) $supplier->accessories_count,
'licenses_count' => (int) $supplier->licenses_count,
'image' => ($supplier->image) ? e($supplier->image) : null,
'image' => ($supplier->image) ? url('/').'/uploads/suppliers/'.e($supplier->image) : null,
'notes' => ($supplier->notes) ? e($supplier->notes) : null,
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
@@ -46,7 +47,7 @@ class SuppliersTransformer
$permissions_array['available_actions'] = [
'update' => Gate::allows('update', Supplier::class) ? true : false,
'delete' => Gate::allows('delete', Supplier::class) ? true : false,
'delete' => (Gate::allows('delete', Supplier::class) && ($supplier->assets_count == 0) && ($supplier->licenses_count == 0) && ($supplier->accessories_count == 0)) ? true : false,
];
$array += $permissions_array;

View File

@@ -33,6 +33,7 @@ class UsersTransformer
'name'=> e($user->manager->username)
] : null,
'jobtitle' => ($user->jobtitle) ? e($user->jobtitle) : null,
'phone' => ($user->phone) ? e($user->phone) : null,
'email' => e($user->email),
'department' => ($user->department) ? [
'id' => (int) $user->department->id,
@@ -58,7 +59,7 @@ class UsersTransformer
$permissions_array['available_actions'] = [
'update' => (Gate::allows('update', User::class) && ($user->deleted_at=='')) ? true : false,
'delete' => (Gate::allows('delete', User::class) && ($user->deleted_at=='')) ? true : false,
'delete' => (Gate::allows('delete', User::class) && ($user->deleted_at=='') && ($user->assets_count == 0) && ($user->licenses_count == 0) && ($user->accessories_count == 0) && ($user->consumables_count == 0)) ? true : false,
'clone' => (Gate::allows('create', User::class) && ($user->deleted_at=='')) ,
'restore' => (Gate::allows('create', User::class) && ($user->deleted_at!='')) ? true : false,
];

View File

@@ -17,7 +17,7 @@ class Accessory extends SnipeModel
use Loggable, Presentable;
use SoftDeletes;
protected $dates = ['deleted_at', 'purchase_date'];
protected $dates = ['deleted_at'];
protected $table = 'accessories';
protected $casts = [
'requestable' => 'boolean'
@@ -61,10 +61,19 @@ class Accessory extends SnipeModel
'purchase_date',
'model_number',
'manufacturer_id',
'supplier_id',
'image',
'qty',
'requestable'
];
public function supplier()
{
return $this->belongsTo('\App\Models\Supplier', 'supplier_id');
}
public function setRequestableAttribute($value)
{
if ($value == '') {

View File

@@ -812,48 +812,50 @@ class Asset extends Depreciable
{
return $query->where(function ($query) use ($filter) {
foreach ($filter as $key => $search_val) {
if ($key =='asset_tag') {
$fieldname = str_replace('custom_fields.','', $key) ;
if ($fieldname =='asset_tag') {
$query->where('assets.asset_tag', 'LIKE', '%'.$search_val.'%');
}
if ($key =='name') {
if ($fieldname =='name') {
$query->where('assets.name', 'LIKE', '%'.$search_val.'%');
}
if ($key =='product_key') {
if ($fieldname =='product_key') {
$query->where('assets.serial', 'LIKE', '%'.$search_val.'%');
}
if ($key =='purchase_date') {
if ($fieldname =='purchase_date') {
$query->where('assets.purchase_date', 'LIKE', '%'.$search_val.'%');
}
if ($key =='purchase_cost') {
if ($fieldname =='purchase_cost') {
$query->where('assets.purchase_cost', 'LIKE', '%'.$search_val.'%');
}
if ($key =='notes') {
if ($fieldname =='notes') {
$query->where('assets.notes', 'LIKE', '%'.$search_val.'%');
}
if ($key =='order_number') {
if ($fieldname =='order_number') {
$query->where('assets.order_number', 'LIKE', '%'.$search_val.'%');
}
if ($key =='status_label') {
if ($fieldname =='status_label') {
$query->whereHas('assetstatus', function ($query) use ($search_val) {
$query->where('status_labels.name', 'LIKE', '%' . $search_val . '%');
});
}
if ($key =='location') {
if ($fieldname =='location') {
$query->whereHas('defaultLoc', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%' . $search_val . '%');
});
}
if ($key =='checkedout_to') {
if ($fieldname =='checkedout_to') {
$query->whereHas('assigneduser', function ($query) use ($search_val) {
$query->where(function ($query) use ($search_val) {
$query->where('users.first_name', 'LIKE', '%' . $search_val . '%')
@@ -863,7 +865,7 @@ class Asset extends Depreciable
}
if ($key =='manufacturer') {
if ($fieldname =='manufacturer') {
$query->whereHas('model', function ($query) use ($search_val) {
$query->whereHas('manufacturer', function ($query) use ($search_val) {
$query->where(function ($query) use ($search_val) {
@@ -873,7 +875,7 @@ class Asset extends Depreciable
});
}
if ($key =='category') {
if ($fieldname =='category') {
$query->whereHas('model', function ($query) use ($search_val) {
$query->whereHas('category', function ($query) use ($search_val) {
$query->where(function ($query) use ($search_val) {
@@ -885,7 +887,7 @@ class Asset extends Depreciable
});
}
if ($key =='model') {
if ($fieldname =='model') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('model', function ($query) use ($search_val) {
$query->where('models.name', 'LIKE', '%' . $search_val . '%');
@@ -893,7 +895,7 @@ class Asset extends Depreciable
});
}
if ($key =='model_number') {
if ($fieldname =='model_number') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('model', function ($query) use ($search_val) {
$query->where('models.model_number', 'LIKE', '%' . $search_val . '%');
@@ -902,7 +904,7 @@ class Asset extends Depreciable
}
if ($key =='company') {
if ($fieldname =='company') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('company', function ($query) use ($search_val) {
$query->where('companies.name', 'LIKE', '%' . $search_val . '%');
@@ -911,11 +913,9 @@ class Asset extends Depreciable
}
}
foreach (CustomField::all() as $field) {
if (array_key_exists('custom_fields.'.$field->db_column_name(), $filter)) {
$query->orWhere($field->db_column_name(), 'LIKE', '%' . $search_val . '%');
}
}
$query->orWhere('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
});
@@ -1060,7 +1060,7 @@ class Asset extends Depreciable
*/
public function scopeOrderLocation($query, $order)
{
return $query->join('locations', 'locations.id', '=', 'assets.rtd_location_id')->orderBy('locations.name', $order);
return $query->leftJoin('locations', 'locations.id', '=', 'assets.rtd_location_id')->orderBy('locations.name', $order);
}

View File

@@ -54,7 +54,7 @@ class Location extends SnipeModel
public function assets()
{
return $this->hasManyThrough('\App\Models\Asset', '\App\Models\User', 'location_id', 'assigned_to', 'id');
return $this->hasManyThrough('\App\Models\Asset', '\App\Models\User', 'location_id', 'assigned_to', 'id')->where("assets.assigned_type",User::class);
}
public function locationAssets()

View File

@@ -38,6 +38,7 @@ class Setting extends Model
"pwd_secure_min" => "numeric|required|min:5",
"audit_warning_days" => "numeric|nullable",
"audit_interval" => "numeric|nullable",
"custom_forgot_pass_url" => "url|nullable",
];
protected $fillable = ['site_name','email_domain','email_format','username_format'];

View File

@@ -29,16 +29,6 @@ class Statuslabel extends SnipeModel
protected $fillable = ['name', 'deployable', 'pending', 'archived'];
/**
* Show count of assets with status label
*
* @todo Remove this. It's dumb.
* @return \Illuminate\Support\Collection
*/
public function has_assets()
{
return $this->hasMany('\App\Models\Asset', 'status_id')->count();
}
/**
* Get assets with associated status label
@@ -64,6 +54,27 @@ class Statuslabel extends SnipeModel
}
}
public function scopePending()
{
return $this->where('pending', '=', 1)
->where('archived', '=', 0)
->where('deployable', '=', 0);
}
public function scopeArchived()
{
return $this->where('pending', '=', 0)
->where('archived', '=', 1)
->where('deployable', '=', 0);
}
public function scopeDeployable()
{
return $this->where('pending', '=', 0)
->where('archived', '=', 0)
->where('deployable', '=', 1);
}
public static function getStatuslabelTypesForDB($type)
{

View File

@@ -68,6 +68,11 @@ class Supplier extends SnipeModel
return $this->hasMany('\App\Models\Asset', 'supplier_id');
}
public function accessories()
{
return $this->hasMany('\App\Models\Accessory', 'supplier_id');
}
public function asset_maintenances()
{
return $this->hasMany('\App\Models\AssetMaintenance', 'supplier_id');

View File

@@ -403,6 +403,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
->orWhere('users.email', 'LIKE', "%$search%")
->orWhere('users.username', 'LIKE', "%$search%")
->orWhere('users.notes', 'LIKE', "%$search%")
->orWhere('users.phone', 'LIKE', "%$search%")
->orWhere('users.jobtitle', 'LIKE', "%$search%")
->orWhere('users.employee_num', 'LIKE', "%$search%")
->orWhere(function ($query) use ($search) {

View File

@@ -31,6 +31,14 @@ class AccessoryPresenter extends Presenter
"switchable" => true,
"title" => trans('general.id'),
"visible" => false
],[
"field" => "image",
"searchable" => false,
"sortable" => true,
"switchable" => true,
"title" => trans('admin/hardware/table.image'),
"visible" => true,
"formatter" => "imageFormatter"
], [
"field" => "company",
"searchable" => true,
@@ -63,6 +71,14 @@ class AccessoryPresenter extends Presenter
"sortable" => true,
"title" => trans('general.manufacturer'),
"formatter" => "manufacturersLinkObjFormatter",
], [
"field" => "supplier",
"searchable" => true,
"sortable" => true,
"switchable" => true,
"title" => trans('general.supplier'),
"visible" => false,
"formatter" => "suppliersLinkObjFormatter"
], [
"field" => "location",
"searchable" => true,
@@ -96,6 +112,7 @@ class AccessoryPresenter extends Presenter
"searchable" => true,
"sortable" => true,
"title" => trans('general.purchase_cost'),
"footerFormatter" => 'sumFormatter',
], [
"field" => "order_number",
"searchable" => true,

View File

@@ -112,7 +112,7 @@ class AssetPresenter extends Presenter
"sortable" => true,
"title" => trans('admin/hardware/table.location'),
"visible" => true,
"formatter" => "locationsLinkObjFormatter"
"formatter" => "deployedLocationFormatter"
], [
"field" => "manufacturer",
"searchable" => true,
@@ -139,6 +139,7 @@ class AssetPresenter extends Presenter
"searchable" => true,
"sortable" => true,
"title" => trans('general.purchase_cost'),
"footerFormatter" => 'sumFormatter',
], [
"field" => "order_number",
"searchable" => true,

View File

@@ -41,25 +41,25 @@ class CategoryPresenter extends Presenter
], [
"field" => "assets_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"title" => trans('general.assets'),
"visible" => true
], [
"field" => "accessories_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"title" => trans('general.accessories'),
"visible" => true
], [
"field" => "consumables_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"title" => trans('general.consumables'),
"visible" => true
], [
"field" => "components_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"title" => trans('general.components'),
"visible" => true
], [
@@ -72,7 +72,7 @@ class CategoryPresenter extends Presenter
], [
"field" => "require_acceptance",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"title" => trans('admin/categories/table.require_acceptance'),
"visible" => true,
"formatter" => 'trueFalseFormatter',

View File

@@ -85,6 +85,7 @@ class ComponentPresenter extends Presenter
"sortable" => true,
"title" => trans('general.purchase_cost'),
"visible" => true,
"footerFormatter" => 'sumFormatter',
],
];

View File

@@ -89,6 +89,7 @@ class ConsumablePresenter extends Presenter
"sortable" => true,
"title" => trans('general.purchase_cost'),
"visible" => true,
"footerFormatter" => 'sumFormatter',
],[
"field" => "change",
"searchable" => false,

View File

@@ -98,6 +98,7 @@ class LicensePresenter extends Presenter
"sortable" => true,
"visible" => false,
"title" => trans('general.purchase_cost'),
"footerFormatter" => 'sumFormatter',
], [
"field" => "purchase_order",
"searchable" => true,

View File

@@ -69,6 +69,14 @@ class UserPresenter extends Presenter
"visible" => true,
"formatter" => "emailFormatter"
],
[
"field" => "phone",
"searchable" => true,
"sortable" => true,
"switchable" => true,
"title" => trans('admin/users/table.phone'),
"visible" => true,
],
[
"field" => "username",
"searchable" => true,
@@ -115,7 +123,7 @@ class UserPresenter extends Presenter
[
"field" => "assets_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Assets</span>'
.'<span class="hidden-xs"><i class="fa fa-barcode fa-lg"></i></span>',
@@ -124,7 +132,7 @@ class UserPresenter extends Presenter
[
"field" => "licenses_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Licenses</span>'
.'<span class="hidden-xs"><i class="fa fa-floppy-o fa-lg"></i></span>',
@@ -133,7 +141,7 @@ class UserPresenter extends Presenter
[
"field" => "consumables_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Consumables</span>'
.'<span class="hidden-xs"><i class="fa fa-tint fa-lg"></i></span>',
@@ -142,7 +150,7 @@ class UserPresenter extends Presenter
[
"field" => "accessories_count",
"searchable" => false,
"sortable" => false,
"sortable" => true,
"switchable" => true,
"title" => ' <span class="hidden-md hidden-lg">Accessories</span>'
.'<span class="hidden-xs"><i class="fa fa-keyboard-o fa-lg"></i></span>',

View File

@@ -19,7 +19,7 @@
"javiereguiluz/easyslugger": "^1.0",
"jenssegers/rollbar": "^1.5",
"laravel/framework": "5.4.*",
"laravel/passport": "^1.0",
"laravel/passport": "^3.0",
"laravel/tinker": "^1.0",
"laravelcollective/html": "^5.3",
"league/csv": "^8.1",

539
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -132,12 +132,9 @@ return [
'sendmail' => '/usr/sbin/sendmail -bs',
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],

View File

@@ -109,7 +109,7 @@ return [
|
*/
'cookie' => env('COOKIE_NAME', 'sl53_session'),
'cookie' => env('COOKIE_NAME', 'snipeitv4_session'),
/*
|--------------------------------------------------------------------------

View File

@@ -1,7 +1,7 @@
<?php
return array (
'app_version' => 'v4.0.12',
'build_version' => '272',
'hash_version' => 'gea63ced',
'full_hash' => 'v4.0.12-272-gea63ced',
'app_version' => 'v4.0.14',
'build_version' => '339',
'hash_version' => 'g17b2719',
'full_hash' => 'v4.0.14-339-g17b2719',
);

View File

@@ -24,7 +24,8 @@ $factory->state(App\Models\Accessory::class, 'apple-bt-keyboard', function ($fak
'category_id' => 8,
'manufacturer_id' => 1,
'qty' => 10,
'min_amt' => 2
'min_amt' => 2,
'supplier_id' => rand(1,5)
];
});
@@ -36,7 +37,8 @@ $factory->state(App\Models\Accessory::class, 'apple-usb-keyboard', function ($fa
'category_id' => 8,
'manufacturer_id' => 1,
'qty' => 15,
'min_amt' => 2
'min_amt' => 2,
'supplier_id' => rand(1,5)
];
});
@@ -48,7 +50,8 @@ $factory->state(App\Models\Accessory::class, 'apple-mouse', function ($faker) {
'category_id' => 9,
'manufacturer_id' => 1,
'qty' => 13,
'min_amt' => 2
'min_amt' => 2,
'supplier_id' => rand(1,5)
];
});
@@ -56,7 +59,7 @@ $factory->state(App\Models\Accessory::class, 'apple-mouse', function ($faker) {
$factory->state(App\Models\Accessory::class, 'microsoft-mouse', function ($faker) {
return [
'name' => 'Sculpt Comfort Mouse\'',
'name' => 'Sculpt Comfort Mouse',
'category_id' => 9,
'manufacturer_id' => 2,
'qty' => 13,

View File

@@ -14,6 +14,17 @@ $factory->define(Actionlog::class, function (Faker\Generator $faker) {
});
$factory->defineAs(App\Models\Actionlog::class, 'asset-upload', function ($faker) {
$asset = factory(App\Models\Asset::class)->create();
return [
'item_type' => get_class($asset),
'item_id' => 1,
'user_id' => 1,
'filename' => $faker->word,
'action_type' => 'uploaded'
];
});
$factory->defineAs(Actionlog::class, 'asset-checkout-user', function (Faker\Generator $faker) {
$target = User::inRandomOrder()->first();

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddCustomForgotPasswordUrl extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->string('custom_forgot_pass_url')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('custom_forgot_pass_url');
});
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddImageAndSupplierToAccessories extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accessories', function (Blueprint $table) {
$table->string('image')->nullable()->default(null);
$table->integer('supplier_id')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accessories', function (Blueprint $table) {
$table->dropColumn('image');
$table->dropColumn('supplier_id');
});
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddLocationIndicesToAssets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('assets', function (Blueprint $table) {
$table->index('rtd_location_id');
$table->index(['assigned_type','assigned_to']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('assets', function (Blueprint $table) {
//
$table->dropIndex(['rtd_location_id']);
$table->dropIndex(['assigned_type','assigned_to']);
});
}
}

1
public/uploads/accessories/.gitignore vendored Executable file
View File

@@ -0,0 +1 @@
!.gitignore

View File

View File

@@ -62,7 +62,8 @@ return array(
'error' => 'Asset was not checked out, please try again',
'success' => 'Asset checked out successfully.',
'user_does_not_exist' => 'That user is invalid. Please try again.',
'not_available' => 'That asset is not available for checkout!'
'not_available' => 'That asset is not available for checkout!',
'no_assets_selected' => 'You must select at least one asset from the list'
),
'checkin' => array(

View File

@@ -23,6 +23,8 @@ return array(
'confirm_purge_help' => 'Enter the text "DELETE" in the box below to purge your deleted records. This action cannot be undone.',
'custom_css' => 'Custom CSS',
'custom_css_help' => 'Enter any custom CSS overrides you would like to use. Do not include the &lt;style&gt;&lt;/style&gt; tags.',
'custom_forgot_pass_url' => 'Custom Password Reset URL',
'custom_forgot_pass_url_help' => 'This replaces the built-in forgotten password URL on the login screen, useful to direct people to internal or hosted LDAP password reset functionality. It will effectively disable local user forgotten password functionality.',
'default_currency' => 'Default Currency',
'default_eula_text' => 'Default EULA',
'default_language' => 'Default Language',
@@ -44,6 +46,8 @@ return array(
'ldap_enabled' => 'LDAP enabled',
'ldap_integration' => 'LDAP Integration',
'ldap_settings' => 'LDAP Settings',
'ldap_login_test_help' => 'Enter a valid LDAP username and password from the base DN you specified above to test whether your LDAP login is configured correctly. YOU MUST SAVE YOUR UPDATED LDAP SETTINGS FIRST.',
'ldap_login_sync_help' => 'This only tests that LDAP can sync correctly. If your LDAP Authentication query is not correct, users may still not be able to login. YOU MUST SAVE YOUR UPDATED LDAP SETTINGS FIRST.',
'ldap_server' => 'LDAP Server',
'ldap_server_help' => 'This should start with ldap:// (for unencrypted or TLS) or ldaps:// (for SSL)',
'ldap_server_cert' => 'LDAP SSL certificate validation',

View File

@@ -3,7 +3,7 @@
return array(
'does_not_exist' => 'Supplier does not exist.',
'assoc_users' => 'This supplier is currently associated with at least one model and cannot be deleted. Please update your models to no longer reference this supplier and try again. ',
'create' => array(
'error' => 'Supplier was not created, please try again.',
@@ -18,7 +18,10 @@ return array(
'delete' => array(
'confirm' => 'Are you sure you wish to delete this supplier?',
'error' => 'There was an issue deleting the supplier. Please try again.',
'success' => 'Supplier was deleted successfully.'
'success' => 'Supplier was deleted successfully.',
'assoc_assets' => 'This supplier is currently associated with :asset_count asset(s) and cannot be deleted. Please update your assets to no longer reference this supplier and try again. ',
'assoc_licenses' => 'This supplier is currently associated with :licenses_count licences(s) and cannot be deleted. Please update your licenses to no longer reference this supplier and try again. ',
'assoc_maintenances' => 'This supplier is currently associated with :asset_maintenances_count asset maintenances(s) and cannot be deleted. Please update your asset maintenances to no longer reference this supplier and try again. ',
)
);

View File

@@ -362,6 +362,7 @@ Form::macro('date_display_format', function ($name = "date_display_format", $sel
'm/d/Y',
'n/d/y',
'm/j/Y',
'd.m.Y',
];
foreach ($formats as $format) {

View File

@@ -12,6 +12,7 @@
@include ('partials.forms.edit.company')
@include ('partials.forms.edit.name', ['translated_name' => trans('admin/accessories/general.accessory_name')])
@include ('partials.forms.edit.category')
@include ('partials.forms.edit.supplier')
@include ('partials.forms.edit.manufacturer')
@include ('partials.forms.edit.location')
@include ('partials.forms.edit.model_number')
@@ -21,4 +22,20 @@
@include ('partials.forms.edit.quantity')
@include ('partials.forms.edit.minimum_quantity')
<!-- Image -->
<div class="form-group {{ $errors->has('image') ? ' has-error' : '' }}">
{{ Form::label('image', trans('general.image_upload'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7">
@if (config('app.lock_passwords'))
<p class="help-block">{{ trans('general.lock_passwords') }}</p>
@else
{{ Form::file('image') }}
{!! $errors->first('image', '<span class="alert-msg">:message</span>') !!}
@endif
</div>
</div>
@stop

View File

@@ -42,6 +42,7 @@
@include ('partials.bootstrap-table', [
'exportFile' => 'accessories-export',
'search' => true,
'showFooter' => true,
'columns' => \App\Presenters\AccessoryPresenter::dataTableLayout()
])
@stop

View File

@@ -30,6 +30,7 @@ View Assets for {{ $user->present()->fullName() }}
<tr>
<th class="col-md-4">{{ trans('admin/hardware/table.asset_model') }}</th>
<th class="col-md-2">{{ trans('admin/hardware/table.asset_tag') }}</th>
<th class="col-md-3">{{ trans('admin/hardware/table.serial') }}</th>
<th class="col-md-3">{{ trans('general.name') }}</th>
<th></th>
</tr>
@@ -43,6 +44,7 @@ View Assets for {{ $user->present()->fullName() }}
@endif
</td>
<td>{{ $asset->asset_tag }}</td>
<td>{{ $asset->serial }}</td>
<td>{{ $asset->name }}</td>
<td>
@if (($asset->image) && ($asset->image!=''))

View File

@@ -4,9 +4,13 @@
{{-- Page content --}}
@section('content')
<form role="form" action="{{ url('/login') }}" method="POST" autocomplete="off">
<form role="form" action="{{ url('/login') }}" method="POST" autocomplete="false">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<!-- this is a hack to prevent Chrome from trying to autocomplete fields -->
<input type="text" name="prevent_autofill" id="prevent_autofill" value="" style="display:none;" />
<input type="password" name="password_fake" id="password_fake" value="" style="display:none;" />
<div class="container">
<div class="row">
@@ -38,7 +42,7 @@
<fieldset>
<div class="form-group{{ $errors->has('username') ? ' has-error' : '' }}">
<input class="form-control" placeholder="{{ trans('admin/users/table.username') }}" name="username" type="text" autofocus>
<input class="form-control" placeholder="{{ trans('admin/users/table.username') }}" name="username" type="text" autocomplete="off" autofocus>
{!! $errors->first('username', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
@@ -59,7 +63,13 @@
<button class="btn btn-lg btn-primary btn-block">{{ trans('auth/general.login') }}</button>
</div>
<div class="col-md-12 col-sm-12 col-xs-12 text-right" style="padding-top: 10px;">
<a href="{{ route('password.request') }}">{{ trans('auth/general.forgot_password') }}</a>
@if ($snipeSettings->custom_forgot_pass_url)
<a href="{{ $snipeSettings->custom_forgot_pass_url }}" rel="noopener">{{ trans('auth/general.forgot_password') }}</a>
@else
<a href="{{ route('password.request') }}">{{ trans('auth/general.forgot_password') }}</a>
@endif
</div>
</div> <!-- end login box -->

View File

@@ -3,6 +3,11 @@
{{-- Page content --}}
@section('content')
@if ($snipeSettings->custom_forgot_pass_url)
<a href="{{ $snipeSettings->custom_forgot_pass_url }}" rel="noopener">{{ trans('auth/general.forgot_password') }}</a>
@else
<form class="form" role="form" method="POST" action="{{ url('/password/email') }}">
{!! csrf_field() !!}
<div class="container">
@@ -50,5 +55,7 @@
</div>
</form>
@endif
@stop

View File

@@ -45,7 +45,7 @@
@stop
@section('moar_scripts')
@include ('partials.bootstrap-table', ['exportFile' => 'components-export', 'search' => true, 'columns' => \App\Presenters\ComponentPresenter::dataTableLayout()])
@include ('partials.bootstrap-table', ['exportFile' => 'components-export', 'search' => true, 'showFooter' => true, 'columns' => \App\Presenters\ComponentPresenter::dataTableLayout()])

View File

@@ -40,5 +40,5 @@
@stop
@section('moar_scripts')
@include ('partials.bootstrap-table', ['exportFile' => 'consumables-export', 'search' => true,'columns' => \App\Presenters\ConsumablePresenter::dataTableLayout()])
@include ('partials.bootstrap-table', ['exportFile' => 'consumables-export', 'search' => true,'showFooter' => true, 'columns' => \App\Presenters\ConsumablePresenter::dataTableLayout()])
@stop

View File

@@ -38,7 +38,7 @@
@if (isset($model_name))
<tr>
<td>
{{ trans('general.model_no') }}:
{{ trans('general.asset_model') }}:
</td>
<td>
<strong>{{ $model_name }}</strong>
@@ -48,7 +48,7 @@
@if (isset($model_number))
<tr>
<td>
{{ trans('general.asset_model') }}:
{{ trans('general.model_no') }}:
</td>
<td>
<strong>{{ $model_number }}</strong>

View File

@@ -35,23 +35,23 @@
</td>
</tr>
@endif
@if (isset($model_name))
@if (isset($model_number))
<tr>
<td style="background-color:#ccc">
{{ trans('general.model_no') }}:
</td>
<td>
<strong>{{ $model_name }}</strong>
<strong>{{ $model_number }}</strong>
</td>
</tr>
@endif
@if (isset($model_number))
@if (isset($model_name))
<tr>
<td style="background-color:#ccc">
{{ trans('general.asset_model') }}:
</td>
<td>
<strong>{{ $model_number }}</strong>
<strong>{{ $model_name }}</strong>
</td>
</tr>
@endif

View File

@@ -101,6 +101,7 @@
@include ('partials.bootstrap-table', [
'exportFile' => 'assets-export',
'search' => true,
'showFooter' => true,
'columns' => \App\Presenters\AssetPresenter::dataTableLayout()
])

View File

@@ -32,6 +32,10 @@
background-color: {{ $snipeSettings->header_color }};
border-color: {{ $snipeSettings->header_color }};
}
@if ($snipeSettings->custom_css)
{{ $snipeSettings->show_custom_css() }}
@endif
</style>
@endif

View File

@@ -303,7 +303,7 @@
</li>
<li>
<a href="{{ route('account.password.index') }}">
<i class="fa fa-asterisk"></i> @lang('general.changepassword')
<i class="fa fa-asterisk fa-fw"></i> @lang('general.changepassword')
</a>
</li>
@@ -379,16 +379,16 @@
<li{!! (Request::query('status') == 'Deployed' ? ' class="active"' : '') !!}>
<a href="{{ url('hardware?status=Deployed') }}"><i class="fa fa-circle-o text-blue"></i>All @lang('general.deployed')
<a href="{{ url('hardware?status=Deployed') }}"><i class="fa fa-circle-o text-blue"></i>@lang('general.all') @lang('general.deployed')
</a>
</li>
<li{!! (Request::query('status') == 'RTD' ? ' class="active"' : '') !!}>
<a href="{{ url('hardware?status=RTD') }}">
<i class="fa fa-circle-o text-green"></i>All @lang('general.ready_to_deploy')</a>
<i class="fa fa-circle-o text-green"></i>@lang('general.all') @lang('general.ready_to_deploy')</a>
</li>
<li{!! (Request::query('status') == 'Pending' ? ' class="active"' : '') !!}><a href="{{ url('hardware?status=Pending') }}"><i class="fa fa-circle-o text-orange"></i>All @lang('general.pending')</a></li>
<li{!! (Request::query('status') == 'Undeployable' ? ' class="active"' : '') !!} ><a href="{{ url('hardware?status=Undeployable') }}"><i class="fa fa-times text-red"></i>All @lang('general.undeployable')</a></li>
<li{!! (Request::query('status') == 'Archived' ? ' class="active"' : '') !!}><a href="{{ url('hardware?status=Archived') }}"><i class="fa fa-times text-red"></i>All @lang('admin/hardware/general.archived')</a></li>
<li{!! (Request::query('status') == 'Pending' ? ' class="active"' : '') !!}><a href="{{ url('hardware?status=Pending') }}"><i class="fa fa-circle-o text-orange"></i>@lang('general.all') @lang('general.pending')</a></li>
<li{!! (Request::query('status') == 'Undeployable' ? ' class="active"' : '') !!} ><a href="{{ url('hardware?status=Undeployable') }}"><i class="fa fa-times text-red"></i>@lang('general.all') @lang('general.undeployable')</a></li>
<li{!! (Request::query('status') == 'Archived' ? ' class="active"' : '') !!}><a href="{{ url('hardware?status=Archived') }}"><i class="fa fa-times text-red"></i>@lang('general.all') @lang('admin/hardware/general.archived')</a></li>
<li{!! (Request::query('status') == 'Requestable' ? ' class="active"' : '') !!}><a href="{{ url('hardware?status=Requestable') }}"><i class="fa fa-check text-blue"></i>@lang('admin/hardware/general.requestable')</a></li>
<li class="divider">&nbsp;</li>

View File

@@ -45,6 +45,7 @@
@include ('partials.bootstrap-table', [
'exportFile' => 'licenses-export',
'search' => true,
'showFooter' => true,
'columns' => \App\Presenters\LicensePresenter::dataTableLayout()])
@stop

View File

@@ -76,7 +76,7 @@
@endif
</td>
<td>
@if ($licensedto->asset_id)
@if ($licensedto->asset)
@can('view', $licensedto->asset)
<a href="{{ route('hardware.show', $licensedto->asset_id) }}">
{{ $licensedto->asset->name }} {{ $licensedto->asset->asset_tag }}

View File

@@ -28,11 +28,15 @@
<!-- Date field -->
@if ($field->format=='DATE')
<div class="input-group col-md-4" style="padding-left: 0px;">
<input type="text" value="{{ Input::old($field->db_column_name(),(isset($item) ? \App\Helpers\Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : "")) }}" id="{{ $field->db_column_name() }}" class="datepicker form-control col-md-4" name="{{ $field->db_column_name() }}" data-date-format="yyyy-mm-dd" placeholder="yyyy-mm-dd">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
</div>
<div class="input-group col-md-4" style="padding-left: 0px;">
<div class="input-group date" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-autoclose="true">
<input type="text" class="form-control" placeholder="{{ trans('general.select_date') }}" name="{{ $field->db_column_name() }}" id="{{ $field->db_column_name() }}" value="{{ Input::old($field->db_column_name(),(isset($item) ? \App\Helpers\Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : "")) }}">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
</div>
</div>
@else
@if (($field->field_encrypted=='0') || (Gate::allows('admin')))
<input type="text" value="{{ Input::old($field->db_column_name(),(isset($item) ? \App\Helpers\Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : "")) }}" id="{{ $field->db_column_name() }}" class="form-control" name="{{ $field->db_column_name() }}" placeholder="Enter {{ strtolower($field->format) }} text">

View File

@@ -38,6 +38,9 @@ $('.snipe-table').bootstrapTable({
cookie: true,
cookieExpire: '2y',
showExport: true,
@if (isset($showFooter))
showFooter: true,
@endif
showColumns: true,
trimOnSearch: false,
@@ -176,33 +179,42 @@ $('.snipe-table').bootstrapTable({
var actions = '<nobr>';
var dest = destination;
if (destination=='groups') {
var dest = 'admin/groups';
}
if ((row.available_actions) && (row.available_actions.clone === true)) {
actions += '<a href="{{ url('/') }}/' + destination + '/' + row.id + '/clone" class="btn btn-sm btn-info" data-tooltip="true" title="Clone"><i class="fa fa-copy"></i></a>&nbsp;';
actions += '<a href="{{ url('/') }}/' + dest + '/' + row.id + '/clone" class="btn btn-sm btn-info" data-tooltip="true" title="Clone"><i class="fa fa-copy"></i></a>&nbsp;';
}
if ((row.available_actions) && (row.available_actions.update === true)) {
actions += '<a href="{{ url('/') }}/' + destination + '/' + row.id + '/edit" class="btn btn-sm btn-warning" data-tooltip="true" title="Update"><i class="fa fa-pencil"></i></a>&nbsp;';
actions += '<a href="{{ url('/') }}/' + dest + '/' + row.id + '/edit" class="btn btn-sm btn-warning" data-tooltip="true" title="Update"><i class="fa fa-pencil"></i></a>&nbsp;';
}
if ((row.available_actions) && (row.available_actions.delete === true)) {
actions += '<a href="{{ url('/') }}/' + destination + '/' + row.id + '" '
actions += '<a href="{{ url('/') }}/' + dest + '/' + row.id + '" '
+ ' class="btn btn-danger btn-sm delete-asset" data-tooltip="true" '
+ ' data-toggle="modal" '
+ ' data-content="{{ trans('general.sure_to_delete') }} ' + row.name + '?" '
+ ' data-title="{{ trans('general.delete') }}" onClick="return false;">'
+ '<i class="fa fa-trash"></i></a></nobr>';
+ '<i class="fa fa-trash"></i></a>&nbsp;';
} else {
actions += '<a class="btn btn-danger btn-sm delete-asset disabled" onClick="return false;"><i class="fa fa-trash"></i></a>&nbsp;';
}
if ((row.available_actions) && (row.available_actions.restore === true)) {
actions += '<a href="{{ url('/') }}/' + destination + '/' + row.id + '/restore" class="btn btn-sm btn-warning" data-tooltip="true" title="Restore"><i class="fa fa-retweet"></i></a>&nbsp;';
actions += '<a href="{{ url('/') }}/' + dest + '/' + row.id + '/restore" class="btn btn-sm btn-warning" data-tooltip="true" title="Restore"><i class="fa fa-retweet"></i></a>&nbsp;';
}
actions +='</nobr>';
return actions;
};
}
// This handles
// This handles the icons and display of polymorphic entries
function polymorphicItemFormatter(value) {
var item_destination = '';
@@ -343,6 +355,15 @@ $('.snipe-table').bootstrapTable({
}
}
function deployedLocationFormatter(row, value) {
if ((row) && (row!=undefined)) {
return '<a href="{{ url('/') }}/locations/' + row.id + '"> ' + row.name + '</a>';
} else if (value.rtd_location) {
return '<a href="{{ url('/') }}/locations/' + value.rtd_location.id + '" data-tooltip="true" title="Default Location"> ' + value.rtd_location.name + '</a>';
}
}
function groupsAdminLinkFormatter(value, row) {
return '<a href="{{ url('/') }}/admin/groups/' + row.id + '"> ' + value + '</a>';
}
@@ -419,6 +440,15 @@ $('.snipe-table').bootstrapTable({
}
}
function sumFormatter(data) {
var field = this.field;
var total_sum = data.reduce(function(sum, row) {
return (sum) + (parseFloat(row[field]) || 0);
}, 0);
return total_sum.toFixed(2);
}
$(function () {
$('#bulkEdit').click(function () {
var selectedIds = $('.snipe-table').bootstrapTable('getSelections');
@@ -429,7 +459,9 @@ $('.snipe-table').bootstrapTable({
});
});
// This is necessary to make the bootstrap tooltips work inside of the wenzhixin/bootstrap-table formatters
// This is necessary to make the bootstrap tooltips work inside of the
// wenzhixin/bootstrap-table formatters
$(function() {
$('#table').on('post-body.bs.table', function () {
$('[data-tooltip="true"]').tooltip({
@@ -439,4 +471,6 @@ $('.snipe-table').bootstrapTable({
});
</script>

View File

@@ -14,76 +14,32 @@
<div class="box-body">
<div class="table-responsive">
<table
name="maintenancesReport"
id="table"
data-cookie="true"
data-click-to-select="true"
data-cookie-id-table="maintenancesReportTable">
<table
name="maintenancesReport"
id="table"
class="table table-striped snipe-table"
data-url="{{route('api.maintenances.index') }}"
data-cookie="true"
data-click-to-select="true"
data-cookie-id-table="maintenancesReport-{{ config('version.hash_version') }}">
<thead>
<tr>
<th data-field="company" data-sortable="false" data-visible="false">{{ trans('admin/companies/table.title') }}</th>
<th data-sortable="true" data-field="id" data-visible="false">{{ trans('general.id') }}</th>
<th data-sortable="false" data-field="asset_name" data-formatter="hardwareLinkObjFormatter">{{ trans('admin/asset_maintenances/table.asset_name') }}</th>
<th data-sortable="false" data-field="supplier" data-formatter="suppliersLinkObjFormatter">{{ trans('general.supplier') }}</th>
<th data-searchable="true" data-sortable="true" data-field="asset_maintenance_type">{{ trans('admin/asset_maintenances/form.asset_maintenance_type') }}</th>
<th data-searchable="true" data-sortable="true" data-field="title">{{ trans('admin/asset_maintenances/form.title') }}</th>
<th data-searchable="true" data-sortable="false" data-field="start_date" data-formatter="dateDisplayFormatter">{{ trans('admin/asset_maintenances/form.start_date') }}</th>
<th data-searchable="true" data-sortable="true" data-field="completion_date" data-formatter="dateDisplayFormatter">{{ trans('admin/asset_maintenances/form.completion_date') }}</th>
<th data-searchable="true" data-sortable="true" data-field="asset_maintenance_time">{{ trans('admin/asset_maintenances/form.asset_maintenance_time') }}</th>
<th data-searchable="true" data-sortable="true" data-field="cost" class="text-right" data-footer-formatter="sumFormatter">{{ trans('admin/asset_maintenances/form.cost') }}</th>
<th data-searchable="true" data-sortable="true" data-field="user_id" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
<th data-searchable="true" data-sortable="true" data-field="notes" data-visible="false">{{ trans('admin/asset_maintenances/form.notes') }}</th>
</tr>
</thead>
</table>
<thead>
<tr role="row">
<th class="col-sm-1">{{ trans('admin/companies/table.title') }}</th>
<th class="col-sm-1">{{ trans('admin/hardware/table.asset_tag') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/table.asset_name') }}</th>
<th class="col-sm-1">{{ trans('general.supplier') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/form.asset_maintenance_type') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/form.title') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/form.start_date') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/form.completion_date') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/form.asset_maintenance_time') }}</th>
<th class="col-sm-1">{{ trans('admin/asset_maintenances/form.cost') }}</th>
</tr>
</thead>
<tbody>
<?php
$totalDays = 0;
$totalCost = 0;
?>
@foreach ($assetMaintenances as $assetMaintenance)
@if ($assetMaintenance->asset)
<tr>
<td>{{ ($assetMaintenance->asset->company) ? $assetMaintenance->asset->company->name : '' }}</td>
<td>{{ $assetMaintenance->asset->asset_tag }}</td>
<td>{{ $assetMaintenance->asset->name }}</td>
<td>{{ $assetMaintenance->supplier->name }}</td>
<td>{{ $assetMaintenance->asset_maintenance_type }}</td>
<td>{{ $assetMaintenance->title }}</td>
<td>{{ $assetMaintenance->start_date }}</td>
<td>{{ is_null($assetMaintenance->completion_date) ? trans('admin/asset_maintenances/message.asset_maintenance_incomplete') : $assetMaintenance->completion_date }}</td>
@if (is_null($assetMaintenance->asset_maintenance_time))
<?php
$assetMaintenanceTime = intval(Carbon::now()->diffInDays(Carbon::parse($assetMaintenance->start_date)));
?>
@else
<?php
$assetMaintenanceTime = intval($assetMaintenance->asset_maintenance_time);
?>
@endif
<td>{{ $assetMaintenanceTime }}</td>
<td>
{{ $snipeSettings->default_currency }}
{{ number_format($assetMaintenance->cost,2) }}
</td>
</tr>
@endif
<?php
$totalDays += $assetMaintenanceTime;
$totalCost += floatval($assetMaintenance->cost);
?>
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="6" align="right"><strong>Totals:</strong></td>
<td>{{number_format($totalDays)}}</td>
<td>
{{ $snipeSettings->default_currency }}
{{ number_format($totalCost,2) }}
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
@@ -92,42 +48,8 @@
@stop
@section('moar_scripts')
<script src="{{ asset('js/bootstrap-table.js') }}"></script>
<script src="{{ asset('js/extensions/cookie/bootstrap-table-cookie.js') }}"></script>
<script src="{{ asset('js/extensions/mobile/bootstrap-table-mobile.js') }}"></script>
<script src="{{ asset('js/extensions/export/bootstrap-table-export.js') }}"></script>
<script src="{{ asset('js/extensions/export/tableExport.js') }}"></script>
<script src="{{ asset('js/extensions/export/jquery.base64.js') }}"></script>
<script type="text/javascript">
$('#table').bootstrapTable({
classes: 'table table-responsive table-no-bordered',
undefinedText: '',
iconsPrefix: 'fa',
showRefresh: true,
search: true,
pageSize: {{ $snipeSettings->per_page }},
pagination: true,
sidePagination: 'client',
sortable: true,
cookie: true,
mobileResponsive: true,
showExport: true,
showColumns: true,
exportDataType: 'all',
exportTypes: ['csv', 'txt','json', 'xml'],
maintainSelected: true,
paginationFirstText: "{{ trans('general.first') }}",
paginationLastText: "{{ trans('general.last') }}",
paginationPreText: "{{ trans('general.previous') }}",
paginationNextText: "{{ trans('general.next') }}",
pageList: ['10','25','50','100','150','200'],
icons: {
paginationSwitchDown: 'fa-caret-square-o-down',
paginationSwitchUp: 'fa-caret-square-o-up',
columns: 'fa-columns',
refresh: 'fa-refresh'
},
});
</script>
@include ('partials.bootstrap-table',
['exportFile' => 'maintenances-export',
'search' => true,
'showFooter' => true ])
@stop

View File

@@ -178,6 +178,27 @@
</div>
</div>
<!-- Mail test -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('login_note', 'Test Mail') }}
</div>
<div class="col-md-9" id="mailtestrow">
<a class="btn btn-default btn-sm pull-left" id="mailtest" style="margin-right: 10px;">
Send Test</a>
<span id="mailtesticon"></span>
<span id="mailtestresult"></span>
<span id="mailteststatus"></span>
</div>
<div class="col-md-9 col-md-offset-3">
<div id="mailteststatus-error" class="text-danger"></div>
</div>
<div class="col-md-9 col-md-offset-3">
<p class="help-block">This will attempt to send a test mail to {{ config('mail.from.address') }}.</p>
</div>
</div>
</div> <!--/.box-body-->
<div class="box-footer">
<div class="text-left col-md-6">
@@ -193,7 +214,7 @@
</div> <!-- /.col-md-8-->
</div> <!-- /.row-->
{{Form::close()}}
{{ Form::close() }}
@stop
@@ -208,5 +229,61 @@
}).on('ifUnchecked', function(){
$('#auto_increment_prefix').prop('disabled', true);
});
// Test Mail
$("#mailtest").click(function(){
$("#mailtestrow").removeClass('text-success');
$("#mailtestrow").removeClass('text-danger');
$("#mailtesticon").html('');
$("#mailteststatus").html('');
$('#mailteststatus-error').html('');
$("#mailtesticon").html('<i class="fa fa-spinner spin"></i> Sending Test Email...');
$.ajax({
url: '{{ route('api.settings.mailtest') }}',
type: 'POST',
headers: {
"X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
},
data: {},
dataType: 'json',
success: function (data) {
console.dir(data);
$("#mailtesticon").html('');
$("#mailteststatus").html('');
$('#mailteststatus-error').html('');
$("#mailteststatus").removeClass('text-danger');
$("#mailteststatus").addClass('text-success');
if (data.message) {
$("#mailteststatus").html('<i class="fa fa-check text-success"></i> ' + data.message);
} else {
$("#mailteststatus").html('<i class="fa fa-check text-success"></i> Mail sent!');
}
},
error: function (data) {
$("#mailtesticon").html('');
$("#mailteststatus").html('');
$('#mailteststatus-error').html('');
$("#mailteststatus").removeClass('text-success');
$("#mailteststatus").addClass('text-danger');
$("#mailtesticon").html('<i class="fa fa-exclamation-triangle text-danger"></i>');
$('#mailteststatus').html('Mail could not be sent.');
if (data.responseJSON) {
$('#mailteststatus-error').html('Error: ' + data.responseJSON.messages);
} else {
console.dir(data);
}
}
});
});
</script>
@stop

View File

@@ -51,7 +51,7 @@
<div class="col-md-4 col-lg-3 col-sm-6 col-xl-1">
<div class="box box-default">
<div class="box-body text-center">
<a href="{{ route('settings.groups.index') }}" class="btn btn-lg btn-white"><i class="fa fa-group fa-3x"></i></a>
<a href="{{ route('groups.index') }}" class="btn btn-lg btn-white"><i class="fa fa-group fa-3x"></i></a>
<br>
Groups
<p class="help-block">Account permission groups</p>

View File

@@ -334,9 +334,9 @@
@if ($setting->ldap_enabled)
<!-- LDAP test -->
<div class="form-group {{ $errors->has('ldap_email') ? 'error' : '' }}">
<div class="form-group">
<div class="col-md-3">
Test LDAP Sync
{{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
</div>
<div class="col-md-9" id="ldaptestrow">
<a class="btn btn-default btn-sm pull-left" id="ldaptest" style="margin-right: 10px;">Test LDAP</a>
@@ -347,14 +347,14 @@
<span id="ldapteststatus"></span>
</div>
<div class="col-md-9 col-md-offset-3">
<p class="help-block">This only tests that LDAP can sync correctly. If your LDAP Authentication query is not correct, users may still not be able to login.</p>
<p class="help-block">{{ trans('admin/settings/general.ldap_login_sync_help') }}</p>
</div>
</div>
<!-- LDAP Login test -->
<div class="form-group {{ $errors->has('ldap_email') ? 'error' : '' }}">
<div class="form-group">
<div class="col-md-3">
Test LDAP Login
{{ Form::label('test_ldap_login', 'Test LDAP Login') }}
</div>
<div class="col-md-9">
<div class="row">
@@ -377,12 +377,29 @@
<span id="ldaptestloginstatus"></span>
</div>
<div class="col-md-9 col-md-offset-3">
<p class="help-block">Enter a valid LDAP username and password to test whether your LDAP login is configured correctly.</p>
<p class="help-block">{{ trans('admin/settings/general.ldap_login_test_help') }}</p>
</div>
</div>
@endif
<!-- LDAP Forgotten password -->
<div class="form-group {{ $errors->has('custom_forgot_pass_url') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('custom_forgot_pass_url', trans('admin/settings/general.custom_forgot_pass_url')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'https://my.ldapserver-forgotpass.com')) }}
@else
{{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), array('class' => 'form-control','placeholder' => 'https://my.ldapserver-forgotpass.com')) }}
@endif
<p class="help-block">{{ trans('admin/settings/general.custom_forgot_pass_url_help') }}</p>
{!! $errors->first('custom_forgot_pass_url', '<span class="alert-msg">:message</span>') !!}
</div>
</div><!-- LDAP Server -->
</div>
</div> <!--/.box-body-->
<div class="box-footer">

View File

@@ -165,13 +165,22 @@ Create a User ::
</td>
</tr>
<tr id="mailrow">
<tr id="mailtestrow" class="warning">
<td>Email</td>
<td id="mailtesticon">
<td>
<a class="btn btn-default btn-sm pull-left" id="mailtest" style="margin-right: 10px;">
Send Test</a>
</td>
<td id="mailtestresult">
<button class="btn btn-default" id="mailtest"> Test Email</button>
<span id="mailtestresult"></span>
<td>
<span id="mailtesticon"></span>
<span id="mailtestresult"></span>
<span id="mailteststatus"></span>
<div class="col-md-12">
<div id="mailteststatus-error" class="text-danger"></div>
</div>
<div class="col-md-12">
<p class="help-block">This will attempt to send a test mail to {{ config('mail.from.address') }}.</p>
</div>
</td>
</tr>
</tbody>
@@ -190,34 +199,61 @@ Create a User ::
<script type="text/javascript">
$(document).ready(function () {
$("#mailtest").click(function(){
// Test Mail
$("#mailtestresult").html('<i class="fa fa-spinner fa-spin"></i> Sending Email');
$("#mailtest").click(function(){
$.ajax({url: "{{ route('setup.mailtest') }}", success: function(result){
if (result=='success') {
$("#mailrow").addClass('success');
$("#mailtesticon").html('<i class="fa fa-check preflight-success"></i>');
$("#mailtestresult").html('No errors on this end! Check your <code>{{ config('mail.from.address') }}</code> email account for a test email.');
} else {
$("#mailrow").addClass('danger');
$("#mailtesticon").html('<i class="fa fa-check preflight-error"></i>');
$("#mailtestresult").html('Something went wrong. Your email was not sent. Check your mail settings in your <code>.env</code> file.');
$("#mailtestrow").removeClass('success').removeClass('danger').removeClass('warning');
$("#mailtestrow").addClass('info');
$("#mailtesticon").html('');
$("#mailteststatus").html('');
$('#mailteststatus-error').html('');
$("#mailtesticon").html('<i class="fa fa-spinner spin"></i> Sending Test Email...');
}
$.ajax({
url: "{{ route('setup.mailtest') }}",
success: function (result) {
if (result.status == 'success') {
$("#mailtestrow").removeClass('info').removeClass('danger').removeClass('warning');
$("#mailtestrow").addClass('success');
$("#mailtesticon").html('');
$("#mailteststatus").html('');
$('#mailteststatus-error').html('');
$("#mailteststatus").removeClass('text-danger');
$("#mailteststatus").addClass('text-success');
$("#mailteststatus").html('<i class="fa fa-check text-success"></i> Mail sent to {{ config('mail.from.address') }}!');
} else {
$("#mailtestrow").removeClass('success').removeClass('info').removeClass('warning');
$("#mailtestrow").addClass('danger');
$("#mailtesticon").html('<i class="fa fa-check preflight-error"></i>');
$("#mailtestresult").html('Something went wrong. Your email was not sent. Check your mail settings in your <code>.env</code> file.');
}
},
error: function (result) {
$("#mailrow").addClass('danger');
$("#mailtesticon").html('<i class="fa fa-check preflight-error"></i>');
$("#mailtestresult").html('Something went wrong. The server returned an error. Check your mail settings in your <code>.env</code> file, and check your <code>storage/logs</code> for additional information..');
}
},
error: function (result) {
$("#mailtestrow").removeClass('success').removeClass('info').removeClass('warning');
$("#mailtestrow").addClass('danger');
$("#mailtesticon").html('');
$("#mailteststatus").html('');
$('#mailteststatus-error').html('');
$("#mailteststatus").removeClass('text-success');
$("#mailteststatus").addClass('text-danger');
$("#mailtesticon").html('<i class="fa fa-exclamation-triangle text-danger"></i>');
$('#mailteststatus').html('Mail could not be sent.');
if (result.responseJSON) {
$('#mailteststatus-error').html('Error: ' + result.responseJSON.messages);
} else {
console.dir(data);
}
}
});
});
});
});
</script>
@stop

View File

@@ -31,7 +31,8 @@
<tr>
<th data-sortable="true" data-field="id" data-visible="false">{{ trans('general.id') }}</th>
<th data-sortable="true" data-field="name" data-formatter="statuslabelsAssetLinkFormatter">{{ trans('admin/statuslabels/table.name') }}</th>
<th data-sortable="false" data-field="type" data-formatter="undeployableFormatter">{{ trans('admin/statuslabels/table.status_type') }}</th>
<th data-sortable="false" data-field="type" data-formatter="statusLabelTypeFormatter">{{ trans('admin/statuslabels/table.status_type') }}</th>
<th data-sortable="true" data-field="assets_count">{{ trans('general.assets') }}</th>
<th data-sortable="false" data-field="color" data-formatter="colorSqFormatter">{{ trans('admin/statuslabels/table.color') }}</th>
<th class="text-center" data-sortable="true" data-field="show_in_nav" data-formatter="trueFalseFormatter">{{ trans('admin/statuslabels/table.show_in_nav') }}</th>
<th data-switchable="false" data-formatter="statuslabelsActionsFormatter" data-searchable="false" data-sortable="false" data-field="actions">{{ trans('table.actions') }}</th>
@@ -47,10 +48,28 @@
<h4>{{ trans('admin/statuslabels/table.about') }}</h4>
<p>{{ trans('admin/statuslabels/table.info') }}</p>
<p><i class="fa fa-circle text-green"></i> <strong>{{ trans('admin/statuslabels/table.deployable') }}</strong>: {!! trans('admin/statuslabels/message.help.deployable') !!}</p>
<p><i class="fa fa-circle text-orange"></i> <strong>Pending</strong>: {{ trans('admin/statuslabels/message.help.pending') }}</p>
<p><i class="fa fa-times text-red"></i> <strong>Undeployable</strong>: {{ trans('admin/statuslabels/message.help.undeployable') }}</p>
<p><i class="fa fa-times text-red"></i> <strong>Archived</strong>: {{ trans('admin/statuslabels/message.help.archived') }}</p>
<div class="box box-success">
<div class="box-body">
<p><i class="fa fa-circle text-green"></i> <strong>{{ trans('admin/statuslabels/table.deployable') }}</strong>: {!! trans('admin/statuslabels/message.help.deployable') !!}</p>
</div>
</div>
<div class="box box-warning">
<div class="box-body">
<p><i class="fa fa-circle text-orange"></i> <strong>Pending</strong>: {{ trans('admin/statuslabels/message.help.pending') }}</p>
</div>
</div>
<div class="box box-danger">
<div class="box-body">
<p><i class="fa fa-times text-red"></i> <strong>Undeployable</strong>: {{ trans('admin/statuslabels/message.help.undeployable') }}</p>
</div>
</div>
<div class="box box-danger">
<div class="box-body">
<p><i class="fa fa-times text-red"></i> <strong>Archived</strong>: {{ trans('admin/statuslabels/message.help.archived') }}</p>
</div>
</div>
</div>
@@ -73,12 +92,30 @@
}
}
function undeployableFormatter(value, row) {
if ((value) && (value!='deployable')) {
return '<span class="text-danger">' + value + '</span> ';
} else {
return '<span class="text-success">' + value + '</span> ';
function statusLabelTypeFormatter (row, value) {
switch (value.type) {
case 'deployed':
text_color = 'blue';
icon_style = 'fa-circle';
break;
case 'deployable':
text_color = 'green';
icon_style = 'fa-circle';
break;
case 'pending':
text_color = 'orange';
icon_style = 'fa-circle';
break;
default:
text_color = 'red';
icon_style = 'fa-times';
}
var typename_lower = value.type;
var typename = typename_lower.charAt(0).toUpperCase() + typename_lower.slice(1);
return '<i class="fa ' + icon_style + ' text-' + text_color + '"></i> ' + typename;
}
</script>
@stop

View File

@@ -30,14 +30,16 @@
<thead>
<tr>
<th data-sortable="true" data-field="id" data-visible="false">{{ trans('admin/suppliers/table.id') }}</th>
<th data-formatter="imageFormatter" data-sortable="true" data-field="image" data-visible="false" data-searchable="false">Image</th>
<th data-sortable="true" data-field="name" data-formatter="suppliersLinkFormatter">{{ trans('admin/suppliers/table.name') }}</th>
<th data-sortable="true" data-field="address">{{ trans('admin/suppliers/table.address') }}</th>
<th data-searchable="true" data-sortable="true" data-field="contact">{{ trans('admin/suppliers/table.contact') }}</th>
<th data-searchable="true" data-sortable="true" data-field="email" data-formatter="emailFormatter">{{ trans('admin/suppliers/table.email') }}</th>
<th data-searchable="true" data-sortable="true" data-field="phone">{{ trans('admin/suppliers/table.phone') }}</th>
<th data-searchable="true" data-sortable="true" data-field="fax" data-visible="false">{{ trans('admin/suppliers/table.fax') }}</th>
<th data-searchable="false" data-sortable="false" data-field="assets_count">{{ trans('admin/suppliers/table.assets') }}</th>
<th data-searchable="false" data-sortable="false" data-field="licenses_count">{{ trans('admin/suppliers/table.licenses') }}</th>
<th data-searchable="false" data-sortable="true" data-field="assets_count">{{ trans('admin/suppliers/table.assets') }}</th>
<th data-searchable="false" data-sortable="true" data-field="accessories_count">{{ trans('general.accessories') }}</th>
<th data-searchable="false" data-sortable="true" data-field="licenses_count">{{ trans('admin/suppliers/table.licenses') }}</th>
<th data-switchable="false" data-formatter="suppliersActionsFormatter" data-searchable="false" data-sortable="false" data-field="actions">{{ trans('table.actions') }}</th>
</tr>
</thead>

View File

@@ -137,6 +137,40 @@
<div class="row">
<div class="col-md-9">
<div class="box box-default">
<div class="box-header with-border">
<div class="box-heading">
<h3 class="box-title">Accessories</h3>
</div>
</div><!-- /.box-header -->
<div class="box-body">
<div class="table-responsive">
<table
name="suppliersAccessories"
id="table"
class="snipe-table"
data-url="{{ route('api.accessories.index', ['supplier_id' => $supplier->id]) }}"
data-cookie="true"
data-export-options='{"fileName": "testo"}'
data-click-to-select="true"
data-cookie-id-table="suppliersAccessories-{{ config('version.hash_version') }}">
<thead>
<tr>
<th class="col-md-4" data-field="name" data-formatter="accessoriesLinkFormatter">Name</th>
<th class="col-md-4" data-field="model_number">Model Number</th>
<th class="col-md-4" data-field="purchase_cost" data-footer-formatter="sumFormatter">Purchase_cost</th>
<th class="col-md-4" data-field="actions" data-formatter="accessoriesActionsFormatter">Actions</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div class="box box-default">
@if ($supplier->id)
@@ -239,3 +273,8 @@
</div> <!-- /.row-->
@stop
@section('moar_scripts')
@include ('partials.bootstrap-table', [
'showFooter' => true,
])
@stop

View File

@@ -2,7 +2,7 @@
{{-- Page title --}}
@section('title')
Bulk Checkin &amp;amp; Delete
Bulk Checkin &amp; Delete
@parent
@stop

View File

@@ -0,0 +1,19 @@
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color or 'blue' }}" target="_blank">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>

View File

@@ -0,0 +1,7 @@
<tr>
<td class="header">
<a href="{{ $url }}">
{{ $slot }}
</a>
</td>
</tr>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0">
{{ $header or '' }}
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0">
<!-- Body content -->
<tr>
<td class="content-cell">
{{ Illuminate\Mail\Markdown::parse($slot) }}
{{ $subcopy or '' }}
</td>
</tr>
</table>
</td>
</tr>
{{ $footer or '' }}
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
&copy; {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
@endcomponent
@endslot
@endcomponent

View File

@@ -0,0 +1,13 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@@ -0,0 +1,7 @@
<table class="promotion" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

View File

@@ -0,0 +1,13 @@
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<a href="{{ $url }}" class="button button-green" target="_blank">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

View File

@@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>

View File

@@ -0,0 +1,285 @@
/* Base */
body, body *:not(html):not(style):not(br):not(tr):not(code) {
font-family: Avenir, Helvetica, sans-serif;
box-sizing: border-box;
}
body {
background-color: #f5f8fa;
color: #74787E;
height: 100%;
hyphens: auto;
line-height: 1.4;
margin: 0;
-moz-hyphens: auto;
-ms-word-break: break-all;
width: 100% !important;
-webkit-hyphens: auto;
-webkit-text-size-adjust: none;
word-break: break-all;
word-break: break-word;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869D4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #2F3133;
font-size: 19px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
color: #2F3133;
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
color: #2F3133;
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
color: #74787E;
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
background-color: #f5f8fa;
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.content {
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #bbbfc3;
font-size: 19px;
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
/* Body */
.body {
background-color: #FFFFFF;
border-bottom: 1px solid #EDEFF2;
border-top: 1px solid #EDEFF2;
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.inner-body {
background-color: #FFFFFF;
margin: 0 auto;
padding: 0;
width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #EDEFF2;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 12px;
}
/* Footer */
.footer {
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
}
.footer p {
color: #AEAEAE;
font-size: 12px;
text-align: center;
}
/* Tables */
.table table {
margin: 30px auto;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.table th {
border-bottom: 1px solid #EDEFF2;
padding-bottom: 8px;
}
.table td {
color: #74787E;
font-size: 15px;
line-height: 18px;
padding: 10px 0;
}
.content-cell {
padding: 35px;
}
/* Buttons */
.action {
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.button {
border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
color: #FFF;
display: inline-block;
text-decoration: none;
-webkit-text-size-adjust: none;
}
.button-blue {
background-color: #3097D1;
border-top: 10px solid #3097D1;
border-right: 18px solid #3097D1;
border-bottom: 10px solid #3097D1;
border-left: 18px solid #3097D1;
}
.button-green {
background-color: #2ab27b;
border-top: 10px solid #2ab27b;
border-right: 18px solid #2ab27b;
border-bottom: 10px solid #2ab27b;
border-left: 18px solid #2ab27b;
}
.button-red {
background-color: #bf5329;
border-top: 10px solid #bf5329;
border-right: 18px solid #bf5329;
border-bottom: 10px solid #bf5329;
border-left: 18px solid #bf5329;
}
/* Panels */
.panel {
margin: 0 0 21px;
}
.panel-content {
background-color: #EDEFF2;
padding: 16px;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Promotions */
.promotion {
background-color: #FFFFFF;
border: 2px dashed #9BA2AB;
margin: 0;
margin-bottom: 25px;
margin-top: 25px;
padding: 24px;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.promotion h1 {
text-align: center;
}
.promotion p {
font-size: 15px;
text-align: center;
}

View File

@@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1 @@
[{{ $slot }}]({{ $url }})

View File

@@ -0,0 +1,9 @@
{!! strip_tags($header) !!}
{!! strip_tags($slot) !!}
@isset($subcopy)
{!! strip_tags($subcopy) !!}
@endisset
{!! strip_tags($footer) !!}

View File

@@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
@endcomponent
@endslot
@endcomponent

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1 @@
{{ $slot }}

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