diff --git a/.all-contributorsrc b/.all-contributorsrc
index 0930d0e55f..42a34dab5f 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2864,6 +2864,24 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "spencerrlongg",
+ "name": "Spencer Long",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7305753?v=4",
+ "profile": "http://spencerlong.com",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "marcusmoore",
+ "name": "Marcus Moore",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1141514?v=4",
+ "profile": "https://github.com/marcusmoore",
+ "contributions": [
+ "code"
+ ]
}
]
}
diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml
index 0d77e1ae7c..758838307d 100644
--- a/.github/workflows/codacy-analysis.yml
+++ b/.github/workflows/codacy-analysis.yml
@@ -36,7 +36,7 @@ jobs:
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
- uses: codacy/codacy-analysis-cli-action@v4.2.0
+ uses: codacy/codacy-analysis-cli-action@v4.3.0
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations
diff --git a/README.md b/README.md
index c0436a4b4e..ceb040145e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![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/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![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-315-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
+[![All Contributors](https://img.shields.io/badge/all_contributors-317-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
## Snipe-IT - Open Source Asset Management System
@@ -141,6 +141,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [
Peace](https://github.com/julian-piehl)
[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") | [
Kyle Gordon](https://github.com/kylegordon)
[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [
Katharina Drexel](http://www.bfh.ch)
[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [
David Sferruzza](https://david.sferruzza.fr/)
[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [
Rick Nelson](https://github.com/rnelsonee)
[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [
BasO12](https://github.com/BasO12)
[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [
Vautia](https://github.com/Vautia)
[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") |
| [
Chris Hartjes](http://www.littlehart.net/atthekeyboard)
[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [
geo-chen](https://github.com/geo-chen)
[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [
Phan Nguyen](https://github.com/nh314)
[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [
Iisakki Jaakkola](https://github.com/StarlessNights)
[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [
Ikko Ashimine](https://bandism.net/)
[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [
Lukas Fehling](https://github.com/lukasfehling)
[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [
Fernando Almeida](https://github.com/fernando-almeida)
[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") |
| [
akemidx](https://github.com/akemidx)
[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [
Oguz Bilgic](http://oguz.site)
[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [
Scooter Crawford](https://github.com/scoo73r)
[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [
subdriven](https://github.com/subdriven)
[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [
Andrew Savinykh](https://github.com/AndrewSav)
[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [
Tadayuki Onishi](https://kenchan0130.github.io)
[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [
Florian](https://github.com/floschoepfer)
[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
+| [
Spencer Long](http://spencerlong.com)
[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [
Marcus Moore](https://github.com/marcusmoore)
[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") |
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php
index a2f824a997..6bd6d1d090 100644
--- a/app/Http/Controllers/Api/UsersController.php
+++ b/app/Http/Controllers/Api/UsersController.php
@@ -20,6 +20,7 @@ use Auth;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\Validator;
class UsersController extends Controller
{
@@ -457,6 +458,13 @@ class UsersController extends Controller
// Check if the request has groups passed and has a value
if ($request->filled('groups')) {
+ $validator = Validator::make($request->all(), [
+ 'groups.*' => 'integer|exists:permission_groups,id',
+ ]);
+
+ if ($validator->fails()){
+ return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
+ }
$user->groups()->sync($request->input('groups'));
// The groups field has been passed but it is null, so we should blank it out
} elseif ($request->has('groups')) {
diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php
index 9b51b7f267..323d238ec1 100755
--- a/app/Http/Controllers/Assets/AssetsController.php
+++ b/app/Http/Controllers/Assets/AssetsController.php
@@ -627,7 +627,11 @@ class AssetsController extends Controller
$csv->setHeaderOffset(0);
$header = $csv->getHeader();
$isCheckinHeaderExplicit = in_array('checkin date', (array_map('strtolower', $header)));
- $results = $csv->getRecords();
+ try {
+ $results = $csv->getRecords();
+ } catch (\Exception $e) {
+ return back()->with('error', 'There was an error reading the CSV file: '.$e->getMessage());
+ }
$item = [];
$status = [];
$status['error'] = [];
diff --git a/app/Http/Livewire/SlackSettingsForm.php b/app/Http/Livewire/SlackSettingsForm.php
index cd34b450a8..f6bc366c20 100644
--- a/app/Http/Livewire/SlackSettingsForm.php
+++ b/app/Http/Livewire/SlackSettingsForm.php
@@ -8,47 +8,82 @@ use App\Models\Setting;
class SlackSettingsForm extends Component
{
- public $slack_endpoint;
- public $slack_channel;
- public $slack_botname;
+ public $webhook_endpoint;
+ public $webhook_channel;
+ public $webhook_botname;
public $isDisabled ='disabled' ;
+ public $webhook_name;
+ public $webhook_link;
+ public $webhook_placeholder;
+ public $webhook_icon;
+ public $webhook_selected;
+ public array $webhook_text;
public Setting $setting;
protected $rules = [
- 'slack_endpoint' => 'url|required_with:slack_channel|starts_with:https://hooks.slack.com/|nullable',
- 'slack_channel' => 'required_with:slack_endpoint|starts_with:#|nullable',
- 'slack_botname' => 'string|nullable',
+ 'webhook_endpoint' => 'url|required_with:webhook_channel|starts_with:https://hooks.slack.com/services|nullable',
+ 'webhook_channel' => 'required_with:webhook_endpoint|starts_with:#|nullable',
+ 'webhook_botname' => 'string|nullable',
];
+
public function mount(){
+ $this->webhook_text= [
+ "slack" => array(
+ "name" => trans('admin/settings/general.slack') ,
+ "icon" => 'fab fa-slack',
+ "placeholder" => "https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXX",
+ "link" => 'https://api.slack.com/messaging/webhooks',
+ ),
+ "general"=> array(
+ "name" => trans('admin/settings/general.general_webhook'),
+ "icon" => "fab fa-hashtag",
+ "placeholder" => "",
+ "link" => "",
+ ),
+ ];
$this->setting = Setting::getSettings();
- $this->slack_endpoint = $this->setting->slack_endpoint;
- $this->slack_channel = $this->setting->slack_channel;
- $this->slack_botname = $this->setting->slack_botname;
+ $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_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;
+
}
public function updated($field){
+ if($this->webhook_selected != 'general') {
+ $this->validateOnly($field, $this->rules);
+ }
+ }
+ public function updatedWebhookSelected(){
+ $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_link = $this->webhook_text[$this->webhook_selected]["link"];
- $this->validateOnly($field ,$this->rules);
}
public function render()
{
- if(empty($this->slack_channel || $this->slack_endpoint)){
+ if(empty($this->webhook_channel || $this->webhook_endpoint)){
$this->isDisabled= 'disabled';
}
- if(empty($this->slack_endpoint && $this->slack_channel)){
+ if(empty($this->webhook_endpoint && $this->webhook_channel)){
$this->isDisabled= '';
}
return view('livewire.slack-settings-form');
}
- public function testSlack(){
+ public function testWebhook(){
- $slack = new Client([
- 'base_url' => e($this->slack_endpoint),
+ $webhook = new Client([
+ 'base_url' => e($this->webhook_endpoint),
'defaults' => [
'exceptions' => false,
],
@@ -56,35 +91,39 @@ class SlackSettingsForm extends Component
$payload = json_encode(
[
- 'channel' => e($this->slack_channel),
- 'text' => trans('general.slack_test_msg'),
- 'username' => e($this->slack_botname),
+ 'channel' => e($this->webhook_channel),
+ 'text' => trans('general.webhook_test_msg'),
+ 'username' => e($this->webhook_botname),
'icon_emoji' => ':heart:',
]);
try {
- $slack->post($this->slack_endpoint, ['body' => $payload]);
+ $webhook->post($this->webhook_endpoint, ['body' => $payload]);
$this->isDisabled='';
- return session()->flash('success' , 'Your Slack Integration works!');
+ return session()->flash('success' , 'Your '.$this->webhook_name.' Integration works!');
} catch (\Exception $e) {
$this->isDisabled= 'disabled';
- return session()->flash('error' , trans('admin/settings/message.slack.error', ['error_message' => $e->getMessage()]));
+ return session()->flash('error' , trans('admin/settings/message.webhook.error', ['error_message' => $e->getMessage(), 'app' => $this->webhook_name]));
}
//}
- return session()->flash('message' , trans('admin/settings/message.slack.error_misc'));
+ return session()->flash('message' , trans('admin/settings/message.webhook.error_misc'));
}
public function submit()
{
- $this->validate($this->rules);
+ if($this->webhook_selected != 'general') {
+ $this->validate($this->rules);
+ }
+
+ $this->setting->webhook_selected = $this->webhook_selected;
+ $this->setting->webhook_endpoint = $this->webhook_endpoint;
+ $this->setting->webhook_channel = $this->webhook_channel;
+ $this->setting->webhook_botname = $this->webhook_botname;
- $this->setting->slack_endpoint = $this->slack_endpoint;
- $this->setting->slack_channel = $this->slack_channel;
- $this->setting->slack_botname = $this->slack_botname;
$this->setting->save();
diff --git a/composer.json b/composer.json
index abcf675b1a..2f1007fa73 100644
--- a/composer.json
+++ b/composer.json
@@ -127,6 +127,9 @@
"sort-packages": true,
"optimize-autoloader": true,
"discard-changes": true,
- "process-timeout": 3000
+ "process-timeout": 3000,
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
}
}
diff --git a/database/factories/ComponentFactory.php b/database/factories/ComponentFactory.php
index 36e0214350..8dae9f1314 100644
--- a/database/factories/ComponentFactory.php
+++ b/database/factories/ComponentFactory.php
@@ -41,7 +41,7 @@ class ComponentFactory extends Factory
'serial' => $this->faker->uuid,
'qty' => $this->faker->numberBetween(3, 10),
'order_number' => $this->faker->numberBetween(1000000, 50000000),
- 'purchase_date' => $this->faker->dateTime(),
+ 'purchase_date' => $this->faker->dateTime()->format('Y-m-d'),
'purchase_cost' => $this->faker->randomFloat(2),
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
'company_id' => Company::factory(),
diff --git a/database/factories/ConsumableFactory.php b/database/factories/ConsumableFactory.php
index 085c9811f1..2875d3d67a 100644
--- a/database/factories/ConsumableFactory.php
+++ b/database/factories/ConsumableFactory.php
@@ -38,7 +38,7 @@ class ConsumableFactory extends Factory
},
'item_no' => $this->faker->numberBetween(1000000, 50000000),
'order_number' => $this->faker->numberBetween(1000000, 50000000),
- 'purchase_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
+ 'purchase_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get())->format('Y-m-d'),
'purchase_cost' => $this->faker->randomFloat(2, 1, 50),
'qty' => $this->faker->numberBetween(5, 10),
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
diff --git a/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php b/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php
index 95c3bff35b..9f5c5aa1e5 100644
--- a/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php
+++ b/database/migrations/2023_01_21_225350_add_eol_date_on_assets_table.php
@@ -23,7 +23,7 @@ class AddEolDateOnAssetsTable extends Migration
});
// Chunk the model query to get the models that do have an EOL date
- AssetModel::whereNotNull('eol')->with('assets')->chunk(200, function ($models) {
+ AssetModel::whereNotNull('eol')->chunk(10, function ($models) {
foreach ($models as $model) {
foreach ($model->assets as $asset) {
diff --git a/database/migrations/2023_02_28_173527_adds_webhook_option_to_settings_table.php b/database/migrations/2023_02_28_173527_adds_webhook_option_to_settings_table.php
new file mode 100644
index 0000000000..c3409c9d6f
--- /dev/null
+++ b/database/migrations/2023_02_28_173527_adds_webhook_option_to_settings_table.php
@@ -0,0 +1,39 @@
+string('webhook_selected')->after('slack_botname')->default('slack')->nullable();
+ $table->renameColumn('slack_botname', 'webhook_botname');
+ $table->renameColumn('slack_endpoint', 'webhook_endpoint');
+ $table->renameColumn('slack_channel', 'webhook_channel');
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('settings', function (Blueprint $table) {
+ $table->dropColumn('webhook_selected');
+ $table->renameColumn('webhook_botname', 'slack_botname');
+ $table->renameColumn('webhook_endpoint', 'slack_endpoint');
+ $table->renameColumn('webhook_channel', 'slack_channel');
+ });
+ }
+}
diff --git a/resources/lang/en/admin/settings/general.php b/resources/lang/en/admin/settings/general.php
index 6416b4ed4d..3316ed09ff 100644
--- a/resources/lang/en/admin/settings/general.php
+++ b/resources/lang/en/admin/settings/general.php
@@ -198,16 +198,20 @@ return [
'show_images_in_email' => 'Show images in emails',
'show_images_in_email_help' => 'Uncheck this box if your Snipe-IT installation is behind a VPN or closed network and users outside the network will not be able to load images served from this installation in their emails.',
'site_name' => 'Site Name',
+ 'integrations' => 'Integrations',
'slack' => 'Slack',
- 'slack_title' => 'Update Slack Settings',
- 'slack_help' => 'Slack settings',
- 'slack_botname' => 'Slack Botname',
- 'slack_channel' => 'Slack Channel',
- 'slack_endpoint' => 'Slack Endpoint',
- 'slack_integration' => 'Slack Settings',
- 'slack_integration_help' => 'Slack integration is optional, however the endpoint and channel are required if you wish to use it. To configure Slack integration, you must first create an incoming webhook on your Slack account. Click on the Test Slack Integration button to confirm your settings are correct before saving. ',
- 'slack_integration_help_button' => 'Once you have saved your Slack information, a test button will appear.',
- 'slack_test_help' => 'Test whether your Slack integration is configured correctly. YOU MUST SAVE YOUR UPDATED SLACK SETTINGS FIRST.',
+ 'general_webhook' => 'General Webhook',
+ 'webhook' => ':app',
+ 'webhook_title' => 'Update Webhook Settings',
+ 'webhook_help' => 'Integration settings',
+ 'webhook_botname' => ':app Botname',
+ 'webhook_channel' => ':app Channel',
+ 'webhook_endpoint' => ':app Endpoint',
+ 'webhook_integration' => ':app Settings',
+ 'webhook_test' =>'Test :app integration',
+ 'webhook_integration_help' => ':app integration is optional, however the endpoint and channel are required if you wish to use it. To configure :app integration, you must first create an incoming webhook on your :app account. Click on the Test :app Integration button to confirm your settings are correct before saving. ',
+ 'webhook_integration_help_button' => 'Once you have saved your :app information, a test button will appear.',
+ 'webhook_test_help' => 'Test whether your :app integration is configured correctly. YOU MUST SAVE YOUR UPDATED :app SETTINGS FIRST.',
'snipe_version' => 'Snipe-IT version',
'support_footer' => 'Support Footer Links ',
'support_footer_help' => 'Specify who sees the links to the Snipe-IT Support info and Users Manual',
diff --git a/resources/lang/en/admin/settings/message.php b/resources/lang/en/admin/settings/message.php
index b0648d1c1c..48b42d1202 100644
--- a/resources/lang/en/admin/settings/message.php
+++ b/resources/lang/en/admin/settings/message.php
@@ -33,12 +33,12 @@ return [
'testing_authentication' => 'Testing LDAP Authentication...',
'authentication_success' => 'User authenticated against LDAP successfully!'
],
- 'slack' => [
- 'sending' => 'Sending Slack test message...',
+ 'webhook' => [
+ 'sending' => 'Sending :app test message...',
'success_pt1' => 'Success! Check the ',
'success_pt2' => ' channel for your test message, and be sure to click SAVE below to store your settings.',
'500' => '500 Server Error.',
- 'error' => 'Something went wrong. Slack responded with: :error_message',
+ 'error' => 'Something went wrong. :app responded with: :error_message',
'error_misc' => 'Something went wrong. :( ',
]
];
diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php
index 97b82d95a0..97be38f509 100644
--- a/resources/lang/en/general.php
+++ b/resources/lang/en/general.php
@@ -250,8 +250,8 @@ return [
'signature' => 'Signature',
'signed_off_by' => 'Signed Off By',
'skin' => 'Skin',
- 'slack_msg_note' => 'A slack message will be sent',
- 'slack_test_msg' => 'Oh hai! Looks like your Slack integration with Snipe-IT is working!',
+ 'webhook_msg_note' => 'A :app message will be sent',
+ 'webhook_test_msg' => 'Oh hai! Looks like your :app integration with Snipe-IT is working!',
'some_features_disabled' => 'DEMO MODE: Some features are disabled for this installation.',
'site_name' => 'Site Name',
'state' => 'State',
@@ -406,6 +406,7 @@ return [
'tasks_view_all' => 'View all tasks',
'true' => 'True',
'false' => 'False',
+ 'integration_option' => 'Integration Option',
diff --git a/resources/views/livewire/slack-settings-form.blade.php b/resources/views/livewire/slack-settings-form.blade.php
index 6d48220ca8..7cce7503c6 100644
--- a/resources/views/livewire/slack-settings-form.blade.php
+++ b/resources/views/livewire/slack-settings-form.blade.php
@@ -1,99 +1,173 @@
+{{-- Page title --}}
+@section('title')
+{{ trans('admin/settings/general.webhook_title') }}
+@parent
+@stop
+
+@section('header_right')
+ {{ trans('general.back') }}
+@stop
-