From: Vincent Vanwaelscappel Date: Tue, 11 Mar 2025 13:05:19 +0000 (+0100) Subject: wip #7382 @6 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=da554a036ebcfe6d72ca72c336f738bbd7d18ab9;p=fluidbook-toolbox.git wip #7382 @6 --- diff --git a/.env.alpha b/.env.alpha index 97a06427c..376d1cf18 100644 --- a/.env.alpha +++ b/.env.alpha @@ -41,11 +41,11 @@ REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp -MAIL_HOST=mail2.cubedesigners.com -MAIL_PORT=587 -MAIL_USERNAME=workshop@cubedesigners.com -MAIL_PASSWORD=4zrmk4Hu9HH97n7UiW5 -MAIL_ENCRYPTION=tls +MAIL_HOST=postal.cubedesigners.com +MAIL_PORT=25 +MAIL_USERNAME=cubedesigners/fluidbook-toolbox-dev +MAIL_PASSWORD=jgqyC9RxBxAM40C5zoJj0WVT +MAIL_ENCRYPTION=false MAIL_FROM_ADDRESS=toolbox+alpha@fluidbook.com MAIL_FROM_NAME="[ALPHA] Fluidbook Toolbox" MAIL_BCC_ALL=test+toolboxalpha@cubedesigners.com diff --git a/.env.dev b/.env.dev index a516e6c68..703841f9f 100644 --- a/.env.dev +++ b/.env.dev @@ -41,18 +41,19 @@ REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp -MAIL_HOST=mail2.cubedesigners.com -MAIL_PORT=587 -MAIL_USERNAME=workshop@cubedesigners.com -MAIL_PASSWORD=4zrmk4Hu9HH97n7UiW5 -MAIL_ENCRYPTION=tls +MAIL_HOST=postal.cubedesigners.com +MAIL_PORT=25 +MAIL_USERNAME=cubedesigners/fluidbook-toolbox-dev +MAIL_PASSWORD=jgqyC9RxBxAM40C5zoJj0WVT +MAIL_ENCRYPTION=false MAIL_FROM_ADDRESS=toolbox+dev@fluidbook.com MAIL_FROM_NAME="[DEV] Fluidbook Toolbox" MAIL_BCC_ALL=test+toolboxdev@cubedesigners.com MAIL_TEAM_NAME=Fluidbook -MAILJET_API_KEY=ca110b35f8735c223d69c9987c2ac47d -MAILJET_API_SECRET=b289d0acb08e0fe56ce98ccf0dd1ed8b +LISTMONK_BASE_URL=https://listmonk.cubedesigners.com +LISTMONK_API_USER=fluidbooktoolbox +LISTMONK_API_TOKEN=b289d0acb08e0fe56ce98ccf0dd1ed8b MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/.env.prod b/.env.prod index 9f1e45ffa..7ee634b55 100644 --- a/.env.prod +++ b/.env.prod @@ -41,11 +41,11 @@ REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp -MAIL_HOST=mail2.cubedesigners.com -MAIL_PORT=587 -MAIL_USERNAME=workshop@cubedesigners.com -MAIL_PASSWORD=4zrmk4Hu9HH97n7UiW5 -MAIL_ENCRYPTION=tls +MAIL_HOST=postal.cubedesigners.com +MAIL_PORT=25 +MAIL_USERNAME=cubedesigners/fluidbook-toolbox +MAIL_PASSWORD=JzKxZdtN730yXwKRorTVDhaK +MAIL_ENCRYPTION=false MAIL_FROM_ADDRESS=toolbox@fluidbook.com MAIL_FROM_NAME="Fluidbook Toolbox" MAIL_BCC_ALL=test+toolbox@cubedesigners.com @@ -54,8 +54,9 @@ MAIL_TEAM_NAME=Fluidbook RATE_PEOPLE_DAY=630 RATE_PEOPLE_HOUR=100 -MAILJET_API_KEY=ca110b35f8735c223d69c9987c2ac47d -MAILJET_API_SECRET=b289d0acb08e0fe56ce98ccf0dd1ed8b +LISTMONK_BASE_URL=https://listmonk.cubedesigners.com +LISTMONK_API_USER=fluidbooktoolbox +LISTMONK_API_TOKEN=f60zrw7kvialdQFlMCrMKLl49nlQSkEe MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 208c3d6ff..2386845be 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -45,7 +45,7 @@ class Kernel extends \Cubist\Backpack\Console\Kernel // Quotes $schedule->command('fluidbook:quote --reminder')->weekdays()->at('10:00'); // Mailjet - $schedule->command('job:dispatchNow MailjetSyncList')->dailyAt('5:00'); + $schedule->command('job:dispatchNow ListmonkSyncList')->dailyAt('5:00'); } $schedule->command('job:dispatchNow ProcessTotals')->everyTwoHours(); diff --git a/app/Jobs/ListmonkSyncList.php b/app/Jobs/ListmonkSyncList.php new file mode 100644 index 000000000..913e8cc9c --- /dev/null +++ b/app/Jobs/ListmonkSyncList.php @@ -0,0 +1,253 @@ +syncStatusFromMailjet(); + //dump('Synced status from mailjet'); + + dd(Listmonk::getSubscribers(['per_page' => 100000, 'page' => 1, 'list_id' => [self::LIST_ID]])); + + $skippedCompanies = []; + $companies = []; + $contacts = ['addnoforce' => [], 'unsub' => [], 'remove' => []]; + + $validator = new EmailValidator(); + $multipleValidations = new MultipleValidationWithAnd([ + new RFCValidation(), + new DNSCheckValidation() + ]); + + foreach (Company::withoutGlobalScopes()->get() as $company) { + if (!$company->created_ok) { + $skippedCompanies[] = $company->id; + continue; + } + /** @var Company $company */ + $adminCompany = Permissions::getCompanyByUser($company->admin); + // Skip companies not directly managed by Cubedesigners + if ($adminCompany != 7 && !$company->marketing_force) { + $skippedCompanies[] = $company->id; + continue; + } + $companies[$company->id] = $company; + } + + foreach (User::withoutGlobalScopes()->where('created_ok', '1')->where('enabled', '1')->get() as $user) { + if (in_array($user->company, $skippedCompanies) || !isset($companies[$user->company])) { + continue; + } + + $c = $companies[$user->company]; + $address = $user->address; + if (is_string($address)) { + $address = json_decode($address); + } + if (null === $address) { + $country = ''; + } else { + $country = is_array($address) ? $address['country'] : $address->country; + } + + $action = 'addnoforce'; + + if (str_ends_with($user->email, '@extranet.cubedesigners.com')) { + $action = 'remove'; + } + + if (!Cache::rememberForever('email_valid_' . $user->email, function () use ($user, $validator, $multipleValidations) { + return $validator->isValid($user->email, $multipleValidations); + })) { + continue; + } + + if (!$user->marketing) { + $action = 'unsub'; + } + + $contact = [ + 'email' => $user->email, + 'status' => 'enabled', + 'name' => trim($user->firstname . ' ' . $user->lastname), + 'preconfirm_subscriptions' => true, + 'lists' => [self::LIST_ID], + 'attribs' => [ + 'toolbox_id' => $user->id, + 'firstname' => trim($user->firstname), + 'lastname' => trim($user->lastname), + 'company' => trim($c->name), + 'fluidbook' => $c->e1_ws_grade > 0 && $c->toolbox_access, + 'elearning' => (bool)$c->permissions_elearning, + 'locale' => $user->locale, + 'country' => $country, + 'last_project' => $c->c_last_project_date ? (new \DateTime($c->c_last_project_date))->format(DateTimeInterface::RFC3339) : '', + ] + ]; + + $contacts[$action][] = $contact; + } + + + foreach ($contacts as $action => $list) { + $this->addContactsToList($list, $action); + } + + } + + protected function deleteContacts() + { + $mj = static::_api(); + $i = 0; + $remove = $this->getRecipientsList(); + $emails = $this->getEmailsFromContactIDs($remove); + $contacts = []; + foreach ($emails as $email) { + $contacts[] = ['Email' => $email]; + } + $this->addContactsToList($contacts, 'remove'); + } + + protected function getRecipientsList($filter = null) + { + $i = 0; + $res = []; + $mj = static::_api(); + + + $filters = ['ContactsList' => static::LIST_ID, + 'Limit' => self::BATCH, + 'Offset' => 0]; + if (null !== $filter) { + $filters = array_merge($filters, $filter); + } + + while (true) { + $filters['Offset'] = $i * self::BATCH; + $response = $mj->get(Resources::$Listrecipient, ['filters' => $filters]); + foreach ($response->getData() as $contact) { + $res[] = $contact['ContactID']; + } + + if ($response->getCount() < self::BATCH) { + break; + } + + $i++; + } + return $res; + } + + protected function getEmailsFromContactIDs($contactIDs) + { + $i = 0; + $res = []; + $mj = static::_api(); + + while (true) { + $response = $mj->get(Resources::$Contact, ['filters' => ['ContactsList' => static::LIST_ID, + 'Limit' => self::BATCH, + 'Offset' => $i * self::BATCH, + ]]); + + foreach ($response->getData() as $contact) { + if (in_array($contact['ID'], $contactIDs)) { + $res[] = $contact['Email']; + } + } + if ($response->getCount() < self::BATCH) { + break; + } + $i++; + } + return $res; + } + + protected function addContactsToList($contacts, $action = 'addnoforce') + { + if (empty($contacts)) { + return; + } + $body = [ + 'Action' => $action, + 'Contacts' => $contacts + ]; + $response = $mj->post(Resources::$ContactslistManagemanycontacts, ['id' => self::LIST_ID, 'body' => $body]); + $jobId = $response->getData()[0]['JobID']; + + while (true) { + $check = $mj->get(Resources::$ContactslistManagemanycontacts, ['id' => self::LIST_ID, 'actionid' => (string)$jobId]); + $status = ($check->getData()[0]['Status']); + if ($status === 'Error') { + dd($check->getData()[0]); + } else if ($status === 'Completed') { + return; + } + dump($status); + sleep(2); + } + } + + protected function syncStatusFromMailjet() + { + $mj = static::_api(); + $i = 0; + $unsub = []; + + $timestamp = time() - (3600 * 24 * 90); // 90 days in the past + + // Check bounces + while (true) { + $response = $mj->get(Resources::$Bouncestatistics, ['filters' => [ + 'Limit' => self::BATCH, + 'Offset' => $i * self::BATCH, + 'FromTS' => $timestamp, + ]]); + + foreach ($response->getData() as $contact) { + if ($contact['IsBlocked'] || $contact['IsStatePermanent']) { + $unsub[] = $contact['ContactID']; + } + } + + if ($response->getCount() < self::BATCH) { + break; + } + $i++; + } + + // Check unsubscribed users + $filters = ['Unsub' => 'true', 'IsExcludedFromCampaigns' => 'true']; + foreach ($filters as $filter => $value) { + $unsub = array_merge($unsub, $this->getRecipientsList([$filter => $value])); + } + + $unsubEmails = $this->getEmailsFromContactIDs($unsub); + DB::table('extranet_users.user')->whereIn('email', $unsubEmails)->update(['marketing' => 0]); + } +} diff --git a/app/Jobs/MailjetSyncList.php b/app/Jobs/MailjetSyncList.php deleted file mode 100644 index d2754ad02..000000000 --- a/app/Jobs/MailjetSyncList.php +++ /dev/null @@ -1,262 +0,0 @@ -deleteContacts(); - return; - } - - $this->syncStatusFromMailjet(); - dump('Synced status from mailjet'); - - $skippedCompanies = []; - $companies = []; - $contacts = ['addnoforce' => [], 'unsub' => [], 'remove' => []]; - - $validator = new EmailValidator(); - $multipleValidations = new MultipleValidationWithAnd([ - new RFCValidation(), - new DNSCheckValidation() - ]); - - foreach (Company::withoutGlobalScopes()->get() as $company) { - if (!$company->created_ok) { - $skippedCompanies[] = $company->id; - continue; - } - /** @var Company $company */ - $adminCompany = Permissions::getCompanyByUser($company->admin); - // Skip companies not directly managed by Cubedesigners - if ($adminCompany != 7 && !$company->marketing_force) { - $skippedCompanies[] = $company->id; - continue; - } - $companies[$company->id] = $company; - } - - foreach (User::withoutGlobalScopes()->where('created_ok', '1')->where('enabled', '1')->get() as $user) { - if (in_array($user->company, $skippedCompanies) || !isset($companies[$user->company])) { - continue; - } - - $c = $companies[$user->company]; - $address = $user->address; - if (is_string($address)) { - $address = json_decode($address); - } - if (null === $address) { - $country = ''; - } else { - $country = is_array($address) ? $address['country'] : $address->country; - } - - $action = 'addnoforce'; - - if (str_ends_with($user->email, '@extranet.cubedesigners.com')) { - $action = 'remove'; - } - - if (!Cache::rememberForever('email_valid_' . $user->email, function () use ($user, $validator, $multipleValidations) { - return $validator->isValid($user->email, $multipleValidations); - })) { - continue; - } - - if (!$user->marketing) { - $action = 'unsub'; - } - - $contact = [ - 'Email' => $user->email, - 'IsExcludedFromCampaigns' => 'false', - 'Name' => trim($user->firstname . ' ' . $user->lastname), - 'Properties' => [ - 'toolbox_id' => $user->id, - 'firstname' => trim($user->firstname), - 'lastname' => trim($user->lastname), - 'company' => trim($c->name), - 'fluidbook' => $c->e1_ws_grade > 0 && $c->toolbox_access, - 'elearning' => (bool)$c->permissions_elearning, - 'locale' => $user->locale, - 'country' => $country, - 'last_project' => $c->c_last_project_date ? (new \DateTime($c->c_last_project_date))->format(DateTimeInterface::RFC3339) : '', - ] - ]; - - $contacts[$action][] = $contact; - } - - - foreach ($contacts as $action => $list) { - $this->addContactsToList($list, $action); - } - - } - - protected function deleteContacts() - { - $mj = static::_api(); - $i = 0; - $remove = $this->getRecipientsList(); - $emails=$this->getEmailsFromContactIDs($remove); - $contacts = []; - foreach ($emails as $email) { - $contacts[] = ['Email' => $email]; - } - $this->addContactsToList($contacts, 'remove'); - } - - protected function getRecipientsList($filter = null) - { - $i = 0; - $res = []; - $mj = static::_api(); - - - $filters = ['ContactsList' => static::LIST_ID, - 'Limit' => self::BATCH, - 'Offset' => 0]; - if (null !== $filter) { - $filters = array_merge($filters, $filter); - } - - while (true) { - $filters['Offset'] = $i * self::BATCH; - $response = $mj->get(Resources::$Listrecipient, ['filters' => $filters]); - foreach ($response->getData() as $contact) { - $res[] = $contact['ContactID']; - } - - if ($response->getCount() < self::BATCH) { - break; - } - - $i++; - } - return $res; - } - - protected function getEmailsFromContactIDs($contactIDs) - { - $i = 0; - $res = []; - $mj = static::_api(); - - while (true) { - $response = $mj->get(Resources::$Contact, ['filters' => ['ContactsList' => static::LIST_ID, - 'Limit' => self::BATCH, - 'Offset' => $i * self::BATCH, - ]]); - - foreach ($response->getData() as $contact) { - if (in_array($contact['ID'], $contactIDs)) { - $res[] = $contact['Email']; - } - } - if ($response->getCount() < self::BATCH) { - break; - } - $i++; - } - return $res; - } - - protected function addContactsToList($contacts, $action = 'addnoforce') - { - if (empty($contacts)) { - return; - } - $mj = static::_api(); - $body = [ - 'Action' => $action, - 'Contacts' => $contacts - ]; - $response = $mj->post(Resources::$ContactslistManagemanycontacts, ['id' => self::LIST_ID, 'body' => $body]); - $jobId = $response->getData()[0]['JobID']; - - while (true) { - $check = $mj->get(Resources::$ContactslistManagemanycontacts, ['id' => self::LIST_ID, 'actionid' => (string)$jobId]); - $status = ($check->getData()[0]['Status']); - if ($status === 'Error') { - dd($check->getData()[0]); - } else if ($status === 'Completed') { - return; - } - dump($status); - sleep(2); - } - } - - protected function syncStatusFromMailjet() - { - $mj = static::_api(); - $i = 0; - $unsub = []; - - $timestamp = time() - (3600 * 24 * 90); // 90 days in the past - - // Check bounces - while (true) { - $response = $mj->get(Resources::$Bouncestatistics, ['filters' => [ - 'Limit' => self::BATCH, - 'Offset' => $i * self::BATCH, - 'FromTS' => $timestamp, - ]]); - - foreach ($response->getData() as $contact) { - if ($contact['IsBlocked'] || $contact['IsStatePermanent']) { - $unsub[] = $contact['ContactID']; - } - } - - if ($response->getCount() < self::BATCH) { - break; - } - $i++; - } - - // Check unsubscribed users - $filters = ['Unsub' => 'true', 'IsExcludedFromCampaigns' => 'true']; - foreach ($filters as $filter => $value) { - $unsub = array_merge($unsub, $this->getRecipientsList([$filter => $value])); - } - - $unsubEmails = $this->getEmailsFromContactIDs($unsub); - DB::table('extranet_users.user')->whereIn('email', $unsubEmails)->update(['marketing' => 0]); - } - - /** - * @return Client - */ - protected static function _api($call = true, $version = 'v3') - { - return new \Mailjet\Client(env('MAILJET_API_KEY'), env('MAILJET_API_SECRET'), $call, ['version' => $version]); - } -} diff --git a/composer.json b/composer.json index e5fa2e68b..b4040faec 100644 --- a/composer.json +++ b/composer.json @@ -68,6 +68,7 @@ "rustici-software/scormcloud-api-v2-client-php": "^2.1.0", "simplesoftwareio/simple-qrcode": "^4.2", "symfony/http-client": "^v6.4.0", + "theafolayan/listmonk-laravel": "^1.3", "voku/simple_html_dom": "^4.8" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 44dc2871c..54cde6611 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "86f3969e7c9517948cd163134578cf66", + "content-hash": "674f394b07cd9069af87322406565da9", "packages": [ { "name": "archtechx/enums", @@ -4109,13 +4109,13 @@ "source": { "type": "git", "url": "git://git.cubedesigners.com/fluidbook_tools.git", - "reference": "4847a6faf6184e235b290babce1d1c72ad54ff22" + "reference": "c06d81f8c352dbf896da52c6168aaf9b625da7b1" }, "dist": { "type": "tar", - "url": "https://composer.cubedesigners.com/dist/fluidbook/tools/fluidbook-tools-dev-master-135a28.tar", - "reference": "4847a6faf6184e235b290babce1d1c72ad54ff22", - "shasum": "22a9e68c5c14e5d6c51b4ef205accbdd6fe2f765" + "url": "https://composer.cubedesigners.com/dist/fluidbook/tools/fluidbook-tools-dev-master-acf208.tar", + "reference": "c06d81f8c352dbf896da52c6168aaf9b625da7b1", + "shasum": "f2c41ec290bbb23ce83084fe6d724f3b88ace0cd" }, "require": { "barryvdh/laravel-debugbar": "*", @@ -4151,7 +4151,7 @@ } ], "description": "Fluidbook Tools", - "time": "2025-02-28T16:32:27+00:00" + "time": "2025-03-04T16:42:05+00:00" }, { "name": "fpdf/fpdf", @@ -4279,16 +4279,16 @@ }, { "name": "graham-campbell/markdown", - "version": "v15.2.0", + "version": "v15.3.0", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Laravel-Markdown.git", - "reference": "d594fc197b9068de5e234a890be361807a1ab34f" + "reference": "042c23ddc5bde61ed09b77a8a17119d70c8e8419" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Laravel-Markdown/zipball/d594fc197b9068de5e234a890be361807a1ab34f", - "reference": "d594fc197b9068de5e234a890be361807a1ab34f", + "url": "https://api.github.com/repos/GrahamCampbell/Laravel-Markdown/zipball/042c23ddc5bde61ed09b77a8a17119d70c8e8419", + "reference": "042c23ddc5bde61ed09b77a8a17119d70c8e8419", "shasum": "" }, "require": { @@ -4296,12 +4296,12 @@ "illuminate/filesystem": "^8.75 || ^9.0 || ^10.0 || ^11.0", "illuminate/support": "^8.75 || ^9.0 || ^10.0 || ^11.0", "illuminate/view": "^8.75 || ^9.0 || ^10.0 || ^11.0", - "league/commonmark": "^2.4.2", + "league/commonmark": "^2.6.1", "php": "^7.4.15 || ^8.0.2" }, "require-dev": { - "graham-campbell/analyzer": "^4.1", - "graham-campbell/testbench": "^6.1", + "graham-campbell/analyzer": "^4.2.1", + "graham-campbell/testbench": "^6.2", "mockery/mockery": "^1.6.6", "phpunit/phpunit": "^9.6.17 || ^10.5.13" }, @@ -4343,7 +4343,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Laravel-Markdown/issues", - "source": "https://github.com/GrahamCampbell/Laravel-Markdown/tree/v15.2.0" + "source": "https://github.com/GrahamCampbell/Laravel-Markdown/tree/v15.3.0" }, "funding": [ { @@ -4355,7 +4355,7 @@ "type": "tidelift" } ], - "time": "2024-03-17T23:07:39+00:00" + "time": "2025-03-02T22:13:22+00:00" }, { "name": "graham-campbell/result-type", @@ -9589,16 +9589,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", "shasum": "" }, "require": { @@ -9606,25 +9606,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -9662,19 +9659,9 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-02T04:48:29+00:00" }, { "name": "ramsey/uuid", @@ -9843,25 +9830,25 @@ }, { "name": "rickselby/laravel-gate-cache", - "version": "3.8.0", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/rickselby/laravel-gate-cache.git", - "reference": "d19bc9aaaffa5853c531e76bc19330dca4a5c606" + "reference": "6403cffb8d8082ff9b2944d3f481ce53c5aa9426" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rickselby/laravel-gate-cache/zipball/d19bc9aaaffa5853c531e76bc19330dca4a5c606", - "reference": "d19bc9aaaffa5853c531e76bc19330dca4a5c606", + "url": "https://api.github.com/repos/rickselby/laravel-gate-cache/zipball/6403cffb8d8082ff9b2944d3f481ce53c5aa9426", + "reference": "6403cffb8d8082ff9b2944d3f481ce53c5aa9426", "shasum": "" }, "require": { - "illuminate/auth": "10.*|11.*", - "illuminate/contracts": "10.*|11.*" + "illuminate/auth": "^10.0|^11.0|^12.0", + "illuminate/contracts": "^10.0|^11.0|^12.0" }, "require-dev": { "graham-campbell/testbench": "^6.1", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.0|^11.0" }, "type": "library", "extra": { @@ -9889,9 +9876,9 @@ "description": "Add a per-request caching layer to Laravel's Gate", "support": { "issues": "https://github.com/rickselby/laravel-gate-cache/issues", - "source": "https://github.com/rickselby/laravel-gate-cache/tree/3.8.0" + "source": "https://github.com/rickselby/laravel-gate-cache/tree/3.9.0" }, - "time": "2024-03-14T12:27:24+00:00" + "time": "2025-03-04T19:22:49+00:00" }, { "name": "rodneyrehm/plist", @@ -14352,6 +14339,58 @@ ], "time": "2025-01-07T12:55:42+00:00" }, + { + "name": "theafolayan/listmonk-laravel", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theafolayan/listmonk-laravel.git", + "reference": "80b17cc74e857fa884fc9394d19889b5470dd7ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theafolayan/listmonk-laravel/zipball/80b17cc74e857fa884fc9394d19889b5470dd7ad", + "reference": "80b17cc74e857fa884fc9394d19889b5470dd7ad", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "php": "^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Listmonk": "Theafolayan\\ListmonkLaravel\\Facades\\Listmonk" + }, + "providers": [ + "Theafolayan\\ListmonkLaravel\\ListmonkServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Theafolayan\\ListmonkLaravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "theafolayan", + "email": "theafolayan@gmail.com" + } + ], + "description": "A Laravel package for interacting with the Listmonk API", + "support": { + "issues": "https://github.com/theafolayan/listmonk-laravel/issues", + "source": "https://github.com/theafolayan/listmonk-laravel/tree/v1.3.0" + }, + "time": "2025-02-04T18:37:57+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.3.0", diff --git a/config/listmonk.php b/config/listmonk.php new file mode 100644 index 000000000..ad8a1e1f3 --- /dev/null +++ b/config/listmonk.php @@ -0,0 +1,7 @@ + env('LISTMONK_BASE_URL', 'http://localhost:9000'), + 'api_user' => env('LISTMONK_API_USER', 'default_user'), + 'api_token' => env('LISTMONK_API_TOKEN', 'your-token'), +];