Merge branch 'release-2.6.9'
Conflicts: app/Http/routes.php app/Ninja/PaymentDrivers/BasePaymentDriver.php composer.lock resources/lang/en/texts.php resources/views/reports/chart_builder.blade.php
@ -1,7 +1,7 @@
 | 
			
		||||
APP_ENV=production
 | 
			
		||||
APP_DEBUG=false
 | 
			
		||||
APP_URL=http://ninja.dev
 | 
			
		||||
APP_KEY=SomeRandomString
 | 
			
		||||
APP_KEY=SomeRandomStringSomeRandomString
 | 
			
		||||
APP_CIPHER=AES-256-CBC
 | 
			
		||||
 | 
			
		||||
DB_TYPE=mysql
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -1,3 +1,8 @@
 | 
			
		||||
* text=auto
 | 
			
		||||
*.css linguist-vendored
 | 
			
		||||
*.less linguist-vendored
 | 
			
		||||
.gitattributes export-ignore
 | 
			
		||||
.gitignore export-ignore
 | 
			
		||||
.codeclimate.yml export-ignore
 | 
			
		||||
.travis.yml export-ignore
 | 
			
		||||
.styleci.yml export-ignore
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ before_install:
 | 
			
		||||
  - if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
 | 
			
		||||
  - composer self-update && composer -V
 | 
			
		||||
#  - export USE_ZEND_ALLOC=0
 | 
			
		||||
  - rvm use 1.9.3 --install --fuzzy
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
  # install Composer dependencies
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,15 @@
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
This project adheres to [Semantic Versioning](http://semver.org/).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [Unreleased]
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
- Auto billing uses credits if they exist
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [2.6.4] - 2016-07-19
 | 
			
		||||
 | 
			
		||||
### Added
 | 
			
		||||
- Added 'Buy Now' buttons
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						@ -104,8 +104,8 @@ module.exports = function(grunt) {
 | 
			
		||||
          'public/vendor/moment-timezone/builds/moment-timezone-with-data.min.js',
 | 
			
		||||
          'public/vendor/stacktrace-js/dist/stacktrace-with-polyfills.min.js',
 | 
			
		||||
          'public/vendor/fuse.js/src/fuse.min.js',
 | 
			
		||||
          'public/vendor/sweetalert/dist/sweetalert.min.js',
 | 
			
		||||
          //'public/vendor/moment-duration-format/lib/moment-duration-format.js',
 | 
			
		||||
          //'public/vendor/handsontable/dist/jquery.handsontable.full.min.js',
 | 
			
		||||
          //'public/vendor/pdfmake/build/pdfmake.min.js',
 | 
			
		||||
          //'public/vendor/pdfmake/build/vfs_fonts.js',
 | 
			
		||||
          //'public/js/vfs_fonts.js',
 | 
			
		||||
@ -116,14 +116,12 @@ module.exports = function(grunt) {
 | 
			
		||||
        dest: 'public/built.js',
 | 
			
		||||
        nonull: true
 | 
			
		||||
      },
 | 
			
		||||
      js_public: {
 | 
			
		||||
      /*js_public: {
 | 
			
		||||
        src: [
 | 
			
		||||
        /*
 | 
			
		||||
          'public/js/simpleexpand.js',
 | 
			
		||||
          'public/js/valign.js',
 | 
			
		||||
          'public/js/bootstrap.min.js',
 | 
			
		||||
          'public/js/simpleexpand.js',
 | 
			
		||||
        */
 | 
			
		||||
          'public/vendor/bootstrap/dist/js/bootstrap.min.js',
 | 
			
		||||
          'public/js/bootstrap-combobox.js',
 | 
			
		||||
 | 
			
		||||
@ -142,7 +140,7 @@ module.exports = function(grunt) {
 | 
			
		||||
          'public/vendor/spectrum/spectrum.css',
 | 
			
		||||
          'public/css/bootstrap-combobox.css',
 | 
			
		||||
          'public/css/typeahead.js-bootstrap.css',
 | 
			
		||||
          //'public/vendor/handsontable/dist/jquery.handsontable.full.css',
 | 
			
		||||
          'public/vendor/sweetalert/dist/sweetalert.css',
 | 
			
		||||
          'public/css/style.css',
 | 
			
		||||
        ],
 | 
			
		||||
        dest: 'public/css/built.css',
 | 
			
		||||
@ -150,8 +148,8 @@ module.exports = function(grunt) {
 | 
			
		||||
        options: {
 | 
			
		||||
            process: false
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      css_public: {
 | 
			
		||||
      },*/
 | 
			
		||||
      /*css_public: {
 | 
			
		||||
        src: [
 | 
			
		||||
          'public/vendor/bootstrap/dist/css/bootstrap.min.css',
 | 
			
		||||
          'public/vendor/font-awesome/css/font-awesome.min.css',
 | 
			
		||||
@ -165,8 +163,8 @@ module.exports = function(grunt) {
 | 
			
		||||
        options: {
 | 
			
		||||
            process: false
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      js_pdf: {
 | 
			
		||||
      },*/
 | 
			
		||||
      /*js_pdf: {
 | 
			
		||||
        src: [
 | 
			
		||||
          'public/js/pdf_viewer.js',
 | 
			
		||||
          'public/js/compatibility.js',
 | 
			
		||||
@ -175,7 +173,7 @@ module.exports = function(grunt) {
 | 
			
		||||
        ],
 | 
			
		||||
        dest: 'public/pdf.built.js',
 | 
			
		||||
        nonull: true
 | 
			
		||||
      }
 | 
			
		||||
      }*/
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
# Invoice Ninja
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
 | 
			
		||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
 | 
			
		||||
[](https://gitter.im/hillelcoren/invoice-ninja?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
			
		||||
 | 
			
		||||
## [Hosted](https://www.invoiceninja.com) | [Self-hosted](https://invoiceninja.org)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										75
									
								
								app/Console/Commands/ResetInvoiceSchemaCounter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,75 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Console\Commands;
 | 
			
		||||
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use Carbon\Carbon;
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
 | 
			
		||||
class ResetInvoiceSchemaCounter extends Command
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * The name and signature of the console command.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $signature = 'ninja:reset-invoice-schema-counter
 | 
			
		||||
                            {account? : The ID of the account}
 | 
			
		||||
                            {--force : Force setting the counter back to "1", regardless if the year changed}';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The console command description.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $description = 'Reset the invoice schema counter at the turn of the year.';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Invoice
 | 
			
		||||
     */
 | 
			
		||||
    protected $invoice;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new command instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @param Invoice $invoice
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(Invoice $invoice)
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct();
 | 
			
		||||
        $this->invoice = $invoice;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the console command.
 | 
			
		||||
     *
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        $force = $this->option('force');
 | 
			
		||||
        $account = $this->argument('account');
 | 
			
		||||
 | 
			
		||||
        $accounts = null;
 | 
			
		||||
 | 
			
		||||
        if ($account) {
 | 
			
		||||
            $accounts = Account::find($account)->get();
 | 
			
		||||
        } else {
 | 
			
		||||
            $accounts = Account::all();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $latestInvoice = $this->invoice->latest()->first();
 | 
			
		||||
        $invoiceYear = Carbon::parse($latestInvoice->created_at)->year;
 | 
			
		||||
 | 
			
		||||
        if(Carbon::now()->year > $invoiceYear || $force) {
 | 
			
		||||
            $accounts->transform(function ($a) {
 | 
			
		||||
                /** @var Account $a */
 | 
			
		||||
                $a->invoice_number_counter = 1;
 | 
			
		||||
                $a->update();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            $this->info('The counter has been resetted successfully for '.$accounts->count().' account(s).');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -21,8 +21,8 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
        'App\Console\Commands\SendRenewalInvoices',
 | 
			
		||||
        'App\Console\Commands\ChargeRenewalInvoices',
 | 
			
		||||
        'App\Console\Commands\SendReminders',
 | 
			
		||||
        'App\Console\Commands\TestOFX',
 | 
			
		||||
        'App\Console\Commands\GenerateResources',
 | 
			
		||||
        'App\Console\Commands\TestOFX',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -1461,6 +1461,7 @@ class AccountController extends BaseController
 | 
			
		||||
            return trans('texts.create_invoice_for_sample');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** @var \App\Models\Account $account */
 | 
			
		||||
        $account = Auth::user()->account;
 | 
			
		||||
        $invitation = $invoice->invitations->first();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,8 @@ class AccountGatewayController extends BaseController
 | 
			
		||||
        $data['hiddenFields'] = Gateway::$hiddenFields;
 | 
			
		||||
        $data['selectGateways'] = Gateway::where('id', '=', $accountGateway->gateway_id)->get();
 | 
			
		||||
 | 
			
		||||
        $this->testGateway($accountGateway);
 | 
			
		||||
 | 
			
		||||
        return View::make('accounts.account_gateway', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -307,7 +309,7 @@ class AccountGatewayController extends BaseController
 | 
			
		||||
                $account->account_gateways()->save($accountGateway);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if(isset($wepayResponse)) {
 | 
			
		||||
            if (isset($wepayResponse)) {
 | 
			
		||||
                return $wepayResponse;
 | 
			
		||||
            } else {
 | 
			
		||||
                if ($accountGatewayPublicId) {
 | 
			
		||||
@ -322,6 +324,16 @@ class AccountGatewayController extends BaseController
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function testGateway($accountGateway)
 | 
			
		||||
    {
 | 
			
		||||
        $paymentDriver = $accountGateway->paymentDriver();
 | 
			
		||||
        $result = $paymentDriver->isValid();
 | 
			
		||||
 | 
			
		||||
        if ($result !== true) {
 | 
			
		||||
            Session::flash('error', $result . ' - ' . trans('texts.gateway_config_error'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getWePayUpdateUri($accountGateway)
 | 
			
		||||
    {
 | 
			
		||||
        if ($accountGateway->gateway_id != GATEWAY_WEPAY) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										323
									
								
								app/Http/Controllers/BotController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,323 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers;
 | 
			
		||||
 | 
			
		||||
use Auth;
 | 
			
		||||
use DB;
 | 
			
		||||
use Utils;
 | 
			
		||||
use Cache;
 | 
			
		||||
use Input;
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Libraries\Skype\SkypeResponse;
 | 
			
		||||
use App\Libraries\CurlUtils;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use App\Models\SecurityCode;
 | 
			
		||||
use App\Ninja\Intents\BaseIntent;
 | 
			
		||||
use App\Ninja\Mailers\UserMailer;
 | 
			
		||||
 | 
			
		||||
class BotController extends Controller
 | 
			
		||||
{
 | 
			
		||||
    protected $userMailer;
 | 
			
		||||
 | 
			
		||||
    public function __construct(UserMailer $userMailer)
 | 
			
		||||
    {
 | 
			
		||||
        $this->userMailer = $userMailer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function handleMessage($platform)
 | 
			
		||||
    {
 | 
			
		||||
        $input = Input::all();
 | 
			
		||||
        $botUserId = $input['from']['id'];
 | 
			
		||||
 | 
			
		||||
        if ( ! $token = $this->authenticate($input)) {
 | 
			
		||||
            return SkypeResponse::message(trans('texts.not_authorized'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if ($input['type'] === 'contactRelationUpdate') {
 | 
			
		||||
                // brand new user, ask for their email
 | 
			
		||||
                if ($input['action'] === 'add') {
 | 
			
		||||
                    $response = SkypeResponse::message(trans('texts.bot_get_email'));
 | 
			
		||||
                    $state = BOT_STATE_GET_EMAIL;
 | 
			
		||||
                } elseif ($input['action'] === 'remove') {
 | 
			
		||||
                    $this->removeBot($botUserId);
 | 
			
		||||
                    $this->saveState($token, false);
 | 
			
		||||
                    return RESULT_SUCCESS;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $state = $this->loadState($token);
 | 
			
		||||
                $text = strip_tags($input['text']);
 | 
			
		||||
 | 
			
		||||
                // user gaves us their email
 | 
			
		||||
                if ($state === BOT_STATE_GET_EMAIL) {
 | 
			
		||||
                    if ($this->validateEmail($text, $botUserId)) {
 | 
			
		||||
                        $response = SkypeResponse::message(trans('texts.bot_get_code'));
 | 
			
		||||
                        $state = BOT_STATE_GET_CODE;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $response = SkypeResponse::message(trans('texts.email_not_found', ['email' => $text]));
 | 
			
		||||
                    }
 | 
			
		||||
                // user sent the scurity code
 | 
			
		||||
                } elseif ($state === BOT_STATE_GET_CODE) {
 | 
			
		||||
                    if ($this->validateCode($text, $botUserId)) {
 | 
			
		||||
                        $response = SkypeResponse::message(trans('texts.bot_welcome') . trans('texts.bot_help_message'));
 | 
			
		||||
                        $state = BOT_STATE_READY;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $response = SkypeResponse::message(trans('texts.invalid_code'));
 | 
			
		||||
                    }
 | 
			
		||||
                // regular chat message
 | 
			
		||||
                } else {
 | 
			
		||||
                    if ($message === 'help') {
 | 
			
		||||
                        $response = SkypeResponse::message(trans('texts.bot_help_message'));
 | 
			
		||||
                    } elseif ($message == 'status') {
 | 
			
		||||
                        $response = SkypeResponse::message(trans('texts.intent_not_supported'));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if ( ! $user = User::whereBotUserId($botUserId)->with('account')->first()) {
 | 
			
		||||
                            return SkypeResponse::message(trans('texts.not_authorized'));
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Auth::onceUsingId($user->id);
 | 
			
		||||
                        $user->account->loadLocalizationSettings();
 | 
			
		||||
 | 
			
		||||
                        $data = $this->parseMessage($text);
 | 
			
		||||
                        $intent = BaseIntent::createIntent($state, $data);
 | 
			
		||||
                        $response = $intent->process();
 | 
			
		||||
                        $state = $intent->getState();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->saveState($token, $state);
 | 
			
		||||
        } catch (Exception $exception) {
 | 
			
		||||
            $response = SkypeResponse::message($exception->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->sendResponse($token, $botUserId, $response);
 | 
			
		||||
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function authenticate($input)
 | 
			
		||||
    {
 | 
			
		||||
        $headers = getallheaders();
 | 
			
		||||
        $token = isset($headers['Authorization']) ? $headers['Authorization'] : false;
 | 
			
		||||
 | 
			
		||||
        if (Utils::isNinjaDev()) {
 | 
			
		||||
            // skip validation for testing
 | 
			
		||||
        } elseif ( ! $this->validateToken($token)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($token = Cache::get('msbot_token')) {
 | 
			
		||||
            return $token;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $clientId = env('MSBOT_CLIENT_ID');
 | 
			
		||||
        $clientSecret = env('MSBOT_CLIENT_SECRET');
 | 
			
		||||
        $scope = 'https://graph.microsoft.com/.default';
 | 
			
		||||
 | 
			
		||||
        $data = sprintf('grant_type=client_credentials&client_id=%s&client_secret=%s&scope=%s', $clientId, $clientSecret, $scope);
 | 
			
		||||
 | 
			
		||||
        $response = CurlUtils::post(MSBOT_LOGIN_URL, $data);
 | 
			
		||||
        $response = json_decode($response);
 | 
			
		||||
 | 
			
		||||
        $expires = ($response->expires_in / 60) - 5;
 | 
			
		||||
        Cache::put('msbot_token', $response->access_token, $expires);
 | 
			
		||||
 | 
			
		||||
        return $response->access_token;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function loadState($token)
 | 
			
		||||
    {
 | 
			
		||||
        $url = sprintf('%s/botstate/skype/conversations/%s', MSBOT_STATE_URL, '29:1C-OsU7OWBEDOYJhQUsDkYHmycOwOq9QOg5FVTwRX9ts');
 | 
			
		||||
 | 
			
		||||
        $headers = [
 | 
			
		||||
            'Authorization: Bearer ' . $token
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $response = CurlUtils::get($url, $headers);
 | 
			
		||||
        $data = json_decode($response);
 | 
			
		||||
 | 
			
		||||
        return json_decode($data->data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function parseMessage($message)
 | 
			
		||||
    {
 | 
			
		||||
        $appId = env('MSBOT_LUIS_APP_ID');
 | 
			
		||||
        $subKey = env('MSBOT_LUIS_SUBSCRIPTION_KEY');
 | 
			
		||||
        $message = rawurlencode($message);
 | 
			
		||||
 | 
			
		||||
        $url = sprintf('%s?id=%s&subscription-key=%s&q=%s', MSBOT_LUIS_URL, $appId, $subKey, $message);
 | 
			
		||||
        $data = file_get_contents($url);
 | 
			
		||||
        $data = json_decode($data);
 | 
			
		||||
 | 
			
		||||
        return $data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function saveState($token, $data)
 | 
			
		||||
    {
 | 
			
		||||
        $url = sprintf('%s/botstate/skype/conversations/%s', MSBOT_STATE_URL, '29:1C-OsU7OWBEDOYJhQUsDkYHmycOwOq9QOg5FVTwRX9ts');
 | 
			
		||||
 | 
			
		||||
        $headers = [
 | 
			
		||||
            'Authorization: Bearer ' . $token,
 | 
			
		||||
            'Content-Type: application/json',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        //echo "STATE<pre>" . htmlentities(json_encode($data), JSON_PRETTY_PRINT) . "</pre>";
 | 
			
		||||
 | 
			
		||||
        $data = '{ eTag: "*", data: "' . addslashes(json_encode($data)) . '" }';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        CurlUtils::post($url, $data, $headers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function sendResponse($token, $to, $message)
 | 
			
		||||
    {
 | 
			
		||||
        $url = sprintf('%s/conversations/%s/activities/', SKYPE_API_URL, $to);
 | 
			
		||||
 | 
			
		||||
        $headers = [
 | 
			
		||||
            'Authorization: Bearer ' . $token,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        //echo "<pre>" . htmlentities(json_encode(json_decode($message), JSON_PRETTY_PRINT)) . "</pre>";
 | 
			
		||||
 | 
			
		||||
        $response = CurlUtils::post($url, $message, $headers);
 | 
			
		||||
 | 
			
		||||
        //var_dump($response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateEmail($email, $botUserId)
 | 
			
		||||
    {
 | 
			
		||||
        if ( ! $email || ! $botUserId) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // delete any expired codes
 | 
			
		||||
        SecurityCode::whereBotUserId($botUserId)
 | 
			
		||||
                    ->where('created_at', '<', DB::raw('now() - INTERVAL 10 MINUTE'))
 | 
			
		||||
                    ->delete();
 | 
			
		||||
 | 
			
		||||
        if (SecurityCode::whereBotUserId($botUserId)->first()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = User::whereEmail($email)
 | 
			
		||||
                    ->whereNull('bot_user_id')
 | 
			
		||||
                    ->first();
 | 
			
		||||
 | 
			
		||||
        if ( ! $user) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $code = new SecurityCode();
 | 
			
		||||
        $code->user_id = $user->id;
 | 
			
		||||
        $code->account_id = $user->account_id;
 | 
			
		||||
        $code->code = mt_rand(100000, 999999);
 | 
			
		||||
        $code->bot_user_id = $botUserId;
 | 
			
		||||
        $code->save();
 | 
			
		||||
 | 
			
		||||
        $this->userMailer->sendSecurityCode($user, $code->code);
 | 
			
		||||
 | 
			
		||||
        return $code->code;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateCode($input, $botUserId)
 | 
			
		||||
    {
 | 
			
		||||
        if ( ! $input || ! $botUserId) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $code = SecurityCode::whereBotUserId($botUserId)
 | 
			
		||||
                    ->where('created_at', '>', DB::raw('now() - INTERVAL 10 MINUTE'))
 | 
			
		||||
                    ->where('attempts', '<', 5)
 | 
			
		||||
                    ->first();
 | 
			
		||||
 | 
			
		||||
        if ( ! $code) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( ! hash_equals($code->code, $input)) {
 | 
			
		||||
            $code->attempts += 1;
 | 
			
		||||
            $code->save();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = User::find($code->user_id);
 | 
			
		||||
        $user->bot_user_id = $code->bot_user_id;
 | 
			
		||||
        $user->save();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function removeBot($botUserId)
 | 
			
		||||
    {
 | 
			
		||||
        $user = User::whereBotUserId($botUserId)->first();
 | 
			
		||||
        $user->bot_user_id = null;
 | 
			
		||||
        $user->save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateToken($token)
 | 
			
		||||
    {
 | 
			
		||||
        if ( ! $token) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // https://blogs.msdn.microsoft.com/tsmatsuz/2016/07/12/developing-skype-bot/
 | 
			
		||||
        // 0:Invalid, 1:Valid
 | 
			
		||||
        $token_valid = 0;
 | 
			
		||||
 | 
			
		||||
        // 1 separate token by dot (.)
 | 
			
		||||
        $token_arr = explode('.', $token);
 | 
			
		||||
        $headers_enc = $token_arr[0];
 | 
			
		||||
        $claims_enc = $token_arr[1];
 | 
			
		||||
        $sig_enc = $token_arr[2];
 | 
			
		||||
 | 
			
		||||
        // 2 base 64 url decoding
 | 
			
		||||
        $headers_arr = json_decode($this->base64_url_decode($headers_enc), TRUE);
 | 
			
		||||
        $claims_arr = json_decode($this->base64_url_decode($claims_enc), TRUE);
 | 
			
		||||
        $sig = $this->base64_url_decode($sig_enc);
 | 
			
		||||
 | 
			
		||||
        // 3 get key list
 | 
			
		||||
        $keylist = file_get_contents('https://api.aps.skype.com/v1/keys');
 | 
			
		||||
        $keylist_arr = json_decode($keylist, TRUE);
 | 
			
		||||
        foreach($keylist_arr['keys'] as $key => $value) {
 | 
			
		||||
 | 
			
		||||
            // 4 select one key (which matches)
 | 
			
		||||
            if($value['kid'] == $headers_arr['kid']) {
 | 
			
		||||
 | 
			
		||||
                // 5 get public key from key info
 | 
			
		||||
                $cert_txt = '-----BEGIN CERTIFICATE-----' . "\n" . chunk_split($value['x5c'][0], 64) . '-----END CERTIFICATE-----';
 | 
			
		||||
                $cert_obj = openssl_x509_read($cert_txt);
 | 
			
		||||
                $pkey_obj = openssl_pkey_get_public($cert_obj);
 | 
			
		||||
                $pkey_arr = openssl_pkey_get_details($pkey_obj);
 | 
			
		||||
                $pkey_txt = $pkey_arr['key'];
 | 
			
		||||
 | 
			
		||||
                // 6 verify signature
 | 
			
		||||
                $token_valid = openssl_verify($headers_enc . '.' . $claims_enc, $sig, $pkey_txt, OPENSSL_ALGO_SHA256);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 7 show result
 | 
			
		||||
        return ($token_valid == 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function base64_url_decode($arg) {
 | 
			
		||||
        $res = $arg;
 | 
			
		||||
        $res = str_replace('-', '+', $res);
 | 
			
		||||
        $res = str_replace('_', '/', $res);
 | 
			
		||||
        switch (strlen($res) % 4) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                $res .= "==";
 | 
			
		||||
                break;
 | 
			
		||||
            case 3:
 | 
			
		||||
                $res .= "=";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        $res = base64_decode($res);
 | 
			
		||||
        return $res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -52,7 +52,10 @@ class DocumentAPIController extends BaseAPIController
 | 
			
		||||
    {
 | 
			
		||||
        $document = $request->entity();
 | 
			
		||||
 | 
			
		||||
        return DocumentController::getDownloadResponse($document);
 | 
			
		||||
        if(array_key_exists($document->type, Document::$types))
 | 
			
		||||
            return DocumentController::getDownloadResponse($document);
 | 
			
		||||
        else
 | 
			
		||||
            return $this->errorResponse(['error'=>'Invalid mime type'],400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -611,4 +611,15 @@ class InvoiceController extends BaseController
 | 
			
		||||
 | 
			
		||||
        return View::make('invoices.history', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function checkInvoiceNumber($invoiceNumber)
 | 
			
		||||
    {
 | 
			
		||||
        $count = Invoice::scope()
 | 
			
		||||
                    ->whereInvoiceNumber($invoiceNumber)
 | 
			
		||||
                    ->withTrashed()
 | 
			
		||||
                    ->count();
 | 
			
		||||
 | 
			
		||||
        return $count ? RESULT_FAILURE : RESULT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -130,8 +130,9 @@ class OnlinePaymentController extends BaseController
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $paymentDriver->completeOffsitePurchase(Input::all());
 | 
			
		||||
            Session::flash('message', trans('texts.applied_payment'));
 | 
			
		||||
            if ($paymentDriver->completeOffsitePurchase(Input::all())) {
 | 
			
		||||
                Session::flash('message', trans('texts.applied_payment'));
 | 
			
		||||
            }
 | 
			
		||||
            return redirect()->to('view/' . $invitation->invitation_key);
 | 
			
		||||
        } catch (Exception $exception) {
 | 
			
		||||
            return $this->error($paymentDriver, $exception);
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,10 @@ class CreateInvoiceAPIRequest extends InvoiceRequest
 | 
			
		||||
            'invoice_items' => 'valid_invoice_items',
 | 
			
		||||
            'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id,
 | 
			
		||||
            'discount' => 'positive',
 | 
			
		||||
            'invoice_date' => 'date',
 | 
			
		||||
            'due_date' => 'date',
 | 
			
		||||
            'start_date' => 'date',
 | 
			
		||||
            'end_date' => 'date',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,10 @@ class CreateInvoiceRequest extends InvoiceRequest
 | 
			
		||||
            'invoice_items' => 'valid_invoice_items',
 | 
			
		||||
            'invoice_number' => 'required|unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id,
 | 
			
		||||
            'discount' => 'positive',
 | 
			
		||||
            'invoice_date' => 'date',
 | 
			
		||||
            'due_date' => 'date',
 | 
			
		||||
            'start_date' => 'date',
 | 
			
		||||
            'end_date' => 'date',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        /* There's a problem parsing the dates
 | 
			
		||||
 | 
			
		||||
@ -44,6 +44,11 @@ class EntityRequest extends Request {
 | 
			
		||||
        return $this->entity;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setEntity($entity)
 | 
			
		||||
    {
 | 
			
		||||
        $this->entity = $entity;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorize()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->entity()) {
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,10 @@ class UpdateInvoiceAPIRequest extends InvoiceRequest
 | 
			
		||||
            'invoice_items' => 'valid_invoice_items',
 | 
			
		||||
            'invoice_number' => 'unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id,
 | 
			
		||||
            'discount' => 'positive',
 | 
			
		||||
            'invoice_date' => 'date',
 | 
			
		||||
            'due_date' => 'date',
 | 
			
		||||
            'start_date' => 'date',
 | 
			
		||||
            'end_date' => 'date',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,10 @@ class UpdateInvoiceRequest extends InvoiceRequest
 | 
			
		||||
            'invoice_items' => 'valid_invoice_items',
 | 
			
		||||
            'invoice_number' => 'required|unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id,
 | 
			
		||||
            'discount' => 'positive',
 | 
			
		||||
            'invoice_date' => 'date',
 | 
			
		||||
            'due_date' => 'date',
 | 
			
		||||
            'start_date' => 'date',
 | 
			
		||||
            'end_date' => 'date',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        /* There's a problem parsing the dates
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								app/Http/ViewComposers/AppLanguageComposer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,37 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Http\ViewComposers;
 | 
			
		||||
 | 
			
		||||
use Illuminate\View\View;
 | 
			
		||||
 | 
			
		||||
class AppLanguageComposer
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Bind data to the view.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  View  $view
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function compose(View $view)
 | 
			
		||||
    {
 | 
			
		||||
        $view->with('appLanguage', $this->getLanguage());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the language from the current locale
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    private function getLanguage()
 | 
			
		||||
    {
 | 
			
		||||
        $code = app()->getLocale();
 | 
			
		||||
 | 
			
		||||
        if(preg_match('/_/', $code)) {
 | 
			
		||||
            $codes = explode('_', $code);
 | 
			
		||||
            $code = $codes[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $code;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -85,6 +85,7 @@ Route::match(['GET', 'POST'], '/buy_now/{gateway_type?}', 'OnlinePaymentControll
 | 
			
		||||
 | 
			
		||||
Route::post('/hook/email_bounced', 'AppController@emailBounced');
 | 
			
		||||
Route::post('/hook/email_opened', 'AppController@emailOpened');
 | 
			
		||||
Route::post('/hook/bot/{platform?}', 'BotController@handleMessage');
 | 
			
		||||
Route::post('/payment_hook/{accountKey}/{gatewayId}', 'OnlinePaymentController@handlePaymentWebhook');
 | 
			
		||||
 | 
			
		||||
// Laravel auth routes
 | 
			
		||||
@ -125,7 +126,8 @@ Route::group(['middleware' => 'auth:user'], function() {
 | 
			
		||||
    Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
 | 
			
		||||
    Route::get('hide_message', 'HomeController@hideMessage');
 | 
			
		||||
    Route::get('force_inline_pdf', 'UserController@forcePDFJS');
 | 
			
		||||
    Route::get('account/getSearchData', ['as' => 'getSearchData', 'uses' => 'AccountController@getSearchData']);
 | 
			
		||||
    Route::get('account/get_search_data', ['as' => 'get_search_data', 'uses' => 'AccountController@getSearchData']);
 | 
			
		||||
    Route::get('check_invoice_number/{invoice_number}', 'InvoiceController@checkInvoiceNumber');
 | 
			
		||||
 | 
			
		||||
    Route::get('settings/user_details', 'AccountController@showUserDetails');
 | 
			
		||||
    Route::post('settings/user_details', 'AccountController@saveUserDetails');
 | 
			
		||||
@ -351,7 +353,7 @@ if (!defined('CONTACT_EMAIL')) {
 | 
			
		||||
    define('ENTITY_CONTACT', 'contact');
 | 
			
		||||
    define('ENTITY_INVOICE', 'invoice');
 | 
			
		||||
    define('ENTITY_DOCUMENT', 'document');
 | 
			
		||||
    define('ENTITY_INVOICE_ITEMS', 'invoice_items');
 | 
			
		||||
    define('ENTITY_INVOICE_ITEM', 'invoice_item');
 | 
			
		||||
    define('ENTITY_INVITATION', 'invitation');
 | 
			
		||||
    define('ENTITY_RECURRING_INVOICE', 'recurring_invoice');
 | 
			
		||||
    define('ENTITY_PAYMENT', 'payment');
 | 
			
		||||
@ -608,7 +610,7 @@ if (!defined('CONTACT_EMAIL')) {
 | 
			
		||||
    define('NINJA_WEB_URL', env('NINJA_WEB_URL', 'https://www.invoiceninja.com'));
 | 
			
		||||
    define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com'));
 | 
			
		||||
    define('NINJA_DATE', '2000-01-01');
 | 
			
		||||
    define('NINJA_VERSION', '2.6.8' . env('NINJA_VERSION_SUFFIX'));
 | 
			
		||||
    define('NINJA_VERSION', '2.6.9' . env('NINJA_VERSION_SUFFIX'));
 | 
			
		||||
 | 
			
		||||
    define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
 | 
			
		||||
    define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'));
 | 
			
		||||
@ -626,6 +628,11 @@ if (!defined('CONTACT_EMAIL')) {
 | 
			
		||||
    define('OFX_HOME_URL', env('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all'));
 | 
			
		||||
    define('GOOGLE_ANALYITCS_URL', env('GOOGLE_ANALYITCS_URL', 'https://www.google-analytics.com/collect'));
 | 
			
		||||
 | 
			
		||||
    define('MSBOT_LOGIN_URL', 'https://login.microsoftonline.com/common/oauth2/v2.0/token');
 | 
			
		||||
    define('MSBOT_LUIS_URL', 'https://api.projectoxford.ai/luis/v1/application');
 | 
			
		||||
    define('SKYPE_API_URL', 'https://apis.skype.com/v3');
 | 
			
		||||
    define('MSBOT_STATE_URL', 'https://state.botframework.com/v3');
 | 
			
		||||
 | 
			
		||||
    define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=');
 | 
			
		||||
 | 
			
		||||
    define('COUNT_FREE_DESIGNS', 4);
 | 
			
		||||
@ -790,6 +797,25 @@ if (!defined('CONTACT_EMAIL')) {
 | 
			
		||||
    define('WEPAY_APP_FEE_MULTIPLIER', env('WEPAY_APP_FEE_MULTIPLIER', 0.002));
 | 
			
		||||
    define('WEPAY_APP_FEE_FIXED', env('WEPAY_APP_FEE_MULTIPLIER', 0.00));
 | 
			
		||||
 | 
			
		||||
    define('SKYPE_CARD_RECEIPT', 'message/card.receipt');
 | 
			
		||||
    define('SKYPE_CARD_CAROUSEL', 'message/card.carousel');
 | 
			
		||||
    define('SKYPE_CARD_HERO', '');
 | 
			
		||||
 | 
			
		||||
    define('BOT_STATE_GET_EMAIL', 'get_email');
 | 
			
		||||
    define('BOT_STATE_GET_CODE', 'get_code');
 | 
			
		||||
    define('BOT_STATE_READY', 'ready');
 | 
			
		||||
    define('SIMILAR_MIN_THRESHOLD', 50);
 | 
			
		||||
 | 
			
		||||
    // https://docs.botframework.com/en-us/csharp/builder/sdkreference/attachments.html
 | 
			
		||||
    define('SKYPE_BUTTON_OPEN_URL', 'openUrl');
 | 
			
		||||
    define('SKYPE_BUTTON_IM_BACK', 'imBack');
 | 
			
		||||
    define('SKYPE_BUTTON_POST_BACK', 'postBack');
 | 
			
		||||
    define('SKYPE_BUTTON_CALL', 'call'); // "tel:123123123123"
 | 
			
		||||
    define('SKYPE_BUTTON_PLAY_AUDIO', 'playAudio');
 | 
			
		||||
    define('SKYPE_BUTTON_PLAY_VIDEO', 'playVideo');
 | 
			
		||||
    define('SKYPE_BUTTON_SHOW_IMAGE', 'showImage');
 | 
			
		||||
    define('SKYPE_BUTTON_DOWNLOAD_FILE', 'downloadFile');
 | 
			
		||||
 | 
			
		||||
    $creditCards = [
 | 
			
		||||
                1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
 | 
			
		||||
                2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								app/Libraries/CurlUtils.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,36 @@
 | 
			
		||||
<?php namespace App\Libraries;
 | 
			
		||||
 | 
			
		||||
class CurlUtils
 | 
			
		||||
{
 | 
			
		||||
    public static function post($url, $data, $headers = false)
 | 
			
		||||
    {
 | 
			
		||||
        return self::exec('POST', $url, $data, $headers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function get($url, $headers = false)
 | 
			
		||||
    {
 | 
			
		||||
        return self::exec('GET', $url, null, $headers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function exec($method, $url, $data, $headers = false)
 | 
			
		||||
    {
 | 
			
		||||
        $curl = curl_init();
 | 
			
		||||
 | 
			
		||||
        $opts = [
 | 
			
		||||
            CURLOPT_URL => $url,
 | 
			
		||||
            CURLOPT_RETURNTRANSFER => true,
 | 
			
		||||
            CURLOPT_POST => $method,
 | 
			
		||||
            CURLOPT_HTTPHEADER => $headers ?: [],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if ($data) {
 | 
			
		||||
            $opts[CURLOPT_POSTFIELDS] = $data;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        curl_setopt_array($curl, $opts);
 | 
			
		||||
        $response = curl_exec($curl);
 | 
			
		||||
        curl_close($curl);
 | 
			
		||||
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								app/Libraries/Skype/ButtonCard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,12 @@
 | 
			
		||||
<?php namespace App\Libraries\Skype;
 | 
			
		||||
 | 
			
		||||
class ButtonCard
 | 
			
		||||
{
 | 
			
		||||
    public function __construct($type, $title, $value, $url = false)
 | 
			
		||||
    {
 | 
			
		||||
        $this->type = $type;
 | 
			
		||||
        $this->title = $title;
 | 
			
		||||
        $this->value = $value;
 | 
			
		||||
        $this->image = $url;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								app/Libraries/Skype/CarouselCard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
<?php namespace App\Libraries\Skype;
 | 
			
		||||
 | 
			
		||||
class CarouselCard
 | 
			
		||||
{
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->contentType = 'application/vnd.microsoft.card.carousel';
 | 
			
		||||
        $this->attachments = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addAttachment($attachment)
 | 
			
		||||
    {
 | 
			
		||||
        $this->attachments[] = $attachment;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								app/Libraries/Skype/HeroCard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,33 @@
 | 
			
		||||
<?php namespace App\Libraries\Skype;
 | 
			
		||||
 | 
			
		||||
use stdClass;
 | 
			
		||||
 | 
			
		||||
class HeroCard
 | 
			
		||||
{
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->contentType = 'application/vnd.microsoft.card.hero';
 | 
			
		||||
        $this->content = new stdClass;
 | 
			
		||||
        $this->content->buttons = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTitle($title)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->title = $title;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setSubitle($subtitle)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->subtitle = $subtitle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setText($text)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->text = $text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addButton($type, $title, $value, $url = false)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->buttons[] = new ButtonCard($type, $title, $value, $url);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								app/Libraries/Skype/InvoiceCard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,79 @@
 | 
			
		||||
<?php namespace App\Libraries\Skype;
 | 
			
		||||
 | 
			
		||||
use HTML;
 | 
			
		||||
use stdClass;
 | 
			
		||||
 | 
			
		||||
class InvoiceCard
 | 
			
		||||
{
 | 
			
		||||
    public function __construct($invoice)
 | 
			
		||||
    {
 | 
			
		||||
        $this->contentType = 'application/vnd.microsoft.card.receipt';
 | 
			
		||||
        $this->content = new stdClass;
 | 
			
		||||
        $this->content->facts = [];
 | 
			
		||||
        $this->content->items = [];
 | 
			
		||||
        $this->content->buttons = [];
 | 
			
		||||
 | 
			
		||||
        $this->setTitle('test');
 | 
			
		||||
 | 
			
		||||
        $this->setTitle(trans('texts.invoice_for_client', [
 | 
			
		||||
            'invoice' => link_to($invoice->getRoute(), $invoice->invoice_number),
 | 
			
		||||
            'client' => link_to($invoice->client->getRoute(), $invoice->client->getDisplayName())
 | 
			
		||||
        ]));
 | 
			
		||||
 | 
			
		||||
        $this->addFact(trans('texts.email'), HTML::mailto($invoice->client->contacts[0]->email)->toHtml());
 | 
			
		||||
 | 
			
		||||
        if ($invoice->due_date) {
 | 
			
		||||
            $this->addFact($invoice->present()->dueDateLabel, $invoice->present()->due_date);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->po_number) {
 | 
			
		||||
            $this->addFact(trans('texts.po_number'), $invoice->po_number);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->discount) {
 | 
			
		||||
            $this->addFact(trans('texts.discount'), $invoice->present()->discount);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($invoice->invoice_items as $item) {
 | 
			
		||||
            $this->addItem($item, $invoice->account);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->setTotal($invoice->present()->requestedAmount);
 | 
			
		||||
 | 
			
		||||
        if (floatval($invoice->amount)) {
 | 
			
		||||
            $this->addButton(SKYPE_BUTTON_OPEN_URL, trans('texts.download_pdf'), $invoice->getInvitationLink('view', true));
 | 
			
		||||
            $this->addButton(SKYPE_BUTTON_IM_BACK, trans('texts.email_invoice'), trans('texts.email_invoice'));
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->addButton(SKYPE_BUTTON_IM_BACK, trans('texts.list_products'), trans('texts.list_products'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTitle($title)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->title = $title;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTotal($value)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->total = $value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addFact($key, $value)
 | 
			
		||||
    {
 | 
			
		||||
        $fact = new stdClass;
 | 
			
		||||
        $fact->key = $key;
 | 
			
		||||
        $fact->value = $value;
 | 
			
		||||
 | 
			
		||||
        $this->content->facts[] = $fact;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addItem($item, $account)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->items[] = new InvoiceItemCard($item, $account);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addButton($type, $title, $value, $url = false)
 | 
			
		||||
    {
 | 
			
		||||
        $this->content->buttons[] = new ButtonCard($type, $title, $value, $url);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								app/Libraries/Skype/InvoiceItemCard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,12 @@
 | 
			
		||||
<?php namespace App\Libraries\Skype;
 | 
			
		||||
 | 
			
		||||
class InvoiceItemCard
 | 
			
		||||
{
 | 
			
		||||
    public function __construct($invoiceItem, $account)
 | 
			
		||||
    {
 | 
			
		||||
        $this->title = intval($invoiceItem->qty) . ' ' . $invoiceItem->product_key;
 | 
			
		||||
        $this->subtitle = $invoiceItem->notes;
 | 
			
		||||
        $this->quantity = $invoiceItem->qty;
 | 
			
		||||
        $this->price = $account->formatMoney($invoiceItem->cost);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								app/Libraries/Skype/SkypeResponse.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,28 @@
 | 
			
		||||
<?php namespace App\Libraries\Skype;
 | 
			
		||||
 | 
			
		||||
class SkypeResponse
 | 
			
		||||
{
 | 
			
		||||
    public function __construct($type)
 | 
			
		||||
    {
 | 
			
		||||
        $this->type = $type;
 | 
			
		||||
        $this->attachments = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function message($message)
 | 
			
		||||
    {
 | 
			
		||||
        $instance = new self('message/text');
 | 
			
		||||
        $instance->setText($message);
 | 
			
		||||
 | 
			
		||||
        return json_encode($instance);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setText($text)
 | 
			
		||||
    {
 | 
			
		||||
        $this->text = $text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addAttachment($attachment)
 | 
			
		||||
    {
 | 
			
		||||
        $this->attachments[] = $attachment;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
use Auth;
 | 
			
		||||
use Eloquent;
 | 
			
		||||
use Utils;
 | 
			
		||||
use Validator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class EntityModel
 | 
			
		||||
@ -86,6 +87,11 @@ class EntityModel extends Eloquent
 | 
			
		||||
        return '[' . $this->getEntityType().':'.$this->public_id.':'.$this->getDisplayName() . ']';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function entityKey()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->public_id . ':' . $this->getEntityType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public function getEntityType()
 | 
			
		||||
    {
 | 
			
		||||
@ -190,4 +196,37 @@ class EntityModel extends Eloquent
 | 
			
		||||
        $name = $parts[count($parts)-1];
 | 
			
		||||
        return strtolower($name) . '_id';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $data
 | 
			
		||||
     * @param $entityType
 | 
			
		||||
     * @return bool|string
 | 
			
		||||
     */
 | 
			
		||||
    public static function validate($data, $entityType, $entity = false)
 | 
			
		||||
    {
 | 
			
		||||
        // Use the API request if it exists
 | 
			
		||||
        $action = $entity ? 'update' : 'create';
 | 
			
		||||
        $requestClass = sprintf('App\\Http\\Requests\\%s%sAPIRequest', ucwords($action), ucwords($entityType));
 | 
			
		||||
        if ( ! class_exists($requestClass)) {
 | 
			
		||||
            $requestClass = sprintf('App\\Http\\Requests\\%s%sRequest', ucwords($action), ucwords($entityType));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $request = new $requestClass();
 | 
			
		||||
        $request->setUserResolver(function() { return Auth::user(); });
 | 
			
		||||
        $request->setEntity($entity);
 | 
			
		||||
        $request->replace($data);
 | 
			
		||||
 | 
			
		||||
        if ( ! $request->authorize()) {
 | 
			
		||||
            return trans('texts.not_allowed');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $validator = Validator::make($data, $request->rules());
 | 
			
		||||
 | 
			
		||||
        if ($validator->fails()) {
 | 
			
		||||
            return $validator->messages()->first();
 | 
			
		||||
        } else {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -516,6 +516,15 @@ class Invoice extends EntityModel implements BalanceAffecting
 | 
			
		||||
        return self::calcLink($this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getInvitationLink($type = 'view', $forceOnsite = false)
 | 
			
		||||
    {
 | 
			
		||||
        if ( ! $this->relationLoaded('invitations')) {
 | 
			
		||||
            $this->load('invitations');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->invitations[0]->getLink($type, $forceOnsite);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,14 @@ use Illuminate\Database\Eloquent\SoftDeletes;
 | 
			
		||||
 */
 | 
			
		||||
class InvoiceItem extends EntityModel
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function getEntityType()
 | 
			
		||||
    {
 | 
			
		||||
        return ENTITY_INVOICE_ITEM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    use SoftDeletes;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?php namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Laracasts\Presenter\PresentableTrait;
 | 
			
		||||
use Illuminate\Database\Eloquent\SoftDeletes;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -7,12 +8,18 @@ use Illuminate\Database\Eloquent\SoftDeletes;
 | 
			
		||||
 */
 | 
			
		||||
class Product extends EntityModel
 | 
			
		||||
{
 | 
			
		||||
    use PresentableTrait;
 | 
			
		||||
    use SoftDeletes;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    protected $dates = ['deleted_at'];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $presenter = 'App\Ninja\Presenters\ProductPresenter';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								app/Models/SecurityCode.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
<?php namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Eloquent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class DatetimeFormat
 | 
			
		||||
 */
 | 
			
		||||
class SecurityCode extends Eloquent
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var bool
 | 
			
		||||
     */
 | 
			
		||||
    public $timestamps = false;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										227
									
								
								app/Ninja/Intents/BaseIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,227 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use stdClass;
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Libraries\CurlUtils;
 | 
			
		||||
use App\Libraries\Skype\SkypeResponse;
 | 
			
		||||
 | 
			
		||||
class BaseIntent
 | 
			
		||||
{
 | 
			
		||||
    protected $state;
 | 
			
		||||
    protected $parameters;
 | 
			
		||||
    protected $fieldMap = [];
 | 
			
		||||
 | 
			
		||||
    public function __construct($state, $data)
 | 
			
		||||
    {
 | 
			
		||||
        //if (true) {
 | 
			
		||||
        if ( ! $state || is_string($state)) {
 | 
			
		||||
            $state = new stdClass;
 | 
			
		||||
            foreach (['current', 'previous'] as $reference) {
 | 
			
		||||
                $state->$reference = new stdClass;
 | 
			
		||||
                $state->$reference->entityType = false;
 | 
			
		||||
                foreach ([ENTITY_INVOICE, ENTITY_CLIENT, ENTITY_INVOICE_ITEM] as $entityType) {
 | 
			
		||||
                    $state->$reference->$entityType = [];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->state = $state;
 | 
			
		||||
        $this->data = $data;
 | 
			
		||||
 | 
			
		||||
        //var_dump($state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function createIntent($state, $data)
 | 
			
		||||
    {
 | 
			
		||||
        if ( ! count($data->intents)) {
 | 
			
		||||
            throw new Exception(trans('texts.intent_not_found'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $intent = $data->intents[0]->intent;
 | 
			
		||||
        $entityType = false;
 | 
			
		||||
 | 
			
		||||
        foreach ($data->entities as $entity) {
 | 
			
		||||
            if ($entity->type === 'EntityType') {
 | 
			
		||||
                $entityType = $entity->entity;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( ! $entityType) {
 | 
			
		||||
            $entityType = $state->current->entityType;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $entityType = ucwords(strtolower($entityType));
 | 
			
		||||
        $intent = str_replace('Entity', $entityType, $intent);
 | 
			
		||||
        $className = "App\\Ninja\\Intents\\{$intent}Intent";
 | 
			
		||||
 | 
			
		||||
        //echo "Intent: $intent<p>";
 | 
			
		||||
 | 
			
		||||
        if ( ! class_exists($className)) {
 | 
			
		||||
            throw new Exception(trans('texts.intent_not_supported'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return (new $className($state, $data));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function process()
 | 
			
		||||
    {
 | 
			
		||||
        // do nothing by default
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setStateEntities($entityType, $entities)
 | 
			
		||||
    {
 | 
			
		||||
        if ( ! is_array($entities)) {
 | 
			
		||||
            $entities = [$entities];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $state = $this->state;
 | 
			
		||||
 | 
			
		||||
        $state->previous->$entityType = $state->current->$entityType;
 | 
			
		||||
        $state->current->$entityType = $entities;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setStateEntityType($entityType)
 | 
			
		||||
    {
 | 
			
		||||
        $state = $this->state;
 | 
			
		||||
 | 
			
		||||
        if ($state->current->entityType == $entityType) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $state->previous->entityType = $state->current->entityType;
 | 
			
		||||
        $state->current->entityType = $entityType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function stateEntities($entityType)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->state->current->$entityType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function stateEntity($entityType)
 | 
			
		||||
    {
 | 
			
		||||
        $entities = $this->state->current->$entityType;
 | 
			
		||||
 | 
			
		||||
        return count($entities) ? $entities[0] : false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function previousStateEntities($entityType)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->state->previous->$entityType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function stateEntityType()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->state->current->entityType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function getState()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function requestClient()
 | 
			
		||||
    {
 | 
			
		||||
        $clientRepo = app('App\Ninja\Repositories\ClientRepository');
 | 
			
		||||
        $client = false;
 | 
			
		||||
 | 
			
		||||
        foreach ($this->data->entities as $param) {
 | 
			
		||||
            if ($param->type == 'Name') {
 | 
			
		||||
                $client = $clientRepo->findPhonetically($param->entity);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function requestFields()
 | 
			
		||||
    {
 | 
			
		||||
        $data = [];
 | 
			
		||||
 | 
			
		||||
        if ( ! isset($this->data->compositeEntities)) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($this->data->compositeEntities as $compositeEntity) {
 | 
			
		||||
            if ($compositeEntity->parentType != 'FieldValuePair') {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $field = false;
 | 
			
		||||
            $value = false;
 | 
			
		||||
 | 
			
		||||
            foreach ($compositeEntity->children as $child) {
 | 
			
		||||
                if ($child->type == 'Field') {
 | 
			
		||||
                    $field = $child->value;;
 | 
			
		||||
                } elseif ($child->type == 'Value') {
 | 
			
		||||
                    $value = $child->value;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($field && $value) {
 | 
			
		||||
                $field = $this->processField($field);
 | 
			
		||||
                $value = $this->processValue($value);
 | 
			
		||||
 | 
			
		||||
                $data[$field] = $value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($this->fieldMap as $key => $value) {
 | 
			
		||||
            if (isset($data[$key])) {
 | 
			
		||||
                $data[$value] = $data[$key];
 | 
			
		||||
                unset($data[$key]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function processField($field)
 | 
			
		||||
    {
 | 
			
		||||
        $field = str_replace(' ', '_', $field);
 | 
			
		||||
 | 
			
		||||
        if (strpos($field, 'date') !== false) {
 | 
			
		||||
            $field .= '_sql';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $field;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function processValue($value)
 | 
			
		||||
    {
 | 
			
		||||
        // look for LUIS pre-built entity matches
 | 
			
		||||
        foreach ($this->data->entities as $entity) {
 | 
			
		||||
            if ($entity->entity === $value) {
 | 
			
		||||
                if ($entity->type == 'builtin.datetime.date') {
 | 
			
		||||
                    $value = $entity->resolution->date;
 | 
			
		||||
                    $value = str_replace('XXXX', date('Y'), $value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createResponse($type, $content)
 | 
			
		||||
    {
 | 
			
		||||
        $response = new SkypeResponse($type);
 | 
			
		||||
 | 
			
		||||
        if (is_string($content)) {
 | 
			
		||||
            $response->setText($content);
 | 
			
		||||
        } else {
 | 
			
		||||
            if ($content instanceof \Illuminate\Database\Eloquent\Collection) {
 | 
			
		||||
                // do nothing
 | 
			
		||||
            } elseif ( ! is_array($content)) {
 | 
			
		||||
                $content = [$content];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            foreach ($content as $item) {
 | 
			
		||||
                $response->addAttachment($item);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return json_encode($response);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								app/Ninja/Intents/CreateInvoiceIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,44 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Models\EntityModel;
 | 
			
		||||
 | 
			
		||||
class CreateInvoiceIntent extends InvoiceIntent
 | 
			
		||||
{
 | 
			
		||||
    public function process()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->requestClient();
 | 
			
		||||
        $invoiceItems = $this->requestInvoiceItems();
 | 
			
		||||
 | 
			
		||||
        if ( ! $client) {
 | 
			
		||||
            throw new Exception(trans('texts.client_not_found'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data = array_merge($this->requestFields(), [
 | 
			
		||||
            'client_id' => $client->id,
 | 
			
		||||
            'invoice_items' => $invoiceItems,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        //var_dump($data);
 | 
			
		||||
 | 
			
		||||
        $valid = EntityModel::validate($data, ENTITY_INVOICE);
 | 
			
		||||
 | 
			
		||||
        if ($valid !== true) {
 | 
			
		||||
            throw new Exception($valid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $invoiceService = app('App\Services\InvoiceService');
 | 
			
		||||
        $invoice = $invoiceService->save($data);
 | 
			
		||||
 | 
			
		||||
        $invoiceItemIds = array_map(function($item) {
 | 
			
		||||
            return $item['public_id'];
 | 
			
		||||
        }, $invoice->invoice_items->toArray());
 | 
			
		||||
 | 
			
		||||
        $this->setStateEntityType(ENTITY_INVOICE);
 | 
			
		||||
        $this->setStateEntities(ENTITY_CLIENT, $client->public_id);
 | 
			
		||||
        $this->setStateEntities(ENTITY_INVOICE, $invoice->public_id);
 | 
			
		||||
        $this->setStateEntities(ENTITY_INVOICE_ITEM, $invoiceItemIds);
 | 
			
		||||
 | 
			
		||||
        return $this->createResponse(SKYPE_CARD_RECEIPT, $invoice->present()->skypeBot);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								app/Ninja/Intents/DownloadInvoiceIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,19 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Auth;
 | 
			
		||||
use App\Models\EntityModel;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use App\Libraries\Skype\SkypeResponse;
 | 
			
		||||
 | 
			
		||||
class DownloadInvoiceIntent extends InvoiceIntent
 | 
			
		||||
{
 | 
			
		||||
    public function process()
 | 
			
		||||
    {
 | 
			
		||||
        $invoice = $this->invoice();
 | 
			
		||||
 | 
			
		||||
        $message = trans('texts.' . $invoice->getEntityType()) . ' ' . $invoice->invoice_number;
 | 
			
		||||
        $message = link_to('/download/' . $invoice->invitations[0]->invitation_key, $message);
 | 
			
		||||
 | 
			
		||||
        return SkypeResponse::message($message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								app/Ninja/Intents/EmailInvoiceIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,32 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Auth;
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Models\EntityModel;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use App\Libraries\Skype\SkypeResponse;
 | 
			
		||||
 | 
			
		||||
class EmailInvoiceIntent extends InvoiceIntent
 | 
			
		||||
{
 | 
			
		||||
    public function process()
 | 
			
		||||
    {
 | 
			
		||||
        $invoice = $this->stateInvoice();
 | 
			
		||||
 | 
			
		||||
        if ( ! Auth::user()->can('edit', $invoice)) {
 | 
			
		||||
            throw new Exception(trans('texts.not_allowed'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $contactMailer = app('App\Ninja\Mailers\ContactMailer');
 | 
			
		||||
        $contactMailer->sendInvoice($invoice);
 | 
			
		||||
 | 
			
		||||
        $message = trans('texts.bot_emailed_' . $invoice->getEntityType());
 | 
			
		||||
 | 
			
		||||
        if (Auth::user()->notify_viewed) {
 | 
			
		||||
            $message .= '<br/>' . trans('texts.bot_emailed_notify_viewed');
 | 
			
		||||
        } elseif (Auth::user()->notify_paid) {
 | 
			
		||||
            $message .= '<br/>' . trans('texts.bot_emailed_notify_paid');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return SkypeResponse::message($message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								app/Ninja/Intents/InvoiceIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,93 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Auth;
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
 | 
			
		||||
class InvoiceIntent extends BaseIntent
 | 
			
		||||
{
 | 
			
		||||
    protected $fieldMap = [
 | 
			
		||||
        'deposit' => 'partial',
 | 
			
		||||
        'due' => 'due_date',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function __construct($state, $data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->invoiceRepo = app('App\Ninja\Repositories\InvoiceRepository');
 | 
			
		||||
 | 
			
		||||
        parent::__construct($state, $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function stateInvoice()
 | 
			
		||||
    {
 | 
			
		||||
        $invoiceId = $this->stateEntity(ENTITY_INVOICE);
 | 
			
		||||
 | 
			
		||||
        if ( ! $invoiceId) {
 | 
			
		||||
            throw new Exception(trans('texts.intent_not_supported'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $invoice = Invoice::scope($invoiceId)->first();
 | 
			
		||||
 | 
			
		||||
        if ( ! $invoice) {
 | 
			
		||||
            throw new Exception(trans('texts.intent_not_supported'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( ! Auth::user()->can('view', $invoice)) {
 | 
			
		||||
            throw new Exception(trans('texts.not_allowed'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $invoice;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function requestInvoiceItems()
 | 
			
		||||
    {
 | 
			
		||||
        $productRepo = app('App\Ninja\Repositories\ProductRepository');
 | 
			
		||||
 | 
			
		||||
        $invoiceItems = [];
 | 
			
		||||
 | 
			
		||||
        if ( ! isset($this->data->compositeEntities) || ! count($this->data->compositeEntities)) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($this->data->compositeEntities as $entity) {
 | 
			
		||||
            if ($entity->parentType == 'InvoiceItem') {
 | 
			
		||||
                $product = false;
 | 
			
		||||
                $qty = 1;
 | 
			
		||||
                foreach ($entity->children as $child) {
 | 
			
		||||
                    if ($child->type == 'Product') {
 | 
			
		||||
                        $product = $productRepo->findPhonetically($child->value);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $qty = $child->value;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ($product) {
 | 
			
		||||
                    $item['qty'] = $qty;
 | 
			
		||||
                    $item['product_key'] = $product->product_key;
 | 
			
		||||
                    $item['cost'] = $product->cost;
 | 
			
		||||
                    $item['notes'] = $product->notes;
 | 
			
		||||
 | 
			
		||||
                    if ($taxRate = $product->default_tax_rate) {
 | 
			
		||||
                        $item['tax_name1'] = $taxRate->name;
 | 
			
		||||
                        $item['tax_rate1'] = $taxRate->rate;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    $invoiceItems[] = $item;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        if ( ! count($invoiceItems)) {
 | 
			
		||||
            foreach ($this->data->entities as $param) {
 | 
			
		||||
                if ($param->type == 'Product') {
 | 
			
		||||
                    $product = $productRepo->findPhonetically($param->entity);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        return $invoiceItems;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								app/Ninja/Intents/ListProductsIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,27 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Auth;
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Models\Product;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListProductsIntent extends ProductIntent
 | 
			
		||||
{
 | 
			
		||||
    public function process()
 | 
			
		||||
    {
 | 
			
		||||
        $account = Auth::user()->account;
 | 
			
		||||
        $products = Product::scope()
 | 
			
		||||
            ->orderBy('product_key')
 | 
			
		||||
            ->limit(10)
 | 
			
		||||
            ->get()
 | 
			
		||||
            ->transform(function($item, $key) use ($account) {
 | 
			
		||||
                $card = $item->present()->skypeBot($account);
 | 
			
		||||
                if ($this->stateEntity(ENTITY_INVOICE)) {
 | 
			
		||||
                    $card->addButton('imBack', trans('texts.add_to_invoice'), trans('texts.add_product_to_invoice', ['product' => $item->product_key]));
 | 
			
		||||
                }
 | 
			
		||||
                return $card;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        return $this->createResponse(SKYPE_CARD_CAROUSEL, $products);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								app/Ninja/Intents/ProductIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Auth;
 | 
			
		||||
use Exception;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProductIntent extends BaseIntent
 | 
			
		||||
{
 | 
			
		||||
    public function __construct($state, $data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->productRepo = app('App\Ninja\Repositories\ProductRepository');
 | 
			
		||||
 | 
			
		||||
        parent::__construct($state, $data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								app/Ninja/Intents/UpdateInvoiceIntent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,54 @@
 | 
			
		||||
<?php namespace App\Ninja\Intents;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
use App\Models\EntityModel;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
 | 
			
		||||
class UpdateInvoiceIntent extends InvoiceIntent
 | 
			
		||||
{
 | 
			
		||||
    public function process()
 | 
			
		||||
    {
 | 
			
		||||
        $invoice = $this->stateInvoice();
 | 
			
		||||
        $invoiceItems = $this->requestInvoiceItems();
 | 
			
		||||
 | 
			
		||||
        $data = array_merge($this->requestFields(), [
 | 
			
		||||
            'public_id' => $invoice->public_id,
 | 
			
		||||
            'invoice_items' => array_merge($invoice->invoice_items->toArray(), $invoiceItems),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // map the cost and qty fields to the invoice items
 | 
			
		||||
        if (isset($data['cost']) || isset($data['quantity'])) {
 | 
			
		||||
            foreach ($data['invoice_items'] as $key => $item) {
 | 
			
		||||
                // if it's new or we recently created it
 | 
			
		||||
                if (empty($item['public_id']) || in_array($item['public_id'], $this->entities(ENTITY_INVOICE_ITEM))) {
 | 
			
		||||
                    $data['invoice_items'][$key]['cost'] = isset($data['cost']) ? $data['cost'] : $item['cost'];
 | 
			
		||||
                    $data['invoice_items'][$key]['qty'] = isset($data['quantity']) ? $data['quantity'] : $item['qty'];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //var_dump($data);
 | 
			
		||||
 | 
			
		||||
        $valid = EntityModel::validate($data, ENTITY_INVOICE, $invoice);
 | 
			
		||||
 | 
			
		||||
        if ($valid !== true) {
 | 
			
		||||
            throw new Exception($valid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $invoice = $this->invoiceRepo->save($data, $invoice);
 | 
			
		||||
 | 
			
		||||
        $invoiceItems = array_slice($invoice->invoice_items->toArray(), count($invoiceItems) * -1);
 | 
			
		||||
        $invoiceItemIds = array_map(function($item) {
 | 
			
		||||
            return $item['public_id'];
 | 
			
		||||
        }, $invoiceItems);
 | 
			
		||||
 | 
			
		||||
        $this->setStateEntities(ENTITY_INVOICE_ITEM, $invoiceItemIds);
 | 
			
		||||
 | 
			
		||||
        $response = $invoice
 | 
			
		||||
            ->load('invoice_items')
 | 
			
		||||
            ->present()
 | 
			
		||||
            ->skypeBot;
 | 
			
		||||
 | 
			
		||||
        return $this->createResponse(SKYPE_CARD_RECEIPT, $response);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -109,4 +109,20 @@ class UserMailer extends Mailer
 | 
			
		||||
 | 
			
		||||
        $this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function sendSecurityCode($user, $code)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$user->email) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $subject = trans('texts.security_code_email_subject');
 | 
			
		||||
        $view = 'security_code';
 | 
			
		||||
        $data = [
 | 
			
		||||
            'userName' => $user->getDisplayName(),
 | 
			
		||||
            'code' => $code,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -44,6 +44,11 @@ class BasePaymentDriver
 | 
			
		||||
        return $this->accountGateway->gateway_id == $gatewayId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isValid()
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // optionally pass a paymentMethod to determine the type from the token
 | 
			
		||||
    protected function isGatewayType($gatewayType, $paymentMethod = false)
 | 
			
		||||
    {
 | 
			
		||||
@ -535,6 +540,15 @@ class BasePaymentDriver
 | 
			
		||||
        $paymentMethod->setRelation('account_gateway_token', $customer);
 | 
			
		||||
        $paymentMethod = $this->creatingPaymentMethod($paymentMethod);
 | 
			
		||||
 | 
			
		||||
        // archive the old payment method
 | 
			
		||||
        $oldPaymentMethod = PaymentMethod::clientId($this->client()->id)
 | 
			
		||||
            ->wherePaymentTypeId($paymentMethod->payment_type_id)
 | 
			
		||||
            ->first();
 | 
			
		||||
 | 
			
		||||
        if ($oldPaymentMethod) {
 | 
			
		||||
            $oldPaymentMethod->delete();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($paymentMethod) {
 | 
			
		||||
            $paymentMethod->save();
 | 
			
		||||
        }
 | 
			
		||||
@ -753,7 +767,7 @@ class BasePaymentDriver
 | 
			
		||||
            } elseif ($paymentMethod->payment_type_id == PAYMENT_TYPE_PAYPAL) {
 | 
			
		||||
                $label = 'PayPal: ' . $paymentMethod->email;
 | 
			
		||||
            } else {
 | 
			
		||||
                $label = trans('texts.use_card_on_file');
 | 
			
		||||
                $label = trans('texts.payment_type_on_file', ['type' => $paymentMethod->payment_type->name]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $links[] = [
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
<?php namespace App\Ninja\PaymentDrivers;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
 | 
			
		||||
class MolliePaymentDriver extends BasePaymentDriver
 | 
			
		||||
{
 | 
			
		||||
    public function completeOffsitePurchase($input)
 | 
			
		||||
@ -10,6 +12,12 @@ class MolliePaymentDriver extends BasePaymentDriver
 | 
			
		||||
 | 
			
		||||
        $response = $this->gateway()->fetchTransaction($details)->send();
 | 
			
		||||
 | 
			
		||||
        if ($response->isCancelled()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        } elseif ( ! $response->isSuccessful()) {
 | 
			
		||||
            throw new Exception($response->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->createPayment($response->getTransactionReference());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,21 @@ class StripePaymentDriver extends BasePaymentDriver
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isValid()
 | 
			
		||||
    {
 | 
			
		||||
        $result = $this->makeStripeCall(
 | 
			
		||||
            'GET',
 | 
			
		||||
            'charges',
 | 
			
		||||
            'limit=1'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (array_get($result, 'object') == 'list') {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return $result;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function checkCustomerExists($customer)
 | 
			
		||||
    {
 | 
			
		||||
        $response = $this->gateway()
 | 
			
		||||
@ -347,15 +362,15 @@ class StripePaymentDriver extends BasePaymentDriver
 | 
			
		||||
        $eventDetails = $this->makeStripeCall('GET', 'events/'.$eventId);
 | 
			
		||||
 | 
			
		||||
        if (is_string($eventDetails) || !$eventDetails) {
 | 
			
		||||
            throw new Exception('Could not get event details');
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($eventType != $eventDetails['type']) {
 | 
			
		||||
            throw new Exception('Event type mismatch');
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!$eventDetails['pending_webhooks']) {
 | 
			
		||||
            throw new Exception('This is not a pending event');
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($eventType == 'charge.failed' || $eventType == 'charge.succeeded' || $eventType == 'charge.refunded') {
 | 
			
		||||
@ -365,7 +380,7 @@ class StripePaymentDriver extends BasePaymentDriver
 | 
			
		||||
            $payment = Payment::scope(false, $accountId)->where('transaction_reference', '=', $transactionRef)->first();
 | 
			
		||||
 | 
			
		||||
            if (!$payment) {
 | 
			
		||||
                throw new Exception('Unknown payment');
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($eventType == 'charge.failed') {
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,22 @@ class ClientPresenter extends EntityPresenter {
 | 
			
		||||
        return $this->entity->country ? $this->entity->country->name : '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function balance()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->entity;
 | 
			
		||||
        $account = $client->account;
 | 
			
		||||
 | 
			
		||||
        return $account->formatMoney($client->balance, $client);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function paid_to_date()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->entity;
 | 
			
		||||
        $account = $client->account;
 | 
			
		||||
 | 
			
		||||
        return $account->formatMoney($client->paid_to_date, $client);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function status()
 | 
			
		||||
    {
 | 
			
		||||
        $class = $text = '';
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ use Laracasts\Presenter\Presenter;
 | 
			
		||||
 | 
			
		||||
class EntityPresenter extends Presenter
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php namespace App\Ninja\Presenters;
 | 
			
		||||
 | 
			
		||||
use Utils;
 | 
			
		||||
use App\Libraries\Skype\InvoiceCard;
 | 
			
		||||
 | 
			
		||||
class InvoicePresenter extends EntityPresenter {
 | 
			
		||||
 | 
			
		||||
@ -14,6 +15,22 @@ class InvoicePresenter extends EntityPresenter {
 | 
			
		||||
        return $this->entity->user->getDisplayName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function amount()
 | 
			
		||||
    {
 | 
			
		||||
        $invoice = $this->entity;
 | 
			
		||||
        $account = $invoice->account;
 | 
			
		||||
 | 
			
		||||
        return $account->formatMoney($invoice->amount, $invoice->client);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function requestedAmount()
 | 
			
		||||
    {
 | 
			
		||||
        $invoice = $this->entity;
 | 
			
		||||
        $account = $invoice->account;
 | 
			
		||||
 | 
			
		||||
        return $account->formatMoney($invoice->getRequestedAmount(), $invoice->client);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function balanceDueLabel()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->entity->partial > 0) {
 | 
			
		||||
@ -25,6 +42,26 @@ class InvoicePresenter extends EntityPresenter {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dueDateLabel()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->entity->isType(INVOICE_TYPE_STANDARD)) {
 | 
			
		||||
            return trans('texts.due_date');
 | 
			
		||||
        } else {
 | 
			
		||||
            return trans('texts.valid_until');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function discount()
 | 
			
		||||
    {
 | 
			
		||||
        $invoice = $this->entity;
 | 
			
		||||
 | 
			
		||||
        if ($invoice->is_amount_discount) {
 | 
			
		||||
            return $invoice->account->formatMoney($invoice->discount);
 | 
			
		||||
        } else {
 | 
			
		||||
            return $invoice->discount . '%';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // https://schema.org/PaymentStatusType
 | 
			
		||||
    public function paymentStatus()
 | 
			
		||||
    {
 | 
			
		||||
@ -99,4 +136,9 @@ class InvoicePresenter extends EntityPresenter {
 | 
			
		||||
 | 
			
		||||
        return trans('texts.auto_bill_notification', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function skypeBot()
 | 
			
		||||
    {
 | 
			
		||||
        return new InvoiceCard($this->entity);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								app/Ninja/Presenters/ProductPresenter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,20 @@
 | 
			
		||||
<?php namespace App\Ninja\Presenters;
 | 
			
		||||
 | 
			
		||||
use App\Libraries\Skype\HeroCard;
 | 
			
		||||
 | 
			
		||||
class ProductPresenter extends EntityPresenter
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function skypeBot($account)
 | 
			
		||||
    {
 | 
			
		||||
        $product = $this->entity;
 | 
			
		||||
 | 
			
		||||
        $card = new HeroCard();
 | 
			
		||||
        $card->setTitle($product->product_key);
 | 
			
		||||
        $card->setSubitle($account->formatMoney($product->cost));
 | 
			
		||||
        $card->setText($product->notes);
 | 
			
		||||
 | 
			
		||||
        return $card;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -132,4 +132,47 @@ class ClientRepository extends BaseRepository
 | 
			
		||||
 | 
			
		||||
        return $client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function findPhonetically($clientName)
 | 
			
		||||
    {
 | 
			
		||||
        $clientNameMeta = metaphone($clientName);
 | 
			
		||||
 | 
			
		||||
        $map = [];
 | 
			
		||||
        $max = SIMILAR_MIN_THRESHOLD;
 | 
			
		||||
        $clientId = 0;
 | 
			
		||||
 | 
			
		||||
        $clients = Client::scope()->get(['id', 'name', 'public_id']);
 | 
			
		||||
 | 
			
		||||
        foreach ($clients as $client) {
 | 
			
		||||
            if ( ! $client->name) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $map[$client->id] = $client;
 | 
			
		||||
            $similar = similar_text($clientNameMeta, metaphone($client->name), $percent);
 | 
			
		||||
 | 
			
		||||
            if ($percent > $max) {
 | 
			
		||||
                $clientId = $client->id;
 | 
			
		||||
                $max = $percent;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $contacts = Contact::scope()->get(['client_id', 'first_name', 'last_name', 'public_id']);
 | 
			
		||||
 | 
			
		||||
        foreach ($contacts as $contact) {
 | 
			
		||||
            if ( ! $contact->getFullName() || ! isset($map[$contact->client_id])) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $similar = similar_text($clientNameMeta, metaphone($contact->getFullName()), $percent);
 | 
			
		||||
 | 
			
		||||
            if ($percent > $max) {
 | 
			
		||||
                $clientId = $contact->client_id;
 | 
			
		||||
                $max = $percent;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ($clientId && isset($map[$clientId])) ? $map[$clientId] : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -262,6 +262,7 @@ class InvoiceRepository extends BaseRepository
 | 
			
		||||
 | 
			
		||||
        if ($invoice) {
 | 
			
		||||
            // do nothing
 | 
			
		||||
            $entityType = $invoice->getEntityType();
 | 
			
		||||
        } elseif ($isNew) {
 | 
			
		||||
            $entityType = ENTITY_INVOICE;
 | 
			
		||||
            if (isset($data['is_recurring']) && filter_var($data['is_recurring'], FILTER_VALIDATE_BOOLEAN)) {
 | 
			
		||||
@ -270,6 +271,7 @@ class InvoiceRepository extends BaseRepository
 | 
			
		||||
                $entityType = ENTITY_QUOTE;
 | 
			
		||||
            }
 | 
			
		||||
            $invoice = $account->createInvoice($entityType, $data['client_id']);
 | 
			
		||||
            $invoice->invoice_date = date_create()->format('Y-m-d');
 | 
			
		||||
            if (isset($data['has_tasks']) && filter_var($data['has_tasks'], FILTER_VALIDATE_BOOLEAN)) {
 | 
			
		||||
                $invoice->has_tasks = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -56,4 +56,34 @@ class ProductRepository extends BaseRepository
 | 
			
		||||
        return $product;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function findPhonetically($productName)
 | 
			
		||||
    {
 | 
			
		||||
        $productNameMeta = metaphone($productName);
 | 
			
		||||
 | 
			
		||||
        $map = [];
 | 
			
		||||
        $max = SIMILAR_MIN_THRESHOLD;
 | 
			
		||||
        $productId = 0;
 | 
			
		||||
 | 
			
		||||
        $products = Product::scope()
 | 
			
		||||
                        ->with('default_tax_rate')
 | 
			
		||||
                        ->get();
 | 
			
		||||
 | 
			
		||||
        foreach ($products as $product) {
 | 
			
		||||
            if ( ! $product->product_key) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $map[$product->id] = $product;
 | 
			
		||||
            $similar = similar_text($productNameMeta, metaphone($product->product_key), $percent);
 | 
			
		||||
 | 
			
		||||
            if ($percent > $max) {
 | 
			
		||||
                $productId = $product->id;
 | 
			
		||||
                $max = $percent;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ($productId && isset($map[$productId])) ? $map[$productId] : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,7 @@ class InvoiceTransformer extends EntityTransformer
 | 
			
		||||
    public function includeInvoiceItems(Invoice $invoice)
 | 
			
		||||
    {
 | 
			
		||||
        $transformer = new InvoiceItemTransformer($this->account, $this->serializer);
 | 
			
		||||
        return $this->includeCollection($invoice->invoice_items, $transformer, ENTITY_INVOICE_ITEMS);
 | 
			
		||||
        return $this->includeCollection($invoice->invoice_items, $transformer, ENTITY_INVOICE_ITEM);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function includeInvitations(Invoice $invoice)
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ class EntityPolicy
 | 
			
		||||
     * @param $ownerUserId
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    public static function viewByOwner(User$user, $ownerUserId) {
 | 
			
		||||
    public static function viewByOwner(User $user, $ownerUserId) {
 | 
			
		||||
        return $user->hasPermission('view_all') || $user->id == $ownerUserId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,11 @@ class ComposerServiceProvider extends ServiceProvider
 | 
			
		||||
            ['accounts.details', 'clients.edit', 'payments.edit', 'invoices.edit', 'accounts.localization'],
 | 
			
		||||
            'App\Http\ViewComposers\TranslationComposer'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        view()->composer(
 | 
			
		||||
             ['header', 'tasks.edit'],
 | 
			
		||||
             'App\Http\ViewComposers\AppLanguageComposer'
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class BaseService
 | 
			
		||||
        $entities = $this->getRepo()->findByPublicIdsWithTrashed($ids);
 | 
			
		||||
 | 
			
		||||
        foreach ($entities as $entity) {
 | 
			
		||||
            if(Auth::user()->can('edit', $entity)){
 | 
			
		||||
            if (Auth::user()->can('edit', $entity)) {
 | 
			
		||||
                $this->getRepo()->$action($entity);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ use Auth;
 | 
			
		||||
use Utils;
 | 
			
		||||
use parsecsv;
 | 
			
		||||
use Session;
 | 
			
		||||
use Validator;
 | 
			
		||||
use League\Fractal\Manager;
 | 
			
		||||
use App\Ninja\Repositories\ContactRepository;
 | 
			
		||||
use App\Ninja\Repositories\ClientRepository;
 | 
			
		||||
@ -141,7 +140,7 @@ class ImportService
 | 
			
		||||
 | 
			
		||||
        foreach ($json['clients'] as $jsonClient) {
 | 
			
		||||
 | 
			
		||||
            if ($this->validate($jsonClient, ENTITY_CLIENT) === true) {
 | 
			
		||||
            if (EntityModel::validate($jsonClient, ENTITY_CLIENT) === true) {
 | 
			
		||||
                $client = $this->clientRepo->save($jsonClient);
 | 
			
		||||
                $this->addSuccess($client);
 | 
			
		||||
            } else {
 | 
			
		||||
@ -151,7 +150,7 @@ class ImportService
 | 
			
		||||
 | 
			
		||||
            foreach ($jsonClient['invoices'] as $jsonInvoice) {
 | 
			
		||||
                $jsonInvoice['client_id'] = $client->id;
 | 
			
		||||
                if ($this->validate($jsonInvoice, ENTITY_INVOICE) === true) {
 | 
			
		||||
                if (EntityModel::validate($jsonInvoice, ENTITY_INVOICE) === true) {
 | 
			
		||||
                    $invoice = $this->invoiceRepo->save($jsonInvoice);
 | 
			
		||||
                    $this->addSuccess($invoice);
 | 
			
		||||
                } else {
 | 
			
		||||
@ -162,7 +161,7 @@ class ImportService
 | 
			
		||||
                foreach ($jsonInvoice['payments'] as $jsonPayment) {
 | 
			
		||||
                    $jsonPayment['client_id'] = $jsonPayment['client'] = $client->id; // TODO: change to client_id once views are updated
 | 
			
		||||
                    $jsonPayment['invoice_id'] = $jsonPayment['invoice'] = $invoice->id; // TODO: change to invoice_id once views are updated
 | 
			
		||||
                    if ($this->validate($jsonPayment, ENTITY_PAYMENT) === true) {
 | 
			
		||||
                    if (EntityModel::validate($jsonPayment, ENTITY_PAYMENT) === true) {
 | 
			
		||||
                        $payment = $this->paymentRepo->save($jsonPayment);
 | 
			
		||||
                        $this->addSuccess($payment);
 | 
			
		||||
                    } else {
 | 
			
		||||
@ -280,7 +279,7 @@ class ImportService
 | 
			
		||||
            $data['invoice_number'] = $account->getNextInvoiceNumber($invoice);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->validate($data, $entityType) !== true) {
 | 
			
		||||
        if (EntityModel::validate($data, $entityType) !== true) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -396,26 +395,6 @@ class ImportService
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $data
 | 
			
		||||
     * @param $entityType
 | 
			
		||||
     * @return bool|string
 | 
			
		||||
     */
 | 
			
		||||
    private function validate($data, $entityType)
 | 
			
		||||
    {
 | 
			
		||||
        $requestClass = 'App\\Http\\Requests\\Create' . ucwords($entityType) . 'Request';
 | 
			
		||||
        $request = new $requestClass();
 | 
			
		||||
        $request->setUserResolver(function() { return Auth::user(); });
 | 
			
		||||
        $request->replace($data);
 | 
			
		||||
 | 
			
		||||
        $validator = Validator::make($data, $request->rules());
 | 
			
		||||
 | 
			
		||||
        if ($validator->fails()) {
 | 
			
		||||
            return $validator->messages()->first();
 | 
			
		||||
        } else {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param array $files
 | 
			
		||||
 | 
			
		||||
@ -57,6 +57,21 @@ class PaymentService extends BaseService
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($credits = $client->credits->sum('balance')) {
 | 
			
		||||
            $balance = $invoice->balance;
 | 
			
		||||
            $amount = min($credits, $balance);
 | 
			
		||||
            $data = [
 | 
			
		||||
                'payment_type_id' => PAYMENT_TYPE_CREDIT,
 | 
			
		||||
                'invoice_id' => $invoice->id,
 | 
			
		||||
                'client_id' => $client->id,
 | 
			
		||||
                'amount' => $amount,
 | 
			
		||||
            ];
 | 
			
		||||
            $payment = $this->paymentRepo->save($data);
 | 
			
		||||
            if ($amount == $balance) {
 | 
			
		||||
                return $payment;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $paymentDriver = $account->paymentDriver($invitation, GATEWAY_TYPE_TOKEN);
 | 
			
		||||
 | 
			
		||||
        if ( ! $paymentDriver) {
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "jquery": "1.11.3",
 | 
			
		||||
    "bootstrap": "3.3.1",
 | 
			
		||||
    "bootstrap-combobox": "1.1.5",
 | 
			
		||||
    "jquery-ui": "1.11.2",
 | 
			
		||||
    "datatables": "1.10.4",
 | 
			
		||||
    "datatables-bootstrap3": "*",
 | 
			
		||||
@ -27,7 +28,8 @@
 | 
			
		||||
    "datetimepicker": "~2.4.5",
 | 
			
		||||
    "stacktrace-js": "~1.0.1",
 | 
			
		||||
    "fuse.js": "~2.0.2",
 | 
			
		||||
    "dropzone": "~4.3.0"
 | 
			
		||||
    "dropzone": "~4.3.0",
 | 
			
		||||
    "sweetalert": "~1.1.3"
 | 
			
		||||
  },
 | 
			
		||||
  "resolutions": {
 | 
			
		||||
    "jquery": "~1.11"
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,10 @@
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "require": {
 | 
			
		||||
        "php": ">=5.5.9",
 | 
			
		||||
        "ext-mcrypt": "*",
 | 
			
		||||
        "ext-gmp": "*",
 | 
			
		||||
        "ext-gd": "*",
 | 
			
		||||
        "turbo124/laravel-push-notification": "dev-laravel5",
 | 
			
		||||
        "omnipay/mollie": "dev-master#22956c1a62a9662afa5f5d119723b413770ac525",
 | 
			
		||||
        "omnipay/2checkout": "dev-master#e9c079c2dde0d7ba461903b3b7bd5caf6dee1248",
 | 
			
		||||
 | 
			
		||||
@ -41,8 +41,7 @@ class AddPageSize extends Migration
 | 
			
		||||
 | 
			
		||||
        Schema::table('expenses', function ($table) {
 | 
			
		||||
            $table->unsignedInteger('expense_category_id')->nullable()->index();
 | 
			
		||||
            
 | 
			
		||||
            $table->foreign('expense_category_id')->references('id')->on('expense_categories')->onDelete('cascade');
 | 
			
		||||
            //$table->foreign('expense_category_id')->references('id')->on('expense_categories')->onDelete('cascade');
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -88,7 +88,7 @@ class PaymentsChanges extends Migration
 | 
			
		||||
            $table->string('email')->nullable();
 | 
			
		||||
 | 
			
		||||
            $table->unsignedInteger('payment_method_id')->nullable();
 | 
			
		||||
            $table->foreign('payment_method_id')->references('id')->on('payment_methods');
 | 
			
		||||
            //$table->foreign('payment_method_id')->references('id')->on('payment_methods');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Schema::table('invoices', function($table)
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,72 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
 | 
			
		||||
class AddSupportForBots extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function up()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('security_codes', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->increments('id');
 | 
			
		||||
            $table->unsignedInteger('account_id')->index();
 | 
			
		||||
            $table->unsignedInteger('user_id')->nullable();
 | 
			
		||||
            $table->unsignedInteger('contact_id')->nullable();
 | 
			
		||||
            $table->smallInteger('attempts');
 | 
			
		||||
            $table->string('code')->nullable();
 | 
			
		||||
            $table->string('bot_user_id')->unique();
 | 
			
		||||
            $table->timestamp('created_at')->useCurrent();
 | 
			
		||||
 | 
			
		||||
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
 | 
			
		||||
            $table->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
 | 
			
		||||
            $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Schema::table('users', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->string('bot_user_id')->nullable();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Schema::table('contacts', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->string('bot_user_id')->nullable();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Schema::table('accounts', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->boolean('include_item_taxes_inline')->default(false);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverse the migrations.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function down()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::dropIfExists('security_codes');
 | 
			
		||||
 | 
			
		||||
        Schema::table('users', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->dropColumn('bot_user_id');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Schema::table('contacts', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->dropColumn('bot_user_id');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Schema::table('accounts', function($table)
 | 
			
		||||
        {
 | 
			
		||||
            $table->dropColumn('include_item_taxes_inline');
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -55,7 +55,7 @@ class DateFormatsSeeder extends Seeder
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        foreach ($formats as $format) {
 | 
			
		||||
            $record = DatetimeFormat::whereFormat($format['format'])->first();
 | 
			
		||||
            $record = DatetimeFormat::whereRaw("BINARY `format`= ?", array($format['format']))->first();
 | 
			
		||||
            if ($record) {
 | 
			
		||||
                $record->format_moment = $format['format_moment'];
 | 
			
		||||
                $record->save();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										100
									
								
								gulpfile.js
									
									
									
									
									
								
							
							
						
						@ -1,16 +1,96 @@
 | 
			
		||||
var elixir = require('laravel-elixir');
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 |--------------------------------------------------------------------------
 | 
			
		||||
 | Elixir Asset Management
 | 
			
		||||
 |--------------------------------------------------------------------------
 | 
			
		||||
 |
 | 
			
		||||
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | 
			
		||||
 | for your Laravel application. By default, we are compiling the Less
 | 
			
		||||
 | file for our application, as well as publishing vendor resources.
 | 
			
		||||
 |
 | 
			
		||||
/**
 | 
			
		||||
 * Set Elixir Source Maps
 | 
			
		||||
 *
 | 
			
		||||
 * @type {boolean}
 | 
			
		||||
 */
 | 
			
		||||
elixir.config.sourcemaps = true;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Configuring assets path.
 | 
			
		||||
 * Explicitly setting it to empty, as we're not using Laravels resources/assets folder
 | 
			
		||||
 *
 | 
			
		||||
 * @type {string}
 | 
			
		||||
 */
 | 
			
		||||
elixir.config.assetsPath = '';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Configuring Javascript assets path.
 | 
			
		||||
 * Explicitly setting it to empty, as we're not using Laravels resources/assets/js folder
 | 
			
		||||
 *
 | 
			
		||||
 * @type {string}
 | 
			
		||||
 */
 | 
			
		||||
elixir.config.js.folder = '';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Configuring CSS assets path.
 | 
			
		||||
 * Explicitly setting it to empty, as we're not using Laravels resources/assets/css folder
 | 
			
		||||
 *
 | 
			
		||||
 * @type {string}
 | 
			
		||||
 */
 | 
			
		||||
elixir.config.css.folder = '';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove all CSS comments
 | 
			
		||||
 *
 | 
			
		||||
 * @type {{discardComments: {removeAll: boolean}}}
 | 
			
		||||
 */
 | 
			
		||||
elixir.config.css.minifier.pluginOptions = {
 | 
			
		||||
    discardComments: {
 | 
			
		||||
        removeAll: true
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directory for bower source files.
 | 
			
		||||
 * If changing this, please also see .bowerrc
 | 
			
		||||
 *
 | 
			
		||||
 * @type {string}
 | 
			
		||||
 */
 | 
			
		||||
var bowerDir = 'public/vendor';
 | 
			
		||||
 | 
			
		||||
elixir(function(mix) {
 | 
			
		||||
    mix.less('app.less');
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS configuration
 | 
			
		||||
     */
 | 
			
		||||
    mix.styles([
 | 
			
		||||
        bowerDir + '/bootstrap/dist/css/bootstrap.css',
 | 
			
		||||
        bowerDir + '/font-awesome/css/font-awesome.css',
 | 
			
		||||
        bowerDir + '/datatables/media/css/jquery.dataTables.css',
 | 
			
		||||
        bowerDir + '/datatables-bootstrap3/BS3/assets/css/datatables.css',
 | 
			
		||||
        'public/css/bootstrap-combobox.css',
 | 
			
		||||
        'public/css/public.style.css'
 | 
			
		||||
    ], 'public/css/built.public.css');
 | 
			
		||||
 | 
			
		||||
    mix.styles([
 | 
			
		||||
        bowerDir + '/bootstrap/dist/css/bootstrap.css',
 | 
			
		||||
        bowerDir + '/bootstrap-datepicker/dist/css/bootstrap-datepicker3.css',
 | 
			
		||||
        bowerDir + '/datatables/media/css/jquery.dataTables.css',
 | 
			
		||||
        bowerDir + '/datatables-bootstrap3/BS3/assets/css/datatables.css',
 | 
			
		||||
        bowerDir + '/font-awesome/css/font-awesome.css',
 | 
			
		||||
        bowerDir + '/dropzone/dist/dropzone.css',
 | 
			
		||||
        bowerDir + '/spectrum/spectrum.css',
 | 
			
		||||
        bowerDir + '/sweetalert/dist/sweetalert.css',
 | 
			
		||||
        'public/css/bootstrap-combobox.css',
 | 
			
		||||
        'public/css/typeahead.js-bootstrap.css',
 | 
			
		||||
        'public/css/style.css'
 | 
			
		||||
    ], 'public/css/built.css');
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * JS configuration
 | 
			
		||||
     */
 | 
			
		||||
    mix.scripts(['resources/assets/js/Chart.js'], 'public/js/Chart.min.js')
 | 
			
		||||
        .scripts(['resources/assets/js/d3.js'], 'public/js/d3.min.js');
 | 
			
		||||
 | 
			
		||||
    mix.scripts([
 | 
			
		||||
        'public/js/pdf_viewer.js',
 | 
			
		||||
        'public/js/compatibility.js',
 | 
			
		||||
        //'public/js/pdfmake.min.js',
 | 
			
		||||
        'public/js/pdfmake.js',
 | 
			
		||||
        'public/js/vfs.js'
 | 
			
		||||
    ], 'public/pdf.built.js');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@
 | 
			
		||||
    "grunt-contrib-uglify": "~0.2.2",
 | 
			
		||||
    "grunt-dump-dir": "^0.1.2",
 | 
			
		||||
    "gulp": "^3.8.8",
 | 
			
		||||
    "laravel-elixir": "*"
 | 
			
		||||
    "laravel-elixir": "^6.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "grunt-dump-dir": "^0.1.2"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								public/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/android-chrome-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 35 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										1
									
								
								public/built.js.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										3
									
								
								public/css/app.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/app.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/bootstrap.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										3235
									
								
								public/css/built.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/built.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										21
									
								
								public/css/built.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/built.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										971
									
								
								public/css/built.public.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/built.public.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										14
									
								
								public/css/built.public.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/built.public.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2
									
								
								public/css/customCss.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,2 @@
 | 
			
		||||
.customContainer{padding:40px 0;margin:0!important;background:-webkit-linear-gradient(#f5f5f5,#fff)}.customFontHead{font-size:20px;text-align:center}.customTextBorder{border-bottom:1px solid #c3c1c1;padding-bottom:5%;margin-bottom:5%}.customSubMenu{margin-left:2%}.customMenuOne{padding-left:5px;padding-right:5px}.shiftLeft{float:left}.customMenuDiv{padding-bottom:30px;float:left;width:100%}
 | 
			
		||||
/*# sourceMappingURL=customCss.min.css.map */
 | 
			
		||||
							
								
								
									
										1
									
								
								public/css/customCss.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
{"version":3,"sources":["customCss.css"],"names":[],"mappings":"AAAA,iBACA,eAAA,AACA,mBAAA,AACA,gDAAA,CACA,AACA,gBACA,eAAA,AACA,iBAAA,CACA,AACA,kBACA,gCAAA,AACA,kBAAA,AACA,gBAAA,CACA,AACA,eACA,cAAA,CACA,AACA,eACA,iBAAA,AACA,iBAAA,CACA,AACA,WACA,UAAA,CACA,AACA,eACA,oBAAA,AACA,WAAA,AACA,UAAA,CACA","file":"customCss.min.css","sourcesContent":[".customContainer{\n\tpadding: 40px 0;\n\tmargin: 0px 0 !important;\n\tbackground: -webkit-linear-gradient(rgb(245, 245, 245), white);\n}\t\n.customFontHead{\n\tfont-size: 20px;\n\ttext-align: center;\n}\t\n.customTextBorder{\n\tborder-bottom: 1px solid rgb(195, 193, 193);\n\tpadding-bottom: 5%;\n\tmargin-bottom: 5%;\n}\n.customSubMenu{\n\tmargin-left: 2%;\n}\n.customMenuOne{\n\tpadding-left: 5px;\n\tpadding-right: 5px;\n}\n.shiftLeft{\n\tfloat: left;\n}\n.customMenuDiv{\n\tpadding-bottom: 30px;\n\tfloat: left;\n\twidth: 100%;\n}"],"sourceRoot":"/source/"}
 | 
			
		||||
							
								
								
									
										893
									
								
								public/css/img/jsoneditor-icons.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,893 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="216"
 | 
			
		||||
   height="144"
 | 
			
		||||
   id="svg4136"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.91 r"
 | 
			
		||||
   sodipodi:docname="jsoneditor-icons.svg">
 | 
			
		||||
  <title
 | 
			
		||||
     id="title6512">JSON Editor Icons</title>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4148">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title>JSON Editor Icons</dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4146" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     objecttolerance="10"
 | 
			
		||||
     gridtolerance="10"
 | 
			
		||||
     guidetolerance="10"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1028"
 | 
			
		||||
     id="namedview4144"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:zoom="4"
 | 
			
		||||
     inkscape:cx="97.217248"
 | 
			
		||||
     inkscape:cy="59.950227"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="0"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     inkscape:current-layer="svg4136"
 | 
			
		||||
     showguides="false"
 | 
			
		||||
     borderlayer="false"
 | 
			
		||||
     inkscape:showpageshadow="true"
 | 
			
		||||
     showborder="true">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid4640"
 | 
			
		||||
       empspacing="24" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 | 
			
		||||
  <g
 | 
			
		||||
     id="g4394">
 | 
			
		||||
    <rect
 | 
			
		||||
       x="4"
 | 
			
		||||
       y="4"
 | 
			
		||||
       width="16"
 | 
			
		||||
       height="16"
 | 
			
		||||
       id="svg_1"
 | 
			
		||||
       style="fill:#1aae1c;fill-opacity:1;stroke:none;stroke-width:0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0"
 | 
			
		||||
       x="28.000006"
 | 
			
		||||
       y="3.999995"
 | 
			
		||||
       width="16"
 | 
			
		||||
       height="16"
 | 
			
		||||
       id="svg_1-7" />
 | 
			
		||||
    <rect
 | 
			
		||||
       id="rect4165"
 | 
			
		||||
       height="16"
 | 
			
		||||
       width="16"
 | 
			
		||||
       y="3.999995"
 | 
			
		||||
       x="52.000004"
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
       x="172.00002"
 | 
			
		||||
       y="3.9999852"
 | 
			
		||||
       width="16"
 | 
			
		||||
       height="16"
 | 
			
		||||
       id="rect4175" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
       x="196"
 | 
			
		||||
       y="3.999995"
 | 
			
		||||
       width="16"
 | 
			
		||||
       height="16"
 | 
			
		||||
       id="rect4175-3" />
 | 
			
		||||
    <g
 | 
			
		||||
       style="stroke:none"
 | 
			
		||||
       id="g4299">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
         id="svg_1-1"
 | 
			
		||||
         height="1.9999986"
 | 
			
		||||
         width="9.9999924"
 | 
			
		||||
         y="10.999998"
 | 
			
		||||
         x="7.0000048" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
         id="svg_1-1-1"
 | 
			
		||||
         height="9.9999838"
 | 
			
		||||
         width="1.9999955"
 | 
			
		||||
         y="7.0000114"
 | 
			
		||||
         x="11.000005" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <g
 | 
			
		||||
       style="stroke:none"
 | 
			
		||||
       transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
 | 
			
		||||
       id="g4299-3">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
         id="svg_1-1-0"
 | 
			
		||||
         height="1.9999986"
 | 
			
		||||
         width="9.9999924"
 | 
			
		||||
         y="10.999998"
 | 
			
		||||
         x="7.0000048" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
         id="svg_1-1-1-9"
 | 
			
		||||
         height="9.9999838"
 | 
			
		||||
         width="1.9999955"
 | 
			
		||||
         y="7.0000114"
 | 
			
		||||
         x="11.000005" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
       x="55.000004"
 | 
			
		||||
       y="7.0000048"
 | 
			
		||||
       width="6.9999909"
 | 
			
		||||
       height="6.9999905"
 | 
			
		||||
       id="svg_1-7-5" />
 | 
			
		||||
    <rect
 | 
			
		||||
       id="rect4354"
 | 
			
		||||
       height="6.9999905"
 | 
			
		||||
       width="6.9999909"
 | 
			
		||||
       y="10.00001"
 | 
			
		||||
       x="58"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647"
 | 
			
		||||
       x="58.000004"
 | 
			
		||||
       y="10.000005"
 | 
			
		||||
       width="6.9999909"
 | 
			
		||||
       height="6.9999905"
 | 
			
		||||
       id="svg_1-7-5-7" />
 | 
			
		||||
    <g
 | 
			
		||||
       id="g4378">
 | 
			
		||||
      <rect
 | 
			
		||||
         id="svg_1-7-5-3"
 | 
			
		||||
         height="1.9999965"
 | 
			
		||||
         width="7.9999909"
 | 
			
		||||
         y="10.999999"
 | 
			
		||||
         x="198"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
         x="198"
 | 
			
		||||
         y="7.0000005"
 | 
			
		||||
         width="11.999995"
 | 
			
		||||
         height="1.9999946"
 | 
			
		||||
         id="rect4374" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
         x="198"
 | 
			
		||||
         y="14.999996"
 | 
			
		||||
         width="3.9999928"
 | 
			
		||||
         height="1.9999995"
 | 
			
		||||
         id="rect4376" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <g
 | 
			
		||||
       id="g4383"
 | 
			
		||||
       transform="matrix(1,0,0,-1,-23.999995,23.999995)">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
         x="198"
 | 
			
		||||
         y="10.999999"
 | 
			
		||||
         width="7.9999909"
 | 
			
		||||
         height="1.9999965"
 | 
			
		||||
         id="rect4385" />
 | 
			
		||||
      <rect
 | 
			
		||||
         id="rect4387"
 | 
			
		||||
         height="1.9999946"
 | 
			
		||||
         width="11.999995"
 | 
			
		||||
         y="7.0000005"
 | 
			
		||||
         x="198"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
      <rect
 | 
			
		||||
         id="rect4389"
 | 
			
		||||
         height="1.9999995"
 | 
			
		||||
         width="3.9999928"
 | 
			
		||||
         y="14.999996"
 | 
			
		||||
         x="198"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <rect
 | 
			
		||||
       y="3.9999199"
 | 
			
		||||
       x="76"
 | 
			
		||||
       height="16"
 | 
			
		||||
       width="16"
 | 
			
		||||
       id="rect3754-4"
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:nodetypes="cccccccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path4351"
 | 
			
		||||
       d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:nodetypes="cccccccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path4351-9"
 | 
			
		||||
       d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="3.9999199"
 | 
			
		||||
       x="100"
 | 
			
		||||
       height="16"
 | 
			
		||||
       width="16"
 | 
			
		||||
       id="rect3754-25"
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path2987"
 | 
			
		||||
       d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path2987-1"
 | 
			
		||||
       d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="3.9999199"
 | 
			
		||||
       x="124"
 | 
			
		||||
       height="16"
 | 
			
		||||
       width="16"
 | 
			
		||||
       id="rect3754-73"
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:nodetypes="ccccccccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path3780"
 | 
			
		||||
       d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path3782"
 | 
			
		||||
       d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="3.9999199"
 | 
			
		||||
       x="148"
 | 
			
		||||
       height="16"
 | 
			
		||||
       width="16"
 | 
			
		||||
       id="rect3754-35"
 | 
			
		||||
       style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:nodetypes="ccccccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path5008-2"
 | 
			
		||||
       d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none" />
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:nodetypes="ccccccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="path5008-2-8"
 | 
			
		||||
       d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none" />
 | 
			
		||||
  </g>
 | 
			
		||||
  <rect
 | 
			
		||||
     x="4"
 | 
			
		||||
     y="27.999994"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     id="rect4432"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
     x="28.000006"
 | 
			
		||||
     y="27.99999"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     id="rect4434" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4436"
 | 
			
		||||
     height="16"
 | 
			
		||||
     width="16"
 | 
			
		||||
     y="27.99999"
 | 
			
		||||
     x="52.000004"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;stroke:#000000;stroke-width:0"
 | 
			
		||||
     x="172.00002"
 | 
			
		||||
     y="27.999981"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     id="rect4446" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;stroke:#000000;stroke-width:0"
 | 
			
		||||
     x="196"
 | 
			
		||||
     y="27.99999"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     id="rect4448" />
 | 
			
		||||
  <g
 | 
			
		||||
     id="g4466"
 | 
			
		||||
     style="stroke:none"
 | 
			
		||||
     transform="translate(0,23.999995)">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
       id="rect4468"
 | 
			
		||||
       height="1.9999986"
 | 
			
		||||
       width="9.9999924"
 | 
			
		||||
       y="10.999998"
 | 
			
		||||
       x="7.0000048" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
       id="rect4470"
 | 
			
		||||
       height="9.9999838"
 | 
			
		||||
       width="1.9999955"
 | 
			
		||||
       y="7.0000114"
 | 
			
		||||
       x="11.000005" />
 | 
			
		||||
  </g>
 | 
			
		||||
  <g
 | 
			
		||||
     transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,35.999996)"
 | 
			
		||||
     id="g4472"
 | 
			
		||||
     style="stroke:none">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
       id="rect4474"
 | 
			
		||||
       height="1.9999986"
 | 
			
		||||
       width="9.9999924"
 | 
			
		||||
       y="10.999998"
 | 
			
		||||
       x="7.0000048" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
 | 
			
		||||
       id="rect4476"
 | 
			
		||||
       height="9.9999838"
 | 
			
		||||
       width="1.9999955"
 | 
			
		||||
       y="7.0000114"
 | 
			
		||||
       x="11.000005" />
 | 
			
		||||
  </g>
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
     x="55.000004"
 | 
			
		||||
     y="31"
 | 
			
		||||
     width="6.9999909"
 | 
			
		||||
     height="6.9999905"
 | 
			
		||||
     id="rect4478" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4480"
 | 
			
		||||
     height="6.9999905"
 | 
			
		||||
     width="6.9999909"
 | 
			
		||||
     y="34.000008"
 | 
			
		||||
     x="58"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     x="58.000004"
 | 
			
		||||
     y="34.000004"
 | 
			
		||||
     width="6.9999909"
 | 
			
		||||
     height="6.9999905"
 | 
			
		||||
     id="rect4482" />
 | 
			
		||||
  <g
 | 
			
		||||
     id="g4484"
 | 
			
		||||
     transform="translate(0,23.999995)">
 | 
			
		||||
    <rect
 | 
			
		||||
       id="rect4486"
 | 
			
		||||
       height="1.9999965"
 | 
			
		||||
       width="7.9999909"
 | 
			
		||||
       y="10.999999"
 | 
			
		||||
       x="198"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
       x="198"
 | 
			
		||||
       y="7.0000005"
 | 
			
		||||
       width="11.999995"
 | 
			
		||||
       height="1.9999946"
 | 
			
		||||
       id="rect4488" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
       x="198"
 | 
			
		||||
       y="14.999996"
 | 
			
		||||
       width="3.9999928"
 | 
			
		||||
       height="1.9999995"
 | 
			
		||||
       id="rect4490" />
 | 
			
		||||
  </g>
 | 
			
		||||
  <g
 | 
			
		||||
     id="g4492"
 | 
			
		||||
     transform="matrix(1,0,0,-1,-23.999995,47.99999)">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
 | 
			
		||||
       x="198"
 | 
			
		||||
       y="10.999999"
 | 
			
		||||
       width="7.9999909"
 | 
			
		||||
       height="1.9999965"
 | 
			
		||||
       id="rect4494" />
 | 
			
		||||
    <rect
 | 
			
		||||
       id="rect4496"
 | 
			
		||||
       height="1.9999946"
 | 
			
		||||
       width="11.999995"
 | 
			
		||||
       y="7.0000005"
 | 
			
		||||
       x="198"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       id="rect4498"
 | 
			
		||||
       height="1.9999995"
 | 
			
		||||
       width="3.9999928"
 | 
			
		||||
       y="14.999996"
 | 
			
		||||
       x="198"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
 | 
			
		||||
  </g>
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none"
 | 
			
		||||
     id="rect3754-8"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     x="76"
 | 
			
		||||
     y="27.99992" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="m 85.10448,30.015537 -0.0156,1.4063 c 3.02668,-0.2402 0.33007,3.6508 2.48438,4.5781 -2.18695,1.0938 0.49191,4.90688 -2.45313,4.57808 l -0.0156,1.4219 c 5.70827,0.559 1.03263,-5.10048 4.70313,-5.26558 l 0,-1.4063 c -3.61304,-0.027 1.11893,-5.707 -4.70313,-5.3125 z"
 | 
			
		||||
     id="path4351-1"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cccccccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="m 82.78126,29.998237 0.0156,1.4063 c -3.02668,-0.2402 -0.33008,3.6507 -2.48438,4.5781 2.18694,1.0938 -0.49191,4.90688 2.45313,4.57808 l 0.0156,1.4219 c -5.70828,0.559 -1.03264,-5.10038 -4.70313,-5.26558 l 0,-1.4063 c 3.61303,-0.027 -1.11893,-5.7071 4.70313,-5.3125 z"
 | 
			
		||||
     id="path4351-9-5"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cccccccc" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none"
 | 
			
		||||
     id="rect3754-65"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     x="100"
 | 
			
		||||
     y="27.99992" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:none"
 | 
			
		||||
     d="m 103.719,29.671937 0,12.71878 3.03125,0 0,-1.5313 -1.34375,0 0,-9.62498 1.375,0 0,-1.5625 z"
 | 
			
		||||
     id="path2987-8"
 | 
			
		||||
     inkscape:connector-curvature="0" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:none"
 | 
			
		||||
     d="m 112.2185,29.671937 0,12.71878 -3.03125,0 0,-1.5313 1.34375,0 0,-9.62498 -1.375,0 0,-1.5625 z"
 | 
			
		||||
     id="path2987-1-9"
 | 
			
		||||
     inkscape:connector-curvature="0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none"
 | 
			
		||||
     id="rect3754-92"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     x="124"
 | 
			
		||||
     y="27.99992" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:none"
 | 
			
		||||
     d="m 126.2824,41.602917 1.78957,0 1.14143,-2.86408 5.65364,0 1.14856,2.86408 1.76565,0 -4.78687,-11.16108 -1.91902,0 z"
 | 
			
		||||
     id="path3780-9"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="ccccccccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none"
 | 
			
		||||
     d="m 129.72704,37.478837 4.60852,0.01 -2.30426,-5.5498 z"
 | 
			
		||||
     id="path3782-2"
 | 
			
		||||
     inkscape:connector-curvature="0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none"
 | 
			
		||||
     id="rect3754-47"
 | 
			
		||||
     width="16"
 | 
			
		||||
     height="16"
 | 
			
		||||
     x="148"
 | 
			
		||||
     y="27.99992" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:none"
 | 
			
		||||
     d="m 156.47656,29.891737 0,2.1797 0.46093,2.3984 1.82813,0 0.39844,-2.3984 0,-2.1797 z"
 | 
			
		||||
     id="path5008-2-1"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="ccccccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;stroke:none"
 | 
			
		||||
     d="m 152.51562,29.890637 0,2.1797 0.46094,2.3984 1.82812,0 0.39844,-2.3984 0,-2.1797 z"
 | 
			
		||||
     id="path5008-2-8-8"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="ccccccc" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="svg_1-7-2"
 | 
			
		||||
     height="1.9999961"
 | 
			
		||||
     width="11.999996"
 | 
			
		||||
     y="64"
 | 
			
		||||
     x="54"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="svg_1-7-2-2"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     y="52"
 | 
			
		||||
     x="80.000008"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     x="85.000008"
 | 
			
		||||
     y="52"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     id="rect4561" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     x="80.000008"
 | 
			
		||||
     y="58"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     id="rect4563" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4565"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     y="58"
 | 
			
		||||
     x="85.000008"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4567"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     y="64"
 | 
			
		||||
     x="80.000008"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     x="85.000008"
 | 
			
		||||
     y="64"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     id="rect4569" />
 | 
			
		||||
  <circle
 | 
			
		||||
     style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     id="path4571"
 | 
			
		||||
     cx="110.06081"
 | 
			
		||||
     cy="57.939209"
 | 
			
		||||
     r="4.7438836" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     x="116.64566"
 | 
			
		||||
     y="-31.79752"
 | 
			
		||||
     width="4.229713"
 | 
			
		||||
     height="6.4053884"
 | 
			
		||||
     id="rect4563-2"
 | 
			
		||||
     transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="M 125,56 138.77027,56.095 132,64 Z"
 | 
			
		||||
     id="path4613"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     sodipodi:nodetypes="cccc"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     id="path4615"
 | 
			
		||||
     d="M 149,64 162.77027,63.905 156,56 Z"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     x="54"
 | 
			
		||||
     y="53"
 | 
			
		||||
     width="11.999996"
 | 
			
		||||
     height="1.9999961"
 | 
			
		||||
     id="rect4638" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="svg_1-7-2-24"
 | 
			
		||||
     height="1.9999957"
 | 
			
		||||
     width="12.99999"
 | 
			
		||||
     y="-56"
 | 
			
		||||
     x="53"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     transform="matrix(0,1,-1,0,0,0)" />
 | 
			
		||||
  <rect
 | 
			
		||||
     transform="matrix(0,1,-1,0,0,0)"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
 | 
			
		||||
     x="53"
 | 
			
		||||
     y="-66"
 | 
			
		||||
     width="12.99999"
 | 
			
		||||
     height="1.9999957"
 | 
			
		||||
     id="rect4657" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4659"
 | 
			
		||||
     height="0.99999291"
 | 
			
		||||
     width="11.999999"
 | 
			
		||||
     y="57"
 | 
			
		||||
     x="54"
 | 
			
		||||
     style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     x="54"
 | 
			
		||||
     y="88.000122"
 | 
			
		||||
     width="11.999996"
 | 
			
		||||
     height="1.9999961"
 | 
			
		||||
     id="rect4661" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     x="80.000008"
 | 
			
		||||
     y="76.000122"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     id="rect4663" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4665"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     y="76.000122"
 | 
			
		||||
     x="85.000008"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4667"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     y="82.000122"
 | 
			
		||||
     x="80.000008"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     x="85.000008"
 | 
			
		||||
     y="82.000122"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     id="rect4669" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     x="80.000008"
 | 
			
		||||
     y="88.000122"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     id="rect4671" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4673"
 | 
			
		||||
     height="2.9999905"
 | 
			
		||||
     width="2.9999907"
 | 
			
		||||
     y="88.000122"
 | 
			
		||||
     x="85.000008"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
 | 
			
		||||
  <circle
 | 
			
		||||
     r="4.7438836"
 | 
			
		||||
     cy="81.939331"
 | 
			
		||||
     cx="110.06081"
 | 
			
		||||
     id="circle4675"
 | 
			
		||||
     style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
  <rect
 | 
			
		||||
     transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
 | 
			
		||||
     id="rect4677"
 | 
			
		||||
     height="6.4053884"
 | 
			
		||||
     width="4.229713"
 | 
			
		||||
     y="-14.826816"
 | 
			
		||||
     x="133.6163"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
 | 
			
		||||
  <path
 | 
			
		||||
     sodipodi:nodetypes="cccc"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     id="path4679"
 | 
			
		||||
     d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
 | 
			
		||||
     id="path4681"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cccc" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4683"
 | 
			
		||||
     height="1.9999961"
 | 
			
		||||
     width="11.999996"
 | 
			
		||||
     y="77.000122"
 | 
			
		||||
     x="54"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
 | 
			
		||||
  <rect
 | 
			
		||||
     transform="matrix(0,1,-1,0,0,0)"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     x="77.000122"
 | 
			
		||||
     y="-56"
 | 
			
		||||
     width="12.99999"
 | 
			
		||||
     height="1.9999957"
 | 
			
		||||
     id="rect4685" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4687"
 | 
			
		||||
     height="1.9999957"
 | 
			
		||||
     width="12.99999"
 | 
			
		||||
     y="-66"
 | 
			
		||||
     x="77.000122"
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     transform="matrix(0,1,-1,0,0,0)" />
 | 
			
		||||
  <rect
 | 
			
		||||
     style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
 | 
			
		||||
     x="54"
 | 
			
		||||
     y="81.000122"
 | 
			
		||||
     width="11.999999"
 | 
			
		||||
     height="0.99999291"
 | 
			
		||||
     id="rect4689" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-1"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="15.99999"
 | 
			
		||||
     y="101"
 | 
			
		||||
     x="76.000008"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-0"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="15.99999"
 | 
			
		||||
     y="105"
 | 
			
		||||
     x="76.000008"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-7"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="9"
 | 
			
		||||
     y="109"
 | 
			
		||||
     x="76.000008"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-1-1"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="12"
 | 
			
		||||
     y="125"
 | 
			
		||||
     x="76.000008"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-1-1-4"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="10"
 | 
			
		||||
     y="137"
 | 
			
		||||
     x="76.000008"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-1-1-4-4"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="10"
 | 
			
		||||
     y="129"
 | 
			
		||||
     x="82"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <rect
 | 
			
		||||
     id="rect4761-1-1-4-4-3"
 | 
			
		||||
     height="1.9999945"
 | 
			
		||||
     width="9"
 | 
			
		||||
     y="133"
 | 
			
		||||
     x="82"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 | 
			
		||||
     d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
 | 
			
		||||
     id="path4138" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 | 
			
		||||
     d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
 | 
			
		||||
     id="path4138-1" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
 | 
			
		||||
     id="path3055-0-77" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.966;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="m 4.9850574,108.015 14.0298856,-0.03"
 | 
			
		||||
     id="path5244-5-0-5"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cc" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.966;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="m 4.9849874,132.015 14.0298866,-0.03"
 | 
			
		||||
     id="path5244-5-0-5-8"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cc" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 | 
			
		||||
     d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
 | 
			
		||||
     id="path4138-12" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 | 
			
		||||
     d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
 | 
			
		||||
     id="path4138-1-3" />
 | 
			
		||||
  <path
 | 
			
		||||
     id="path6191"
 | 
			
		||||
     d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
 | 
			
		||||
     style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     inkscape:connector-curvature="0" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
 | 
			
		||||
     id="path6193" />
 | 
			
		||||
  <path
 | 
			
		||||
     id="path6195"
 | 
			
		||||
     d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
 | 
			
		||||
     style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     inkscape:connector-curvature="0" />
 | 
			
		||||
  <path
 | 
			
		||||
     sodipodi:type="star"
 | 
			
		||||
     style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     id="path4500"
 | 
			
		||||
     sodipodi:sides="3"
 | 
			
		||||
     sodipodi:cx="11.55581"
 | 
			
		||||
     sodipodi:cy="60.073242"
 | 
			
		||||
     sodipodi:r1="5.1116104"
 | 
			
		||||
     sodipodi:r2="2.5558052"
 | 
			
		||||
     sodipodi:arg1="0"
 | 
			
		||||
     sodipodi:arg2="1.0471976"
 | 
			
		||||
     inkscape:flatsided="false"
 | 
			
		||||
     inkscape:rounded="0"
 | 
			
		||||
     inkscape:randomized="0"
 | 
			
		||||
     d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
 | 
			
		||||
     inkscape:transform-center-x="-1.2779026" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:transform-center-x="1.277902"
 | 
			
		||||
     d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
 | 
			
		||||
     inkscape:randomized="0"
 | 
			
		||||
     inkscape:rounded="0"
 | 
			
		||||
     inkscape:flatsided="false"
 | 
			
		||||
     sodipodi:arg2="1.0471976"
 | 
			
		||||
     sodipodi:arg1="0"
 | 
			
		||||
     sodipodi:r2="2.5558052"
 | 
			
		||||
     sodipodi:r1="5.1116104"
 | 
			
		||||
     sodipodi:cy="60.073242"
 | 
			
		||||
     sodipodi:cx="-36.611614"
 | 
			
		||||
     sodipodi:sides="3"
 | 
			
		||||
     id="path4502"
 | 
			
		||||
     style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     sodipodi:type="star"
 | 
			
		||||
     transform="scale(-1,1)" />
 | 
			
		||||
  <path
 | 
			
		||||
     d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
 | 
			
		||||
     inkscape:randomized="0"
 | 
			
		||||
     inkscape:rounded="0"
 | 
			
		||||
     inkscape:flatsided="false"
 | 
			
		||||
     sodipodi:arg2="1.0471976"
 | 
			
		||||
     sodipodi:arg1="0"
 | 
			
		||||
     sodipodi:r2="2.5558052"
 | 
			
		||||
     sodipodi:r1="5.1116104"
 | 
			
		||||
     sodipodi:cy="60.073212"
 | 
			
		||||
     sodipodi:cx="11.55581"
 | 
			
		||||
     sodipodi:sides="3"
 | 
			
		||||
     id="path4504"
 | 
			
		||||
     style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     sodipodi:type="star"
 | 
			
		||||
     transform="matrix(0,1,-1,0,72.0074,71.7877)"
 | 
			
		||||
     inkscape:transform-center-y="1.2779029" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:transform-center-y="-1.2779026"
 | 
			
		||||
     transform="matrix(0,-1,-1,0,96,96)"
 | 
			
		||||
     sodipodi:type="star"
 | 
			
		||||
     style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
     id="path4506"
 | 
			
		||||
     sodipodi:sides="3"
 | 
			
		||||
     sodipodi:cx="11.55581"
 | 
			
		||||
     sodipodi:cy="60.073212"
 | 
			
		||||
     sodipodi:r1="5.1116104"
 | 
			
		||||
     sodipodi:r2="2.5558052"
 | 
			
		||||
     sodipodi:arg1="0"
 | 
			
		||||
     sodipodi:arg2="1.0471976"
 | 
			
		||||
     inkscape:flatsided="false"
 | 
			
		||||
     inkscape:rounded="0"
 | 
			
		||||
     inkscape:randomized="0"
 | 
			
		||||
     d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
 | 
			
		||||
  <path
 | 
			
		||||
     sodipodi:nodetypes="cccc"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     id="path4615-5"
 | 
			
		||||
     d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
 | 
			
		||||
     style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
     d="m 179,55 0,6 2,0 0,-6"
 | 
			
		||||
     id="path4300"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
     d="m 179,62 0,2 2,0 0,-2"
 | 
			
		||||
     id="path4300-6"
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     sodipodi:nodetypes="cccc" />
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 35 KiB  | 
							
								
								
									
										2
									
								
								public/css/jquery.datetimepicker.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/jquery.datetimepicker.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/jsoneditor.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2
									
								
								public/css/lightbox.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,2 @@
 | 
			
		||||
body:after{content:url(../images/close.png) url(../images/loading.gif) url(../images/prev.png) url(../images/next.png);display:none}body.lb-disable-scrolling{overflow:hidden}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);opacity:.8;display:none}.lightbox{position:absolute;left:0;width:100%;z-index:10000;text-align:center;line-height:0;font-weight:400}.lightbox .lb-image{display:block;height:auto;max-width:inherit;border-radius:3px}.lightbox a img{border:none}.lb-outerContainer{position:relative;background-color:#fff;*zoom:1;width:250px;height:250px;margin:0 auto;border-radius:4px}.lb-outerContainer:after{content:"";display:table;clear:both}.lb-container{padding:4px}.lb-loader{position:absolute;top:43%;left:0;height:25%;width:100%;text-align:center;line-height:0}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(../images/loading.gif) no-repeat}.lb-nav{position:absolute;top:0;left:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:none;background-image:url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==')}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(../images/prev.png) left 48% no-repeat;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(../images/next.png) right 48% no-repeat;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;*zoom:1;width:100%;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{content:"";display:table;clear:both}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(../images/close.png) 100% 0 no-repeat;text-align:right;outline:none;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=70);opacity:.7;-webkit-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}
 | 
			
		||||
/*# sourceMappingURL=lightbox.min.css.map */
 | 
			
		||||
							
								
								
									
										1
									
								
								public/css/lightbox.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										6
									
								
								public/css/quill.snow.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/quill.snow.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2
									
								
								public/css/style.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/css/style.min.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 939 B  | 
							
								
								
									
										
											BIN
										
									
								
								public/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/images/close.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 280 B  | 
							
								
								
									
										
											BIN
										
									
								
								public/images/loading.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.3 KiB  |