mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 07:27:32 -05:00 
			
		
		
		
	Merge branch 'v5-develop' into v5-stable
This commit is contained in:
		
						commit
						953194bb48
					
				@ -1 +1 @@
 | 
			
		||||
5.4.2
 | 
			
		||||
5.4.3
 | 
			
		||||
@ -163,7 +163,7 @@ class CompanySettings extends BaseSettings
 | 
			
		||||
    public $require_quote_signature = false;  //@TODO ben to confirm
 | 
			
		||||
 | 
			
		||||
    //email settings
 | 
			
		||||
    public $email_sending_method = 'default'; //enum 'default','gmail' //@implemented
 | 
			
		||||
    public $email_sending_method = 'default'; //enum 'default','gmail','office365' //@implemented
 | 
			
		||||
    public $gmail_sending_user_id = '0'; //@implemented
 | 
			
		||||
 | 
			
		||||
    public $reply_to_email = ''; //@implemented
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,10 @@
 | 
			
		||||
 */
 | 
			
		||||
namespace App\Helpers\Mail;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Mail\MailManager;
 | 
			
		||||
use App\CustomMailDriver\CustomTransport;
 | 
			
		||||
use App\Helpers\Mail\Office365MailTransport;
 | 
			
		||||
use Dacastro4\LaravelGmail\Services\Message\Mail;
 | 
			
		||||
use Illuminate\Mail\MailManager;
 | 
			
		||||
use Illuminate\Support\Facades\Config;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,4 +23,9 @@ class GmailTransportManager extends MailManager
 | 
			
		||||
    {
 | 
			
		||||
        return new GmailTransport(new Mail);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createOffice365Transport()
 | 
			
		||||
    {
 | 
			
		||||
        return new Office365MailTransport();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										301
									
								
								app/Helpers/Mail/Office365MailTransport.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								app/Helpers/Mail/Office365MailTransport.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,301 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Helpers\Mail;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Mail\Transport\Transport;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
use Swift_Mime_SimpleMessage;
 | 
			
		||||
use Microsoft\Graph\Graph;
 | 
			
		||||
use Microsoft\Graph\Model\UploadSession;
 | 
			
		||||
 | 
			
		||||
class Office365MailTransport extends Transport
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->beforeSendPerformed($message);
 | 
			
		||||
 | 
			
		||||
        $graph = new Graph();
 | 
			
		||||
        $token = $message->getHeaders()->get('GmailToken')->getValue();
 | 
			
		||||
 | 
			
		||||
        $graph->setAccessToken($token);
 | 
			
		||||
 | 
			
		||||
        // Special treatment if the message has too large attachments
 | 
			
		||||
        $messageBody = $this->getBody($message, true);
 | 
			
		||||
        $messageBodySizeMb = json_encode($messageBody);
 | 
			
		||||
        $messageBodySizeMb = strlen($messageBodySizeMb);
 | 
			
		||||
        $messageBodySizeMb = $messageBodySizeMb / 1048576; //byte -> mb
 | 
			
		||||
 | 
			
		||||
        if ($messageBodySizeMb >= 4) {
 | 
			
		||||
            unset($messageBody);
 | 
			
		||||
            $graphMessage = $graph->createRequest("POST", "/users/" . key($message->getFrom()) . "/messages")
 | 
			
		||||
                ->attachBody($this->getBody($message))
 | 
			
		||||
                ->setReturnType(\Microsoft\Graph\Model\Message::class)
 | 
			
		||||
                ->execute();
 | 
			
		||||
 | 
			
		||||
            foreach ($message->getChildren() as $attachment) {
 | 
			
		||||
                if ($attachment instanceof \Swift_Mime_SimpleMimeEntity) {
 | 
			
		||||
                    $fileName = $attachment->getHeaders()->get('Content-Type')->getParameter('name');
 | 
			
		||||
                    $content = $attachment->getBody();
 | 
			
		||||
                    $fileSize = strlen($content);
 | 
			
		||||
                    $size = $fileSize / 1048576; //byte -> mb
 | 
			
		||||
                    $id = $attachment->getId();
 | 
			
		||||
                    $attachmentMessage = [
 | 
			
		||||
                        'AttachmentItem' => [
 | 
			
		||||
                            'attachmentType' => 'file',
 | 
			
		||||
                            'name' => $fileName,
 | 
			
		||||
                            'size' => strlen($content)
 | 
			
		||||
                        ]
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                    if ($size <= 3) { //ErrorAttachmentSizeShouldNotBeLessThanMinimumSize if attachment <= 3mb, then we need to add this
 | 
			
		||||
                        $attachmentBody = [
 | 
			
		||||
                            "@odata.type" => "#microsoft.graph.fileAttachment",
 | 
			
		||||
                            "name" => $attachment->getHeaders()->get('Content-Type')->getParameter('name'),
 | 
			
		||||
                            "contentType" => $attachment->getBodyContentType(),
 | 
			
		||||
                            "contentBytes" => base64_encode($attachment->getBody()),
 | 
			
		||||
                            'contentId'    => $id
 | 
			
		||||
                        ];
 | 
			
		||||
 | 
			
		||||
                        $addAttachment = $graph->createRequest("POST", "/users/" . key($message->getFrom()) . "/messages/" . $graphMessage->getId() . "/attachments")
 | 
			
		||||
                            ->attachBody($attachmentBody)
 | 
			
		||||
                            ->setReturnType(UploadSession::class)
 | 
			
		||||
                            ->execute();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        //upload the files in chunks of 4mb....
 | 
			
		||||
                        $uploadSession = $graph->createRequest("POST", "/users/" . key($message->getFrom()) . "/messages/" . $graphMessage->getId() . "/attachments/createUploadSession")
 | 
			
		||||
                            ->attachBody($attachmentMessage)
 | 
			
		||||
                            ->setReturnType(UploadSession::class)
 | 
			
		||||
                            ->execute();
 | 
			
		||||
 | 
			
		||||
                        $fragSize =  1024 * 1024 * 4; //4mb at once...
 | 
			
		||||
                        $numFragments = ceil($fileSize / $fragSize);
 | 
			
		||||
                        $contentChunked = str_split($content, $fragSize);
 | 
			
		||||
                        $bytesRemaining = $fileSize;
 | 
			
		||||
 | 
			
		||||
                        $i = 0;
 | 
			
		||||
                        while ($i < $numFragments) {
 | 
			
		||||
                            $chunkSize = $numBytes = $fragSize;
 | 
			
		||||
                            $start = $i * $fragSize;
 | 
			
		||||
                            $end = $i * $fragSize + $chunkSize - 1;
 | 
			
		||||
                            if ($bytesRemaining < $chunkSize) {
 | 
			
		||||
                                $chunkSize = $numBytes = $bytesRemaining;
 | 
			
		||||
                                $end = $fileSize - 1;
 | 
			
		||||
                            }
 | 
			
		||||
                            $data = $contentChunked[$i];
 | 
			
		||||
                            $content_range = "bytes " . $start . "-" . $end . "/" . $fileSize;
 | 
			
		||||
                            $headers = [
 | 
			
		||||
                                "Content-Length" => $numBytes,
 | 
			
		||||
                                "Content-Range" => $content_range
 | 
			
		||||
                            ];
 | 
			
		||||
                            $client = new \GuzzleHttp\Client();
 | 
			
		||||
                            $tmp = $client->put($uploadSession->getUploadUrl(), [
 | 
			
		||||
                                'headers'         => $headers,
 | 
			
		||||
                                'body'            => $data,
 | 
			
		||||
                                'allow_redirects' => false,
 | 
			
		||||
                                'timeout'         => 1000
 | 
			
		||||
                            ]);
 | 
			
		||||
                            $result = $tmp->getBody() . '';
 | 
			
		||||
                            $result = json_decode($result); //if body == empty, then the file was successfully uploaded
 | 
			
		||||
                            $bytesRemaining = $bytesRemaining - $chunkSize;
 | 
			
		||||
                            $i++;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //definetly send the message
 | 
			
		||||
            $graph->createRequest("POST", "/users/" . key($message->getFrom()) . "/messages/" . $graphMessage->getId() . "/send")->execute();
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $graphMessage = $graph->createRequest("POST", "/users/" . key($message->getFrom()) . "/sendmail")
 | 
			
		||||
                    ->attachBody($messageBody)
 | 
			
		||||
                    ->setReturnType(\Microsoft\Graph\Model\Message::class)
 | 
			
		||||
                    ->execute();
 | 
			
		||||
            }
 | 
			
		||||
            catch(\Exception $e){
 | 
			
		||||
 | 
			
		||||
                sleep(5);
 | 
			
		||||
                $graphMessage = $graph->createRequest("POST", "/users/" . key($message->getFrom()) . "/sendmail")
 | 
			
		||||
                    ->attachBody($messageBody)
 | 
			
		||||
                    ->setReturnType(\Microsoft\Graph\Model\Message::class)
 | 
			
		||||
                    ->execute();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->sendPerformed($message);
 | 
			
		||||
 | 
			
		||||
        return $this->numberOfRecipients($message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get body for the message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Swift_Mime_SimpleMessage $message
 | 
			
		||||
     * @param bool $withAttachments
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    protected function getBody(Swift_Mime_SimpleMessage $message, $withAttachments = false)
 | 
			
		||||
    {
 | 
			
		||||
        $messageData = [
 | 
			
		||||
            'from' => [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => key($message->getFrom()),
 | 
			
		||||
                    'name' => current($message->getFrom())
 | 
			
		||||
                ]
 | 
			
		||||
            ],
 | 
			
		||||
            'toRecipients' => $this->getTo($message),
 | 
			
		||||
            'ccRecipients' => $this->getCc($message),
 | 
			
		||||
            'bccRecipients' => $this->getBcc($message),
 | 
			
		||||
            'replyTo' => $this->getReplyTo($message),
 | 
			
		||||
            'subject' => $message->getSubject(),
 | 
			
		||||
            'body' => [
 | 
			
		||||
                'contentType' => $message->getBodyContentType() == "text/html" ? 'html' : 'text',
 | 
			
		||||
                'content' => $message->getBody()
 | 
			
		||||
            ]
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if ($withAttachments) {
 | 
			
		||||
            $messageData = ['message' => $messageData];
 | 
			
		||||
            //add attachments if any
 | 
			
		||||
            $attachments = [];
 | 
			
		||||
            foreach ($message->getChildren() as $attachment) {
 | 
			
		||||
                if ($attachment instanceof \Swift_Mime_SimpleMimeEntity && $attachment->getContentType() != 'text/plain') {
 | 
			
		||||
                    $attachments[] = [
 | 
			
		||||
                        "@odata.type" => "#microsoft.graph.fileAttachment",
 | 
			
		||||
                        "name" => $attachment->getHeaders()->get('Content-Type')->getParameter('name'),
 | 
			
		||||
                        "contentType" => $attachment->getBodyContentType(),
 | 
			
		||||
                        "contentBytes" => base64_encode($attachment->getBody()),
 | 
			
		||||
                        'contentId'    => $attachment->getId()
 | 
			
		||||
                    ];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (count($attachments) > 0) {
 | 
			
		||||
                $messageData['message']['attachments'] = $attachments;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $messageData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the "to" payload field for the API request.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Swift_Mime_SimpleMessage $message
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    protected function getTo(Swift_Mime_SimpleMessage $message)
 | 
			
		||||
    {
 | 
			
		||||
        return collect((array) $message->getTo())->map(function ($display, $address) {
 | 
			
		||||
            return $display ? [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address,
 | 
			
		||||
                    'name' => $display
 | 
			
		||||
                ]
 | 
			
		||||
            ] : [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address
 | 
			
		||||
                ]
 | 
			
		||||
            ];
 | 
			
		||||
        })->values()->toArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the "Cc" payload field for the API request.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Swift_Mime_SimpleMessage $message
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    protected function getCc(Swift_Mime_SimpleMessage $message)
 | 
			
		||||
    {
 | 
			
		||||
        return collect((array) $message->getCc())->map(function ($display, $address) {
 | 
			
		||||
            return $display ? [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address,
 | 
			
		||||
                    'name' => $display
 | 
			
		||||
                ]
 | 
			
		||||
            ] : [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address
 | 
			
		||||
                ]
 | 
			
		||||
            ];
 | 
			
		||||
        })->values()->toArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the "replyTo" payload field for the API request.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Swift_Mime_SimpleMessage $message
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    protected function getReplyTo(Swift_Mime_SimpleMessage $message)
 | 
			
		||||
    {
 | 
			
		||||
        return collect((array) $message->getReplyTo())->map(function ($display, $address) {
 | 
			
		||||
            return $display ? [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address,
 | 
			
		||||
                    'name' => $display
 | 
			
		||||
                ]
 | 
			
		||||
            ] : [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address
 | 
			
		||||
                ]
 | 
			
		||||
            ];
 | 
			
		||||
        })->values()->toArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the "Bcc" payload field for the API request.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Swift_Mime_SimpleMessage $message
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    protected function getBcc(Swift_Mime_SimpleMessage $message)
 | 
			
		||||
    {
 | 
			
		||||
        return collect((array) $message->getBcc())->map(function ($display, $address) {
 | 
			
		||||
            return $display ? [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address,
 | 
			
		||||
                    'name' => $display
 | 
			
		||||
                ]
 | 
			
		||||
            ] : [
 | 
			
		||||
                'emailAddress' => [
 | 
			
		||||
                    'address' => $address
 | 
			
		||||
                ]
 | 
			
		||||
            ];
 | 
			
		||||
        })->values()->toArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all of the contacts for the message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Swift_Mime_SimpleMessage $message
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    protected function allContacts(Swift_Mime_SimpleMessage $message)
 | 
			
		||||
    {
 | 
			
		||||
        return array_merge(
 | 
			
		||||
            (array) $message->getTo(),
 | 
			
		||||
            (array) $message->getCc(),
 | 
			
		||||
            (array) $message->getBcc(),
 | 
			
		||||
            (array) $message->getReplyTo()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								app/Helpers/Mail/Office365TransportManager.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/Helpers/Mail/Office365TransportManager.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Helpers\Mail;
 | 
			
		||||
 | 
			
		||||
use App\Helpers\Mail\Office365MailTransport;
 | 
			
		||||
use Illuminate\Mail\MailManager;
 | 
			
		||||
 | 
			
		||||
class Office365TransportManager extends MailManager
 | 
			
		||||
{
 | 
			
		||||
    protected function createOffice365Transport()
 | 
			
		||||
    {
 | 
			
		||||
        return new Office365MailTransport();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -23,6 +23,7 @@ use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Libraries\OAuth\OAuth;
 | 
			
		||||
use App\Libraries\OAuth\Providers\Google;
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Models\CompanyToken;
 | 
			
		||||
@ -44,6 +45,7 @@ use Illuminate\Support\Str;
 | 
			
		||||
use Laravel\Socialite\Facades\Socialite;
 | 
			
		||||
use PragmaRX\Google2FA\Google2FA;
 | 
			
		||||
use Turbo124\Beacon\Facades\LightLogs;
 | 
			
		||||
use Microsoft\Graph\Model;
 | 
			
		||||
 | 
			
		||||
class LoginController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
@ -211,44 +213,11 @@ class LoginController extends BaseController
 | 
			
		||||
                $user = $user->fresh();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // $user->setCompany($user->account->default_company);
 | 
			
		||||
            // $this->setLoginCache($user);
 | 
			
		||||
 | 
			
		||||
            // $cu = CompanyUser::query()
 | 
			
		||||
            //       ->where('user_id', auth()->user()->id);
 | 
			
		||||
 | 
			
		||||
            $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
            if($cu->count() == 0)
 | 
			
		||||
                return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
            // $truth = app()->make(TruthSource::class);
 | 
			
		||||
 | 
			
		||||
            // $truth->setCompanyUser($cu->first());
 | 
			
		||||
            // $truth->setUser(auth()->user());
 | 
			
		||||
            // $truth->setCompany($user->account->default_company);
 | 
			
		||||
 | 
			
		||||
            // if(!$cu->exists())
 | 
			
		||||
            //     return response()->json(['message' => 'User not linked to any companies'], 403);
 | 
			
		||||
 | 
			
		||||
            // /* Ensure the user has a valid token */
 | 
			
		||||
            // if($user->company_users()->count() != $user->tokens()->count())
 | 
			
		||||
            // {
 | 
			
		||||
 | 
			
		||||
            //   $user->companies->each(function($company) use($user, $request){
 | 
			
		||||
 | 
			
		||||
            //     if(!CompanyToken::where('user_id', $user->id)->where('company_id', $company->id)->exists()){
 | 
			
		||||
 | 
			
		||||
            //       CreateCompanyToken::dispatchNow($company, $user, $request->server('HTTP_USER_AGENT'));
 | 
			
		||||
 | 
			
		||||
            //     }
 | 
			
		||||
 | 
			
		||||
            //   });
 | 
			
		||||
 | 
			
		||||
            // }
 | 
			
		||||
 | 
			
		||||
            // $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $user->account->default_company->id)->first());
 | 
			
		||||
 | 
			
		||||
            /*On the hosted platform, only owners can login for free/pro accounts*/
 | 
			
		||||
            if (Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
 | 
			
		||||
                return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
@ -363,17 +332,18 @@ class LoginController extends BaseController
 | 
			
		||||
        if (request()->input('provider') == 'google') {
 | 
			
		||||
            return $this->handleGoogleOauth();
 | 
			
		||||
        } elseif (request()->input('provider') == 'microsoft') {
 | 
			
		||||
            if (request()->has('token')) {
 | 
			
		||||
                return $this->handleSocialiteLogin('microsoft', request()->get('token'));
 | 
			
		||||
            } else {
 | 
			
		||||
                $message = 'Bearer token missing for the microsoft login';
 | 
			
		||||
            }
 | 
			
		||||
            // if (request()->has('token')) {
 | 
			
		||||
            //     return $this->handleSocialiteLogin('microsoft', request()->get('token'));
 | 
			
		||||
            // } else {
 | 
			
		||||
            //     $message = 'Bearer token missing for the microsoft login';
 | 
			
		||||
            // }
 | 
			
		||||
            return $this->handleMicrosoftOauth();
 | 
			
		||||
        } elseif (request()->input('provider') == 'apple') {
 | 
			
		||||
            if (request()->has('token')) {
 | 
			
		||||
                return $this->handleSocialiteLogin('apple', request()->get('token'));
 | 
			
		||||
            } else {
 | 
			
		||||
                $message = 'Token is missing for the apple login';
 | 
			
		||||
            }
 | 
			
		||||
            // if (request()->has('token')) {
 | 
			
		||||
            //     return $this->handleSocialiteLogin('apple', request()->get('token'));
 | 
			
		||||
            // } else {
 | 
			
		||||
            //     $message = 'Token is missing for the apple login';
 | 
			
		||||
            // }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response()
 | 
			
		||||
@ -483,6 +453,9 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
        $cu = CompanyUser::query()->where('user_id', auth()->user()->id);
 | 
			
		||||
 | 
			
		||||
        if($cu->count() == 0)
 | 
			
		||||
            return $cu;
 | 
			
		||||
 | 
			
		||||
        if (CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists())
 | 
			
		||||
            $set_company = auth()->user()->account->default_company;
 | 
			
		||||
        else {
 | 
			
		||||
@ -509,7 +482,7 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
                    CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth");
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
@ -521,6 +494,100 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function handleMicrosoftOauth()
 | 
			
		||||
    {
 | 
			
		||||
        if(request()->has('accessToken'))
 | 
			
		||||
            $accessToken = request()->input('accessToken');
 | 
			
		||||
        else
 | 
			
		||||
            return response()->json(['message' => 'Invalid response from oauth server'], 400);
 | 
			
		||||
 | 
			
		||||
        $graph = new \Microsoft\Graph\Graph();
 | 
			
		||||
        $graph->setAccessToken($accessToken);
 | 
			
		||||
 | 
			
		||||
        $user = $graph->createRequest("GET", "/me")
 | 
			
		||||
                      ->setReturnType(Model\User::class)
 | 
			
		||||
                      ->execute();
 | 
			
		||||
 | 
			
		||||
        if($user){
 | 
			
		||||
 | 
			
		||||
            $account = request()->input('account');
 | 
			
		||||
            $email = $user->getMail() ?: $user->getUserPrincipalName();
 | 
			
		||||
 | 
			
		||||
            $query = [
 | 
			
		||||
                'oauth_user_id' => $user->getId(),
 | 
			
		||||
                'oauth_provider_id'=> 'microsoft',
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            if ($existing_user = MultiDB::hasUser($query)) {
 | 
			
		||||
 | 
			
		||||
                if(!$existing_user->account)
 | 
			
		||||
                    return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
			
		||||
 | 
			
		||||
                return $this->existingOauthUser($existing_user);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //If this is a result user/email combo - lets add their OAuth details details
 | 
			
		||||
            if($existing_login_user = MultiDB::hasUser(['email' => $email]))
 | 
			
		||||
            {
 | 
			
		||||
                if(!$existing_login_user->account)
 | 
			
		||||
                    return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
			
		||||
 | 
			
		||||
                Auth::login($existing_login_user, true);
 | 
			
		||||
 | 
			
		||||
                return $this->existingLoginUser($user->getId(), 'microsoft');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Signup!
 | 
			
		||||
            $new_account = [
 | 
			
		||||
                'first_name' => $user->getGivenName() ?: '',
 | 
			
		||||
                'last_name' => $user->getSurname() ?: '' ,
 | 
			
		||||
                'password' => '',
 | 
			
		||||
                'email' => $email,
 | 
			
		||||
                'oauth_user_id' => $user->getId(),
 | 
			
		||||
                'oauth_provider_id' => 'microsoft',
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            return $this->createNewAccount($new_account);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function existingOauthUser($existing_user)
 | 
			
		||||
    {
 | 
			
		||||
        Auth::login($existing_user, true);
 | 
			
		||||
 | 
			
		||||
        $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
        if($cu->count() == 0)
 | 
			
		||||
            return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
        if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
 | 
			
		||||
            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
        return $this->timeConstrainedResponse($cu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function existingLoginUser($oauth_user_id, $provider)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        auth()->user()->update([
 | 
			
		||||
            'oauth_user_id' => $oauth_user_id,
 | 
			
		||||
            'oauth_provider_id'=> $provider,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
        $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
        if($cu->count() == 0)
 | 
			
		||||
            return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
        if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
 | 
			
		||||
            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
        return $this->timeConstrainedResponse($cu);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function handleGoogleOauth()
 | 
			
		||||
    {
 | 
			
		||||
        $user = false;
 | 
			
		||||
@ -541,18 +608,7 @@ class LoginController extends BaseController
 | 
			
		||||
                if(!$existing_user->account)
 | 
			
		||||
                    return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
			
		||||
 | 
			
		||||
                Auth::login($existing_user, true);
 | 
			
		||||
 | 
			
		||||
                $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
                if($cu->count() == 0)
 | 
			
		||||
                    return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
                if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
 | 
			
		||||
                    return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
                return $this->timeConstrainedResponse($cu);
 | 
			
		||||
 | 
			
		||||
                return $this->existingOauthUser($existing_user);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //If this is a result user/email combo - lets add their OAuth details details
 | 
			
		||||
@ -563,20 +619,7 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
                Auth::login($existing_login_user, true);
 | 
			
		||||
 | 
			
		||||
                auth()->user()->update([
 | 
			
		||||
                    'oauth_user_id' => $google->harvestSubField($user),
 | 
			
		||||
                    'oauth_provider_id'=> 'google',
 | 
			
		||||
                    ]);
 | 
			
		||||
 | 
			
		||||
                $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
                if($cu->count() == 0)
 | 
			
		||||
                    return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
                if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
 | 
			
		||||
                    return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
                return $this->timeConstrainedResponse($cu);
 | 
			
		||||
                return $this->existingLoginUser($google->harvestSubField($user), 'google');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
@ -584,7 +627,6 @@ class LoginController extends BaseController
 | 
			
		||||
        if ($user) {
 | 
			
		||||
 | 
			
		||||
            //check the user doesn't already exist in some form
 | 
			
		||||
 | 
			
		||||
            if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
 | 
			
		||||
            {
 | 
			
		||||
                if(!$existing_login_user->account)
 | 
			
		||||
@ -592,26 +634,9 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
                Auth::login($existing_login_user, true);
 | 
			
		||||
 | 
			
		||||
                auth()->user()->update([
 | 
			
		||||
                    'oauth_user_id' => $google->harvestSubField($user),
 | 
			
		||||
                    'oauth_provider_id'=> 'google',
 | 
			
		||||
                    ]);
 | 
			
		||||
 | 
			
		||||
                $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
                // $cu = CompanyUser::query()
 | 
			
		||||
                //                   ->where('user_id', auth()->user()->id);
 | 
			
		||||
 | 
			
		||||
                if ($cu->count() == 0)
 | 
			
		||||
                    return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
                if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
 | 
			
		||||
                    return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
                return $this->timeConstrainedResponse($cu);
 | 
			
		||||
                return $this->existingLoginUser($google->harvestSubField($user), 'google');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //user not found anywhere - lets sign them up.
 | 
			
		||||
            $name = OAuth::splitName($google->harvestName($user));
 | 
			
		||||
 | 
			
		||||
@ -624,23 +649,7 @@ class LoginController extends BaseController
 | 
			
		||||
                'oauth_provider_id' => 'google',
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            MultiDB::setDefaultDatabase();
 | 
			
		||||
 | 
			
		||||
            $account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
 | 
			
		||||
 | 
			
		||||
            Auth::login($account->default_company->owner(), true);
 | 
			
		||||
            auth()->user()->email_verified_at = now();
 | 
			
		||||
            auth()->user()->save();
 | 
			
		||||
 | 
			
		||||
            $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
            if($cu->count() == 0)
 | 
			
		||||
                return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
            if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
 | 
			
		||||
                return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
            return $this->timeConstrainedResponse($cu);
 | 
			
		||||
            return $this->createNewAccount($new_account);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response()
 | 
			
		||||
@ -649,6 +658,32 @@ class LoginController extends BaseController
 | 
			
		||||
        ->header('X-Api-Version', config('ninja.minimum_client_version'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function createNewAccount($new_account)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        MultiDB::setDefaultDatabase();
 | 
			
		||||
 | 
			
		||||
        $account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
 | 
			
		||||
 | 
			
		||||
        if(!$account instanceOf Account)
 | 
			
		||||
            return $account;
 | 
			
		||||
 | 
			
		||||
        Auth::login($account->default_company->owner(), true);
 | 
			
		||||
        auth()->user()->email_verified_at = now();
 | 
			
		||||
        auth()->user()->save();
 | 
			
		||||
 | 
			
		||||
        $cu = $this->hydrateCompanyUser();
 | 
			
		||||
 | 
			
		||||
        if($cu->count() == 0)
 | 
			
		||||
            return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
			
		||||
 | 
			
		||||
        if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
 | 
			
		||||
            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
			
		||||
 | 
			
		||||
        return $this->timeConstrainedResponse($cu);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function redirectToProvider(string $provider)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -662,11 +697,16 @@ class LoginController extends BaseController
 | 
			
		||||
            $parameters = ['access_type' => 'offline', "prompt" => "consent select_account", 'redirect_uri' => config('ninja.app_url')."/auth/google"];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($provider == 'microsoft'){
 | 
			
		||||
            $scopes = ['email', 'Mail.ReadWrite', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid'];
 | 
			
		||||
            $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url')."/auth/microsoft"];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (request()->has('code')) {
 | 
			
		||||
            return $this->handleProviderCallback($provider);
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            if(!in_array($provider, ['google']))
 | 
			
		||||
            if(!in_array($provider, ['google','microsoft']))
 | 
			
		||||
                return abort(400, 'Invalid provider');
 | 
			
		||||
 | 
			
		||||
            return Socialite::driver($provider)->with($parameters)->scopes($scopes)->redirect();
 | 
			
		||||
@ -675,6 +715,10 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
    public function handleProviderCallback(string $provider)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if($provider == 'microsoft')
 | 
			
		||||
            return $this->handleMicrosoftProviderCallback();
 | 
			
		||||
 | 
			
		||||
        $socialite_user = Socialite::driver($provider)->user();
 | 
			
		||||
 | 
			
		||||
        $oauth_user_token = '';
 | 
			
		||||
@ -714,4 +758,43 @@ class LoginController extends BaseController
 | 
			
		||||
 | 
			
		||||
        return redirect('/#/');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function handleMicrosoftProviderCallback($provider = 'microsoft')
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $socialite_user = Socialite::driver($provider)->user();
 | 
			
		||||
        nlog($socialite_user);
 | 
			
		||||
 | 
			
		||||
        nlog("refresh token " . $socialite_user->accessTokenResponseBody['refresh_token']);
 | 
			
		||||
        nlog("access token " . $socialite_user->accessTokenResponseBody['access_token']);
 | 
			
		||||
 | 
			
		||||
        $oauth_user_token = $socialite_user->accessTokenResponseBody['access_token'];
 | 
			
		||||
 | 
			
		||||
        if($user = OAuth::handleAuth($socialite_user, $provider))
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            nlog('found user and updating their user record');
 | 
			
		||||
            $name = OAuth::splitName($socialite_user->getName());
 | 
			
		||||
 | 
			
		||||
            $update_user = [
 | 
			
		||||
                'first_name' => $name[0],
 | 
			
		||||
                'last_name' => $name[1],
 | 
			
		||||
                'email' => $socialite_user->getEmail(),
 | 
			
		||||
                'oauth_user_id' => $socialite_user->getId(),
 | 
			
		||||
                'oauth_provider_id' => $provider,
 | 
			
		||||
                'oauth_user_token' => $oauth_user_token,
 | 
			
		||||
                'oauth_user_refresh_token' => $socialite_user->accessTokenResponseBody['refresh_token']
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            $user->update($update_user);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            nlog("user not found for oauth");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return redirect('/#/');
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,8 @@ class InvitationController extends Controller
 | 
			
		||||
 | 
			
		||||
        $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
 | 
			
		||||
 | 
			
		||||
        $invitation = $entity_obj::where('key', $invitation_key)
 | 
			
		||||
        $invitation = $entity_obj::withTrashed()
 | 
			
		||||
                                    ->where('key', $invitation_key)
 | 
			
		||||
                                    ->whereHas($entity, function ($query) {
 | 
			
		||||
                                         $query->where('is_deleted',0);
 | 
			
		||||
                                    })
 | 
			
		||||
@ -186,7 +187,8 @@ class InvitationController extends Controller
 | 
			
		||||
 | 
			
		||||
        $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
 | 
			
		||||
 | 
			
		||||
        $invitation = $entity_obj::where('key', $invitation_key)
 | 
			
		||||
        $invitation = $entity_obj::withTrashed()
 | 
			
		||||
                                    ->where('key', $invitation_key)
 | 
			
		||||
                                    ->with('contact.client')
 | 
			
		||||
                                    ->firstOrFail();
 | 
			
		||||
 | 
			
		||||
@ -228,7 +230,8 @@ class InvitationController extends Controller
 | 
			
		||||
 | 
			
		||||
    public function payInvoice(Request $request, string $invitation_key)
 | 
			
		||||
    {
 | 
			
		||||
        $invitation = InvoiceInvitation::where('key', $invitation_key)
 | 
			
		||||
        $invitation = InvoiceInvitation::withTrashed()
 | 
			
		||||
                                    ->where('key', $invitation_key)
 | 
			
		||||
                                    ->with('contact.client')
 | 
			
		||||
                                    ->firstOrFail();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@ class LogoutController extends BaseController
 | 
			
		||||
    public function index(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $ct = CompanyToken::with('company.tokens')
 | 
			
		||||
                    ->whereRaw('BINARY `token`= ?', [$request->header('X-API-TOKEN')])
 | 
			
		||||
                    ->where('token', $request->header('X-API-TOKEN'))
 | 
			
		||||
                    ->first();
 | 
			
		||||
 | 
			
		||||
                    $ct->company
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,8 @@ class InvitationController extends Controller
 | 
			
		||||
 | 
			
		||||
        Auth::logout();
 | 
			
		||||
 | 
			
		||||
        $invitation = PurchaseOrderInvitation::where('key', $invitation_key)
 | 
			
		||||
        $invitation = PurchaseOrderInvitation::withTrashed()
 | 
			
		||||
                                    ->where('key', $invitation_key)
 | 
			
		||||
                                    ->whereHas('purchase_order', function ($query) {
 | 
			
		||||
                                         $query->where('is_deleted',0);
 | 
			
		||||
                                    })
 | 
			
		||||
 | 
			
		||||
@ -141,6 +141,8 @@ class PurchaseOrderController extends Controller
 | 
			
		||||
                                        ->whereIn('id', $this->transformKeys($data['purchase_orders']))
 | 
			
		||||
                                        ->where('company_id', auth()->guard('vendor')->user()->vendor->company_id)
 | 
			
		||||
                                        ->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT])
 | 
			
		||||
                                        ->where('is_deleted', 0)
 | 
			
		||||
                                        ->withTrashed()
 | 
			
		||||
                                        ->cursor()->each(function ($purchase_order){
 | 
			
		||||
 | 
			
		||||
                                        $purchase_order->service()
 | 
			
		||||
@ -159,7 +161,7 @@ class PurchaseOrderController extends Controller
 | 
			
		||||
 | 
			
		||||
        if(count($data['purchase_orders']) == 1){ 
 | 
			
		||||
 | 
			
		||||
            $purchase_order = PurchaseOrder::whereIn('id', $this->transformKeys($data['purchase_orders']))->first();
 | 
			
		||||
            $purchase_order = PurchaseOrder::withTrashed()->where('is_deleted', 0)->whereIn('id', $this->transformKeys($data['purchase_orders']))->first();
 | 
			
		||||
            
 | 
			
		||||
            return redirect()->route('vendor.purchase_order.show', ['purchase_order' => $purchase_order->hashed_id]);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
@ -184,12 +184,49 @@ class NinjaMailerJob implements ShouldQueue
 | 
			
		||||
                $this->mailer = 'gmail';
 | 
			
		||||
                $this->setGmailMailer();
 | 
			
		||||
                break;
 | 
			
		||||
            case 'office365':
 | 
			
		||||
                $this->mailer = 'office365';
 | 
			
		||||
                $this->setOfficeMailer();
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setOfficeMailer()
 | 
			
		||||
    {
 | 
			
		||||
        $sending_user = $this->nmo->settings->gmail_sending_user_id;
 | 
			
		||||
 | 
			
		||||
        $user = User::find($this->decodePrimaryKey($sending_user));
 | 
			
		||||
        
 | 
			
		||||
        nlog("Sending via {$user->name()}");
 | 
			
		||||
 | 
			
		||||
        $token = $this->refreshOfficeToken($user);
 | 
			
		||||
 | 
			
		||||
        if($token)
 | 
			
		||||
        {
 | 
			
		||||
            $user->oauth_user_token = $token;
 | 
			
		||||
            $user->save();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
 | 
			
		||||
            $this->nmo->settings->email_sending_method = 'default';
 | 
			
		||||
            return $this->setMailDriver();
 | 
			
		||||
        
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->nmo
 | 
			
		||||
             ->mailable
 | 
			
		||||
             ->from($user->email, $user->name())
 | 
			
		||||
             ->withSwiftMessage(function ($message) use($token) {
 | 
			
		||||
                $message->getHeaders()->addTextHeader('GmailToken', $token);     
 | 
			
		||||
             });
 | 
			
		||||
 | 
			
		||||
        sleep(rand(1,3));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setGmailMailer()
 | 
			
		||||
    {
 | 
			
		||||
        if(LaravelGmail::check())
 | 
			
		||||
@ -303,4 +340,25 @@ class NinjaMailerJob implements ShouldQueue
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function refreshOfficeToken($user)
 | 
			
		||||
    {
 | 
			
		||||
        $guzzle = new \GuzzleHttp\Client(); 
 | 
			
		||||
        $url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; 
 | 
			
		||||
 | 
			
		||||
        $token = json_decode($guzzle->post($url, [
 | 
			
		||||
            'form_params' => [
 | 
			
		||||
                'client_id' => config('ninja.o365.client_id') ,
 | 
			
		||||
                'client_secret' => config('ninja.o365.client_secret') ,
 | 
			
		||||
                'scope' => 'email Mail.ReadWrite Mail.Send offline_access profile User.Read openid',
 | 
			
		||||
                'grant_type' => 'refresh_token',
 | 
			
		||||
                'refresh_token' => $user->oauth_user_refresh_token
 | 
			
		||||
            ],
 | 
			
		||||
        ])->getBody()->getContents());
 | 
			
		||||
 | 
			
		||||
        if($token)
 | 
			
		||||
            return $token->access_token;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -88,8 +88,14 @@ class TemplateEmail extends Mailable
 | 
			
		||||
 | 
			
		||||
        $this->from(config('mail.from.address'), $email_from_name);
 | 
			
		||||
 | 
			
		||||
        if (strlen($settings->bcc_email) > 1)
 | 
			
		||||
            $this->bcc(explode(",",str_replace(" ", "", $settings->bcc_email)));//remove whitespace if any has been inserted.
 | 
			
		||||
        if (strlen($settings->bcc_email) > 1){
 | 
			
		||||
 | 
			
		||||
            if(Ninja::isHosted())
 | 
			
		||||
                $this->bcc(reset(explode(",",str_replace(" ", "", $settings->bcc_email))));//remove whitespace if any has been inserted.
 | 
			
		||||
            else
 | 
			
		||||
                $this->bcc(explode(",",str_replace(" ", "", $settings->bcc_email)));//remove whitespace if any has been inserted.
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->subject($this->build_email->getSubject())
 | 
			
		||||
            ->text('email.template.text', [
 | 
			
		||||
 | 
			
		||||
@ -374,6 +374,9 @@ class Account extends BaseModel
 | 
			
		||||
    public function getDailyEmailLimit()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() == 0)
 | 
			
		||||
            return 50;
 | 
			
		||||
 | 
			
		||||
        if($this->isPaid()){
 | 
			
		||||
            $limit = $this->paid_plan_email_quota;
 | 
			
		||||
            $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,6 @@ class MailServiceProvider extends MailProvider
 | 
			
		||||
            return new GmailTransportManager($app);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //this is octane ready - but is untested
 | 
			
		||||
        // $this->app->bind('mail.manager', function ($app){
 | 
			
		||||
        //     return new GmailTransportManager($app);
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ use App\Models\CreditInvitation;
 | 
			
		||||
use App\Models\Document;
 | 
			
		||||
use App\Transformers\ActivityTransformer;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use League\Fractal\Resource\Item;
 | 
			
		||||
 | 
			
		||||
class CreditTransformer extends EntityTransformer
 | 
			
		||||
{
 | 
			
		||||
@ -30,6 +31,7 @@ class CreditTransformer extends EntityTransformer
 | 
			
		||||
 | 
			
		||||
    protected $availableIncludes = [
 | 
			
		||||
        'activities',
 | 
			
		||||
        'client',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function includeActivities(Credit $credit)
 | 
			
		||||
@ -53,28 +55,13 @@ class CreditTransformer extends EntityTransformer
 | 
			
		||||
        return $this->includeCollection($credit->invitations, $transformer, CreditInvitation::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
        public function includePayments(quote $credit)
 | 
			
		||||
        {
 | 
			
		||||
            $transformer = new PaymentTransformer($this->account, $this->serializer, $credit);
 | 
			
		||||
    public function includeClient(Credit $credit): Item
 | 
			
		||||
    {
 | 
			
		||||
        $transformer = new ClientTransformer($this->serializer);
 | 
			
		||||
 | 
			
		||||
            return $this->includeCollection($credit->payments, $transformer, ENTITY_PAYMENT);
 | 
			
		||||
        }
 | 
			
		||||
        return $this->includeItem($credit->client, $transformer, Client::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        public function includeClient(quote $credit)
 | 
			
		||||
        {
 | 
			
		||||
            $transformer = new ClientTransformer($this->account, $this->serializer);
 | 
			
		||||
 | 
			
		||||
            return $this->includeItem($credit->client, $transformer, ENTITY_CLIENT);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public function includeExpenses(quote $credit)
 | 
			
		||||
        {
 | 
			
		||||
            $transformer = new ExpenseTransformer($this->account, $this->serializer);
 | 
			
		||||
 | 
			
		||||
            return $this->includeCollection($credit->expenses, $transformer, ENTITY_EXPENSE);
 | 
			
		||||
        }
 | 
			
		||||
*/
 | 
			
		||||
    public function includeDocuments(Credit $credit)
 | 
			
		||||
    {
 | 
			
		||||
        $transformer = new DocumentTransformer($this->serializer);
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,7 @@
 | 
			
		||||
        "league/fractal": "^0.17.0",
 | 
			
		||||
        "league/omnipay": "^3.1",
 | 
			
		||||
        "livewire/livewire": "^2.6",
 | 
			
		||||
        "microsoft/microsoft-graph": "^1.69",
 | 
			
		||||
        "mollie/mollie-api-php": "^2.36",
 | 
			
		||||
        "nelexa/zip": "^4.0",
 | 
			
		||||
        "nwidart/laravel-modules": "8.3",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										53
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										53
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							@ -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": "6845489fdc254427c4536e22f025ff51",
 | 
			
		||||
    "content-hash": "df84a1903809a8e781d937e679821e74",
 | 
			
		||||
    "packages": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "afosto/yaac",
 | 
			
		||||
@ -4928,6 +4928,57 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2022-04-07T21:38:12+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "microsoft/microsoft-graph",
 | 
			
		||||
            "version": "1.69.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/microsoftgraph/msgraph-sdk-php.git",
 | 
			
		||||
                "reference": "dc867afdb2c89ea7ead37d6bcfcaf0389e7a85f4"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/microsoftgraph/msgraph-sdk-php/zipball/dc867afdb2c89ea7ead37d6bcfcaf0389e7a85f4",
 | 
			
		||||
                "reference": "dc867afdb2c89ea7ead37d6bcfcaf0389e7a85f4",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
                "ext-json": "*",
 | 
			
		||||
                "guzzlehttp/guzzle": "^6.0 || ^7.0",
 | 
			
		||||
                "php": "^8.0 || ^7.3",
 | 
			
		||||
                "psr/http-message": "^1.0"
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "mikey179/vfsstream": "^1.2",
 | 
			
		||||
                "phpstan/phpstan": "^0.12.90 || ^1.0.0",
 | 
			
		||||
                "phpunit/phpunit": "^8.0 || ^9.0"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "library",
 | 
			
		||||
            "autoload": {
 | 
			
		||||
                "psr-4": {
 | 
			
		||||
                    "Microsoft\\Graph\\": "src/",
 | 
			
		||||
                    "Beta\\Microsoft\\Graph\\": "src/Beta/Microsoft/Graph/"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "notification-url": "https://packagist.org/downloads/",
 | 
			
		||||
            "license": [
 | 
			
		||||
                "MIT"
 | 
			
		||||
            ],
 | 
			
		||||
            "authors": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Microsoft Graph Client Tooling",
 | 
			
		||||
                    "email": "graphtooling@service.microsoft.com",
 | 
			
		||||
                    "role": "Developer"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "The Microsoft Graph SDK for PHP",
 | 
			
		||||
            "homepage": "https://developer.microsoft.com/en-us/graph",
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/microsoftgraph/msgraph-sdk-php/issues",
 | 
			
		||||
                "source": "https://github.com/microsoftgraph/msgraph-sdk-php/tree/1.69.0"
 | 
			
		||||
            },
 | 
			
		||||
            "time": "2022-06-15T11:11:33+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "mollie/mollie-api-php",
 | 
			
		||||
            "version": "v2.44.1",
 | 
			
		||||
 | 
			
		||||
@ -73,6 +73,9 @@ return [
 | 
			
		||||
        'gmail' => [
 | 
			
		||||
            'transport' => 'gmail',
 | 
			
		||||
        ],
 | 
			
		||||
        'office365' => [
 | 
			
		||||
            'transport' => 'office365',
 | 
			
		||||
        ],
 | 
			
		||||
        'cocopostmark' => [
 | 
			
		||||
            'transport' => 'cocopostmark',
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
@ -14,8 +14,8 @@ return [
 | 
			
		||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
			
		||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
			
		||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
			
		||||
    'app_version' => '5.4.2',
 | 
			
		||||
    'app_tag' => '5.4.2',
 | 
			
		||||
    'app_version' => '5.4.3',
 | 
			
		||||
    'app_tag' => '5.4.3',
 | 
			
		||||
    'minimum_client_version' => '5.0.16',
 | 
			
		||||
    'terms_version' => '1.0.1',
 | 
			
		||||
    'api_secret' => env('API_SECRET', ''),
 | 
			
		||||
@ -155,6 +155,11 @@ return [
 | 
			
		||||
    'designs' => [
 | 
			
		||||
        'base_path' => resource_path('views/pdf-designs/'),
 | 
			
		||||
    ],
 | 
			
		||||
    'o365' => [
 | 
			
		||||
        'client_secret' => env('MICROSOFT_CLIENT_SECRET', false),
 | 
			
		||||
        'client_id' => env('MICROSOFT_CLIENT_ID', false),
 | 
			
		||||
        'tenant_id' => env('MICROSOFT_TENANT_ID', false),
 | 
			
		||||
    ],
 | 
			
		||||
    'maintenance' => [
 | 
			
		||||
        'delete_pdfs' => env('DELETE_PDF_DAYS', 0),
 | 
			
		||||
        'delete_backups' => env('DELETE_BACKUP_DAYS', 0),
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,33 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
class ChangeRefreshTokenColumnSize extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function up()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        Schema::table('users', function (Blueprint $table) {
 | 
			
		||||
            $table->text('oauth_user_refresh_token')->change();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverse the migrations.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function down()
 | 
			
		||||
    {
 | 
			
		||||
        //
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user