merged develope

This commit is contained in:
Godfrey M 2024-01-29 11:21:00 -08:00
commit 30c2927987
35 changed files with 689 additions and 154 deletions

View file

@ -0,0 +1,10 @@
{
"DOC1": "This file is meant to be pulled from the current HEAD of the desired branch, NOT referenced locally",
"DOC2": "In other words, what you see locally are the requirements for your _current_ install",
"DOC3": "Please don't rely on these versions for planning upgrades unless you've fetched the most recent version",
"DOC4": "You should really just ignore it and run upgrade.php. Really",
"php_min_version": "7.4.0",
"php_max_major_minor": "8.1",
"php_max_wontwork": "8.2.0",
"current_snipeit_version": "6.3"
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\SamlNonce;
class SamlClearExpiredNonces extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'saml:clear_expired_nonces';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clears out expired SAML assertions from the saml_nonces table';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
SamlNonce::where('not_valid_after','<=',now())->delete();
return 0;
}
}

View file

@ -25,6 +25,7 @@ class Kernel extends ConsoleKernel
$schedule->command('backup:clean')->daily();
$schedule->command('snipeit:upcoming-audits')->daily();
$schedule->command('auth:clear-resets')->everyFifteenMinutes();
$schedule->command('saml:clear_expired_nonces')->weekly();
}
/**

View file

@ -288,7 +288,7 @@ class AssetModelsController extends Controller
public function show($modelId = null)
{
$this->authorize('view', AssetModel::class);
$model = AssetModel::withTrashed()->find($modelId);
$model = AssetModel::withTrashed()->withCount('assets')->find($modelId);
if (isset($model->id)) {
return view('models/view', compact('model'));

View file

@ -49,15 +49,86 @@ class BulkAssetsController extends Controller
return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected'));
}
$asset_ids = $request->input('ids');
// Figure out where we need to send the user after the update is complete, and store that in the session
$bulk_back_url = request()->headers->get('referer');
session(['bulk_back_url' => $bulk_back_url]);
$allowed_columns = [
'id',
'name',
'asset_tag',
'serial',
'model_number',
'last_checkout',
'notes',
'expected_checkin',
'order_number',
'image',
'assigned_to',
'created_at',
'updated_at',
'purchase_date',
'purchase_cost',
'last_audit_date',
'next_audit_date',
'warranty_months',
'checkout_counter',
'checkin_counter',
'requests_counter',
'byod',
'asset_eol_date',
];
$asset_ids = $request->input('ids');
// Using the 'short-ternary' A/K/A "Elvis operator" '?:' here because ->input() might return an empty string
list($sortname,$sortdir) = explode(" ",$request->input('sort') ?: 'id ASC');
$assets = Asset::with('assignedTo', 'location', 'model')->whereIn('id', $asset_ids)->orderBy($sortname, $sortdir)->get();
/**
* Make sure the column is allowed, and if it's a custom field, make sure we strip the custom_fields. prefix
*/
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort_override = str_replace('custom_fields.', '', $request->input('sort'));
// This handles all of the pivot sorting below (versus the assets.* fields in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.id';
$assets = Asset::with('assignedTo', 'location', 'model')->whereIn('assets.id', $asset_ids);
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
case 'company':
$assets->OrderCompany($order);
break;
case 'location':
$assets->OrderLocation($order);
case 'rtd_location':
$assets->OrderRtdLocation($order);
break;
case 'status_label':
$assets->OrderStatus($order);
break;
case 'supplier':
$assets->OrderSupplier($order);
break;
case 'assigned_to':
$assets->OrderAssigned($order);
break;
default:
$assets->orderBy($column_sort, $order);
break;
}
$assets = $assets->get();
$models = $assets->unique('model_id');
$modelNames = [];

View file

@ -3,6 +3,7 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\SamlNonce;
use App\Models\Setting;
use App\Models\User;
use App\Models\Ldap;
@ -109,7 +110,14 @@ class LoginController extends Controller
try {
$user = $saml->samlLogin($samlData);
$notValidAfter = new \Carbon\Carbon(@$samlData['assertionNotOnOrAfter']);
if(\Carbon::now()->greaterThanOrEqualTo($notValidAfter)) {
abort(400,"Expired SAML Assertion");
}
if(SamlNonce::where('nonce', @$samlData['nonce'])->count() > 0) {
abort(400,"Assertion has already been used");
}
Log::debug("okay, fine, this is a new nonce then. Good for you.");
if (!is_null($user)) {
Auth::login($user);
} else {
@ -123,10 +131,14 @@ class LoginController extends Controller
$user->last_login = \Carbon::now();
$user->saveQuietly();
}
$s = new SamlNonce();
$s->nonce = @$samlData['nonce'];
$s->not_valid_after = $notValidAfter;
$s->save();
} catch (\Exception $e) {
\Log::debug('There was an error authenticating the SAML user: '.$e->getMessage());
throw new \Exception($e->getMessage());
throw $e;
}
// Fallthrough with better logging

View file

@ -16,7 +16,6 @@ class SlackSettingsForm extends Component
public $isDisabled ='disabled' ;
public $webhook_name;
public $webhook_link;
public $webhook_test;
public $webhook_placeholder;
public $webhook_icon;
public $webhook_selected;
@ -43,35 +42,32 @@ class SlackSettingsForm extends Component
"icon" => 'fab fa-slack',
"placeholder" => "https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXX",
"link" => 'https://api.slack.com/messaging/webhooks',
"test" => "testWebhook"
),
"general" => array(
"general"=> array(
"name" => trans('admin/settings/general.general_webhook'),
"icon" => "fab fa-hashtag",
"placeholder" => "",
"placeholder" => trans('general.url'),
"link" => "",
"test" => "testWebhook"
),
"google" => array(
"name" => trans('admin/settings/general.google_workspaces'),
"icon" => "fa-brands fa-google",
"placeholder" => "https://chat.googleapis.com/v1/spaces/xxxxxxxx/messages?key=xxxxxx",
"link" => "https://developers.google.com/chat/how-tos/webhooks#register_the_incoming_webhook",
"test" => "googleWebhookTest"
"microsoft" => array(
"name" => trans('admin/settings/general.ms_teams'),
"icon" => "fa-brands fa-microsoft",
"placeholder" => "https://abcd.webhook.office.com/webhookb2/XXXXXXX",
"link" => "https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=dotnet#create-incoming-webhooks-1",
),
];
$this->setting = Setting::getSettings();
$this->save_button = trans('general.save');
$this->webhook_selected = $this->setting->webhook_selected;
$this->webhook_placeholder = $this->webhook_text[$this->setting->webhook_selected]["placeholder"];
$this->webhook_name = $this->webhook_text[$this->setting->webhook_selected]["name"];
$this->webhook_icon = $this->webhook_text[$this->setting->webhook_selected]["icon"];
$this->webhook_placeholder = $this->webhook_text[$this->setting->webhook_selected]["placeholder"];
$this->webhook_link = $this->webhook_text[$this->setting->webhook_selected]["link"];
$this->webhook_endpoint = $this->setting->webhook_endpoint;
$this->webhook_channel = $this->setting->webhook_channel;
$this->webhook_botname = $this->setting->webhook_botname;
$this->webhook_options = $this->setting->webhook_selected;
$this->webhook_test = $this->webhook_text[$this->setting->webhook_selected]["test"];
if($this->setting->webhook_endpoint != null && $this->setting->webhook_channel != null){
@ -89,11 +85,13 @@ class SlackSettingsForm extends Component
$this->webhook_name = $this->webhook_text[$this->webhook_selected]['name'];
$this->webhook_icon = $this->webhook_text[$this->webhook_selected]["icon"]; ;
$this->webhook_placeholder = $this->webhook_text[$this->webhook_selected]["placeholder"];
$this->webhook_endpoint = null;
$this->webhook_link = $this->webhook_text[$this->webhook_selected]["link"];
if($this->webhook_selected != 'slack'){
$this->isDisabled= '';
$this->save_button = trans('general.save');
}
}
private function isButtonDisabled() {
@ -152,31 +150,7 @@ class SlackSettingsForm extends Component
return session()->flash('error' , trans('admin/settings/message.webhook.error_misc'));
}
public function googleWebhookTest(){
$url = $this->webhook_endpoint;
$data = json_encode([ 'text' => trans('general.webhook_test_msg', ['app' => $this->webhook_name])]);
$headers = [
'Authorization' => 'Bearer'. 'AIzaSyBu-61gEOhYGfrmT3fHQj6vS8TDWpo1B5U',
'Content-Type' => 'application/json',
];
$client = new Client();
try {
$response = $client->post($url,[
'headers' => $headers,
'json' => $data,
]);
if (($response->getStatusCode() == 302) || ($response->getStatusCode() == 301)) {
return session()->flash('error', trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
}
} catch (\Exception $e) {
$this->isDisabled='disabled';
$this->save_button = trans('admin/settings/general.webhook_presave');
return session()->flash('error' , trans('admin/settings/message.webhook.error', ['error_message' => $e->getMessage(), 'app' => $this->webhook_name]));
}
}
public function clearSettings(){
@ -211,8 +185,40 @@ class SlackSettingsForm extends Component
$this->setting->save();
session()->flash('success',trans('admin/settings/message.update.success'));
}
}
public function msTeamTestWebhook(){
$payload =
[
"@type" => "MessageCard",
"@context" => "http://schema.org/extensions",
"summary" => trans('mail.snipe_webhook_summary'),
"title" => trans('mail.snipe_webhook_test'),
"text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
];
try {
$response = Http::withHeaders([
'content-type' => 'applications/json',
])->post($this->webhook_endpoint,
$payload)->throw();
if(($response->getStatusCode() == 302)||($response->getStatusCode() == 301)){
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
}
$this->isDisabled='';
$this->save_button = trans('general.save');
return session()->flash('success' , trans('admin/settings/message.webhook.success', ['webhook_name' => $this->webhook_name]));
} catch (\Exception $e) {
$this->isDisabled='disabled';
$this->save_button = trans('admin/settings/general.webhook_presave');
return session()->flash('error' , trans('admin/settings/message.webhook.error', ['error_message' => $e->getMessage(), 'app' => $this->webhook_name]));
}
return session()->flash('error' , trans('admin/settings/message.webhook.error_misc'));
}
}

View file

@ -58,9 +58,14 @@ class CheckoutableListener
}
if ($this->shouldSendWebhookNotification()) {
//slack doesn't include the url in its messaging format so this is needed to hit the endpoint
if(Setting::getSettings()->webhook_selected =='slack') {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event));
}
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
} catch (Exception $e) {
@ -107,11 +112,15 @@ class CheckoutableListener
$this->getCheckinNotification($event)
);
}
//slack doesn't include the url in its messaging format so this is needed to hit the endpoint
if(Setting::getSettings()->webhook_selected =='slack') {
if ($this->shouldSendWebhookNotification()) {
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
}
} catch (ClientException $e) {
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
} catch (Exception $e) {
@ -219,6 +228,7 @@ class CheckoutableListener
break;
}
return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note);
}

View file

@ -46,15 +46,6 @@ class AssetModel extends SnipeModel
protected $injectUniqueIdentifier = true;
use ValidatingTrait;
public function setEolAttribute($value)
{
if ($value == '') {
$value = 0;
}
$this->attributes['eol'] = $value;
}
/**
* The attributes that are mass assignable.
*

15
app/Models/SamlNonce.php Normal file
View file

@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SamlNonce extends Model
{
use HasFactory;
protected $fillable = ['nonce','not_on_or_after'];
public $timestamps = false;
}

View file

@ -9,6 +9,8 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckinAccessoryNotification extends Notification
{
@ -35,10 +37,14 @@ class CheckinAccessoryNotification extends Notification
*/
public function via()
{
\Log::debug('via called');
$notifyBy = [];
if (Setting::getSettings()->webhook_endpoint != '') {
if (Setting::getSettings()->webhook_selected == 'microsoft'){
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack') {
$notifyBy[] = 'slack';
}
@ -108,6 +114,24 @@ class CheckinAccessoryNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('Accessory_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Accessory_Checkin_Notification')." by ", $admin->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -10,6 +10,8 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckinAssetNotification extends Notification
{
@ -44,7 +46,12 @@ class CheckinAssetNotification extends Notification
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->webhook_endpoint != '') {
if (Setting::getSettings()->webhook_selected == 'microsoft'){
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack') {
\Log::debug('use webhook');
$notifyBy[] = 'slack';
}
@ -84,6 +91,23 @@ class CheckinAssetNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->title(trans('mail.Asset_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText')
->fact(trans('mail.checked_into'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Asset_Checkin_Notification')." by ", $admin->present()->fullName())
->fact(trans('admin/hardware/form.status'), $item->assetstatus->name)
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -9,6 +9,8 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckinLicenseSeatNotification extends Notification
{
@ -41,7 +43,12 @@ class CheckinLicenseSeatNotification extends Notification
{
$notifyBy = [];
if (Setting::getSettings()->webhook_endpoint != '') {
if (Setting::getSettings()->webhook_selected == 'microsoft'){
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack') {
$notifyBy[] = 'slack';
}
@ -87,6 +94,25 @@ class CheckinLicenseSeatNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.License_Checkin_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'header')
->fact(trans('mail.License_Checkin_Notification')." by ", $admin->present()->fullName() ?: 'CLI tool')
->fact(trans('mail.checkedin_from'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count())
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -9,6 +9,8 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckoutAccessoryNotification extends Notification
{
@ -24,7 +26,6 @@ class CheckoutAccessoryNotification extends Notification
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
}
@ -37,7 +38,12 @@ class CheckoutAccessoryNotification extends Notification
{
$notifyBy = [];
if (Setting::getSettings()->webhook_endpoint != '') {
if (Setting::getSettings()->webhook_selected == 'microsoft'){
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack') {
$notifyBy[] = 'slack';
}
@ -96,6 +102,27 @@ class CheckoutAccessoryNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.Accessory_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.assigned_to'), $target->present()->name)
->fact(trans('mail.checkedout_from'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Accessory_Checkout_Notification') . " by ", $admin->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -6,10 +6,13 @@ use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\Setting;
use App\Models\User;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckoutAssetNotification extends Notification
{
@ -51,9 +54,13 @@ class CheckoutAssetNotification extends Notification
*/
public function via()
{
if (Setting::getSettings()->webhook_selected == 'microsoft'){
return [MicrosoftTeamsChannel::class];
}
$notifyBy = [];
if ((Setting::getSettings()) && (Setting::getSettings()->webhook_endpoint != '')) {
if ((Setting::getSettings()) && (Setting::getSettings()->webhook_selected == 'slack')) {
\Log::debug('use webhook');
$notifyBy[] = 'slack';
}
@ -117,6 +124,25 @@ class CheckoutAssetNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->title(trans('mail.Asset_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(trans('mail.assigned_to'), $target->present()->name)
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityText')
->fact(trans('mail.Asset_Checkout_Notification') . " by ", $admin->present()->fullName())
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -9,6 +9,8 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckoutConsumableNotification extends Notification
{
@ -43,7 +45,12 @@ class CheckoutConsumableNotification extends Notification
{
$notifyBy = [];
if (Setting::getSettings()->webhook_endpoint != '') {
if (Setting::getSettings()->webhook_selected == 'microsoft'){
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack') {
$notifyBy[] = 'slack';
}
@ -102,6 +109,25 @@ class CheckoutConsumableNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.Consumable_checkout_notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.Consumable_checkout_notification')." by ", $admin->present()->fullName())
->fact(trans('mail.assigned_to'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -9,6 +9,8 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsChannel;
use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;
class CheckoutLicenseSeatNotification extends Notification
{
@ -41,9 +43,15 @@ class CheckoutLicenseSeatNotification extends Notification
*/
public function via()
{
$notifyBy = [];
if (Setting::getSettings()->webhook_endpoint != '') {
if (Setting::getSettings()->webhook_selected == 'microsoft'){
$notifyBy[] = MicrosoftTeamsChannel::class;
}
if (Setting::getSettings()->webhook_selected == 'slack') {
$notifyBy[] = 'slack';
}
@ -102,6 +110,25 @@ class CheckoutLicenseSeatNotification extends Notification
->content($note);
});
}
public function toMicrosoftTeams()
{
$target = $this->target;
$admin = $this->admin;
$item = $this->item;
$note = $this->note;
return MicrosoftTeamsMessage::create()
->to($this->settings->webhook_endpoint)
->type('success')
->addStartGroupToSection('activityTitle')
->title(trans('mail.License_Checkout_Notification'))
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.License_Checkout_Notification')." by ", $admin->present()->fullName())
->fact(trans('mail.assigned_to'), $target->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->availCount()->count())
->fact(trans('mail.notes'), $note ?: '');
}
/**
* Get the mail representation of the notification.

View file

@ -0,0 +1,35 @@
<?php
namespace App\Providers;
use App\Services\SnipeTranslator;
use Illuminate\Translation\TranslationServiceProvider;
class SnipeTranslationServiceProvider extends TranslationServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//This is almost an *EXACT* carbon-copy of the TranslationServiceProvider, except with a modified Translator
$this->registerLoader();
$this->app->singleton('translator', function ($app) {
$loader = $app['translation.loader'];
// When registering the translator component, we'll need to set the default
// locale as well as the fallback locale. So, we'll grab the application
// configuration so we can easily get both of these values from there.
$locale = $app['config']['app.locale'];
$trans = new SnipeTranslator($loader, $locale); //the ONLY changed line
$trans->setFallback($app['config']['app.fallback_locale']);
return $trans;
});
}
}

View file

@ -394,6 +394,8 @@ class Saml
'nameIdSPNameQualifier' => $auth->getNameIdSPNameQualifier(),
'sessionIndex' => $auth->getSessionIndex(),
'sessionExpiration' => $auth->getSessionExpiration(),
'nonce' => $auth->getLastAssertionId(),
'assertionNotOnOrAfter' => $auth->getLastAssertionNotOnOrAfter(),
];
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Services;
use Illuminate\Translation\Translator;
/***************************************************************
* This is just a very, very light modification to the default Laravel Translator.
* The only difference it has is that it modifies the $locale
* value when the pluralizations are done so we can switch over from en-US to en_US (for example).
*
* This means our translation directories can stay where they are (en-US), but the
* pluralization code can get executed against a locale of en_US
* (Which is required by Symfony, for some inexplicable reason)
*
* This method is called by the trans_choice() helper, which we *do* use a lot.
***************************************************************/
class SnipeTranslator extends Translator {
//This is copied-and-pasted (almost) verbatim from Illuminate\Translation\Translator
public function choice($key, $number, array $replace = [], $locale = null)
{
$line = $this->get(
$key, $replace, $locale = $this->localeForChoice($locale)
);
// If the given "number" is actually an array or countable we will simply count the
// number of elements in an instance. This allows developers to pass an array of
// items without having to count it on their end first which gives bad syntax.
if (is_array($number) || $number instanceof Countable) {
$number = count($number);
}
$replace['count'] = $number;
$underscored_locale = str_replace("-","_",$locale); // OUR CHANGE.
return $this->makeReplacements( // BELOW - that $underscored_locale is the *ONLY* modified part
$this->getSelector()->choose($line, $number, $underscored_locale), $replace
);
}
}

View file

@ -42,6 +42,7 @@
"guzzlehttp/guzzle": "^7.0.1",
"intervention/image": "^2.5",
"javiereguiluz/easyslugger": "^1.0",
"laravel-notification-channels/microsoft-teams": "^1.1",
"laravel/framework": "^8.46",
"laravel/helpers": "^1.4",
"laravel/passport": "^10.1",

61
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f4f3b6b02d044ed3e54cdd509b01c3dc",
"content-hash": "89580c52de91168aac8321460bd428e2",
"packages": [
{
"name": "alek13/slack",
@ -3188,6 +3188,63 @@
},
"time": "2015-04-12T19:57:10+00:00"
},
{
"name": "laravel-notification-channels/microsoft-teams",
"version": "v1.1.4",
"source": {
"type": "git",
"url": "https://github.com/laravel-notification-channels/microsoft-teams.git",
"reference": "e2df0129ba430666979eb2ad7033455fd0f6b577"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-notification-channels/microsoft-teams/zipball/e2df0129ba430666979eb2ad7033455fd0f6b577",
"reference": "e2df0129ba430666979eb2ad7033455fd0f6b577",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"illuminate/notifications": "~5.5 || ~6.0 || ~7.0 || ^8.0 || ^9.0 || ^10.0",
"illuminate/support": "~5.5 || ~6.0 || ~7.0 || ^8.0 || ^9.0 || ^10.0",
"php": ">=7.2"
},
"require-dev": {
"mockery/mockery": "^1.2.3",
"phpunit/phpunit": "^8.0|^9.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"NotificationChannels\\MicrosoftTeams\\MicrosoftTeamsServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"NotificationChannels\\MicrosoftTeams\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Tobias Madner",
"email": "tobias.madner@gmx.at",
"homepage": "https://www.pinpoll.com",
"role": "Developer"
}
],
"description": "A Laravel Notification Channel for Microsoft Teams",
"homepage": "https://github.com/laravel-notification-channels/microsoft-teams",
"support": {
"issues": "https://github.com/laravel-notification-channels/microsoft-teams/issues",
"source": "https://github.com/laravel-notification-channels/microsoft-teams/tree/v1.1.4"
},
"time": "2023-01-25T16:56:40+00:00"
},
{
"name": "laravel/framework",
"version": "v8.83.22",
@ -16974,5 +17031,5 @@
"ext-pdo": "*"
},
"platform-dev": [],
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.3.0"
}

View file

@ -277,7 +277,8 @@ return [
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
// Illuminate\Translation\TranslationServiceProvider::class, //replaced on next line
App\Providers\SnipeTranslationServiceProvider::class, //we REPLACE the default Laravel translator with our own
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Barryvdh\DomPDF\ServiceProvider::class,

View file

@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v6.2.4-pre',
'full_app_version' => 'v6.2.4-pre - build 12343-gb23ce6cfc',
'build_version' => '12343',
'app_version' => 'v6.3.0',
'full_app_version' => 'v6.3.0 - build 12490-g9136415bb',
'build_version' => '12490',
'prerelease_version' => '',
'hash_version' => 'gb23ce6cfc',
'full_hash' => 'v6.2.4-pre-582-gb23ce6cfc',
'hash_version' => 'g9136415bb',
'full_hash' => 'v6.3.0-729-g9136415bb',
'branch' => 'develop',
);

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSamlNonceTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (! Schema::hasTable('saml_nonces') ) {
Schema::create('saml_nonces', function (Blueprint $table) {
$table->id();
$table->string('nonce')->index();
$table->datetime('not_valid_after')->index();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('saml_nonces');
}
}

View file

@ -34,7 +34,7 @@ return array(
'bulkedit' => array(
'error' => 'No fields were changed, so nothing was updated.',
'success' => 'Model successfully updated. |:model_count models successfully updated.',
'warn' => 'You are about to update the properies of the following model: |You are about to edit the properties of the following :model_count models:',
'warn' => 'You are about to update the properties of the following model:|You are about to edit the properties of the following :model_count models:',
),

View file

@ -204,7 +204,7 @@ return [
'integrations' => 'Integrations',
'slack' => 'Slack',
'general_webhook' => 'General Webhook',
'google_workspaces' => 'Google Workspaces',
'ms_teams' => 'Microsoft Teams',
'webhook' => ':app',
'webhook_presave' => 'Test to Save',
'webhook_title' => 'Update Webhook Settings',

View file

@ -1,10 +1,33 @@
<?php
return [
'acceptance_asset_accepted' => 'A user has accepted an item',
'acceptance_asset_declined' => 'A user has declined an item',
'Accessory_Checkin_Notification' => 'Accessory checked in',
'Accessory_Checkout_Notification' => 'Accessory checked out',
'Asset_Checkin_Notification' => 'Asset checked in',
'Asset_Checkout_Notification' => 'Asset checked out',
'Confirm_Accessory_Checkin' => 'Accessory checkin confirmation',
'Confirm_Asset_Checkin' => 'Asset checkin confirmation',
'Confirm_accessory_delivery' => 'Accessory delivery confirmation',
'Confirm_asset_delivery' => 'Asset delivery confirmation',
'Confirm_consumable_delivery' => 'Consumable delivery confirmation',
'Confirm_license_delivery' => 'License delivery confirmation',
'Consumable_checkout_notification' => 'Consumable checked out',
'Days' => 'Days',
'Expected_Checkin_Date' => 'An asset checked out to you is due to be checked back in on :date',
'Expected_Checkin_Notification' => 'Reminder: :name checkin deadline approaching',
'Expected_Checkin_Report' => 'Expected asset checkin report',
'Expiring_Assets_Report' => 'Expiring Assets Report.',
'Expiring_Licenses_Report' => 'Expiring Licenses Report.',
'Item_Request_Canceled' => 'Item Request Canceled',
'Item_Requested' => 'Item Requested',
'License_Checkin_Notification' => 'License checked in',
'License_Checkout_Notification' => 'License checked out',
'Low_Inventory_Report' => 'Low Inventory Report',
'a_user_canceled' => 'A user has canceled an item request on the website',
'a_user_requested' => 'A user has requested an item on the website',
'acceptance_asset_accepted' => 'A user has accepted an item',
'acceptance_asset_declined' => 'A user has declined an item',
'accessory_name' => 'Accessory Name:',
'additional_notes' => 'Additional Notes:',
'admin_has_created' => 'An administrator has created an account for you on the :web website.',
@ -12,59 +35,51 @@ return [
'asset_name' => 'Asset Name:',
'asset_requested' => 'Asset requested',
'asset_tag' => 'Asset Tag',
'assets_warrantee_alert' => 'There is :count asset with a warranty expiring in the next :threshold days.|There are :count assets with warranties expiring in the next :threshold days.',
'assigned_to' => 'Assigned To',
'best_regards' => 'Best regards,',
'canceled' => 'Canceled:',
'checkin_date' => 'Checkin Date:',
'checkout_date' => 'Checkout Date:',
'click_to_confirm' => 'Please click on the following link to confirm your :web account:',
'checkedout_from' => 'Checked out from',
'checked_into' => 'Checked into',
'click_on_the_link_accessory' => 'Please click on the link at the bottom to confirm that you have received the accessory.',
'click_on_the_link_asset' => 'Please click on the link at the bottom to confirm that you have received the asset.',
'Confirm_Asset_Checkin' => 'Asset checkin confirmation',
'Confirm_Accessory_Checkin' => 'Accessory checkin confirmation',
'Confirm_accessory_delivery' => 'Accessory delivery confirmation',
'Confirm_license_delivery' => 'License delivery confirmation',
'Confirm_asset_delivery' => 'Asset delivery confirmation',
'Confirm_consumable_delivery' => 'Consumable delivery confirmation',
'click_to_confirm' => 'Please click on the following link to confirm your :web account:',
'current_QTY' => 'Current QTY',
'Days' => 'Days',
'days' => 'Days',
'expecting_checkin_date' => 'Expected Checkin Date:',
'expires' => 'Expires',
'Expiring_Assets_Report' => 'Expiring Assets Report.',
'Expiring_Licenses_Report' => 'Expiring Licenses Report.',
'hello' => 'Hello',
'hi' => 'Hi',
'i_have_read' => 'I have read and agree to the terms of use, and have received this item.',
'item' => 'Item:',
'Item_Request_Canceled' => 'Item Request Canceled',
'Item_Requested' => 'Item Requested',
'link_to_update_password' => 'Please click on the following link to update your :web password:',
'login_first_admin' => 'Login to your new Snipe-IT installation using the credentials below:',
'login' => 'Login:',
'Low_Inventory_Report' => 'Low Inventory Report',
'inventory_report' => 'Inventory Report',
'item' => 'Item:',
'license_expiring_alert' => 'There is :count license expiring in the next :threshold days.|There are :count licenses expiring in the next :threshold days.',
'link_to_update_password' => 'Please click on the following link to update your :web password:',
'login' => 'Login:',
'login_first_admin' => 'Login to your new Snipe-IT installation using the credentials below:',
'low_inventory_alert' => 'There is :count item that is below minimum inventory or will soon be low.|There are :count items that are below minimum inventory or will soon be low.',
'min_QTY' => 'Min QTY',
'name' => 'Name',
'new_item_checked' => 'A new item has been checked out under your name, details are below.',
'notes' => 'Notes',
'password' => 'Password:',
'password_reset' => 'Password Reset',
'read_the_terms' => 'Please read the terms of use below.',
'read_the_terms_and_click' => 'Please read the terms of use below, and click on the link at the bottom to confirm that you read
and agree to the terms of use, and have received the asset.',
'read_the_terms_and_click' => 'Please read the terms of use below, and click on the link at the bottom to confirm that you read and agree to the terms of use, and have received the asset.',
'requested' => 'Requested:',
'reset_link' => 'Your Password Reset Link',
'reset_password' => 'Click here to reset your password:',
'rights_reserved' => 'All rights reserved.',
'serial' => 'Serial',
'snipe_webhook_test' => 'Snipe-IT Integration Test',
'snipe_webhook_summary' => 'Snipe-IT Integration Test Summary',
'supplier' => 'Supplier',
'tag' => 'Tag',
'test_email' => 'Test Email from Snipe-IT',
'test_mail_text' => 'This is a test from the Snipe-IT Asset Management System. If you got this, mail is working :)',
'the_following_item' => 'The following item has been checked in: ',
'low_inventory_alert' => 'There is :count item that is below minimum inventory or will soon be low.|There are :count items that are below minimum inventory or will soon be low.',
'assets_warrantee_alert' => 'There is :count asset with a warranty expiring in the next :threshold days.|There are :count assets with warranties expiring in the next :threshold days.',
'license_expiring_alert' => 'There is :count license expiring in the next :threshold days.|There are :count licenses expiring in the next :threshold days.',
'to_reset' => 'To reset your :web password, complete this form:',
'type' => 'Type',
'upcoming-audits' => 'There is :count asset that is coming up for audit within :threshold days.|There are :count assets that are coming up for audit within :threshold days.',
@ -72,14 +87,6 @@ return [
'username' => 'Username',
'welcome' => 'Welcome :name',
'welcome_to' => 'Welcome to :web!',
'your_credentials' => 'Your Snipe-IT credentials',
'Accessory_Checkin_Notification' => 'Accessory checked in',
'Asset_Checkin_Notification' => 'Asset checked in',
'Asset_Checkout_Notification' => 'Asset checked out',
'License_Checkin_Notification' => 'License checked in',
'Expected_Checkin_Report' => 'Expected asset checkin report',
'Expected_Checkin_Notification' => 'Reminder: :name checkin deadline approaching',
'Expected_Checkin_Date' => 'An asset checked out to you is due to be checked back in on :date',
'your_assets' => 'View Your Assets',
'rights_reserved' => 'All rights reserved.',
'your_credentials' => 'Your Snipe-IT credentials',
];

View file

@ -61,9 +61,9 @@
<div class="col-md-9 required" wire:ignore>
@if (Helper::isDemoMode())
{{ Form::select('webhook_selected', array('slack' => trans('admin/settings/general.slack'), 'general' => trans('admin/settings/general.general_webhook'), 'google' => trans('admin/settings/general.google_workspaces')), old('webhook_selected', $webhook_selected), array('class'=>'select2 form-control', 'aria-label' => 'webhook_selected', 'id' => 'select2', 'style'=>'width:100%', 'disabled')) }}
{{ Form::select('webhook_selected', array('slack' => trans('admin/settings/general.slack'), 'general' => trans('admin/settings/general.general_webhook'), 'microsoft' => trans('admin/settings/general.ms_teams')), old('webhook_selected', $webhook_selected), array('class'=>'select2 form-control', 'aria-label' => 'webhook_selected', 'id' => 'select2', 'style'=>'width:100%', 'disabled')) }}
@else
{{ Form::select('webhook_selected', array('slack' => trans('admin/settings/general.slack'), 'general' => trans('admin/settings/general.general_webhook'), 'google' => trans('admin/settings/general.google_workspaces')), old('webhook_selected', $webhook_selected), array('class'=>'select2 form-control', 'aria-label' => 'webhook_selected', 'id' => 'select2', 'data-minimum-results-for-search' => '-1', 'style'=>'width:100%')) }}
{{ Form::select('webhook_selected', array('slack' => trans('admin/settings/general.slack'), 'general' => trans('admin/settings/general.general_webhook'), 'microsoft' => trans('admin/settings/general.ms_teams')), old('webhook_selected', $webhook_selected), array('class'=>'select2 form-control', 'aria-label' => 'webhook_selected', 'id' => 'select2', 'data-minimum-results-for-search' => '-1', 'style'=>'width:100%')) }}
@endif
</div>
@ -106,7 +106,7 @@
@endif
<!-- Webhook botname -->
@if($webhook_selected != 'microsoft' && $webhook_selected != 'google')
@if($webhook_selected != 'microsoft')
<div class="form-group{{ $errors->has('webhook_botname') ? ' error' : '' }}">
<div class="col-md-2">
{{ Form::label('webhook_botname', trans('admin/settings/general.webhook_botname',['app' => $webhook_name ])) }}
@ -117,7 +117,6 @@
</div><!--col-md-10-->
</div>
@endif
@if (!Helper::isDemoMode())
@include('partials.forms.demo-mode')
@endif
@ -126,7 +125,11 @@
@if($webhook_endpoint != null && $webhook_channel != null)
<div class="form-group">
<div class="col-md-offset-2 col-md-9">
<a href="#" wire:click.prevent="{{$webhook_test}}"
@if($webhook_selected == "microsoft")
<a href="#" wire:click.prevent="msTeamTestWebhook"
@else
<a href="#" wire:click.prevent="testWebhook"
@endif
class="btn btn-default btn-sm pull-left">
<i class="{{$webhook_icon}}" aria-hidden="true"></i>
{!! trans('admin/settings/general.webhook_test',['app' => ucwords($webhook_selected) ]) !!}

View file

@ -43,7 +43,7 @@
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.assets') }}
{!! (($model->assets) && ($model->assets->count() > 0 )) ? '<badge class="badge badge-secondary">'.number_format($model->assets->count()).'</badge>' : '' !!}
{!! (($model->assets_count) && ($model->assets_count > 0 )) ? '<badge class="badge badge-secondary">'.number_format($model->assets_count).'</badge>' : '' !!}
</span>
</a>
</li>
@ -342,7 +342,7 @@
@endcan
@can('delete', \App\Models\AssetModel::class)
@if ($model->assets->count() > 0)
@if ($model->assets_count > 0)
<div class="col-md-12" style="padding-bottom: 5px;">
<button class="btn btn-block btn-sm btn-danger hidden-print disabled" data-tooltip="true" data-placement="top" data-title="{{ trans('general.cannot_be_deleted') }}">{{ trans('general.delete') }}</button>

View file

@ -6,8 +6,9 @@
'id' => (isset($id_formname)) ? $id_formname : 'assetsBulkForm',
]) }}
{{-- The 'id ASC' will only be used if the cookie is actually empty (like on first-use) --}}
<input name="sort" type="hidden" value="id ASC">
{{-- The sort and order will only be used if the cookie is actually empty (like on first-use) --}}
<input name="sort" type="hidden" value="assets.id">
<input name="order" type="hidden" value="asc">
<label for="bulk_actions">
<span class="sr-only">
{{ trans('button.bulk_actions') }}

View file

@ -164,23 +164,24 @@
// Initialize sort-order for bulk actions (label-generation) for snipe-tables
$('.snipe-table').each(function (i, table) {
table_cookie_segment = $(table).data('cookie-id-table');
name = '';
direction = '';
sort = '';
order = '';
cookies = document.cookie.split(";");
for(i in cookies) {
cookiedef = cookies[i].split("=", 2);
cookiedef[0] = cookiedef[0].trim();
if (cookiedef[0] == table_cookie_segment + ".bs.table.sortOrder") {
direction = cookiedef[1];
order = cookiedef[1];
}
if (cookiedef[0] == table_cookie_segment + ".bs.table.sortName") {
name = cookiedef[1];
sort = cookiedef[1];
}
}
if (name && direction) {
if (sort && order) {
domnode = $($(this).data('bulk-form-id')).get(0);
if ( domnode && domnode.elements && domnode.elements.sort ) {
domnode.elements.sort.value = name + " " + direction;
domnode.elements.sort.value = sort;
domnode.elements.order.value = order;
}
}
});
@ -190,7 +191,8 @@
domnode = $($(this).data('bulk-form-id')).get(0);
// make safe in case there isn't a bulk-form-id, or it's not found, or has no 'sort' element
if ( domnode && domnode.elements && domnode.elements.sort ) {
domnode.elements.sort.value = name + " " + order;
domnode.elements.sort.value = name;
domnode.elements.order.value = order;
}
});

View file

@ -522,7 +522,7 @@
{{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
</div>
<div class="col-md-8" id="ldaptestrow">
<a {{ $setting->demoMode }} class="btn btn-default btn-sm pull-left" id="ldaptest" style="margin-right: 10px;">{{ trans('admin/settings/general.ldap_test_sync') }}</a>
<a {{ $setting->demoMode }} class="btn btn-default btn-sm" id="ldaptest" style="margin-right: 10px;">{{ trans('admin/settings/general.ldap_test_sync') }}</a>
</div>
<div class="col-md-8 col-md-offset-3">
<br />
@ -700,11 +700,13 @@
html += '<li class="text-success"><i class="fas fa-check" aria-hidden="true"></i> ' + results.login.message + ' </li>'
html += '<li class="text-success"><i class="fas fa-check" aria-hidden="true"></i> ' + results.bind.message + ' </li>'
html += '</ul>'
html += '<div style="overflow:auto;">'
html += '<div>{{ trans('admin/settings/message.ldap.sync_success') }}</div>'
html += '<table class="table table-bordered table-condensed" style="background-color: #fff">'
html += '<table class="table table-bordered table-condensed" style=" table-layout:fixed; width:100%;background-color: #fff">'
html += buildLdapResultsTableHeader()
html += buildLdapResultsTableBody(results.user_sync.users)
html += '<table>'
html += '</table>'
html += '</div>'
return html;
}

View file

@ -11,15 +11,6 @@ class AssetModelTest extends TestCase
{
use InteractsWithSettings;
public function testAnAssetModelZerosOutBlankEols()
{
$am = new AssetModel;
$am->eol = '';
$this->assertTrue($am->eol === 0);
$am->eol = '4';
$this->assertTrue($am->eol == 4);
}
public function testAnAssetModelContainsAssets()
{
$category = Category::factory()->create([

View file

@ -1,8 +1,24 @@
<?php
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('Access denied.');
$php_min_works = '7.4.0';
$php_max_wontwork = '8.2.0';
$app_environment = 'develop';
// Check if a branch or tag was passed in the command line,
// otherwise just use master
(array_key_exists('1', $argv)) ? $branch = $argv[1] : $branch = 'master';
// Fetching most current upgrade requirements from github. Read more here: https://github.com/snipe/snipe-it/pull/14127
$remote_requirements_file = "https://raw.githubusercontent.com/snipe/snipe-it/$branch/.upgrade_requirements.json";
$upgrade_requirements = json_decode(file_get_contents($remote_requirements_file), true);
if (! $upgrade_requirements) {
die("\nERROR: Failed to retrieve remote requirements from $remote_requirements_file\nExiting.\n\n");
}
$php_min_works = $upgrade_requirements['php_min_version'];
$php_max_wontwork = $upgrade_requirements['php_max_wontwork'];
// done fetching requirements
if ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') || (!function_exists('posix_getpwuid'))) {
@ -17,12 +33,6 @@ if ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') || (!function_exists('posix_get
}
$app_environment = 'develop';
// Check if a branch or tag was passed in the command line,
// otherwise just use master
(array_key_exists('1', $argv)) ? $branch = $argv[1] : $branch = 'master';
echo "--------------------------------------------------------\n";
echo "WELCOME TO THE SNIPE-IT UPGRADER! \n";
echo "--------------------------------------------------------\n\n";
@ -45,6 +55,12 @@ echo "--------------------------------------------------------\n\n";
// Check the .env looks ok
$env = file('.env');
if (! $env){
echo "\n!!!!!!!!!!!!!!!!!!!!!!!!!! .ENV FILE ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!\n";
echo "Your .env file doesn't seem to exist in this directory or isn't readable! Please look into that.\n";
exit(1);
}
$env_good = '';
$env_bad = '';
@ -133,7 +149,7 @@ if ($env_bad !='') {
echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
echo "Please correct the issues above in ".getcwd()."/.env and try again.\n";
echo "--------------------------------------------------------\n";
exit;
exit(1);
}
@ -152,7 +168,7 @@ if ((version_compare(phpversion(), $php_min_works, '>=')) && (version_compare(ph
echo "Snipe-IT requires PHP versions between ".$php_min_works." and ".$php_max_wontwork.".\n";
echo "Please install a compatible version of PHP and re-run this script again. \n";
echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
exit;
exit(1);
}
echo "Checking Required PHP extensions... \n\n";
@ -240,7 +256,7 @@ if ($ext_missing!='') {
echo "ABORTING THE INSTALLER \n";
echo "Please install the extensions above and re-run this script.\n";
echo "--------------------------------------------------------\n";
exit;
exit(1);
} else {
echo $ext_installed."\n";
@ -295,6 +311,7 @@ if ($dirs_not_writable!='') {
echo "--------------------- !! ERROR !! ----------------------\n";
echo "Please check the permissions on the directories above and re-run this script.\n";
echo "------------------------- :( ---------------------------\n\n";
exit(1);
}