Compare commits

...

45 Commits

Author SHA1 Message Date
snipe b9e19468e5 Bumped version for v4.9.5 2020-09-15 18:43:19 -07:00
snipe 5b68a321a6 Added comapny formatter to asset maintenance report - fixes [ch15119] 2020-09-08 18:15:45 -07:00
snipe 37568ae9ec Merge pull request #8365 from snipe/fixes/8338_google_maps_CSP
Fixed #8338 - Added google maps to CSP
2020-08-25 20:49:37 -07:00
snipe 32ad9050cf Added google maps to CSP 2020-08-25 20:48:53 -07:00
snipe 01a832169c Merge pull request #8364 from snipe/fixes/8335_assigned_to_null_on_status_assetlist
Fixed #8335 - added assignedTo scope on status labels API call for assetlist
2020-08-25 20:38:31 -07:00
snipe 3c6883489c Added assignedTo scope 2020-08-25 20:37:30 -07:00
snipe bcad49ce79 Try to better handle slack “too many requests” issue 2020-08-14 16:10:22 -07:00
snipe b5acca89d7 Check for admin for slack notifications 2020-08-14 16:02:15 -07:00
snipe e52919cf1b Merge pull request #8327 from snipe/features/checkin_license_from_all_users
Checkin license from all users cli tool
2020-08-14 15:35:15 -07:00
snipe 29f3a5c48f Use more verbose annotation for Auth::user if/else 2020-08-14 15:27:40 -07:00
snipe 134e8e6fb9 Moved user email nulling until after the save 2020-08-14 15:25:07 -07:00
Brady Wetherington 714576be45 Merge pull request #8328 from snipe/fix_deprecation_report
Fix deprecation report for customers with many active assets
2020-08-14 15:24:03 -07:00
Brady Wetherington 5128992940 Fix deprecation report for customers with many active assets 2020-08-14 15:03:03 -07:00
snipe 0291323502 Use the user as the target 2020-08-14 14:57:58 -07:00
snipe e0f6f9b839 Artisan command to check in licenses from all users 2020-08-14 14:43:37 -07:00
snipe f1a6308002 Check for Auth::user before trying to log id (for cli) 2020-08-14 14:43:07 -07:00
snipe b999c50a2e Merge pull request #8316 from Godmartinz/bug/ch15028/missing-or-incorrect-error-message-translation
Looks great, thank you!
2020-08-12 12:37:47 -07:00
Godfrey M e3906b245c added translation for admin/licenses/message.not_found 2020-08-12 12:27:18 -07:00
Brady Wetherington 9ca20e4964 Merge pull request #8313 from snipe/improve_ldap_search_error_reporting
Improve ldap search error reporting
2020-08-11 17:33:19 -07:00
Brady Wetherington 456a74d88c De-merge out incorrectly merged files. Whoops! 2020-08-11 16:41:20 -07:00
Brady Wetherington 799c059070 Add internationalized version of LDAP error message 2020-08-11 16:39:02 -07:00
Brady Wetherington c62d43a778 Improve Exception management in Artisan LDAP Sync method. Still need to localize this better 2020-08-11 16:39:02 -07:00
Brady Wetherington b725bd0fae Add @PeterUpfold as a contributor 2020-08-11 16:39:02 -07:00
Brady Wetherington e0644dbbf6 Merge pull request #8105 from PeterUpfold/PeterUpfold-7661workaround
Propose workaround for #7661 — suppress E_DEPRECATED on ldap_control_paged_result()
2020-08-10 17:22:31 -07:00
snipe 5b6925b00c Removed debugging :( 2020-08-04 21:00:37 -07:00
snipe df17a859bf Changed modal IDs so manager creation modal works on user creation main page 2020-08-04 20:59:54 -07:00
snipe 24c43056ba Moved pGenerator script to default layout footer
This fixes an issue where the password generator wouldn’t load in a modal in Chrome
2020-08-04 20:58:28 -07:00
snipe 606b7e905d Small edits to PR template
Slight text changes to ask specifics about versions
2020-07-31 17:02:33 -07:00
snipe d73ddad477 Created a PR template
First draft of the PR guidelines template
2020-07-31 16:59:26 -07:00
snipe 9a39cf721e Merge pull request #8258 from ballertv/features/consumable-api
This looks great, thank you!
2020-07-31 12:18:49 -07:00
Brady Wetherington 7410b16835 Merge pull request #8270 from snipe/improve_ad_useraccountcontrol_v4
Add new useraccountcontrol value for valid AD users
2020-07-24 16:22:44 -07:00
andres 8994f3e15e cleanup 2020-07-22 19:57:06 -04:00
andres d23f1a77ca implement checkout API 2020-07-22 19:56:31 -04:00
snipe e955c983a3 Merge pull request #8250 from snipe/features/adds_addr_city_state_to_importer
Added address, city, state and country to importer and city to bulk editor
2020-07-22 13:43:29 -07:00
Brady Wetherington b09e7d19b3 Add new useraccountcontrol value for valid AD users; document algorithm and values 2020-07-22 13:32:16 -07:00
snipe 2fa17ac185 Merge pull request #8254 from Godmartinz/gmartinez_adds_email_formats
Added firstinitial.lastname, lastname_firstinitial, firstnamelastname…
2020-07-22 12:06:31 -07:00
Godfrey Martinez 3b1e46f72b Update general.php 2020-07-22 11:25:57 -07:00
Godfrey Martinez 0c1a1de2a2 Update general.php
fixed typo
2020-07-22 11:24:36 -07:00
Godfrey M 20c9ae5818 Added firstinitial.lastname, lastname_firstinitial, firstnamelastname and firstnamelastinitial to username formats 2020-07-22 10:21:19 -07:00
snipe eed41e4549 Moved address down further, fixed broken HTML 2020-07-21 16:57:32 -07:00
snipe b750f4754f Added city to bulk user importer 2020-07-21 16:49:54 -07:00
snipe c17a06792a Added address, city, state, country to user importer 2020-07-21 16:49:38 -07:00
snipe 4f76cc6cfb I don’t actually know what this file is for 2020-07-21 16:46:13 -07:00
snipe b905154373 Fixed #8247 - added notes field to user details display 2020-07-20 14:29:32 -07:00
Peter Upfold 004ecad059 Force suppress deprecation warning on ldap_control_paged_result() 2020-06-03 08:59:50 +01:00
34 changed files with 441 additions and 80 deletions
+40
View File
@@ -0,0 +1,40 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [ ] Test A
- [ ] Test B
**Test Configuration**:
* PHP version:
* MySQL version
* Webserver version
* OS version
# Checklist:
- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview
- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
@@ -0,0 +1,95 @@
<?php
namespace App\Console\Commands;
use App\Models\LicenseSeat;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\License;
use Illuminate\Database\Eloquent\Model;
class CheckinLicensesFromAllUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:checkin-from-all {--license_id=} {--notify}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Checks in licenses from all users';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$license_id = $this->option('license_id');
$notify = $this->option('notify');
if (!$license_id) {
$this->error('ERROR: License ID is required.');
return false;
}
if (!$license = License::where('id','=',$license_id)->first()) {
$this->error('Invalid license ID');
return false;
}
$this->info('Checking in ALL seats for '.$license->name);
$licenseSeats = LicenseSeat::where('license_id', '=', $license_id)
->whereNotNull('assigned_to')
->with('user')
->get();
$this->info(' There are ' .$licenseSeats->count(). ' seats checked out: ');
if (!$notify) {
$this->info('No mail will be sent.');
}
foreach ($licenseSeats as $seat) {
$this->info($seat->user->username .' has a license seat for '.$license->name);
$seat->assigned_to = null;
if ($seat->save()) {
// Override the email address so we don't notify on checkin
if (!$notify) {
$seat->user->email = null;
}
// Log the checkin
$seat->logCheckin($seat->user, 'Checked in via cli tool');
}
}
}
}
+37 -3
View File
@@ -124,7 +124,16 @@ class LdapSync extends Command
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
try {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
} catch (\Exception $e) { // FIXME: this is stolen from line 77 or so above
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => trans('admin/users/message.error.ldap_could_not_search')." Location: ".$ldap_loc['name']." (ID: ".$ldap_loc['id'].") cannot connect to \"".$ldap_loc["ldap_ou"]."\" - ".$e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::info($e);
return [];
}
$usernames = array();
for ($i = 0; $i < $location_users["count"]; $i++) {
@@ -187,8 +196,33 @@ class LdapSync extends Command
// Sync activated state for Active Directory.
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
/* The following is _probably_ the correct logic, but we can't use it because
some users may have been dependent upon the previous behavior, and this
could cause additional access to be available to users they don't want
to allow to log in.
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
if(
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
) {
$user->activated = 1;
} else {
$user->activated = 0;
} */
$enabled_accounts = [
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224', '4260352'
'512', // 0x200 NORMAL_ACCOUNT
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'4260352',// 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
'1049088',// 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
];
$user->activated = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
}
@@ -239,7 +273,7 @@ class LdapSync extends Command
}
}
} else if ($this->option('json_summary')) {
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ];
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ]; // hardcoding the error to false and the error_message to blank seems a bit weird
$this->info(json_encode($json_summary));
} else {
return $summary;
@@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Company;
use App\Models\Consumable;
use App\Models\User;
use App\Http\Transformers\ConsumablesTransformer;
use App\Helpers\Helper;
@@ -157,7 +158,7 @@ class ConsumablesController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.delete.success')));
}
/**
/**
* Returns a JSON response containing details on the users associated with this consumable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -196,4 +197,55 @@ class ConsumablesController extends Controller
$data = array('total' => $consumableCount, 'rows' => $rows);
return $data;
}
/**
* Checkout a consumable
*
* @author [A. Gutierrez] [<andres@baller.tv>]
* @param int $id
* @since [v4.9.5]
* @return JsonResponse
*/
public function checkout(Request $request, $id)
{
// Check if the consumable exists
if (is_null($consumable = Consumable::find($id))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.does_not_exist')));
}
$this->authorize('checkout', $consumable);
if ($consumable->qty > 0) {
// Check if the user exists
$assigned_to = $request->input('assigned_to');
if (is_null($user = User::find($assigned_to))) {
// Return error message
return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found'));
}
// Update the consumable data
$consumable->assigned_to = e($assigned_to);
$consumable->users()->attach($consumable->id, [
'consumable_id' => $consumable->id,
'user_id' => $user->id,
'assigned_to' => $assigned_to
]);
// Log checkout event
$logaction = $consumable->logCheckout(e($request->input('note')), $user);
$data['log_id'] = $logaction->id;
$data['eula'] = $consumable->getEula();
$data['first_name'] = $user->first_name;
$data['item_name'] = $consumable->name;
$data['checkout_date'] = $logaction->created_at;
$data['note'] = $logaction->note;
$data['require_acceptance'] = $consumable->requireAcceptance();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'No consumables remaining'));
}
}
@@ -209,7 +209,7 @@ class StatuslabelsController extends Controller
{
$this->authorize('view', Statuslabel::class);
$this->authorize('index', Asset::class);
$assets = Asset::where('status_id','=',$id);
$assets = Asset::where('status_id','=',$id)->with('assignedTo');
$allowed_columns = [
'id',
+1 -1
View File
@@ -102,7 +102,7 @@ class ReportsController extends Controller
$depreciations = Depreciation::get();
// Grab all the assets
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'assetlog', 'company', 'model.category', 'model.depreciation')
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'company', 'model.category', 'model.depreciation')
->orderBy('created_at', 'DESC')->get();
return view('reports/depreciation', compact('assets'))->with('depreciations',$depreciations);
+4
View File
@@ -435,6 +435,10 @@ class UsersController extends Controller
if ($request->filled('department_id')) {
$update_array['department_id'] = $request->input('department_id');
}
if ($request->filled('city')) {
$update_array['city'] = $request->input('city');
}
if ($request->filled('company_id')) {
$update_array['company_id'] = $request->input('company_id');
}
+1 -1
View File
@@ -106,7 +106,7 @@ class SecurityHeaders
$csp_policy[] = "connect-src 'self'";
$csp_policy[] = "object-src 'none'";
$csp_policy[] = "font-src 'self' data:";
$csp_policy[] = "img-src 'self' data: gravatar.com";
$csp_policy[] = "img-src 'self' data: gravatar.com maps.google.com maps.gstatic.com *.googleapis.com";
$csp_policy = join(';', $csp_policy);
$response->headers->set('Content-Security-Policy', $csp_policy);
}
+4
View File
@@ -63,6 +63,10 @@ abstract class Importer
'full_name' => 'full name',
'email' => 'email',
'username' => 'username',
'address' => 'address',
'city' => 'city',
'state' => 'state',
'country' => 'country',
'jobtitle' => 'job title',
'employee_num' => 'employee number',
'phone_number' => 'phone number',
+4
View File
@@ -47,6 +47,10 @@ class UserImporter extends ItemImporter
$this->item['email'] = $this->findCsvMatch($row, 'email');
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
$this->item['address'] = $this->findCsvMatch($row, 'address');
$this->item['city'] = $this->findCsvMatch($row, 'city');
$this->item['state'] = $this->findCsvMatch($row, 'state');
$this->item['country'] = $this->findCsvMatch($row, 'country');
$this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0;
\Log::debug('UserImporter.php Activated: '.$this->findCsvMatch($row, 'activated'));
+5 -1
View File
@@ -14,7 +14,7 @@
| licensed to email | license_email | License |
| licensed to name | license_name | License |
| maintained | maintained | License |
| manager_id | | User |
| manager_id | | User |
| manufacturer | manufacturer | All |
| model name | asset_model | Asset |
| model number | model_number | Asset |
@@ -34,4 +34,8 @@
| User Related Fields | assigned_to | Asset |
| name | | |
| username | | |
| address | address | User |
| city | city | User |
| state | state | User |
| country | country | User |
+3 -3
View File
@@ -265,13 +265,13 @@ class Ldap extends Model
$search_results = ldap_search($ldapconn, $base_dn, '('.$filter.')');
if (!$search_results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn));
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
}
// Get results from page
$results = ldap_get_entries($ldapconn, $search_results);
if (!$results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn));
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
}
// Add results to result set
@@ -286,7 +286,7 @@ class Ldap extends Model
// Clean up after search
$result_set['count'] = $global_count;
$results = $result_set;
ldap_control_paged_result($ldapconn, 0);
@ldap_control_paged_result($ldapconn, 0);
return $results;
+19 -4
View File
@@ -41,8 +41,9 @@ trait Loggable
$settings = Setting::getSettings();
$log = new Actionlog;
$log = $this->determineLogItemType($log);
if(Auth::user())
if (Auth::user()) {
$log->user_id = Auth::user()->id;
}
if (!isset($target)) {
throw new \Exception('All checkout logs require a target.');
@@ -144,7 +145,11 @@ trait Loggable
$log->location_id = null;
$log->note = $note;
$log->user_id = Auth::user()->id;
if (Auth::user()) {
$log->user_id = Auth::user()->id;
}
$log->logaction('checkin from');
$params = [
@@ -160,14 +165,24 @@ trait Loggable
$checkinClass = null;
if (method_exists($target, 'notify')) {
$target->notify(new static::$checkinClass($params));
try {
$target->notify(new static::$checkinClass($params));
} catch (\Exception $e) {
\Log::debug($e);
}
}
// Send to the admin, if settings dictate
$recipient = new \App\Models\Recipients\AdminRecipient();
if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) {
$recipient->notify(new static::$checkinClass($params));
try {
$recipient->notify(new static::$checkinClass($params));
} catch (\Exception $e) {
\Log::debug($e);
}
}
return $log;
+12
View File
@@ -379,6 +379,18 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
} elseif ($format=='firstname') {
$username = str_slug($first_name);
}
elseif ($format=='firstinitial.lastname') {
$username = str_slug(substr($first_name, 0, 1). '.' . str_slug($last_name));
}
elseif ($format=='lastname_firstinitial') {
$username = str_slug($last_name).'_'.str_slug(substr($first_name, 0, 1));
}
elseif ($format=='firstnamelastname') {
$username = str_slug($first_name) . str_slug($last_name);
}
elseif ($format=='firstnamelastinitial') {
$username = str_slug(($first_name.substr($last_name, 0, 1)));
}
}
$user['first_name'] = $first_name;
@@ -76,10 +76,18 @@ class CheckinLicenseNotification extends Notification
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
if ($admin) {
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
];
} else {
$fields = [
'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
'By' => 'CLI tool',
];
}
+6 -6
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v4.9.4',
'full_app_version' => 'v4.9.4 - build 4437-g799a93c46',
'build_version' => '4437',
'app_version' => 'v4.9.5',
'full_app_version' => 'v4.9.5 - build 4482-g5b68a321a',
'build_version' => '4482',
'prerelease_version' => '',
'hash_version' => 'g799a93c46',
'full_hash' => 'v4.9.4-41-g799a93c46',
'hash_version' => 'g5b68a321a',
'full_hash' => 'v4.9.5-44-g5b68a321a',
'branch' => 'master',
);
);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+27 -27
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -1,5 +1,5 @@
{
"/js/build/vue.js": "/js/build/vue.js?id=461ae9803574a5a52d43",
"/js/build/vue.js": "/js/build/vue.js?id=aff26ce7202625817ca1",
"/css/AdminLTE.css": "/css/AdminLTE.css?id=56b8066cfbc70df10545",
"/css/app.css": "/css/app.css?id=407edb63cc6b6dc62405",
"/css/overrides.css": "/css/overrides.css?id=d1fe6296eb548247a5ad",
@@ -18,7 +18,7 @@
"/css/skins/skin-blue-dark.css": "/css/skins/skin-blue-dark.css?id=d25c77d9c6f4cfe2efd4",
"/css/skins/skin-orange-dark.css": "/css/skins/skin-orange-dark.css?id=abc219c1fed59cecb860",
"/css/skins/skin-orange.css": "/css/skins/skin-orange.css?id=59664dbd286988d2a438",
"/js/build/vue.js.map": "/js/build/vue.js.map?id=1760ae00d44238e44172",
"/js/build/vue.js.map": "/js/build/vue.js.map?id=41a870ef3b9b5c6688ca",
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map?id=5a2d6f3c59191ce716e2",
"/css/app.css.map": "/css/app.css.map?id=96b5c985e860716e6a16",
"/css/overrides.css.map": "/css/overrides.css.map?id=f3e3cf42859eb4a28a7b",
@@ -38,7 +38,7 @@
"/css/skins/skin-orange-dark.css.map": "/css/skins/skin-orange-dark.css.map?id=68b998638217fd08ef29",
"/css/skins/skin-orange.css.map": "/css/skins/skin-orange.css.map?id=f90fda3cc0a48c048a9e",
"/css/dist/all.css": "/css/dist/all.css?id=0491555899142b86167d",
"/js/dist/all.js": "/js/dist/all.js?id=4513ccdd4533b52922a8",
"/js/dist/all.js": "/js/dist/all.js?id=5ac062af7b26fb838213",
"/css/build/all.css": "/css/build/all.css?id=0491555899142b86167d",
"/js/build/all.js": "/js/build/all.js?id=4513ccdd4533b52922a8"
"/js/build/all.js": "/js/build/all.js?id=5ac062af7b26fb838213"
}
@@ -169,6 +169,10 @@
{id: 'manager_first_name', text: 'Manager First Name' },
{id: 'manager_last_name', text: 'Manager Last Name' },
{id: 'activated', text: 'Activated' },
{id: 'address', text: 'Address' },
{id: 'city', text: 'City' },
{id: 'state', text: 'State' },
{id: 'country', text: 'Country' },
],
customFields: this.customFields,
+3
View File
@@ -88,6 +88,9 @@
'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)',
'lastnamefirstinitial_format' => 'Last Name First Initial (smithj@example.com)',
'first' => 'First',
'firstnamelastname' => 'First Name Last Name (janesmith@example.com)',
'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)',
'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)', 'first' => 'First',
'first_name' => 'First Name',
'first_name_format' => 'First Name (jane@example.com)',
'files' => 'Files',
@@ -8,6 +8,7 @@ return array(
'owner_doesnt_match_asset' => 'The asset you are trying to associate with this license is owned by somene other than the person selected in the assigned to dropdown.',
'assoc_users' => 'This license is currently checked out to a user and cannot be deleted. Please check the license in first, and then try deleting again. ',
'select_asset_or_person' => 'You must select an asset or a user, but not both.',
'not_found' => 'License not found',
'create' => array(
+5
View File
@@ -88,6 +88,11 @@
'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)',
'lastnamefirstinitial_format' => 'Last Name First Initial (smithj@example.com)',
'first' => 'First',
'firstnamelastname' => 'First Name Last Name (janesmith@example.com)',
'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)',
'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)',
'firstnamelastinitial' => 'First Name Last Initial (janes@example.com)',
'first' => 'First',
'first_name' => 'First Name',
'first_name_format' => 'First Name (jane@example.com)',
'files' => 'Files',
+4 -1
View File
@@ -469,7 +469,10 @@ Form::macro('username_format', function ($name = "username_format", $selected =
'filastname' => trans('general.filastname_format'),
'lastnamefirstinitial' => trans('general.lastnamefirstinitial_format'),
'firstname_lastname' => trans('general.firstname_lastname_underscore_format'),
'firstinitial.lastname' => trans('general.firstinitial.lastname'),
'lastname_firstinitial' => trans('general.lastname_firstinitial'),
'firstnamelastname' => trans('general.firstnamelastname'),
'firstnamelastinitial' => trans('general.firstnamelastinitial')
);
$select = '<select name="'.$name.'" class="'.$class.'" style="width: 100%" aria-label="'.$name.'">';
@@ -829,6 +829,7 @@
<script src="{{ url(mix('js/dist/all.js')) }}" nonce="{{ csrf_token() }}"></script>
<script src="/js/pGenerator.jquery.js"></script>
@section('moar_scripts')
@show
+9 -8
View File
@@ -1,13 +1,13 @@
{{-- See snipeit_modals.js for what powers this --}}
<script src="/js/pGenerator.jquery.js"></script>
<script nonce="{{ csrf_token() }}">
$(document).ready(function () {
$('#genPassword').pGenerator({
window.setTimeout(function () {
$('#modal-genPassword').pGenerator({
'bind': 'click',
'passwordElement': '#modal-password',
'displayElement': '#generated-password',
'displayElement': '#modal-generated-password',
'passwordLength': 16,
'uppercase': true,
'lowercase': true,
@@ -17,13 +17,14 @@
$('#modal-password_confirmation').val($('#modal-password').val());
}
});
});
}, 1000);
</script>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h2 class="modal-title">{{ trans('admin/users/table.createuser') }}</h4>
<h2 class="modal-title">{{ trans('admin/users/table.createuser') }}</h2>
</div>
<div class="modal-body">
<form action="{{ route('api.users.store') }}" onsubmit="return false">
@@ -47,14 +48,14 @@
<div class="dynamic-form-row">
<div class="col-md-4 col-xs-12"><label for="modal-password">{{ trans('admin/users/table.password') }}:</label></div>
<div class="col-md-8 col-xs-12 required"><input type='password' name="password" id='modal-password' class="form-control">
<a href="#" class="left" id="genPassword">Generate</a>
<a href="#" class="left" id="modal-genPassword">Generate</a>
</div>
</div>
<div class="dynamic-form-row">
<div class="col-md-4 col-xs-12"><label for="modal-password_confirmation">{{ trans('admin/users/table.password_confirm') }}:</label></div>
<div class="col-md-8 col-xs-12 required"><input type='password' name="password_confirmation" id='modal-password_confirmation' class="form-control">
<div id="generated-password"></div>
<div id="modal-generated-password"></div>
</div>
</div>
</form>
@@ -35,7 +35,7 @@
}'>
<thead>
<tr>
<th data-field="company" data-sortable="false" data-visible="false">{{ trans('admin/companies/table.title') }}</th>
<th data-field="company" data-sortable="false" data-visible="false" data-formatter="companiesLinkObjFormatter">{{ 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="assetNameLinkFormatter">{{ trans('admin/asset_maintenances/table.asset_name') }}</th>
<th data-sortable="false" data-field="supplier" data-formatter="suppliersLinkObjFormatter">{{ trans('general.supplier') }}</th>
@@ -55,6 +55,15 @@
</div>
</div>
<!-- City -->
<div class="form-group{{ $errors->has('city') ? ' has-error' : '' }}">
<label class="col-md-3 control-label" for="city">{{ trans('general.city') }}</label>
<div class="col-md-4">
<input class="form-control" type="text" name="city" id="city" aria-label="city" />
{!! $errors->first('city', '<span class="alert-msg" aria-hidden="true">:message</span>') !!}
</div>
</div>
<!-- activated -->
<div class="form-group">
<div class="col-sm-3 control-label">
-1
View File
@@ -610,7 +610,6 @@
@stop
@section('moar_scripts')
<script src="{{ asset('js/pGenerator.jquery.js') }}"></script>
<script nonce="{{ csrf_token() }}">
$(document).ready(function() {
+28 -1
View File
@@ -142,11 +142,31 @@
<td class="text-nowrap">{{ trans('admin/users/table.name') }}</td>
<td>{{ $user->present()->fullName() }}</td>
</tr>
<tr>
<td class="text-nowrap">{{ trans('admin/users/table.username') }}</td>
<td>{{ $user->username }}</td>
</tr>
@if (($user->address) || ($user->city) || ($user->state) || ($user->country))
<tr>
<td class="text-nowrap">{{ trans('general.address') }}</td>
<td>
@if ($user->address)
{{ $user->address }} <br>
@endif
@if ($user->city)
{{ $user->city }}
@endif
@if ($user->state)
{{ $user->state }}
@endif
@if ($user->country)
{{ $user->country }}
@endif
</td>
</tr>
@endif
<tr>
<td class="text-nowrap">{{ trans('general.groups') }}</td>
<td>
@@ -285,6 +305,13 @@
@endif
@if ($user->notes)
<tr>
<td class="text-nowrap">{{ trans('admin/users/table.notes') }}</td>
<td>{{ $user->notes }}</td>
</tr>
@endif
</table>
</div>
@@ -297,7 +324,7 @@
<a href="{{ route('users.edit', $user->id) }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">{{ trans('admin/users/general.edit') }}</a>
</div>
@endcan
@can('create', $user)
<div class="col-md-12" style="padding-top: 5px;">
<a href="{{ route('clone/user', $user->id) }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">{{ trans('admin/users/general.clone') }}</a>
+16 -7
View File
@@ -189,7 +189,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'api'], fun
/*--- Consumables API ---*/
Route::resource('consumables', 'ConsumablesController',
[
'names' =>
@@ -204,12 +203,22 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'api'], fun
'parameters' => ['consumable' => 'consumable_id']
]
); // Consumables resource
Route::get('consumables/view/{id}/users',
[
'as' => 'api.consumables.showUsers',
'uses' => 'ConsumablesController@getDataView'
]
);
Route::group(['prefix' => 'consumables'], function () {
Route::get('view/{id}/users',
[
'as' => 'api.consumables.showUsers',
'uses' => 'ConsumablesController@getDataView'
]
);
Route::post('{consumable}/checkout',
[
'as' => 'api.consumables.checkout',
'uses' => 'ConsumablesController@checkout'
]
);
});
/*--- Depreciations API ---*/
+29 -2
View File
@@ -74,7 +74,7 @@ class UserTest extends BaseTest
public function testFirstInitialUnderscoreLastName()
{
$fullname = "Natalia Allanovna Romanova-O'Shostakova";
$expected_username = 'natalia_allanovna-romanova-oshostakova';
$expected_username = 'n_allanovna-romanova-oshostakova';
$user = User::generateFormattedNameFromFullName('firstname_lastname', $fullname);
$this->assertEquals($expected_username, $user['username']);
}
@@ -86,6 +86,33 @@ class UserTest extends BaseTest
$user = User::generateFormattedNameFromFullName('firstname_lastname', $fullname);
$this->assertEquals($expected_username, $user['username']);
}
public function firstInitialDotLastname()
{
$fullname = "Natalia Allanovna Romanova-O'Shostakova";
$expected_username = 'n.allanovnaromanovaoshostakova';
$user = User::generateFormattedNameFromFullName('firstinitial.lastname', $fullname);
$this->assertEquals($expected_username, $user['username']);
}
public function lastNameUnderscoreFirstInitial()
{
$fullname = "Natalia Allanovna Romanova-O'Shostakova";
$expected_username = 'allanovnaromanovaoshostakova_n';
$user = User::generateFormattedNameFromFullName('lastname_firstinitial', $fullname);
$this->assertEquals($expected_username, $user['username']);
}
public function firstNameLastName()
{
$fullname = "Natalia Allanovna Romanova-O'Shostakova";
$expected_username = 'nataliaallanovnaromanovaoshostakova';
$user = User::generateFormattedNameFromFullName('firstnamelastname', $fullname);
$this->assertEquals($expected_username, $user['username']);
}
public function firstNameLastInitial()
{
$fullname = "Natalia Allanovna Romanova-O'Shostakova";
$expected_username = 'nataliaa';
$user = User::generateFormattedNameFromFullName('firstnamelastinitial', $fullname);
$this->assertEquals($expected_username, $user['username']);
}
}