mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 10:03:34 -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
 | 
					    public $require_quote_signature = false;  //@TODO ben to confirm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //email settings
 | 
					    //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 $gmail_sending_user_id = '0'; //@implemented
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $reply_to_email = ''; //@implemented
 | 
					    public $reply_to_email = ''; //@implemented
 | 
				
			||||||
 | 
				
			|||||||
@ -10,9 +10,10 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
namespace App\Helpers\Mail;
 | 
					namespace App\Helpers\Mail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Mail\MailManager;
 | 
					 | 
				
			||||||
use App\CustomMailDriver\CustomTransport;
 | 
					use App\CustomMailDriver\CustomTransport;
 | 
				
			||||||
 | 
					use App\Helpers\Mail\Office365MailTransport;
 | 
				
			||||||
use Dacastro4\LaravelGmail\Services\Message\Mail;
 | 
					use Dacastro4\LaravelGmail\Services\Message\Mail;
 | 
				
			||||||
 | 
					use Illuminate\Mail\MailManager;
 | 
				
			||||||
use Illuminate\Support\Facades\Config;
 | 
					use Illuminate\Support\Facades\Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,4 +23,9 @@ class GmailTransportManager extends MailManager
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return new GmailTransport(new Mail);
 | 
					        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\MultiDB;
 | 
				
			||||||
use App\Libraries\OAuth\OAuth;
 | 
					use App\Libraries\OAuth\OAuth;
 | 
				
			||||||
use App\Libraries\OAuth\Providers\Google;
 | 
					use App\Libraries\OAuth\Providers\Google;
 | 
				
			||||||
 | 
					use App\Models\Account;
 | 
				
			||||||
use App\Models\Client;
 | 
					use App\Models\Client;
 | 
				
			||||||
use App\Models\Company;
 | 
					use App\Models\Company;
 | 
				
			||||||
use App\Models\CompanyToken;
 | 
					use App\Models\CompanyToken;
 | 
				
			||||||
@ -44,6 +45,7 @@ use Illuminate\Support\Str;
 | 
				
			|||||||
use Laravel\Socialite\Facades\Socialite;
 | 
					use Laravel\Socialite\Facades\Socialite;
 | 
				
			||||||
use PragmaRX\Google2FA\Google2FA;
 | 
					use PragmaRX\Google2FA\Google2FA;
 | 
				
			||||||
use Turbo124\Beacon\Facades\LightLogs;
 | 
					use Turbo124\Beacon\Facades\LightLogs;
 | 
				
			||||||
 | 
					use Microsoft\Graph\Model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginController extends BaseController
 | 
					class LoginController extends BaseController
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -211,44 +213,11 @@ class LoginController extends BaseController
 | 
				
			|||||||
                $user = $user->fresh();
 | 
					                $user = $user->fresh();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // $user->setCompany($user->account->default_company);
 | 
					 | 
				
			||||||
            // $this->setLoginCache($user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // $cu = CompanyUser::query()
 | 
					 | 
				
			||||||
            //       ->where('user_id', auth()->user()->id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $cu = $this->hydrateCompanyUser();
 | 
					            $cu = $this->hydrateCompanyUser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if($cu->count() == 0)
 | 
					            if($cu->count() == 0)
 | 
				
			||||||
                return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
					                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*/
 | 
					            /*On the hosted platform, only owners can login for free/pro accounts*/
 | 
				
			||||||
            if (Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
 | 
					            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);
 | 
					                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') {
 | 
					        if (request()->input('provider') == 'google') {
 | 
				
			||||||
            return $this->handleGoogleOauth();
 | 
					            return $this->handleGoogleOauth();
 | 
				
			||||||
        } elseif (request()->input('provider') == 'microsoft') {
 | 
					        } elseif (request()->input('provider') == 'microsoft') {
 | 
				
			||||||
            if (request()->has('token')) {
 | 
					            // if (request()->has('token')) {
 | 
				
			||||||
                return $this->handleSocialiteLogin('microsoft', request()->get('token'));
 | 
					            //     return $this->handleSocialiteLogin('microsoft', request()->get('token'));
 | 
				
			||||||
            } else {
 | 
					            // } else {
 | 
				
			||||||
                $message = 'Bearer token missing for the microsoft login';
 | 
					            //     $message = 'Bearer token missing for the microsoft login';
 | 
				
			||||||
            }
 | 
					            // }
 | 
				
			||||||
 | 
					            return $this->handleMicrosoftOauth();
 | 
				
			||||||
        } elseif (request()->input('provider') == 'apple') {
 | 
					        } elseif (request()->input('provider') == 'apple') {
 | 
				
			||||||
            if (request()->has('token')) {
 | 
					            // if (request()->has('token')) {
 | 
				
			||||||
                return $this->handleSocialiteLogin('apple', request()->get('token'));
 | 
					            //     return $this->handleSocialiteLogin('apple', request()->get('token'));
 | 
				
			||||||
            } else {
 | 
					            // } else {
 | 
				
			||||||
                $message = 'Token is missing for the apple login';
 | 
					            //     $message = 'Token is missing for the apple login';
 | 
				
			||||||
            }
 | 
					            // }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response()
 | 
					        return response()
 | 
				
			||||||
@ -483,6 +453,9 @@ class LoginController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $cu = CompanyUser::query()->where('user_id', auth()->user()->id);
 | 
					        $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())
 | 
					        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;
 | 
					            $set_company = auth()->user()->account->default_company;
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
@ -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()
 | 
					    private function handleGoogleOauth()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $user = false;
 | 
					        $user = false;
 | 
				
			||||||
@ -541,18 +608,7 @@ class LoginController extends BaseController
 | 
				
			|||||||
                if(!$existing_user->account)
 | 
					                if(!$existing_user->account)
 | 
				
			||||||
                    return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
					                    return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Auth::login($existing_user, true);
 | 
					                return $this->existingOauthUser($existing_user);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                $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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //If this is a result user/email combo - lets add their OAuth details details
 | 
					            //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::login($existing_login_user, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                auth()->user()->update([
 | 
					                return $this->existingLoginUser($google->harvestSubField($user), 'google');
 | 
				
			||||||
                    '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);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -584,7 +627,6 @@ class LoginController extends BaseController
 | 
				
			|||||||
        if ($user) {
 | 
					        if ($user) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //check the user doesn't already exist in some form
 | 
					            //check the user doesn't already exist in some form
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
 | 
					            if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if(!$existing_login_user->account)
 | 
					                if(!$existing_login_user->account)
 | 
				
			||||||
@ -592,26 +634,9 @@ class LoginController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                Auth::login($existing_login_user, true);
 | 
					                Auth::login($existing_login_user, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                auth()->user()->update([
 | 
					                return $this->existingLoginUser($google->harvestSubField($user), 'google');
 | 
				
			||||||
                    '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);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
            //user not found anywhere - lets sign them up.
 | 
					            //user not found anywhere - lets sign them up.
 | 
				
			||||||
            $name = OAuth::splitName($google->harvestName($user));
 | 
					            $name = OAuth::splitName($google->harvestName($user));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -624,10 +649,25 @@ class LoginController extends BaseController
 | 
				
			|||||||
                'oauth_provider_id' => 'google',
 | 
					                'oauth_provider_id' => 'google',
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return $this->createNewAccount($new_account);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()
 | 
				
			||||||
 | 
					        ->json(['message' => ctrans('texts.invalid_credentials')], 401)
 | 
				
			||||||
 | 
					        ->header('X-App-Version', config('ninja.app_version'))
 | 
				
			||||||
 | 
					        ->header('X-Api-Version', config('ninja.minimum_client_version'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function createNewAccount($new_account)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        MultiDB::setDefaultDatabase();
 | 
					        MultiDB::setDefaultDatabase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
 | 
					        $account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!$account instanceOf Account)
 | 
				
			||||||
 | 
					            return $account;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Auth::login($account->default_company->owner(), true);
 | 
					        Auth::login($account->default_company->owner(), true);
 | 
				
			||||||
        auth()->user()->email_verified_at = now();
 | 
					        auth()->user()->email_verified_at = now();
 | 
				
			||||||
        auth()->user()->save();
 | 
					        auth()->user()->save();
 | 
				
			||||||
@ -641,12 +681,7 @@ class LoginController extends BaseController
 | 
				
			|||||||
            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
					            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this->timeConstrainedResponse($cu);
 | 
					        return $this->timeConstrainedResponse($cu);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response()
 | 
					 | 
				
			||||||
        ->json(['message' => ctrans('texts.invalid_credentials')], 401)
 | 
					 | 
				
			||||||
        ->header('X-App-Version', config('ninja.app_version'))
 | 
					 | 
				
			||||||
        ->header('X-Api-Version', config('ninja.minimum_client_version'));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function redirectToProvider(string $provider)
 | 
					    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"];
 | 
					            $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')) {
 | 
					        if (request()->has('code')) {
 | 
				
			||||||
            return $this->handleProviderCallback($provider);
 | 
					            return $this->handleProviderCallback($provider);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(!in_array($provider, ['google']))
 | 
					            if(!in_array($provider, ['google','microsoft']))
 | 
				
			||||||
                return abort(400, 'Invalid provider');
 | 
					                return abort(400, 'Invalid provider');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Socialite::driver($provider)->with($parameters)->scopes($scopes)->redirect();
 | 
					            return Socialite::driver($provider)->with($parameters)->scopes($scopes)->redirect();
 | 
				
			||||||
@ -675,6 +715,10 @@ class LoginController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function handleProviderCallback(string $provider)
 | 
					    public function handleProviderCallback(string $provider)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($provider == 'microsoft')
 | 
				
			||||||
 | 
					            return $this->handleMicrosoftProviderCallback();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $socialite_user = Socialite::driver($provider)->user();
 | 
					        $socialite_user = Socialite::driver($provider)->user();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $oauth_user_token = '';
 | 
					        $oauth_user_token = '';
 | 
				
			||||||
@ -714,4 +758,43 @@ class LoginController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return redirect('/#/');
 | 
					        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';
 | 
					        $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) {
 | 
					                                    ->whereHas($entity, function ($query) {
 | 
				
			||||||
                                         $query->where('is_deleted',0);
 | 
					                                         $query->where('is_deleted',0);
 | 
				
			||||||
                                    })
 | 
					                                    })
 | 
				
			||||||
@ -186,7 +187,8 @@ class InvitationController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
 | 
					        $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')
 | 
					                                    ->with('contact.client')
 | 
				
			||||||
                                    ->firstOrFail();
 | 
					                                    ->firstOrFail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -228,7 +230,8 @@ class InvitationController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function payInvoice(Request $request, string $invitation_key)
 | 
					    public function payInvoice(Request $request, string $invitation_key)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $invitation = InvoiceInvitation::where('key', $invitation_key)
 | 
					        $invitation = InvoiceInvitation::withTrashed()
 | 
				
			||||||
 | 
					                                    ->where('key', $invitation_key)
 | 
				
			||||||
                                    ->with('contact.client')
 | 
					                                    ->with('contact.client')
 | 
				
			||||||
                                    ->firstOrFail();
 | 
					                                    ->firstOrFail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -60,7 +60,7 @@ class LogoutController extends BaseController
 | 
				
			|||||||
    public function index(Request $request)
 | 
					    public function index(Request $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $ct = CompanyToken::with('company.tokens')
 | 
					        $ct = CompanyToken::with('company.tokens')
 | 
				
			||||||
                    ->whereRaw('BINARY `token`= ?', [$request->header('X-API-TOKEN')])
 | 
					                    ->where('token', $request->header('X-API-TOKEN'))
 | 
				
			||||||
                    ->first();
 | 
					                    ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    $ct->company
 | 
					                    $ct->company
 | 
				
			||||||
 | 
				
			|||||||
@ -46,7 +46,8 @@ class InvitationController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Auth::logout();
 | 
					        Auth::logout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $invitation = PurchaseOrderInvitation::where('key', $invitation_key)
 | 
					        $invitation = PurchaseOrderInvitation::withTrashed()
 | 
				
			||||||
 | 
					                                    ->where('key', $invitation_key)
 | 
				
			||||||
                                    ->whereHas('purchase_order', function ($query) {
 | 
					                                    ->whereHas('purchase_order', function ($query) {
 | 
				
			||||||
                                         $query->where('is_deleted',0);
 | 
					                                         $query->where('is_deleted',0);
 | 
				
			||||||
                                    })
 | 
					                                    })
 | 
				
			||||||
 | 
				
			|||||||
@ -141,6 +141,8 @@ class PurchaseOrderController extends Controller
 | 
				
			|||||||
                                        ->whereIn('id', $this->transformKeys($data['purchase_orders']))
 | 
					                                        ->whereIn('id', $this->transformKeys($data['purchase_orders']))
 | 
				
			||||||
                                        ->where('company_id', auth()->guard('vendor')->user()->vendor->company_id)
 | 
					                                        ->where('company_id', auth()->guard('vendor')->user()->vendor->company_id)
 | 
				
			||||||
                                        ->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT])
 | 
					                                        ->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT])
 | 
				
			||||||
 | 
					                                        ->where('is_deleted', 0)
 | 
				
			||||||
 | 
					                                        ->withTrashed()
 | 
				
			||||||
                                        ->cursor()->each(function ($purchase_order){
 | 
					                                        ->cursor()->each(function ($purchase_order){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                        $purchase_order->service()
 | 
					                                        $purchase_order->service()
 | 
				
			||||||
@ -159,7 +161,7 @@ class PurchaseOrderController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if(count($data['purchase_orders']) == 1){ 
 | 
					        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]);
 | 
					            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->mailer = 'gmail';
 | 
				
			||||||
                $this->setGmailMailer();
 | 
					                $this->setGmailMailer();
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'office365':
 | 
				
			||||||
 | 
					                $this->mailer = 'office365';
 | 
				
			||||||
 | 
					                $this->setOfficeMailer();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                break;
 | 
					                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()
 | 
					    private function setGmailMailer()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if(LaravelGmail::check())
 | 
					        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,9 +88,15 @@ class TemplateEmail extends Mailable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->from(config('mail.from.address'), $email_from_name);
 | 
					        $this->from(config('mail.from.address'), $email_from_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (strlen($settings->bcc_email) > 1)
 | 
					        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->bcc(explode(",",str_replace(" ", "", $settings->bcc_email)));//remove whitespace if any has been inserted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->subject($this->build_email->getSubject())
 | 
					        $this->subject($this->build_email->getSubject())
 | 
				
			||||||
            ->text('email.template.text', [
 | 
					            ->text('email.template.text', [
 | 
				
			||||||
                'text_body' => $this->build_email->getTextBody(),
 | 
					                'text_body' => $this->build_email->getTextBody(),
 | 
				
			||||||
 | 
				
			|||||||
@ -374,6 +374,9 @@ class Account extends BaseModel
 | 
				
			|||||||
    public function getDailyEmailLimit()
 | 
					    public function getDailyEmailLimit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() == 0)
 | 
				
			||||||
 | 
					            return 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($this->isPaid()){
 | 
					        if($this->isPaid()){
 | 
				
			||||||
            $limit = $this->paid_plan_email_quota;
 | 
					            $limit = $this->paid_plan_email_quota;
 | 
				
			||||||
            $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
 | 
					            $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,6 @@ class MailServiceProvider extends MailProvider
 | 
				
			|||||||
            return new GmailTransportManager($app);
 | 
					            return new GmailTransportManager($app);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        //this is octane ready - but is untested
 | 
					        //this is octane ready - but is untested
 | 
				
			||||||
        // $this->app->bind('mail.manager', function ($app){
 | 
					        // $this->app->bind('mail.manager', function ($app){
 | 
				
			||||||
        //     return new GmailTransportManager($app);
 | 
					        //     return new GmailTransportManager($app);
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@ use App\Models\CreditInvitation;
 | 
				
			|||||||
use App\Models\Document;
 | 
					use App\Models\Document;
 | 
				
			||||||
use App\Transformers\ActivityTransformer;
 | 
					use App\Transformers\ActivityTransformer;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use League\Fractal\Resource\Item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreditTransformer extends EntityTransformer
 | 
					class CreditTransformer extends EntityTransformer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -30,6 +31,7 @@ class CreditTransformer extends EntityTransformer
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    protected $availableIncludes = [
 | 
					    protected $availableIncludes = [
 | 
				
			||||||
        'activities',
 | 
					        'activities',
 | 
				
			||||||
 | 
					        'client',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function includeActivities(Credit $credit)
 | 
					    public function includeActivities(Credit $credit)
 | 
				
			||||||
@ -53,28 +55,13 @@ class CreditTransformer extends EntityTransformer
 | 
				
			|||||||
        return $this->includeCollection($credit->invitations, $transformer, CreditInvitation::class);
 | 
					        return $this->includeCollection($credit->invitations, $transformer, CreditInvitation::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    public function includeClient(Credit $credit): Item
 | 
				
			||||||
        public function includePayments(quote $credit)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
            $transformer = new PaymentTransformer($this->account, $this->serializer, $credit);
 | 
					        $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)
 | 
					    public function includeDocuments(Credit $credit)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $transformer = new DocumentTransformer($this->serializer);
 | 
					        $transformer = new DocumentTransformer($this->serializer);
 | 
				
			||||||
 | 
				
			|||||||
@ -71,6 +71,7 @@
 | 
				
			|||||||
        "league/fractal": "^0.17.0",
 | 
					        "league/fractal": "^0.17.0",
 | 
				
			||||||
        "league/omnipay": "^3.1",
 | 
					        "league/omnipay": "^3.1",
 | 
				
			||||||
        "livewire/livewire": "^2.6",
 | 
					        "livewire/livewire": "^2.6",
 | 
				
			||||||
 | 
					        "microsoft/microsoft-graph": "^1.69",
 | 
				
			||||||
        "mollie/mollie-api-php": "^2.36",
 | 
					        "mollie/mollie-api-php": "^2.36",
 | 
				
			||||||
        "nelexa/zip": "^4.0",
 | 
					        "nelexa/zip": "^4.0",
 | 
				
			||||||
        "nwidart/laravel-modules": "8.3",
 | 
					        "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",
 | 
					        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
				
			||||||
        "This file is @generated automatically"
 | 
					        "This file is @generated automatically"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "content-hash": "6845489fdc254427c4536e22f025ff51",
 | 
					    "content-hash": "df84a1903809a8e781d937e679821e74",
 | 
				
			||||||
    "packages": [
 | 
					    "packages": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "afosto/yaac",
 | 
					            "name": "afosto/yaac",
 | 
				
			||||||
@ -4928,6 +4928,57 @@
 | 
				
			|||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "time": "2022-04-07T21:38:12+00:00"
 | 
					            "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",
 | 
					            "name": "mollie/mollie-api-php",
 | 
				
			||||||
            "version": "v2.44.1",
 | 
					            "version": "v2.44.1",
 | 
				
			||||||
 | 
				
			|||||||
@ -73,6 +73,9 @@ return [
 | 
				
			|||||||
        'gmail' => [
 | 
					        'gmail' => [
 | 
				
			||||||
            'transport' => 'gmail',
 | 
					            'transport' => 'gmail',
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					        'office365' => [
 | 
				
			||||||
 | 
					            'transport' => 'office365',
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
        'cocopostmark' => [
 | 
					        'cocopostmark' => [
 | 
				
			||||||
            'transport' => 'cocopostmark',
 | 
					            'transport' => 'cocopostmark',
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
				
			|||||||
@ -14,8 +14,8 @@ return [
 | 
				
			|||||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
					    'require_https' => env('REQUIRE_HTTPS', true),
 | 
				
			||||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
					    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
				
			||||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
					    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
				
			||||||
    'app_version' => '5.4.2',
 | 
					    'app_version' => '5.4.3',
 | 
				
			||||||
    'app_tag' => '5.4.2',
 | 
					    'app_tag' => '5.4.3',
 | 
				
			||||||
    'minimum_client_version' => '5.0.16',
 | 
					    'minimum_client_version' => '5.0.16',
 | 
				
			||||||
    'terms_version' => '1.0.1',
 | 
					    'terms_version' => '1.0.1',
 | 
				
			||||||
    'api_secret' => env('API_SECRET', ''),
 | 
					    'api_secret' => env('API_SECRET', ''),
 | 
				
			||||||
@ -155,6 +155,11 @@ return [
 | 
				
			|||||||
    'designs' => [
 | 
					    'designs' => [
 | 
				
			||||||
        'base_path' => resource_path('views/pdf-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' => [
 | 
					    'maintenance' => [
 | 
				
			||||||
        'delete_pdfs' => env('DELETE_PDF_DAYS', 0),
 | 
					        'delete_pdfs' => env('DELETE_PDF_DAYS', 0),
 | 
				
			||||||
        'delete_backups' => env('DELETE_BACKUP_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