From 5640c74f35df1c6e3d6c8679039113a1a2ad7e3b Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 22 Mar 2016 16:19:55 -0400 Subject: [PATCH 001/258] Migrate logos to use Laravel Filesystem --- app/Models/Account.php | 92 +++++++++++++------ app/Ninja/Repositories/AccountRepository.php | 2 +- app/Providers/AppServiceProvider.php | 11 ++- config/filesystems.php | 30 ++++-- .../2016_03_22_168364_add_documents.php | 37 ++++++++ resources/views/accounts/details.blade.php | 4 +- .../emails/partials/account_logo.blade.php | 2 +- resources/views/header.blade.php | 6 +- resources/views/invited/dashboard.blade.php | 2 +- resources/views/invoices/edit.blade.php | 2 +- resources/views/invoices/pdf.blade.php | 2 +- resources/views/user_account.blade.php | 4 +- .../views/users/account_management.blade.php | 4 +- 13 files changed, 143 insertions(+), 55 deletions(-) create mode 100644 database/migrations/2016_03_22_168364_add_documents.php diff --git a/app/Models/Account.php b/app/Models/Account.php index f6a7194bdf6f..26e1e0f6b7dc 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -9,6 +9,7 @@ use Cache; use App; use File; use App\Events\UserSettingsChanged; +use Illuminate\Support\Facades\Storage; use Illuminate\Database\Eloquent\SoftDeletes; use Laracasts\Presenter\PresentableTrait; @@ -384,26 +385,64 @@ class Account extends Eloquent public function hasLogo() { - return file_exists($this->getLogoFullPath()); + if($this->logo == ''){ + $this->calculateLogoDetails(); + } + + return !empty($this->logo); + } + + public function getLogoDisk(){ + return Storage::disk(env('LOGO_DISK', 'logos')); + } + + protected function calculateLogoDetails(){ + $disk = $this->getLogoDisk(); + + if($disk->exists($this->account_key.'.png')){ + $this->logo = $this->account_key.'.png'; + } else if($disk->exists($this->account_key.'.jpg')) { + $this->logo = $this->account_key.'.jpg'; + } + + if(!empty($this->logo)){ + $image = imagecreatefromstring($disk->get($this->logo)); + $this->logo_width = imagesx($image); + $this->logo_height = imagesy($image); + $this->logo_size = $disk->size($this->logo); + } else { + $this->logo = null; + } + $this->save(); } - public function getLogoPath() - { - $fileName = 'logo/' . $this->account_key; - - return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg'; + public function getLogoRaw(){ + if(!$this->hasLogo()){ + return null; + } + + $disk = $this->getLogoDisk(); + return $disk->get($this->logo); } - - public function getLogoFullPath() - { - $fileName = public_path() . '/logo/' . $this->account_key; - - return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg'; - } - + public function getLogoURL() { - return SITE_URL . '/' . $this->getLogoPath(); + if(!$this->hasLogo()){ + return null; + } + + $disk = $this->getLogoDisk(); + $adapter = $disk->getAdapter(); + + if($adapter instanceof \League\Flysystem\Adapter\Local) { + // Stored locally + $logo_url = str_replace(public_path(), url('/'), $adapter->applyPathPrefix($this->logo), $count); + if($count == 1){ + return str_replace(DIRECTORY_SEPARATOR, '/', $logo_url); + } + } + + return null; } public function getToken($name) @@ -419,24 +458,20 @@ class Account extends Eloquent public function getLogoWidth() { - $path = $this->getLogoFullPath(); - if (!file_exists($path)) { - return 0; + if(!$this->hasLogo()){ + return null; } - list($width, $height) = getimagesize($path); - return $width; + return $this->logo_width; } public function getLogoHeight() { - $path = $this->getLogoFullPath(); - if (!file_exists($path)) { - return 0; + if(!$this->hasLogo()){ + return null; } - list($width, $height) = getimagesize($path); - return $height; + return $this->logo_height; } public function createInvoice($entityType = ENTITY_INVOICE, $clientId = null) @@ -815,12 +850,11 @@ class Account extends Eloquent public function getLogoSize() { - if (!$this->hasLogo()) { - return 0; + if(!$this->hasLogo()){ + return null; } - $filename = $this->getLogoFullPath(); - return round(File::size($filename) / 1000); + return round($this->logo_size / 1000); } public function isLogoTooLarge() diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 0e08fbba5d06..cd07a37b5db2 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -475,7 +475,7 @@ class AccountRepository $item->account_id = $user->account->id; $item->account_name = $user->account->getDisplayName(); $item->pro_plan_paid = $user->account->pro_plan_paid; - $item->logo_path = $user->account->hasLogo() ? $user->account->getLogoPath() : null; + $item->logo_url = $user->account->hasLogo() ? $user->account->getLogoUrl() : null; $data[] = $item; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3cd691098747..c5d597ded259 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -22,8 +22,15 @@ class AppServiceProvider extends ServiceProvider { */ public function boot() { - Form::macro('image_data', function($imagePath) { - return 'data:image/jpeg;base64,' . base64_encode(file_get_contents($imagePath)); + Form::macro('image_data', function($image, $contents = false) { + if(!$contents){ + $contents = file_get_contents($image); + } + else{ + $contents = $image; + } + + return 'data:image/jpeg;base64,' . base64_encode($contents); }); Form::macro('nav_link', function($url, $text, $url2 = '', $extra = '') { diff --git a/config/filesystems.php b/config/filesystems.php index 0221fa70dbe1..d0c683352bd5 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -47,23 +47,33 @@ return [ 'driver' => 'local', 'root' => storage_path().'/app', ], + + 'logos' => [ + 'driver' => 'local', + 'root' => env('LOGO_PATH', public_path().'/logo'), + ], + + 'documents' => [ + 'driver' => 'local', + 'root' => storage_path().'/documents', + ], 's3' => [ 'driver' => 's3', - 'key' => 'your-key', - 'secret' => 'your-secret', - 'region' => 'your-region', - 'bucket' => 'your-bucket', + 'key' => env('S3_KEY', ''), + 'secret' => env('S3_SECRET', ''), + 'region' => env('S3_REGION', ''), + 'bucket' => env('S3_BUCKET', ''), ], 'rackspace' => [ 'driver' => 'rackspace', - 'username' => 'your-username', - 'key' => 'your-key', - 'container' => 'your-container', - 'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/', - 'region' => 'IAD', - 'url_type' => 'publicURL' + 'username' => env('RACKSPACE_USERNAME', ''), + 'key' => env('RACKSPACE_KEY', ''), + 'container' => env('RACKSPACE_CONTAINER', ''), + 'endpoint' => env('RACKSPACE_ENDPOINT', 'https://identity.api.rackspacecloud.com/v2.0/'), + 'region' => env('RACKSPACE_REGION', 'https://identity.api.rackspacecloud.com/v2.0/'), + 'url_type' => env('RACKSPACE_URL_TYPE', 'publicURL') ], ], diff --git a/database/migrations/2016_03_22_168364_add_documents.php b/database/migrations/2016_03_22_168364_add_documents.php new file mode 100644 index 000000000000..97b0927570e0 --- /dev/null +++ b/database/migrations/2016_03_22_168364_add_documents.php @@ -0,0 +1,37 @@ +string('logo')->nullable()->default(null); + $table->unsignedInteger('logo_width'); + $table->unsignedInteger('logo_height'); + $table->unsignedInteger('logo_size'); + });*/ + + DB::table('accounts')->update(array('logo' => '')); + } + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accounts', function($table) { + $table->dropColumn('logo'); + $table->dropColumn('logo_width'); + $table->dropColumn('logo_height'); + $table->dropColumn('logo_size'); + }); + } +} diff --git a/resources/views/accounts/details.blade.php b/resources/views/accounts/details.blade.php index 56dc8f33e73c..7201ed2f00ea 100644 --- a/resources/views/accounts/details.blade.php +++ b/resources/views/accounts/details.blade.php @@ -51,8 +51,8 @@
diff --git a/resources/views/emails/partials/account_logo.blade.php b/resources/views/emails/partials/account_logo.blade.php index cfe158153fe7..d79002d6b938 100644 --- a/resources/views/emails/partials/account_logo.blade.php +++ b/resources/views/emails/partials/account_logo.blade.php @@ -3,7 +3,7 @@ @endif - + @if ($account->website) diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index 3ce364da6441..1191aac2eee5 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -448,7 +448,7 @@ 'user_id' => $item->user_id, 'account_name' => $item->account_name, 'user_name' => $item->user_name, - 'logo_path' => isset($item->logo_path) ? $item->logo_path : "", + 'logo_url' => isset($item->logo_url) ? $item->logo_url : "", 'selected' => true, ]) @endif @@ -460,7 +460,7 @@ 'user_id' => $item->user_id, 'account_name' => $item->account_name, 'user_name' => $item->user_name, - 'logo_path' => isset($item->logo_path) ? $item->logo_path : "", + 'logo_url' => isset($item->logo_url) ? $item->logo_url : "", 'selected' => false, ]) @endif @@ -469,7 +469,7 @@ @include('user_account', [ 'account_name' => Auth::user()->account->name ?: trans('texts.untitled'), 'user_name' => Auth::user()->getDisplayName(), - 'logo_path' => Auth::user()->account->getLogoPath(), + 'logo_url' => Auth::user()->account->getLogoURL(), 'selected' => true, ]) @endif diff --git a/resources/views/invited/dashboard.blade.php b/resources/views/invited/dashboard.blade.php index a5b698c46e2e..4af2cc53c9d3 100644 --- a/resources/views/invited/dashboard.blade.php +++ b/resources/views/invited/dashboard.blade.php @@ -95,7 +95,7 @@
') !!}
+
+
+
+ +
+
+
+ +
+
@@ -675,10 +686,12 @@ @include('invoices.knockout') diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php index 029a81bc0b67..dbb8b281e8b8 100644 --- a/resources/views/invoices/knockout.blade.php +++ b/resources/views/invoices/knockout.blade.php @@ -226,6 +226,7 @@ function InvoiceModel(data) { self.auto_bill = ko.observable(); self.invoice_status_id = ko.observable(0); self.invoice_items = ko.observableArray(); + self.documents = ko.observableArray(); self.amount = ko.observable(0); self.balance = ko.observable(0); self.invoice_design_id = ko.observable(1); @@ -251,6 +252,11 @@ function InvoiceModel(data) { return new ItemModel(options.data); } }, + 'documents': { + create: function(options) { + return new DocumentModel(options.data); + } + }, 'tax': { create: function(options) { return new TaxRateModel(options.data); @@ -267,6 +273,18 @@ function InvoiceModel(data) { applyComboboxListeners(); return itemModel; } + + self.addDocument = function() { + var documentModel = new DocumentModel(); + self.documents.push(documentModel); + return documentModel; + } + + self.removeDocument = function(public_id) { + self.documents.remove(function(document) { + return document.public_id() == public_id; + }); + } if (data) { ko.mapping.fromJS(data, self.mapping, self); @@ -810,6 +828,23 @@ function ItemModel(data) { this.onSelect = function() {} } + +function DocumentModel(data) { + var self = this; + self.public_id = ko.observable(0); + self.size = ko.observable(0); + self.name = ko.observable(''); + self.type = ko.observable(''); + self.url = ko.observable(''); + + self.update = function(data){ + ko.mapping.fromJS(data, {}, this); + } + + if (data) { + self.update(data); + } +} /* Custom binding for product key typeahead */ ko.bindingHandlers.typeahead = { From c57dfb7f0d106eebae1ef3cc5accdfbabc5dde26 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 22 Mar 2016 22:59:20 -0400 Subject: [PATCH 003/258] Restrict documents to pro users --- app/Http/Controllers/DocumentController.php | 4 ++++ resources/views/invoices/edit.blade.php | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 695289950786..03b1ffa917ef 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -48,6 +48,10 @@ class DocumentController extends BaseController public function postUpload() { + if (!Auth::user()->account->isPro()) { + return; + } + if(!$this->checkCreatePermission($response)){ return $response; } diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index b5f36c1b3d39..59c662ccb5f6 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -270,7 +270,9 @@
  • {{ trans("texts.{$entityType}_terms") }}
  • {{ trans("texts.{$entityType}_footer") }}
  • + @if (Auth::user()->account->isPro())
  • {{ trans("texts.{$entityType}_documents") }}
  • + @endif
    @@ -302,6 +304,7 @@
    ') !!} + @if (Auth::user()->account->isPro())
    @@ -312,6 +315,7 @@
    + @endif
    @@ -919,6 +923,7 @@ applyComboboxListeners(); + @if (Auth::user()->account->isPro()) // Initialize document upload dropzone = new Dropzone('#document-upload', { url:{!! json_encode(url('document')) !!}, @@ -955,6 +960,7 @@ } dropzone.files.push(mockFile); } + @endif }); function onFrequencyChange(){ @@ -1313,6 +1319,7 @@ model.invoice().invoice_number(number); } + @if (Auth::user()->account->isPro()) function handleDocumentAdded(file){ if(file.mock)return; file.index = model.invoice().documents().length; @@ -1327,6 +1334,7 @@ file.public_id = response.document.public_id model.invoice().documents()[file.index].update(response.document); } + @endif From aae4f57c097c235673f3662eb0442e559cbfb291 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 23 Mar 2016 15:02:05 +0200 Subject: [PATCH 004/258] Prevent duplicate deletions --- app/Ninja/Repositories/BaseRepository.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/Ninja/Repositories/BaseRepository.php b/app/Ninja/Repositories/BaseRepository.php index bec95fb96921..f674c406549e 100644 --- a/app/Ninja/Repositories/BaseRepository.php +++ b/app/Ninja/Repositories/BaseRepository.php @@ -20,6 +20,10 @@ class BaseRepository public function archive($entity) { + if ($entity->trashed()) { + return; + } + $entity->delete(); $className = $this->getEventClass($entity, 'Archived'); @@ -31,6 +35,10 @@ class BaseRepository public function restore($entity) { + if ( ! $entity->trashed()) { + return; + } + $fromDeleted = false; $entity->restore(); @@ -49,6 +57,10 @@ class BaseRepository public function delete($entity) { + if ($entity->is_deleted) { + return; + } + $entity->is_deleted = true; $entity->save(); From bdf24f0ce32b29991f01c562122d956d697dd92a Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 23 Mar 2016 15:15:18 +0200 Subject: [PATCH 005/258] Tweaking the drop shadows --- public/css/built.css | 6 +++--- public/css/built.public.css | 6 +++--- public/css/public.style.css | 6 +++--- public/css/style.css | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/css/built.css b/public/css/built.css index 8e2782ebbffc..948849790278 100644 --- a/public/css/built.css +++ b/public/css/built.css @@ -2534,9 +2534,9 @@ font-weight: bold; ul.dropdown-menu, .twitter-typeahead .tt-menu, canvas { - x-moz-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - x-webkit-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - box-shadow: 0 0 1px 1px rgba(0,0,0,.05); + x-moz-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + x-webkit-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } .navbar .active > a { diff --git a/public/css/built.public.css b/public/css/built.public.css index 3e91dc0ed193..2a02c2db94b6 100644 --- a/public/css/built.public.css +++ b/public/css/built.public.css @@ -805,9 +805,9 @@ html { } .navbar { - x-moz-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - x-webkit-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - box-shadow: 0 0 1px 1px rgba(0,0,0,.05); + x-moz-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + x-webkit-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } #footer { diff --git a/public/css/public.style.css b/public/css/public.style.css index 95b158cb9b62..b1f91c25739f 100644 --- a/public/css/public.style.css +++ b/public/css/public.style.css @@ -22,9 +22,9 @@ html { } .navbar { - x-moz-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - x-webkit-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - box-shadow: 0 0 1px 1px rgba(0,0,0,.05); + x-moz-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + x-webkit-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } #footer { diff --git a/public/css/style.css b/public/css/style.css index 2cf0cdceca24..0b1b53b1e311 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -407,9 +407,9 @@ font-weight: bold; ul.dropdown-menu, .twitter-typeahead .tt-menu, canvas { - x-moz-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - x-webkit-box-shadow: 0 0 1px 1px rgba(0,0,0,.05); - box-shadow: 0 0 1px 1px rgba(0,0,0,.05); + x-moz-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + x-webkit-box-shadow: 0 0 10px 2px rgba(0,0,0,.05); + box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } .navbar .active > a { From d810ba7f6bc1adef15b6b4d2e685599c66886577 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Wed, 23 Mar 2016 15:41:05 -0400 Subject: [PATCH 006/258] Improved document support --- app/Http/Controllers/DocumentController.php | 34 +- app/Http/routes.php | 4 +- app/Models/Account.php | 5 +- app/Models/Document.php | 98 +++++- app/Models/EntityModel.php | 11 +- app/Ninja/Repositories/DocumentRepository.php | 78 +++-- app/Ninja/Repositories/InvoiceRepository.php | 5 +- composer.json | 4 +- composer.lock | 319 +++++++++++++++++- config/filesystems.php | 4 +- .../2016_03_22_168362_add_documents.php | 2 +- resources/views/invoices/edit.blade.php | 6 +- 12 files changed, 508 insertions(+), 62 deletions(-) diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 03b1ffa917ef..4b7718bfd817 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -27,16 +27,15 @@ class DocumentController extends BaseController public function get($publicId) { $document = Document::scope($publicId) - ->withTrashed() ->firstOrFail(); if(!$this->checkViewPermission($document, $response)){ return $response; } - $public_url = $document->getPublicUrl(); - if($public_url){ - return redirect($public_url); + $direct_url = $document->getDirectUrl(); + if($direct_url){ + return redirect($direct_url); } @@ -46,9 +45,34 @@ class DocumentController extends BaseController return $response; } + public function getPreview($publicId) + { + $document = Document::scope($publicId) + ->firstOrFail(); + + if(!$this->checkViewPermission($document, $response)){ + return $response; + } + + if(empty($document->preview)){ + return Response::view('error', array('error'=>'Preview does not exist!'), 404); + } + + $direct_url = $document->getDirectPreviewUrl(); + if($direct_url){ + return redirect($direct_url); + } + + $extension = pathinfo($document->preview, PATHINFO_EXTENSION); + $response = Response::make($document->getRawPreview(), 200); + $response->header('content-type', Document::$extensions[$extension]); + + return $response; + } + public function postUpload() { - if (!Auth::user()->account->isPro()) { + if (!Utils::isPro()) { return; } diff --git a/app/Http/routes.php b/app/Http/routes.php index f9156ffc84ce..6a4970194ca6 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -133,6 +133,7 @@ Route::group(['middleware' => 'auth:user'], function() { Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); Route::get('document/{public_id}/{filename?}', 'DocumentController@get'); + Route::get('document/preview/{public_id}/{filename?}', 'DocumentController@getPreview'); Route::post('document', 'DocumentController@postUpload'); Route::get('quotes/create/{client_id?}', 'QuoteController@create'); @@ -428,7 +429,8 @@ if (!defined('CONTACT_EMAIL')) { define('MAX_IFRAME_URL_LENGTH', 250); define('MAX_LOGO_FILE_SIZE', 200); // KB define('MAX_FAILED_LOGINS', 10); - define('DEFAULT_MAX_DOCUMENT_SIZE', 10000);// KB + define('MAX_DOCUMENT_SIZE', env('MAX_DOCUMENT_SIZE', 10000));// KB + define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 150));// pixels define('DEFAULT_FONT_SIZE', 9); define('DEFAULT_HEADER_FONT', 1);// Roboto define('DEFAULT_BODY_FONT', 1);// Roboto diff --git a/app/Models/Account.php b/app/Models/Account.php index 26e1e0f6b7dc..4f8b6834ba48 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -6,6 +6,7 @@ use Session; use DateTime; use Event; use Cache; +use Document; use App; use File; use App\Events\UserSettingsChanged; @@ -393,7 +394,7 @@ class Account extends Eloquent } public function getLogoDisk(){ - return Storage::disk(env('LOGO_DISK', 'logos')); + return Storage::disk(env('LOGO_FILESYSTEM', 'logos')); } protected function calculateLogoDetails(){ @@ -442,7 +443,7 @@ class Account extends Eloquent } } - return null; + Document::getDirectFileUrl($this->logo, $this->getDisk()); } public function getToken($name) diff --git a/app/Models/Document.php b/app/Models/Document.php index 8f1a01448f76..f342d1ff08e1 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -1,7 +1,7 @@ 'image/png', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', 'pdf' => 'application/pdf', 'gif' => 'image/gif' ); @@ -16,32 +18,27 @@ class Document extends EntityModel public static $types = array( 'image/png' => array( 'extension' => 'png', - 'image' => true, ), 'image/jpeg' => array( 'extension' => 'jpeg', - 'image' => true, + ), + 'image/tiff' => array( + 'extension' => 'tiff', ), 'image/gif' => array( 'extension' => 'gif', - 'image' => true, ), 'application/pdf' => array( 'extension' => 'pdf', ), ); - // Expenses - use SoftDeletes; - - protected $dates = ['deleted_at']; - public function fill(array $attributes) { parent::fill($attributes); if(empty($this->attributes['disk'])){ - $this->attributes['disk'] = env('LOGO_DISK', 'documents'); + $this->attributes['disk'] = env('DOCUMENT_FILESYSTEM', 'documents'); } return $this; @@ -68,17 +65,53 @@ class Document extends EntityModel } public function getDisk(){ - return Storage::disk(!empty($this->disk)?$this->disk:env('LOGO_DISK', 'documents')); + return Storage::disk(!empty($this->disk)?$this->disk:env('DOCUMENT_FILESYSTEM', 'documents')); } public function setDiskAttribute($value) { - $this->attributes['disk'] = $value?$value:env('LOGO_DISK', 'documents'); + $this->attributes['disk'] = $value?$value:env('DOCUMENT_FILESYSTEM', 'documents'); } - public function getPublicUrl(){ - $disk = $this->getDisk(); + public function getDirectUrl(){ + return static::getDirectFileUrl($this->path, $this->getDisk()); + } + + public function getDirectPreviewUrl(){ + return $this->preview?static::getDirectFileUrl($this->preview, $this->getDisk(), true):null; + } + + public static function getDirectFileUrl($path, $disk, $prioritizeSpeed = false){ $adapter = $disk->getAdapter(); + $fullPath = $adapter->applyPathPrefix($path); + + if($adapter instanceof \League\Flysystem\AwsS3v3\AwsS3Adapter) { + $client = $adapter->getClient(); + $command = $client->getCommand('GetObject', [ + 'Bucket' => $adapter->getBucket(), + 'Key' => $fullPath + ]); + + return (string) $client->createPresignedRequest($command, '+10 minutes')->getUri(); + } else if (!$prioritizeSpeed // Rackspace temp URLs are slow, so we don't use them for previews + && $adapter instanceof \League\Flysystem\Rackspace\RackspaceAdapter) { + $secret = env('RACKSPACE_TEMP_URL_SECRET'); + if($secret){ + $object = $adapter->getContainer()->getObject($fullPath); + + if(env('RACKSPACE_TEMP_URL_SECRET_SET')){ + // Go ahead and set the secret too + $object->getService()->getAccount()->setTempUrlSecret($secret); + } + + $url = $object->getUrl(); + $expiry = strtotime('+10 minutes'); + $urlPath = urldecode($url->getPath()); + $body = sprintf("%s\n%d\n%s", 'GET', $expiry, $urlPath); + $hash = hash_hmac('sha1', $body, $secret); + return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry); + } + } return null; } @@ -89,14 +122,49 @@ class Document extends EntityModel return $disk->get($this->path); } + public function getRawPreview(){ + $disk = $this->getDisk(); + + return $disk->get($this->preview); + } + public function getUrl(){ return url('document/'.$this->public_id.'/'.$this->name); } + public function getPreviewUrl(){ + return $this->preview?url('document/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; + } + public function toArray() { $array = parent::toArray(); $array['url'] = $this->getUrl(); + $array['preview_url'] = $this->getPreviewUrl(); return $array; } -} \ No newline at end of file +} + +Document::deleted(function ($document) { + $same_path_count = DB::table('documents') + ->where('documents.account_id', '=', $document->account_id) + ->where('documents.path', '=', $document->path) + ->where('documents.disk', '=', $document->disk) + ->count(); + + if(!$same_path_count){ + $document->getDisk()->delete($document->path); + } + + if($document->preview){ + $same_preview_count = DB::table('documents') + ->where('documents.account_id', '=', $document->account_id) + ->where('documents.preview', '=', $document->preview) + ->where('documents.disk', '=', $document->disk) + ->count(); + if(!$same_preview_count){ + $document->getDisk()->delete($document->preview); + } + } + +}); \ No newline at end of file diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index aa6544e52f8b..53bb1d0d1a48 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -24,9 +24,14 @@ class EntityModel extends Eloquent Utils::fatalError(); } - $lastEntity = $className::withTrashed() - ->scope(false, $entity->account_id) - ->orderBy('public_id', 'DESC') + if(method_exists($className, 'withTrashed')){ + $lastEntity = $className::withTrashed() + ->scope(false, $entity->account_id); + } else { + $lastEntity = $className::scope(false, $entity->account_id); + } + + $lastEntity = $lastEntity->orderBy('public_id', 'DESC') ->first(); if ($lastEntity) { diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index 97bd3750078f..8697a232624e 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -5,7 +5,7 @@ use Utils; use Response; use App\Models\Document; use App\Ninja\Repositories\BaseRepository; -use Intervention\Image\Facades\Image; +use Intervention\Image\ImageManager; use Session; class DocumentRepository extends BaseRepository @@ -20,8 +20,6 @@ class DocumentRepository extends BaseRepository { return Document::scope() ->with('user') - ->withTrashed() - ->where('is_deleted', '=', false) ->get(); } @@ -72,48 +70,80 @@ class DocumentRepository extends BaseRepository $documentType = Document::$extensions[$extension]; $filePath = $uploaded->path(); - $fileContents = null; $name = $uploaded->getClientOriginalName(); - if(filesize($filePath)/1000 > env('MAX_DOCUMENT_SIZE', DEFAULT_MAX_DOCUMENT_SIZE)){ + if(filesize($filePath)/1000 > MAX_DOCUMENT_SIZE){ return Response::json([ 'error' => 'File too large', 'code' => 400 ], 400); } - if($documentType == 'image/gif'){ - // Convert gif to png - $img = Image::make($filePath); - - $fileContents = (string) $img->encode('png'); - $documentType = 'image/png'; - $name = pathinfo($name)['filename'].'.png'; - } - $documentTypeData = Document::$types[$documentType]; - - $hash = $fileContents?sha1($fileContents):sha1_file($filePath); + $hash = sha1_file($filePath); $filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentTypeData['extension']; $document = Document::createNew(); $disk = $document->getDisk(); if(!$disk->exists($filename)){// Have we already stored the same file - $disk->put($filename, $fileContents?$fileContents:file_get_contents($filePath)); + $disk->put($filename, file_get_contents($filePath)); + } + + // This is an image; check if we need to create a preview + if(in_array($documentType, array('image/jpeg','image/png','image/gif','image/bmp','image/tiff'))){ + $makePreview = false; + $imageSize = getimagesize($filePath); + $imgManagerConfig = array(); + if(in_array($documentType, array('image/gif','image/bmp','image/tiff'))){ + // Needs to be converted + $makePreview = true; + } else { + if($imageSize[0] > DOCUMENT_PREVIEW_SIZE || $imageSize[1] > DOCUMENT_PREVIEW_SIZE){ + $makePreview = true; + } + } + + if($documentType == 'image/bmp' || $documentType == 'image/tiff'){ + if(!class_exists('Imagick')){ + // Cant't read this + $makePreview = false; + } else { + $imgManagerConfig['driver'] = 'imagick'; + } + } + + if($makePreview){ + $previewType = 'jpg'; + if(in_array($documentType, array('image/png','image/gif','image/bmp','image/tiff'))){ + // Has transparency + $previewType = 'png'; + } + + $document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentTypeData['extension'].'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType; + if(!$disk->exists($document->preview)){ + // We haven't created a preview yet + $imgManager = new ImageManager($imgManagerConfig); + + $img = $imgManager->make($filePath); + $img->fit(DOCUMENT_PREVIEW_SIZE, DOCUMENT_PREVIEW_SIZE, function ($constraint) { + $constraint->upsize(); + }); + + $previewContent = (string) $img->encode($previewType); + $disk->put($document->preview, $previewContent); + } + } } $document->path = $filename; $document->type = $documentType; - $document->size = $fileContents?strlen($fileContents):filesize($filePath); + $document->size = filesize($filePath); $document->name = substr($name, -255); - if(!empty($documentTypeData['image'])){ - $imageSize = getimagesize($filePath); - if($imageSize){ - $document->width = $imageSize[0]; - $document->height = $imageSize[1]; - } + if(!empty($imageSize)){ + $document->width = $imageSize[0]; + $document->height = $imageSize[1]; } $document->save(); diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index a2b94af80e84..d69df1e2ae54 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -399,7 +399,8 @@ class InvoiceRepository extends BaseRepository $invoice->invoice_items()->forceDelete(); } - foreach ($data['documents'] as $document_id){ + $document_ids = !empty($data['documents'])?array_map('intval', $data['documents']):array();; + foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); if($document && !$checkSubPermissions || $document->canEdit()){ $document->invoice_id = $invoice->id; @@ -408,7 +409,7 @@ class InvoiceRepository extends BaseRepository } foreach ($invoice->documents as $document){ - if(!in_array($document->id, $data['documents'])){ + if(!in_array($document->public_id, $document_ids)){ // Removed if(!$checkSubPermissions || $document->canEdit()){ $document->delete(); diff --git a/composer.json b/composer.json index 45e6e1872640..9a52431d4511 100644 --- a/composer.json +++ b/composer.json @@ -66,7 +66,9 @@ "maatwebsite/excel": "~2.0", "ezyang/htmlpurifier": "~v4.7", "cerdic/css-tidy": "~v1.5", - "asgrim/ofxparser": "^1.1" + "asgrim/ofxparser": "^1.1", + "league/flysystem-aws-s3-v3": "~1.0", + "league/flysystem-rackspace": "~1.0" }, "require-dev": { "phpunit/phpunit": "~4.0", diff --git a/composer.lock b/composer.lock index 6ce01e65ba67..74b6ceefecbc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "e5e8524886bd38794a15e406acc3745a", - "content-hash": "6b3f343959ba3f330c425325574dfe28", + "hash": "aa7451471a31552d5d6dce491ec20272", + "content-hash": "d70446a01a12bb41eece33b979adc975", "packages": [ { "name": "agmscode/omnipay-agms", @@ -321,6 +321,86 @@ ], "time": "2015-12-11 11:08:57" }, + { + "name": "aws/aws-sdk-php", + "version": "3.17.1", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "f8c0cc9357e10896a5c57104f2c79d1b727d97d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/f8c0cc9357e10896a5c57104f2c79d1b727d97d0", + "reference": "f8c0cc9357e10896a5c57104f2c79d1b727d97d0", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "~5.3|~6.0.1|~6.1", + "guzzlehttp/promises": "~1.0", + "guzzlehttp/psr7": "~1.0", + "mtdowling/jmespath.php": "~2.2", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-json": "*", + "ext-openssl": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "ext-spl": "*", + "nette/neon": "^2.3", + "phpunit/phpunit": "~4.0|~5.0", + "psr/cache": "^1.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "time": "2016-03-22 19:19:22" + }, { "name": "barryvdh/laravel-debugbar", "version": "v2.2.0", @@ -579,7 +659,7 @@ "laravel" ], "abandoned": "OpenSkill/Datatable", - "time": "2015-04-29 07:00:36" + "time": "2015-11-23 21:33:41" }, { "name": "classpreloader/classpreloader", @@ -3056,6 +3136,100 @@ ], "time": "2016-03-14 21:54:11" }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "595e24678bf78f8107ebc9355d8376ae0eb712c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/595e24678bf78f8107ebc9355d8376ae0eb712c6", + "reference": "595e24678bf78f8107ebc9355d8376ae0eb712c6", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.0.0", + "league/flysystem": "~1.0", + "php": ">=5.5.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "^2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3v3\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Flysystem adapter for the AWS S3 SDK v3.x", + "time": "2015-11-19 08:44:16" + }, + { + "name": "league/flysystem-rackspace", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-rackspace.git", + "reference": "ba877e837f5dce60e78a0555de37eb9bfc7dd6b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-rackspace/zipball/ba877e837f5dce60e78a0555de37eb9bfc7dd6b9", + "reference": "ba877e837f5dce60e78a0555de37eb9bfc7dd6b9", + "shasum": "" + }, + "require": { + "league/flysystem": "~1.0", + "php": ">=5.4.0", + "rackspace/php-opencloud": "~1.16" + }, + "require-dev": { + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\Rackspace\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Flysystem adapter for Rackspace", + "time": "2016-03-11 12:13:42" + }, { "name": "league/fractal", "version": "0.13.0", @@ -3585,6 +3759,33 @@ ], "time": "2015-07-14 19:53:54" }, + { + "name": "mikemccabe/json-patch-php", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/mikemccabe/json-patch-php.git", + "reference": "b3af30a6aec7f6467c773cd49b2d974a70f7c0d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikemccabe/json-patch-php/zipball/b3af30a6aec7f6467c773cd49b2d974a70f7c0d4", + "reference": "b3af30a6aec7f6467c773cd49b2d974a70f7c0d4", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "mikemccabe\\JsonPatch\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "description": "Produce and apply json-patch objects", + "time": "2015-01-05 21:19:54" + }, { "name": "monolog/monolog", "version": "1.18.1", @@ -3707,6 +3908,61 @@ ], "time": "2016-01-26 21:23:30" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "192f93e43c2c97acde7694993ab171b3de284093" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/192f93e43c2c97acde7694993ab171b3de284093", + "reference": "192f93e43c2c97acde7694993ab171b3de284093", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "time": "2016-01-05 18:25:05" + }, { "name": "nesbot/carbon", "version": "1.21.0", @@ -5906,6 +6162,63 @@ ], "time": "2016-03-09 05:03:14" }, + { + "name": "rackspace/php-opencloud", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/rackspace/php-opencloud.git", + "reference": "d6b71feed7f9e7a4b52e0240a79f06473ba69c8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rackspace/php-opencloud/zipball/d6b71feed7f9e7a4b52e0240a79f06473ba69c8c", + "reference": "d6b71feed7f9e7a4b52e0240a79f06473ba69c8c", + "shasum": "" + }, + "require": { + "guzzle/guzzle": "~3.8", + "mikemccabe/json-patch-php": "~0.1", + "php": ">=5.4", + "psr/log": "~1.0" + }, + "require-dev": { + "apigen/apigen": "~4.0", + "fabpot/php-cs-fixer": "1.0.*@dev", + "jakub-onderka/php-parallel-lint": "0.*", + "phpspec/prophecy": "~1.4", + "phpunit/phpunit": "4.3.*", + "satooshi/php-coveralls": "0.6.*@dev" + }, + "type": "library", + "autoload": { + "psr-0": { + "OpenCloud": [ + "lib/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Jamie Hannaford", + "email": "jamie.hannaford@rackspace.com", + "homepage": "https://github.com/jamiehannaford" + } + ], + "description": "PHP SDK for Rackspace/OpenStack APIs", + "keywords": [ + "Openstack", + "nova", + "opencloud", + "rackspace", + "swift" + ], + "time": "2016-01-29 10:34:57" + }, { "name": "samvaughton/omnipay-barclays-epdq", "version": "2.2.0", diff --git a/config/filesystems.php b/config/filesystems.php index d0c683352bd5..da16e0e16766 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -62,7 +62,7 @@ return [ 'driver' => 's3', 'key' => env('S3_KEY', ''), 'secret' => env('S3_SECRET', ''), - 'region' => env('S3_REGION', ''), + 'region' => env('S3_REGION', 'us-east-1'), 'bucket' => env('S3_BUCKET', ''), ], @@ -72,7 +72,7 @@ return [ 'key' => env('RACKSPACE_KEY', ''), 'container' => env('RACKSPACE_CONTAINER', ''), 'endpoint' => env('RACKSPACE_ENDPOINT', 'https://identity.api.rackspacecloud.com/v2.0/'), - 'region' => env('RACKSPACE_REGION', 'https://identity.api.rackspacecloud.com/v2.0/'), + 'region' => env('RACKSPACE_REGION', 'IAD'), 'url_type' => env('RACKSPACE_URL_TYPE', 'publicURL') ], diff --git a/database/migrations/2016_03_22_168362_add_documents.php b/database/migrations/2016_03_22_168362_add_documents.php index 811fb3a1a5b8..f37ab0e859d5 100644 --- a/database/migrations/2016_03_22_168362_add_documents.php +++ b/database/migrations/2016_03_22_168362_add_documents.php @@ -28,6 +28,7 @@ class AddDocuments extends Migration { $t->unsignedInteger('invoice_id')->nullable(); $t->unsignedInteger('expense_id')->nullable(); $t->string('path'); + $t->string('preview'); $t->string('name'); $t->string('type'); $t->string('disk'); @@ -36,7 +37,6 @@ class AddDocuments extends Migration { $t->unsignedInteger('height')->nullable(); $t->timestamps(); - $t->softDeletes(); $t->foreign('account_id')->references('id')->on('accounts'); $t->foreign('user_id')->references('id')->on('users'); diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 59c662ccb5f6..0a9810d4160f 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -932,7 +932,7 @@ }, acceptedFiles:{!! json_encode(implode(',',array_keys(\App\Models\Document::$types))) !!}, addRemoveLinks:true, - maxFileSize:{{floatval(env('MAX_DOCUMENT_SIZE', DEFAULT_MAX_DOCUMENT_SIZE)/1000)}}, + maxFileSize:{{floatval(MAX_DOCUMENT_SIZE/1000)}}, dictDefaultMessage:{!! json_encode(trans('texts.document_upload_message')) !!} }); dropzone.on("addedfile",handleDocumentAdded); @@ -948,7 +948,7 @@ public_id:document.public_id(), status:Dropzone.SUCCESS, accepted:true, - url:document.url(), + url:document.preview_url()||document.url(), mock:true, index:i }; @@ -956,7 +956,7 @@ dropzone.emit('addedfile', mockFile); dropzone.emit('complete', mockFile); if(document.type().match(/image.*/)){ - dropzone.emit('thumbnail', mockFile, document.url()); + dropzone.emit('thumbnail', mockFile, document.preview_url()||document.url()); } dropzone.files.push(mockFile); } From 942f543bbb6b3d73319dc107cd65fda518fbfc13 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Wed, 23 Mar 2016 18:40:42 -0400 Subject: [PATCH 007/258] Display documents in invoice PDF --- Gruntfile.js | 2 +- app/Http/Controllers/AccountController.php | 5 +++ app/Http/Controllers/DocumentController.php | 25 +++++++++++ app/Http/Controllers/InvoiceController.php | 2 +- .../Controllers/PublicClientController.php | 44 ++++++++++++++++++- app/Http/routes.php | 4 +- app/Models/Document.php | 8 ++++ app/Ninja/Repositories/DocumentRepository.php | 35 ++++++++++++--- app/Ninja/Repositories/InvoiceRepository.php | 2 +- .../2016_03_22_168362_add_documents.php | 6 ++- public/built.js | 35 +++++++++++++-- public/js/pdf.pdfmake.js | 35 +++++++++++++-- public/js/{vfs_fonts.js => vfs.js} | 7 ++- public/pdf.built.js | 7 ++- resources/lang/en/texts.php | 5 ++- .../views/accounts/invoice_design.blade.php | 2 + resources/views/invoices/edit.blade.php | 13 ++++-- resources/views/invoices/history.blade.php | 7 ++- resources/views/invoices/pdf.blade.php | 2 +- resources/views/invoices/view.blade.php | 9 ++-- 20 files changed, 222 insertions(+), 33 deletions(-) rename public/js/{vfs_fonts.js => vfs.js} (52%) diff --git a/Gruntfile.js b/Gruntfile.js index 0b004f4ba583..ce22ba6de51a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -171,7 +171,7 @@ module.exports = function(grunt) { 'public/js/pdf_viewer.js', 'public/js/compatibility.js', 'public/js/pdfmake.min.js', - 'public/js/vfs_fonts.js', + 'public/js/vfs.js', ], dest: 'public/pdf.built.js', nonull: true diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 912ade56f962..6d06a2611022 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -347,6 +347,7 @@ class AccountController extends BaseController $client = new stdClass(); $contact = new stdClass(); $invoiceItem = new stdClass(); + $document = new stdClass(); $client->name = 'Sample Client'; $client->address1 = trans('texts.address1'); @@ -372,8 +373,11 @@ class AccountController extends BaseController $invoiceItem->notes = 'Notes'; $invoiceItem->product_key = 'Item'; + $document->base64 = 'data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAyAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNBCUAAAAAABAAAAAAAAAAAAAAAAAAAAAA/+4AIUFkb2JlAGTAAAAAAQMAEAMDBgkAAAW8AAALrQAAEWf/2wCEAAgGBgYGBggGBggMCAcIDA4KCAgKDhANDQ4NDRARDA4NDQ4MEQ8SExQTEg8YGBoaGBgjIiIiIycnJycnJycnJycBCQgICQoJCwkJCw4LDQsOEQ4ODg4REw0NDg0NExgRDw8PDxEYFhcUFBQXFhoaGBgaGiEhICEhJycnJycnJycnJ//CABEIAGQAlgMBIgACEQEDEQH/xADtAAABBQEBAAAAAAAAAAAAAAAAAQIDBAUGBwEBAAMBAQEAAAAAAAAAAAAAAAIDBAUBBhAAAQQCAQMDBQEAAAAAAAAAAgABAwQRBRIQIBMwIQYxIiMUFUARAAIBAgMFAwgHBwUBAAAAAAECAwARIRIEMUFRYROhIkIgcYGRsdFSIzDBMpKyFAVA4WJyM0MkUPGiU3OTEgABAgQBCQYEBwAAAAAAAAABEQIAITESAyBBUWFxkaGxIhAwgdEyE8HxYnLw4UJSgiMUEwEAAgIBAwQCAwEBAAAAAAABABEhMVFBYXEQgZGhILEwwdHw8f/aAAwDAQACEQMRAAAA9ScqiDlGjgRUUcqSCOVfTEeETZI/TABQBHCxAiDmcvz1O3rM7i7HG29J1nGW6c/ZO4i1ry9ZZwJOzk2Gc11N8YVe6FsZKEQqwR8v0vnEpz4isza7FaovCjNThxulztSxiz6597PwkfQ99R6vxT0S7N2yuXJpQceKrkIq3L9kK/OuR9F8rpjCsmdZXLUN+H0Obp9Hp8azkdPd1q58T21bV6XK6dcjW2UPGl0amXp5VdnIV3c5n6t508/srbbd+3Hbl2Ib8GXV2E59tXOvLwNmfv5sueVzWhPqsNggNdcKwOifnXlS4iDvkho4bP8ASEeyPrpZktFYLMbCPudZsNzzcsTdVc5CemqECqHoAEQBABXAOABAGtD0AH//2gAIAQIAAQUB9TkSnkPEFiKNhvcnhfysQuPbJwZijLkNUGZicWCZ3X1DsIRdZZlnKmPMnOImhsWBQSifR/o7sy+5fb0OIuU8EblCBxtFGQv14ssdjQxMXqf/2gAIAQMAAQUB9Qa5LwxipBck8bMjIY0BsXYJ4Q2QT2BdFK7uMGW/QJmKIo5OrimGZ0MDm4xjEw+PMhDibBi7Y6DjkIkT/iZn8uEzoSLBYdE7dcrzGmkFn68nx6n/2gAIAQEAAQUB9HCwsLHq5XJkxC/+ByZmsbSpCi2JG3GOM68rcOZOuU7IJuRJ+uFjsd8K1tCE55wIYpBYqrzHIAQlKdmty5KG6POC2RSTXwjUGxm8ywsLHX6KMJLrXNdLXCarQd4jeY5ZrHmLYwk0Vo5k85FJZlPjTOxYDySNa2H4wpTNYrLHZKQxhHJsHGzYsRFHe17KbYHI5tVZeGlxI67yOZmTx2wYbDpmsSu9iKCL49M/DtswNZrjb2GvjtW9XsY/EKliOSQXAXnaubRQ2JWoNJWvXbu1G0FmS0MOur+L+VPKNGs0FzvvaSjZUma8xwX5isVyhUFOWwUGg2LtV+OiSOnLAMNeig1tJ1Jr5RNor9Zq91pHz12N0dfTCtvbkcl7f6xr/wAjjvUKW3LgWv2VlRaXVg8NWnHG1aBNBaFmmtiQVDIJIJIyCyYEF1ibDSms9NlUa/THY7vXtb2tSzshj+JbBF8TeI/2vklNVvkVOeV61ck9SB1+qQLx3UVa9C47HDhHDJKEQw2eS5LKz0wzqbX1LCsfF6Mqajv6S/s7eurtmbeRg/EeS5LKyjCORnpCzxxNGsrksrKysrKysrKysrKysrKysrPXK917r3Xuvde/rf/aAAgBAgIGPwHvOlq6z0t3wbnNAFWg1+mS84LiQC6drJgfCJYTrf3UHlxhWA1T8GJ5KEF1aRb7YaD6cNovcmcn5xPDnXq6o9QaIQ9Z1S/OC3OyfgckXL/FxaeESBHjAkvARd7RxGNVtLgNJatYH+XG9p6+k9LdgFF2Q9uJhh7gJoUcQaEKoO8QUUJUGRG3slFSDrhQVifHsuY8jV6m7s3hDi9rsIn9Y6mH7tEe5h4oQuDNN2YIDDnPdc5yUCBBSU8jRsiuReGNu0pPvf/aAAgBAwIGPwHvFdLnEq6awBXWUhC8LojqcIlkETU6NEI5xJGq3eYJYiCpJQecJ7hI0Ycod/SVdS4pxcnKFb0pWrifhxgPUFuJ0+I05CgpEgHbacYAMytEoBXq+cG1zcMlM1x5+UTMzUhGkmEtKZ86iGNCMa1yyElHLtF1FnsijXN+kDdmi1zS3OLgUWJIn0JyHYhA5GJG7VQwhGZdkIM2Qh6vunzi4MC7Sm7IRe9//9oACAEBAQY/Af2u18eH7Bjsq2bO3wpjQUrldsRED3wvxGlkGpbvYAtgQeOHDzVYTdf+I7f+N/ZXcYX4Gx/CQeysYwfM1vxCspRkPP3j6MxQAYYGR9noG+i+q1Dtw8CUrRfNP2sO6gA8TE7qkeRMkUpvfHPMeWw5aMussuXBIr7uYW/qoJFpgzHYcAMOdXkyIN1+9b0sbVkXW7d+FhblsrLJKGTaGAC+uu4Q5pV1GQxObBk8J3X+g6rgvcmwZssY5ALiaZxNg7fZC4JzBONXn62olH/YTl7KJy5kG24GUEbBYbbbhXXDBpVwyKLqF3hicMaPX06cdpAvzzHGm6EkcEY4WUdgzH0CssbjUMONx3ud8ppRPpelN4Zdg9GXbSZFjY+IsQT90mo5XcRMD0mVAtrfFaszsGK3ubANy+ztxqOXiMfP5TPJgqgsTyFGXTuNPBISVVw5w43AIpfzMqzq++KS34lwodXSl5PCSc/Ze1dOJQFawyLhbje9hQSR3aTeLgKvIZb+2nZ5cbd1AM3o3UhddgtfxYbMBWWOMkbl/wBsTV54nEe0KFbtNArkj4bj7GolXTL8Ze1z671G6SNK4/qxnvxm+BymwtUulP8AbN18x8qSC9uopW/npYtVozLHGMomgN8Bh9miA/SnA7okGUE8G3dtG36fKrn+7G90B4gi+FWnMmYWsxxJvwzWvsoxh2yri4Pd5bi9Hpl5bDFU7q+ktc9lHoBQvEkAe+o1lkUByEkZTsW/xCpAJzB02ISFLgADZev8zRpqD8QBVv8A6Jann0yNplkFssq9RVIO0MmK7N4oMZBKhPe6FmHZa3qqPKdkdpBwPD6Bpf6L4szqbDmTfCsn6fqGmO54wV9m2upqcyse6WlNvRdhXSzJlOLMDm9GFZNMjytwQfXWX8uYv59nrx9lP+aPUbYFUlFHp2mguqTqxKLJK+LKP/VMfWKvKrsu5y5ZfWmFdTRytAx8UbYdtxQMpDFjhqYflSA7s4XBquttRz2NaunIpR+DeRJqiuYrgq8WOAoaiXVPEzYqkZCKOVt9X1DJPFsvKMp+8hqTStE0Er2xBDobG5FxY40kGi02nifZfMSSfNtr/OlcRHwxKO0A3q8smduDfL/FXTiQCPbbKHHrF6+WbH+B3TsufZRyTSfyu1/usR7ayPKM3wulj2VnAVGOJTZjxBGNZiuVvi+w331wPprLIbkbn7resd013hbz4fupbDYb38iTTE2z7DzGIoJrNN+ZjXDOO61h5rg0mp1Wmkk0yplEDG2Vt5wwNWH+NIdxJj9t1pZ/0/V5WQhk6gvzGI91fP0sesUeKI5W9X7qXTauJ9JM2AWYd0nhermNb+a3srxfeP118qdhyYBhWEkf81jf1Vnim658QfA+giulqUyNwbC/1GiLfLOOU7jypek3d8Q3Vw8r5sKt6PdV4i0Z5Yjtq2k1YmQbI5cfxe+ra39OLD44fd3qXSQaJ0uwJnlFsluFBSb2Fr+TldQw518pynLaO2rli7cT9Q/0r//aAAgBAgMBPxD8BHIj4/gUu+n/AKDL7Eqh2LDnpJp36uxcBVJSQBqzju2/1Mo/rVB3tkuO1ZHHZYne4pQ3+A1jS9SIA5pdrL6FN29E1HHIwAiNNrOl06RtUaBbO7u6gApbHBXuAv3EB7MGADleztFGRKsm7wY7RPX6jyyGlEcPVK65Tfd263KMLBdl5vh/uDZC0O5wdmKVo4YKKAOVMbNnutFAI9eEuQ4e6ahKuKj2+B/en0tbqrHmAfYICaGFNJdQyMh/5uV4l03drL4SfIR6aL1b1BlPXXmNhFlAM7NwL0U7zACUS0VtC3J6+u9zqhb2fqLSlI+JcuIO5SQ4R9ofyf/aAAgBAwMBPxD+RAWF0BeXwHuzQV9CbX26fUGyI3Q+OsxIrVsvtv6l5UovefjcHV637+PwAhSpEW03npcCcYFf6CUJoVSLxaKfBDaWsSw47vyTCEodeVls2/8AUQ7CBsMHauvOIZ9gwKrOdefH4MthVWOO9y9BzaCnDeJ8kzpIwbaLNkqtAQS0QFwTYlN+IQGULuC0pXHSWlpFWocCQV3A4dhwVblrrFrfXSZH08asO7MfiaKWfA2PeN7MUMgK5fu4Urrgge+T6jfLDqw7/wBkMAgG2DxzG9uzsd1xQBRbbbn1ENij2hXaE6AkMCOSsjnKOW/Qai9iTi/5f//aAAgBAQMBPxAIEqVKlSpUCEHoUiRjGX6BAlSpUqIIaIhUI6G34hXMIeiRjE9OkqB63HygG1aCOt3TKzCFkCino59iplOlzY8tvCMIxuwf0/mBqJ40DUb89L4/sgg43QRGuFT0ESVfo0gRlyha0dVlpKlKrm6raQySjYol1lVfgj8C3g6iJbHNxPeAW9yDaQdgrpMZAK1eq2o7Q7EFEVS8X6HaIQYrdr7U0YQobDxRja4mPhsgnSp/cLbjYA4K51OOKoU0zRiegjSEq4oFegvxGpy4QRr5JcRHqajXulVBqlghaxQnLR092G41E0g3djqcHWMXuExr0VmhZdW7FsLT+gynKYpXXjGV7wreJppoapXL7oQD0sBYvCAX4tIpESrHmFyooWQqCbMCN1vpBgtacBgtAYVZcF7afsYf9lQisQlRdvDkWyqGZBthXx7RPvKkUrlb5Q/CrdFT5neoWdIZSWgR/VBQwZ0nUGPeBAJdZvWE38qghbIlumjVcdMzdAL5o/BAVDYFa5xT2qVhDQIAA5pB+5aemryoxhX0jk3pALPvUXhzAK5y/XUnskCEqEqMLSHNUwwLAQBRotLMeIdlDn5FpRZUUm5R2ZJ7EpNZRMobAO5K5hOAUuBYHYG+8SddNHz0+EKEOCcKzlT1BZYb4uB90OpYUAVM2rcL3vCknNK+bjWGKs6bZa9oVhmRdpg/YWAAlUVJkcjdXD11Lgke0VcU2MbHfygaFKWEnTL5GJZzMyGuGMPMbSQlbPagPOZaKOHjusEyaLtXgeW3iK4+oDc4bNYnwcKiQaks/Caxh5wK7kdeZvb3LEJhAMqbKrhAqim522Qv5gPgqp9FxlL7mnZpXi3MxIMgDkG/ug65qHbsEF8zXvjwBFAU4jmwArRmKjV6XLdNd1TvoiF1X5vX/fMHBChWDvd+4paeJz4FDgzLjs70CdhHznQBjzv7Sxo8bd2NfcZmYNWs8RxQGYGe1+olGV9n7Z+0UPFyYwlYvmDNJctGQPGwnyQAWPv0haPhQ4abtsUxZfaFBalqvypK8pGizJpYO+aShBw+h2xgHf3CNeSAXzRnTRxS/szKo3P+IMAszsGE7iUiOwZy99tXZg3BCqz2L+qH0gU09RzxfaMDrstvwgKoDsPRrCLj7jcKSy6oH5pLZC0I+L/UPAvRNDQUa9oMU7aNedH3NWIKBWuO+m4lsAS60VfopKsCajNR6AT7l8D418EaQCisod0YIUK9U/PBh6loQegqKly/QfkBmNzMzM/i+jOk/9k='; + $invoice->client = $client; $invoice->invoice_items = [$invoiceItem]; + $invoice->documents = $account->isPro()?[$document,$document,$document,$document]:[]; $data['account'] = $account; $data['invoice'] = $invoice; @@ -749,6 +753,7 @@ class AccountController extends BaseController $account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false; $account->all_pages_header = Input::get('all_pages_header') ? true : false; $account->all_pages_footer = Input::get('all_pages_footer') ? true : false; + $account->invoice_embed_documents = Input::get('invoice_embed_documents') ? true : false; $account->header_font_id = Input::get('header_font_id'); $account->body_font_id = Input::get('body_font_id'); $account->primary_color = Input::get('primary_color'); diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 4b7718bfd817..d19909bae0ad 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -70,6 +70,31 @@ class DocumentController extends BaseController return $response; } + public function getVFSJS($publicId, $name){ + $document = Document::scope($publicId) + ->firstOrFail(); + + if(substr($name, -3)=='.js'){ + $name = substr($name, 0, -3); + } + + if(!$this->checkViewPermission($document, $response)){ + return $response; + } + + if(substr($document->type, 0, 6) != 'image/'){ + return Response::view('error', array('error'=>'Image does not exist!'), 404); + } + + $content = $document->preview?$document->getRawPreview():$document->getRaw(); + $content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")'; + $response = Response::make($content, 200); + $response->header('content-type', 'text/javascript'); + $response->header('cache-control', 'max-age=31536000'); + + return $response; + } + public function postUpload() { if (!Utils::isPro()) { diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 138909a9e13d..8cd6049d0be4 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -536,7 +536,7 @@ class InvoiceController extends BaseController public function invoiceHistory($publicId) { $invoice = Invoice::withTrashed()->scope($publicId)->firstOrFail(); - $invoice->load('user', 'invoice_items', 'account.country', 'client.contacts', 'client.country'); + $invoice->load('user', 'invoice_items', 'documents', 'account.country', 'client.contacts', 'client.country'); $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->is_pro = Auth::user()->isPro(); diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index d067ff38a740..c854189ac240 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -7,10 +7,12 @@ use URL; use Input; use Utils; use Request; +use Response; use Session; use Datatable; use App\Models\Gateway; use App\Models\Invitation; +use App\Models\Document; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\PaymentRepository; use App\Ninja\Repositories\ActivityRepository; @@ -22,8 +24,9 @@ class PublicClientController extends BaseController { private $invoiceRepo; private $paymentRepo; + private $documentRepo; - public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo, PaymentService $paymentService) + public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo, PaymentService $paymentService) { $this->invoiceRepo = $invoiceRepo; $this->paymentRepo = $paymentRepo; @@ -372,5 +375,44 @@ class PublicClientController extends BaseController return $invitation; } + + + + public function getDocumentVFSJS($publicId, $name){ + if (!$invitation = $this->getInvitation()) { + return $this->returnError(); + } + + $clientId = $invitation->invoice->client_id; + $document = Document::scope($publicId, $invitation->account_id)->first(); + + + if(!$document || substr($document->type, 0, 6) != 'image/'){ + return Response::view('error', array('error'=>'Image does not exist!'), 404); + } + + $authorized = false; + if($document->expense && $document->expense->client_id == $invitation->invoice->client_id){ + $authorized = true; + } else if($document->invoice && $document->invoice->client_id == $invitation->invoice->client_id){ + $authorized = true; + } + + if(!$authorized){ + return Response::view('error', array('error'=>'Not authorized'), 403); + } + + if(substr($name, -3)=='.js'){ + $name = substr($name, 0, -3); + } + + $content = $document->preview?$document->getRawPreview():$document->getRaw(); + $content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")'; + $response = Response::make($content, 200); + $response->header('content-type', 'text/javascript'); + $response->header('cache-control', 'max-age=31536000'); + + return $response; + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 6a4970194ca6..e70444847a58 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -47,6 +47,7 @@ Route::group(['middleware' => 'auth:client'], function() { Route::get('client/invoices', 'PublicClientController@invoiceIndex'); Route::get('client/payments', 'PublicClientController@paymentIndex'); Route::get('client/dashboard', 'PublicClientController@dashboard'); + Route::get('client/document/js/{public_id}/{filename}', 'PublicClientController@getDocumentVFSJS'); }); Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable')); @@ -133,6 +134,7 @@ Route::group(['middleware' => 'auth:user'], function() { Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); Route::get('document/{public_id}/{filename?}', 'DocumentController@get'); + Route::get('document/js/{public_id}/{filename}', 'DocumentController@getVFSJS'); Route::get('document/preview/{public_id}/{filename?}', 'DocumentController@getPreview'); Route::post('document', 'DocumentController@postUpload'); @@ -430,7 +432,7 @@ if (!defined('CONTACT_EMAIL')) { define('MAX_LOGO_FILE_SIZE', 200); // KB define('MAX_FAILED_LOGINS', 10); define('MAX_DOCUMENT_SIZE', env('MAX_DOCUMENT_SIZE', 10000));// KB - define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 150));// pixels + define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300));// pixels define('DEFAULT_FONT_SIZE', 9); define('DEFAULT_HEADER_FONT', 1);// Roboto define('DEFAULT_BODY_FONT', 1);// Roboto diff --git a/app/Models/Document.php b/app/Models/Document.php index f342d1ff08e1..2d61cb7e3ba6 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -132,6 +132,14 @@ class Document extends EntityModel return url('document/'.$this->public_id.'/'.$this->name); } + public function getVFSJSUrl(){ + return url('document/js/'.$this->public_id.'/'.$this->name.'.js'); + } + + public function getClientVFSJSUrl(){ + return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js'); + } + public function getPreviewUrl(){ return $this->preview?url('document/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; } diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index 8697a232624e..08af5bb57780 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -94,12 +94,14 @@ class DocumentRepository extends BaseRepository if(in_array($documentType, array('image/jpeg','image/png','image/gif','image/bmp','image/tiff'))){ $makePreview = false; $imageSize = getimagesize($filePath); + $width = $imageSize[0]; + $height = $imageSize[1]; $imgManagerConfig = array(); if(in_array($documentType, array('image/gif','image/bmp','image/tiff'))){ // Needs to be converted $makePreview = true; } else { - if($imageSize[0] > DOCUMENT_PREVIEW_SIZE || $imageSize[1] > DOCUMENT_PREVIEW_SIZE){ + if($width > DOCUMENT_PREVIEW_SIZE || $height > DOCUMENT_PREVIEW_SIZE){ $makePreview = true; } } @@ -126,14 +128,30 @@ class DocumentRepository extends BaseRepository $imgManager = new ImageManager($imgManagerConfig); $img = $imgManager->make($filePath); - $img->fit(DOCUMENT_PREVIEW_SIZE, DOCUMENT_PREVIEW_SIZE, function ($constraint) { - $constraint->upsize(); - }); + + if($width <= DOCUMENT_PREVIEW_SIZE && $height <= DOCUMENT_PREVIEW_SIZE){ + $previewWidth = $width; + $previewHeight = $height; + } else if($width > $height) { + $previewWidth = DOCUMENT_PREVIEW_SIZE; + $previewHeight = $height * DOCUMENT_PREVIEW_SIZE / $width; + } else { + $previewHeight = DOCUMENT_PREVIEW_SIZE; + $previewWidth = $width * DOCUMENT_PREVIEW_SIZE / $height; + } + + $img->resize($previewWidth, $previewHeight); $previewContent = (string) $img->encode($previewType); $disk->put($document->preview, $previewContent); + $base64 = base64_encode($previewContent); } - } + else{ + $base64 = base64_encode($disk->get($document->preview)); + } + }else{ + $base64 = base64_encode(file_get_contents($filePath)); + } } $document->path = $filename; @@ -147,11 +165,16 @@ class DocumentRepository extends BaseRepository } $document->save(); + $doc_array = $document->toArray(); + if(!empty($base64)){ + $mime = !empty($previewType)?Document::$extensions[$previewType]:$documentType; + $doc_array['base64'] = 'data:'.$mime.';base64,'.$base64; + } return Response::json([ 'error' => false, - 'document' => $document, + 'document' => $doc_array, 'code' => 200 ], 200); } diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index d69df1e2ae54..bf9cc59c39df 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -600,7 +600,7 @@ class InvoiceRepository extends BaseRepository return false; } - $invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country'); + $invoice->load('user', 'invoice_items', 'documents', 'invoice_design', 'account.country', 'client.contacts', 'client.country'); $client = $invoice->client; if (!$client || $client->is_deleted) { diff --git a/database/migrations/2016_03_22_168362_add_documents.php b/database/migrations/2016_03_22_168362_add_documents.php index f37ab0e859d5..1fcaea379021 100644 --- a/database/migrations/2016_03_22_168362_add_documents.php +++ b/database/migrations/2016_03_22_168362_add_documents.php @@ -10,14 +10,15 @@ class AddDocuments extends Migration { */ public function up() { - /*Schema::table('accounts', function($table) { + Schema::table('accounts', function($table) { $table->string('logo')->nullable()->default(null); $table->unsignedInteger('logo_width'); $table->unsignedInteger('logo_height'); $table->unsignedInteger('logo_size'); + $table->boolean('invoice_embed_documents')->default(1); }); - DB::table('accounts')->update(array('logo' => ''));*/ + DB::table('accounts')->update(array('logo' => '')); Schema::dropIfExists('documents'); Schema::create('documents', function($t) { @@ -59,6 +60,7 @@ class AddDocuments extends Migration { $table->dropColumn('logo_width'); $table->dropColumn('logo_height'); $table->dropColumn('logo_size'); + $table->dropColumn('invoice_embed_documents'); }); Schema::dropIfExists('documents'); diff --git a/public/built.js b/public/built.js index 8b12b9693fbb..6a2954d3a786 100644 --- a/public/built.js +++ b/public/built.js @@ -31101,11 +31101,12 @@ function GetPdfMake(invoice, javascript, callback) { function addFont(font){ if(window.ninjaFontVfs[font.folder]){ + folder = 'fonts/'+font.folder; pdfMake.fonts[font.name] = { - normal: font.folder+'/'+font.normal, - italics: font.folder+'/'+font.italics, - bold: font.folder+'/'+font.bold, - bolditalics: font.folder+'/'+font.bolditalics + normal: folder+'/'+font.normal, + italics: folder+'/'+font.italics, + bold: folder+'/'+font.bold, + bolditalics: folder+'/'+font.bolditalics } } } @@ -31136,6 +31137,7 @@ NINJA.decodeJavascript = function(invoice, javascript) 'invoiceDetailsHeight': (NINJA.invoiceDetails(invoice).length * 16) + 16, 'invoiceLineItems': NINJA.invoiceLines(invoice), 'invoiceLineItemColumns': NINJA.invoiceColumns(invoice), + 'invoiceDocuments' : NINJA.invoiceDocuments(invoice), 'quantityWidth': NINJA.quantityWidth(invoice), 'taxWidth': NINJA.taxWidth(invoice), 'clientDetails': NINJA.clientDetails(invoice), @@ -31393,6 +31395,31 @@ NINJA.invoiceLines = function(invoice) { return NINJA.prepareDataTable(grid, 'invoiceItems'); } +NINJA.invoiceDocuments = function(invoice) { + if(!invoice.documents || !invoice.account.invoice_embed_documents)return[]; + var stack = []; + var stackItem = null; + + var j = 0; + for (var i = 0; i < invoice.documents.length; i++) { + var document = invoice.documents[i]; + var path = document.base64; + if(!path && (document.preview_url || document.type == 'image/png' || document.type == 'image/jpeg')){ + path = 'docs/'+document.public_id+'/'+document.name; + } + if(path && (window.pdfMake.vfs[path] || document.base64)){ + if(j%3==0){ + stackItem = {columns:[]}; + stack.push(stackItem); + } + stackItem.columns.push({stack:[{image:path,style:'invoiceDocument',fit:[150,150]}], width:175}) + j++; + } + } + + return {stack:stack}; +} + NINJA.subtotals = function(invoice, hideBalance) { if (!invoice) { diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index f8f30f4b546f..6989916a887e 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -109,11 +109,12 @@ function GetPdfMake(invoice, javascript, callback) { function addFont(font){ if(window.ninjaFontVfs[font.folder]){ + folder = 'fonts/'+font.folder; pdfMake.fonts[font.name] = { - normal: font.folder+'/'+font.normal, - italics: font.folder+'/'+font.italics, - bold: font.folder+'/'+font.bold, - bolditalics: font.folder+'/'+font.bolditalics + normal: folder+'/'+font.normal, + italics: folder+'/'+font.italics, + bold: folder+'/'+font.bold, + bolditalics: folder+'/'+font.bolditalics } } } @@ -144,6 +145,7 @@ NINJA.decodeJavascript = function(invoice, javascript) 'invoiceDetailsHeight': (NINJA.invoiceDetails(invoice).length * 16) + 16, 'invoiceLineItems': NINJA.invoiceLines(invoice), 'invoiceLineItemColumns': NINJA.invoiceColumns(invoice), + 'invoiceDocuments' : NINJA.invoiceDocuments(invoice), 'quantityWidth': NINJA.quantityWidth(invoice), 'taxWidth': NINJA.taxWidth(invoice), 'clientDetails': NINJA.clientDetails(invoice), @@ -401,6 +403,31 @@ NINJA.invoiceLines = function(invoice) { return NINJA.prepareDataTable(grid, 'invoiceItems'); } +NINJA.invoiceDocuments = function(invoice) { + if(!invoice.documents || !invoice.account.invoice_embed_documents)return[]; + var stack = []; + var stackItem = null; + + var j = 0; + for (var i = 0; i < invoice.documents.length; i++) { + var document = invoice.documents[i]; + var path = document.base64; + if(!path && (document.preview_url || document.type == 'image/png' || document.type == 'image/jpeg')){ + path = 'docs/'+document.public_id+'/'+document.name; + } + if(path && (window.pdfMake.vfs[path] || document.base64)){ + if(j%3==0){ + stackItem = {columns:[]}; + stack.push(stackItem); + } + stackItem.columns.push({stack:[{image:path,style:'invoiceDocument',fit:[150,150]}], width:175}) + j++; + } + } + + return {stack:stack}; +} + NINJA.subtotals = function(invoice, hideBalance) { if (!invoice) { diff --git a/public/js/vfs_fonts.js b/public/js/vfs.js similarity index 52% rename from public/js/vfs_fonts.js rename to public/js/vfs.js index 76e2d87ee432..fcdb3d206138 100644 --- a/public/js/vfs_fonts.js +++ b/public/js/vfs.js @@ -3,7 +3,12 @@ if(window.ninjaFontVfs)ninjaLoadFontVfs(); function ninjaLoadFontVfs(){ jQuery.each(window.ninjaFontVfs, function(font, files){ jQuery.each(files, function(filename, file){ - window.pdfMake.vfs[font+'/'+filename] = file; + window.pdfMake.vfs['fonts/'+font+'/'+filename] = file; }); }) +} +function ninjaAddVFSDoc(name,content){ + window.pdfMake.vfs['docs/'+name] = content; + if(window.refreshPDF)refreshPDF(true); + jQuery(document).trigger('ninjaVFSDocAdded'); } \ No newline at end of file diff --git a/public/pdf.built.js b/public/pdf.built.js index 9b663502b712..892149f88bd9 100644 --- a/public/pdf.built.js +++ b/public/pdf.built.js @@ -7926,7 +7926,12 @@ if(window.ninjaFontVfs)ninjaLoadFontVfs(); function ninjaLoadFontVfs(){ jQuery.each(window.ninjaFontVfs, function(font, files){ jQuery.each(files, function(filename, file){ - window.pdfMake.vfs[font+'/'+filename] = file; + window.pdfMake.vfs['fonts/'+font+'/'+filename] = file; }); }) +} +function ninjaAddVFSDoc(name,content){ + window.pdfMake.vfs['docs/'+name] = content; + if(window.refreshPDF)refreshPDF(true); + jQuery(document).trigger('ninjaVFSDocAdded'); } \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index b855b65c9a6c..284ad739d853 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1099,8 +1099,9 @@ $LANG = array( // Documents 'invoice_documents' => 'Attached Documents', - 'document_upload_message' => 'Drop files here or click to upload.' - + 'document_upload_message' => 'Drop files here or click to upload.', + 'invoice_embed_documents' => 'Embed Documents', + 'invoice_embed_documents_help' => 'Include attached images in the invoice.', ); return $LANG; diff --git a/resources/views/accounts/invoice_design.blade.php b/resources/views/accounts/invoice_design.blade.php index 4af173818dad..9576afbf1d92 100644 --- a/resources/views/accounts/invoice_design.blade.php +++ b/resources/views/accounts/invoice_design.blade.php @@ -48,6 +48,7 @@ function getPDFString(cb) { invoice.is_pro = {!! Auth::user()->isPro() ? 'true' : 'false' !!}; invoice.account.hide_quantity = $('#hide_quantity').is(":checked"); + invoice.account.invoice_embed_documents = $('#invoice_embed_documents').is(":checked"); invoice.account.hide_paid_to_date = $('#hide_paid_to_date').is(":checked"); invoice.invoice_design_id = $('#invoice_design_id').val(); @@ -207,6 +208,7 @@ {!! Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) !!} {!! Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) !!} + {!! Former::checkbox('invoice_embed_documents')->text(trans('texts.invoice_embed_documents_help')) !!} diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 0a9810d4160f..bf2ce391c67a 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -270,7 +270,7 @@
  • {{ trans("texts.{$entityType}_terms") }}
  • {{ trans("texts.{$entityType}_footer") }}
  • - @if (Auth::user()->account->isPro()) + @if ($account->isPro())
  • {{ trans("texts.{$entityType}_documents") }}
  • @endif @@ -304,7 +304,7 @@ ') !!} - @if (Auth::user()->account->isPro()) + @if ($account->isPro())
    @@ -1319,7 +1319,7 @@ model.invoice().invoice_number(number); } - @if (Auth::user()->account->isPro()) + @if ($account->isPro()) function handleDocumentAdded(file){ if(file.mock)return; file.index = model.invoice().documents().length; @@ -1328,14 +1328,21 @@ function handleDocumentRemoved(file){ model.invoice().removeDocument(file.public_id); + refreshPDF(true); } function handleDocumentUploaded(file, response){ file.public_id = response.document.public_id model.invoice().documents()[file.index].update(response.document); + refreshPDF(true); } @endif + @if ($account->isPro() && $account->invoice_embed_documents) + @foreach ($invoice->documents as $document) + + @endforeach + @endif @stop diff --git a/resources/views/invoices/history.blade.php b/resources/views/invoices/history.blade.php index 3a9fd36e333d..19d7eeff5223 100644 --- a/resources/views/invoices/history.blade.php +++ b/resources/views/invoices/history.blade.php @@ -56,5 +56,10 @@
     
    @include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800]) - + + @if (Auth::user()->account->isPro() && Auth::user()->account->invoice_embed_documents) + @foreach ($invoice->documents as $document) + + @endforeach + @endif @stop \ No newline at end of file diff --git a/resources/views/invoices/pdf.blade.php b/resources/views/invoices/pdf.blade.php index 2c4d97cf97ed..1c8389affeba 100644 --- a/resources/views/invoices/pdf.blade.php +++ b/resources/views/invoices/pdf.blade.php @@ -123,7 +123,7 @@ $('#theFrame').attr('src', string).show(); } else { if (isRefreshing) { - //needsRefresh = true; + needsRefresh = true; return; } isRefreshing = true; diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index c1ba3822899a..2ad564f9fdd7 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -45,7 +45,11 @@ @endif

     

    - + @if ($account->isPro() && $account->invoice_embed_documents) + @foreach ($invoice->documents as $document) + + @endforeach + @endif From e3c59101ba31485be1daa968cdcf66c517898433 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 24 Mar 2016 09:14:27 +0200 Subject: [PATCH 013/258] Fixed invitation iframe URL --- app/Models/Invitation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Invitation.php b/app/Models/Invitation.php index 2cc71029578d..7abd074a08b4 100644 --- a/app/Models/Invitation.php +++ b/app/Models/Invitation.php @@ -40,7 +40,7 @@ class Invitation extends EntityModel if ($this->account->isPro()) { if ($iframe_url) { - return "{$iframe_url}/?{$this->invitation_key}"; + return "{$iframe_url}?{$this->invitation_key}"; } elseif ($this->account->subdomain) { $url = Utils::replaceSubdomain($url, $this->account->subdomain); } From 1c5c45a1e19e6fa87af231afe1fc4046c3ff987a Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Thu, 24 Mar 2016 11:33:28 -0400 Subject: [PATCH 014/258] Improved handling of various document types; better documents zip --- app/Http/Controllers/DocumentController.php | 10 +- .../Controllers/PublicClientController.php | 128 +++++++++--------- app/Models/Document.php | 79 ++++++++--- app/Ninja/Repositories/DocumentRepository.php | 32 +++-- app/Providers/AppServiceProvider.php | 6 + public/built.js | 6 +- public/js/pdf.pdfmake.js | 6 +- resources/lang/en/texts.php | 2 +- resources/views/invoices/edit.blade.php | 19 ++- resources/views/invoices/history.blade.php | 4 +- resources/views/invoices/view.blade.php | 8 +- 11 files changed, 183 insertions(+), 117 deletions(-) diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 6c824dab61f6..593ab595f377 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -46,7 +46,7 @@ class DocumentController extends BaseController if($stream){ $headers = [ - 'Content-Type' => $document->type, + 'Content-Type' => Document::$types[$document->type]['mime'], 'Content-Length' => $document->size, ]; @@ -56,7 +56,7 @@ class DocumentController extends BaseController } else{ $response = Response::make($document->getRaw(), 200); - $response->header('content-type', $document->type); + $response->header('content-type', Document::$types[$document->type]['mime']); } return $response; @@ -80,9 +80,9 @@ class DocumentController extends BaseController return redirect($direct_url); } - $extension = pathinfo($document->preview, PATHINFO_EXTENSION); + $previewType = pathinfo($document->preview, PATHINFO_EXTENSION); $response = Response::make($document->getRawPreview(), 200); - $response->header('content-type', Document::$extensions[$extension]); + $response->header('content-type', Document::$types[$previewType]['mime']); return $response; } @@ -99,7 +99,7 @@ class DocumentController extends BaseController return $response; } - if(substr($document->type, 0, 6) != 'image/'){ + if(!$document->isPDFEmbeddable()){ return Response::view('error', array('error'=>'Image does not exist!'), 404); } diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index 6c2ad8cd4960..7aca779532ba 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -19,8 +19,7 @@ use App\Ninja\Repositories\ActivityRepository; use App\Events\InvoiceInvitationWasViewed; use App\Events\QuoteInvitationWasViewed; use App\Services\PaymentService; -use League\Flysystem\Filesystem; -use League\Flysystem\ZipArchive\ZipArchiveAdapter; +use Barracuda\ArchiveStream\ZipArchive; class PublicClientController extends BaseController { @@ -138,8 +137,13 @@ class PublicClientController extends BaseController 'phantomjs' => Input::has('phantomjs'), ); - if($account->isPro() && $this->canCreateInvoiceDocsZip($invoice)){ - $data['documentsZipURL'] = URL::to("client/documents/{$invitation->invitation_key}"); + if($account->isPro() && $this->canCreateZip()){ + $zipDocs = $this->getInvoiceZipDocuments($invoice, $size); + + if(count($zipDocs) > 1){ + $data['documentsZipURL'] = URL::to("client/documents/{$invitation->invitation_key}"); + $data['documentsZipSize'] = $size; + } } return View::make('invoices.view', $data); @@ -172,6 +176,12 @@ class PublicClientController extends BaseController return $paymentTypes; } + + protected function humanFilesize($bytes, $decimals = 2) { + $size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB'); + $factor = floor((strlen($bytes) - 1) / 3); + return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor]; + } public function download($invitationKey) { @@ -391,7 +401,7 @@ class PublicClientController extends BaseController $document = Document::scope($publicId, $invitation->account_id)->first(); - if(!$document || substr($document->type, 0, 6) != 'image/'){ + if(!$document->isPDFEmbeddable()){ return Response::view('error', array('error'=>'Image does not exist!'), 404); } @@ -423,19 +433,43 @@ class PublicClientController extends BaseController return function_exists('gmp_init'); } - protected function canCreateInvoiceDocsZip($invoice){ - if(!$this->canCreateZip())return false; - if(count($invoice->documents) == 1)return false; - + protected function getInvoiceZipDocuments($invoice, &$size=0){ + $documents = $invoice->documents->sortBy('size'); + + $size = 0; $maxSize = MAX_ZIP_DOCUMENTS_SIZE * 1000; - $i = 0; - foreach($invoice->documents as $document){ - if($document->size <= $maxSize)$i++; - if($i > 1){ - return true; + $toZip = array(); + foreach($documents as $document){ + if($size + $document->size > $maxSize)break; + + if(!empty($toZip[$document->name])){ + // This name is taken + if($toZip[$document->name]->hash != $document->hash){ + // 2 different files with the same name + $nameInfo = pathinfo($document->name); + + for($i = 1;; $i++){ + $name = $nameInfo['filename'].' ('.$i.').'.$nameInfo['extension']; + + if(empty($toZip[$name])){ + $toZip[$name] = $document; + $size += $document->size; + break; + } else if ($toZip[$name]->hash == $document->hash){ + // We're not adding this after all + break; + } + } + + } + } + else{ + $toZip[$document->name] = $document; + $size += $document->size; } } - return false; + + return $toZip; } public function getInvoiceDocumentsZip($invitationKey){ @@ -451,64 +485,24 @@ class PublicClientController extends BaseController return Response::view('error', array('error'=>'No documents'), 404); } - $documents = $invoice->documents->sortBy('size'); - - $size = 0; - $maxSize = MAX_ZIP_DOCUMENTS_SIZE * 1000; - $toZip = array(); - foreach($documents as $document){ - $size += $document->size; - if($size > $maxSize)break; - - if(!empty($toZip[$document->name])){ - $hasSameHash = false; - foreach($toZip[$document->name] as $sameName){ - if($sameName->hash == $document->hash){ - $hasSameHash = true; - break; - } - } - - if(!$hasSameHash){ - // 2 different files with the same name - $toZip[$document->name][] = $document; - } - else{ - // We're not adding this after all - $size -= $document->size; - } - } - else{ - $toZip[$document->name] = array($document); - } - } + $toZip = $this->getInvoiceZipDocuments($invoice); if(!count($toZip)){ return Response::view('error', array('error'=>'No documents small enough'), 404); } - $zip = new \Barracuda\ArchiveStream\ZipArchive($invitation->account->name.' Invoice '.$invoice->invoice_number.'.zip'); + $zip = new ZipArchive($invitation->account->name.' Invoice '.$invoice->invoice_number.'.zip'); return Response::stream(function() use ($toZip, $zip) { - foreach($toZip as $documentsSameName){ - $i = 0; - foreach($documentsSameName as $document){ - $name = $document->name; - - if($i){ - $nameInfo = pathinfo($document->name); - $name = $nameInfo['filename'].' ('.$i.').'.$nameInfo['extension']; - } - - $fileStream = $document->getStream(); - if($fileStream){ - $zip->init_file_stream_transfer($name, $document->size); - while ($buffer = fread($fileStream, 8192))$zip->stream_file_part($buffer); - fclose($fileStream); - $zip->complete_file_stream(); - } - else{ - $zip->add_file($name, $document->getRaw()); - } + foreach($toZip as $name=>$document){ + $fileStream = $document->getStream(); + if($fileStream){ + $zip->init_file_stream_transfer($name, $document->size, array('time'=>$document->created_at->timestamp)); + while ($buffer = fread($fileStream, 256000))$zip->stream_file_part($buffer); + fclose($fileStream); + $zip->complete_file_stream(); + } + else{ + $zip->add_file($name, $document->getRaw()); } } $zip->finish(); diff --git a/app/Models/Document.php b/app/Models/Document.php index 92d3e79439d6..e1f01b58421f 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -5,31 +5,68 @@ use DB; class Document extends EntityModel { - public static $extensions = array( - 'png' => 'image/png', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'pdf' => 'application/pdf', - 'gif' => 'image/gif' + public static $extraExtensions = array( + 'jpg' => 'jpeg', + 'tif' => 'tiff', + ); + + public static $allowedMimes = array(// Used by Dropzone.js; does not affect what the server accepts + 'image/png', 'image/jpeg', 'image/tiff', 'application/pdf', 'image/gif', 'image/vnd.adobe.photoshop', 'text/plain', + 'application/zip', 'application/msword', + 'application/excel', 'application/vnd.ms-excel', 'application/x-excel', 'application/x-msexcel', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/postscript', 'image/svg+xml', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.ms-powerpoint', ); public static $types = array( - 'image/png' => array( - 'extension' => 'png', + 'png' => array( + 'mime' => 'image/png', ), - 'image/jpeg' => array( - 'extension' => 'jpeg', + 'ai' => array( + 'mime' => 'application/postscript', ), - 'image/tiff' => array( - 'extension' => 'tiff', + 'svg' => array( + 'mime' => 'image/svg+xml', ), - 'image/gif' => array( - 'extension' => 'gif', + 'jpeg' => array( + 'mime' => 'image/jpeg', ), - 'application/pdf' => array( - 'extension' => 'pdf', + 'tiff' => array( + 'mime' => 'image/tiff', + ), + 'pdf' => array( + 'mime' => 'application/pdf', + ), + 'gif' => array( + 'mime' => 'image/gif', + ), + 'psd' => array( + 'mime' => 'image/vnd.adobe.photoshop', + ), + 'txt' => array( + 'mime' => 'text/plain', + ), + 'zip' => array( + 'mime' => 'application/zip', + ), + 'doc' => array( + 'mime' => 'application/msword', + ), + 'xls' => array( + 'mime' => 'application/vnd.ms-excel', + ), + 'ppt' => array( + 'mime' => 'application/vnd.ms-powerpoint', + ), + 'xlsx' => array( + 'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ), + 'docx' => array( + 'mime' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + ), + 'pptx' => array( + 'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ), ); @@ -142,11 +179,17 @@ class Document extends EntityModel return url('client/document/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); } + public function isPDFEmbeddable(){ + return $this->type == 'jpeg' || $this->type == 'png' || $this->preview; + } + public function getVFSJSUrl(){ + if(!$this->isPDFEmbeddable())return null; return url('document/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getClientVFSJSUrl(){ + if(!$this->isPDFEmbeddable())return null; return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js'); } diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index 2ce58720de03..ce4c7fd9d8a4 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -61,14 +61,22 @@ class DocumentRepository extends BaseRepository $uploaded = $input['file']; $extension = strtolower($uploaded->extension()); - if(empty(Document::$extensions[$extension])){ + if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){ + $documentType = Document::$extraExtensions[$extension]; + } + else{ + $documentType = $extension; + } + + if(empty(Document::$types[$documentType])){ return Response::json([ 'error' => 'Unsupported extension', 'code' => 400 ], 400); } - - $documentType = Document::$extensions[$extension]; + + $documentTypeData = Document::$types[$documentType]; + $filePath = $uploaded->path(); $name = $uploaded->getClientOriginalName(); $size = filesize($filePath); @@ -80,10 +88,10 @@ class DocumentRepository extends BaseRepository ], 400); } - $documentTypeData = Document::$types[$documentType]; + $hash = sha1_file($filePath); - $filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentTypeData['extension']; + $filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType; $document = Document::createNew(); $disk = $document->getDisk(); @@ -94,20 +102,20 @@ class DocumentRepository extends BaseRepository } // This is an image; check if we need to create a preview - if(in_array($documentType, array('image/jpeg','image/png','image/gif','image/bmp','image/tiff'))){ + if(in_array($documentType, array('jpeg','png','gif','bmp','tiff','psd'))){ $makePreview = false; $imageSize = getimagesize($filePath); $width = $imageSize[0]; $height = $imageSize[1]; $imgManagerConfig = array(); - if(in_array($documentType, array('image/gif','image/bmp','image/tiff'))){ + if(in_array($documentType, array('gif','bmp','tiff','psd'))){ // Needs to be converted $makePreview = true; } else if($width > DOCUMENT_PREVIEW_SIZE || $height > DOCUMENT_PREVIEW_SIZE){ $makePreview = true; } - if($documentType == 'image/bmp' || $documentType == 'image/tiff'){ + if(in_array($documentType,array('bmp','tiff','psd'))){ if(!class_exists('Imagick')){ // Cant't read this $makePreview = false; @@ -117,13 +125,13 @@ class DocumentRepository extends BaseRepository } if($makePreview){ - $previewType = 'jpg'; - if(in_array($documentType, array('image/png','image/gif','image/bmp','image/tiff'))){ + $previewType = 'jpeg'; + if(in_array($documentType, array('png','gif','tiff','psd'))){ // Has transparency $previewType = 'png'; } - $document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentTypeData['extension'].'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType; + $document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType.'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType; if(!$disk->exists($document->preview)){ // We haven't created a preview yet $imgManager = new ImageManager($imgManagerConfig); @@ -170,7 +178,7 @@ class DocumentRepository extends BaseRepository $doc_array = $document->toArray(); if(!empty($base64)){ - $mime = !empty($previewType)?Document::$extensions[$previewType]:$documentType; + $mime = Document::$types[!empty($previewType)?$previewType:$documentType]['mime']; $doc_array['base64'] = 'data:'.$mime.';base64,'.$base64; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c5d597ded259..8a6760718a75 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -159,6 +159,12 @@ class AppServiceProvider extends ServiceProvider { return $str . ''; }); + Form::macro('human_filesize', function($bytes, $decimals = 1) { + $size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB'); + $factor = floor((strlen($bytes) - 1) / 3); + return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor]; + }); + Validator::extend('positive', function($attribute, $value, $parameters) { return Utils::parseFloat($value) >= 0; }); diff --git a/public/built.js b/public/built.js index 6a2954d3a786..0b46c5659d41 100644 --- a/public/built.js +++ b/public/built.js @@ -31404,10 +31404,10 @@ NINJA.invoiceDocuments = function(invoice) { for (var i = 0; i < invoice.documents.length; i++) { var document = invoice.documents[i]; var path = document.base64; - if(!path && (document.preview_url || document.type == 'image/png' || document.type == 'image/jpeg')){ - path = 'docs/'+document.public_id+'/'+document.name; - } + + if(!path)path = 'docs/'+document.public_id+'/'+document.name; if(path && (window.pdfMake.vfs[path] || document.base64)){ + // Only embed if we actually have an image for it if(j%3==0){ stackItem = {columns:[]}; stack.push(stackItem); diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 6989916a887e..29da491112da 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -412,10 +412,10 @@ NINJA.invoiceDocuments = function(invoice) { for (var i = 0; i < invoice.documents.length; i++) { var document = invoice.documents[i]; var path = document.base64; - if(!path && (document.preview_url || document.type == 'image/png' || document.type == 'image/jpeg')){ - path = 'docs/'+document.public_id+'/'+document.name; - } + + if(!path)path = 'docs/'+document.public_id+'/'+document.name; if(path && (window.pdfMake.vfs[path] || document.base64)){ + // Only embed if we actually have an image for it if(j%3==0){ stackItem = {columns:[]}; stack.push(stackItem); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 7eec8138a503..ec8ba2a2c474 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1107,7 +1107,7 @@ $LANG = array( 'invoice_embed_documents' => 'Embed Documents', 'invoice_embed_documents_help' => 'Include attached images in the invoice.', 'document_email_attachment' => 'Attach Documents', - 'download_documents' => 'Download Documents', + 'download_documents' => 'Download Documents (:size)', ); return $LANG; diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index bf2ce391c67a..134826e44a42 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -930,7 +930,7 @@ params:{ _token:"{{ Session::getToken() }}" }, - acceptedFiles:{!! json_encode(implode(',',array_keys(\App\Models\Document::$types))) !!}, + acceptedFiles:{!! json_encode(implode(',',\App\Models\Document::$allowedMimes)) !!}, addRemoveLinks:true, maxFileSize:{{floatval(MAX_DOCUMENT_SIZE/1000)}}, dictDefaultMessage:{!! json_encode(trans('texts.document_upload_message')) !!} @@ -955,9 +955,12 @@ dropzone.emit('addedfile', mockFile); dropzone.emit('complete', mockFile); - if(document.type().match(/image.*/)){ + if(document.preview_url()){ dropzone.emit('thumbnail', mockFile, document.preview_url()||document.url()); } + else if(document.type()=='jpeg' || document.type()=='png' || document.type()=='svg'){ + dropzone.emit('thumbnail', mockFile, document.url()); + } dropzone.files.push(mockFile); } @endif @@ -1005,7 +1008,9 @@ } function createInvoiceModel() { - var invoice = ko.toJS(window.model).invoice; + var model = ko.toJS(window.model); + if(!model)return; + var invoice = model.invoice; invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }}; invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }}; invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true}); @@ -1335,13 +1340,19 @@ file.public_id = response.document.public_id model.invoice().documents()[file.index].update(response.document); refreshPDF(true); + + if(response.document.preview_url){ + dropzone.emit('thumbnail', file, response.document.preview_url); + } } @endif @if ($account->isPro() && $account->invoice_embed_documents) @foreach ($invoice->documents as $document) - + @if($document->isPDFEmbeddable()) + + @endif @endforeach @endif diff --git a/resources/views/invoices/history.blade.php b/resources/views/invoices/history.blade.php index 19d7eeff5223..256f541f02e0 100644 --- a/resources/views/invoices/history.blade.php +++ b/resources/views/invoices/history.blade.php @@ -59,7 +59,9 @@ @if (Auth::user()->account->isPro() && Auth::user()->account->invoice_embed_documents) @foreach ($invoice->documents as $document) - + @if($document->isPDFEmbeddable()) + + @endif @endforeach @endif @stop \ No newline at end of file diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index 16479df68444..bad07e9e7321 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -43,7 +43,7 @@
    @if(!empty($documentsZipURL)) - {!! Button::normal(trans('texts.download_documents'))->asLinkTo($documentsZipURL)->large() !!} + {!! Button::normal(trans('texts.download_documents', array('size'=>Form::human_filesize($documentsZipSize))))->asLinkTo($documentsZipURL)->large() !!} @endif
    @endif @@ -54,7 +54,7 @@

    {{ trans('texts.documents_header') }}

    @@ -62,7 +62,9 @@ @if ($account->isPro() && $account->invoice_embed_documents) @foreach ($invoice->documents as $document) - + @if($document->isPDFEmbeddable()) + + @endif @endforeach @endif @stop \ No newline at end of file diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index a383a1b0cb32..c6aaa7230309 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -38,9 +38,8 @@ {!! Former::open($url) ->method($method) - ->addClass('warn-on-exit') + ->addClass('warn-on-exit main-form') ->autocomplete('off') - ->attributes(array('enctype'=>'multipart/form-data')) ->onsubmit('return onFormSubmit(event)') ->rules(array( 'client' => 'required', @@ -307,17 +306,27 @@
    @if ($account->isPro())
    -
    -
    - -
    -
    -
    - - - +
    +
    +
    + +
    +
    +
    + + + +
    + @if ($invoice->hasExpenseDocuments()) +

    {{trans('texts.documents_from_expenses')}}

    + @foreach($invoice->expenses as $expense) + @foreach($expense->documents as $document) +
    {{$document->name}}
    + @endforeach + @endforeach + @endif
    @endif @@ -778,24 +787,24 @@ model.invoice().has_tasks(true); @endif - @if (isset($expenses) && $expenses) + if(model.invoice().expenses() && !model.invoice().public_id()){ model.expense_currency_id({{ $expenseCurrencyId }}); // move the blank invoice line item to the end var blank = model.invoice().invoice_items.pop(); - var expenses = {!! $expenses !!}; + var expenses = model.invoice().expenses(); for (var i=0; iaccount->isPro()) + $('.main-form').submit(function(){ + if($('#document-upload .dropzone .fallback input').val())$(this).attr('enctype', 'multipart/form-data') + else $(this).removeAttr('enctype') + }) + // Initialize document upload - dropzone = new Dropzone('#document-upload', { + dropzone = new Dropzone('#document-upload .dropzone', { url:{!! json_encode(url('document')) !!}, params:{ _token:"{{ Session::getToken() }}" @@ -1360,6 +1374,13 @@ @endif @endforeach + @foreach ($invoice->expenses as $expense) + @foreach ($expense->documents as $document) + @if($document->isPDFEmbeddable()) + + @endif + @endforeach + @endforeach @endif @stop diff --git a/resources/views/invoices/history.blade.php b/resources/views/invoices/history.blade.php index 256f541f02e0..baa5c925aadb 100644 --- a/resources/views/invoices/history.blade.php +++ b/resources/views/invoices/history.blade.php @@ -57,11 +57,18 @@ @include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800]) - @if (Auth::user()->account->isPro() && Auth::user()->account->invoice_embed_documents) + @if (Utils::isPro() && $invoice->account->invoice_embed_documents) @foreach ($invoice->documents as $document) @if($document->isPDFEmbeddable()) @endif @endforeach + @foreach ($invoice->expenses as $expense) + @foreach ($expense->documents as $document) + @if($document->isPDFEmbeddable()) + + @endif + @endforeach + @endforeach @endif @stop \ No newline at end of file diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php index 886eb985512e..9db13abe1910 100644 --- a/resources/views/invoices/knockout.blade.php +++ b/resources/views/invoices/knockout.blade.php @@ -227,6 +227,7 @@ function InvoiceModel(data) { self.invoice_status_id = ko.observable(0); self.invoice_items = ko.observableArray(); self.documents = ko.observableArray(); + self.expenses = ko.observableArray(); self.amount = ko.observable(0); self.balance = ko.observable(0); self.invoice_design_id = ko.observable(1); @@ -257,6 +258,11 @@ function InvoiceModel(data) { return new DocumentModel(options.data); } }, + 'expenses': { + create: function(options) { + return new ExpenseModel(options.data); + } + }, 'tax': { create: function(options) { return new TaxRateModel(options.data); @@ -846,6 +852,28 @@ function DocumentModel(data) { self.update(data); } } + +var ExpenseModel = function(data) { + var self = this; + + self.mapping = { + 'documents': { + create: function(options) { + return new DocumentModel(options.data); + } + } + } + + self.description = ko.observable(''); + self.qty = ko.observable(0); + self.public_id = ko.observable(0); + self.amount = ko.observable(); + self.converted_amount = ko.observable(); + + if (data) { + ko.mapping.fromJS(data, self.mapping, this); + } +}; /* Custom binding for product key typeahead */ ko.bindingHandlers.typeahead = { diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index bad07e9e7321..08f5cd9827cd 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -49,13 +49,18 @@ @endif

     

    - @if ($account->isPro() && count($invoice->documents)) + @if ($account->isPro() && $invoice->hasDocuments())

    {{ trans('texts.documents_header') }}

    @endif @@ -63,9 +68,16 @@ @if ($account->isPro() && $account->invoice_embed_documents) @foreach ($invoice->documents as $document) @if($document->isPDFEmbeddable()) - + @endif @endforeach + @foreach ($invoice->expenses as $expense) + @foreach ($expense->documents as $document) + @if($document->isPDFEmbeddable()) + + @endif + @endforeach + @endforeach @endif +@stop \ No newline at end of file From 9b1bfef6984874f4904a9449169c0337d56ceef8 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 16 Apr 2016 18:34:39 -0400 Subject: [PATCH 086/258] Add support for multiple plans/terms --- app/Http/Controllers/AccountController.php | 136 ++++++++++++- app/Http/routes.php | 15 +- app/Libraries/Utils.php | 39 +--- app/Models/Account.php | 184 +++++++++++++++--- app/Models/Company.php | 21 ++ app/Models/User.php | 4 +- app/Ninja/Repositories/AccountRepository.php | 56 ++++-- app/Ninja/Repositories/ReferralRepository.php | 8 +- app/Services/PaymentService.php | 45 +++-- .../2016_04_16_103943_enterprise_plan.php | 111 +++++++++++ resources/lang/en/texts.php | 32 ++- resources/views/accounts/management.blade.php | 154 +++++++++++++-- resources/views/header.blade.php | 4 +- 13 files changed, 687 insertions(+), 122 deletions(-) create mode 100644 app/Models/Company.php create mode 100644 database/migrations/2016_04_16_103943_enterprise_plan.php diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index b22de1887169..b8d7b84035a4 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -30,6 +30,7 @@ use App\Ninja\Mailers\ContactMailer; use App\Events\UserSignedUp; use App\Events\UserSettingsChanged; use App\Services\AuthService; +use App\Services\PaymentService; use App\Http\Requests\UpdateAccountRequest; @@ -39,8 +40,9 @@ class AccountController extends BaseController protected $userMailer; protected $contactMailer; protected $referralRepository; + protected $paymentService; - public function __construct(AccountRepository $accountRepo, UserMailer $userMailer, ContactMailer $contactMailer, ReferralRepository $referralRepository) + public function __construct(AccountRepository $accountRepo, UserMailer $userMailer, ContactMailer $contactMailer, ReferralRepository $referralRepository, PaymentService $paymentService) { //parent::__construct(); @@ -48,6 +50,7 @@ class AccountController extends BaseController $this->userMailer = $userMailer; $this->contactMailer = $contactMailer; $this->referralRepository = $referralRepository; + $this->paymentService = $paymentService; } public function demo() @@ -110,10 +113,125 @@ class AccountController extends BaseController public function enableProPlan() { - $invitation = $this->accountRepo->enableProPlan(); + if (Auth::user()->isPro() && ! Auth::user()->isTrial()) { + return false; + } + + $invitation = $this->accountRepo->enablePlan(); return $invitation->invitation_key; } + + public function changePlan() { + $user = Auth::user(); + $account = $user->account; + + $plan = Input::get('plan'); + $term = Input::get('plan_term'); + + $planDetails = $account->getPlanDetails(false); + + $credit = 0; + if ($planDetails['active']) { + if ($planDetails['plan'] == PLAN_PRO && $plan == PLAN_ENTERPRISE) { + // Upgrade from pro to enterprise + if($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) { + // Upgrade to yearly for now; switch to monthly in a year + $pending_monthly = true; + $term = PLAN_TERM_YEARLY; + } + + $new_plan = array( + 'plan' => PLAN_ENTERPRISE, + 'term' => $term, + ); + } elseif ($planDetails['plan'] == $plan) { + // Term switch + if ($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) { + $pending_change = array( + 'plan' => $plan, + 'term' => $term + ); + } elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY) { + $new_plan = array( + 'plan' => $plan, + 'term' => $term, + ); + } else { + // Cancel the pending change + $account->company->pending_plan = null; + $account->company->pending_term = null; + $account->company->save(); + Session::flash('message', trans('texts.updated_plan')); + } + } else { + // Downgrade + $refund_deadline = clone $planDetails['started']; + $refund_deadline->modify('+30 days'); + + if ($plan == PLAN_FREE && $refund_deadline > date_create()) { + // Refund + $account->company->plan = null; + $account->company->plan_term = null; + $account->company->plan_started = null; + $account->company->plan_expires = null; + $account->company->plan_paid = null; + $account->company->pending_plan = null; + $account->company->pending_term = null; + + if ($account->company->payment) { + $payment = $account->company->payment; + + $gateway = $this->paymentService->createGateway($payment->account_gateway); + $refund = $gateway->refund(array( + 'transactionReference' => $payment->transaction_reference, + 'amount' => $payment->amount * 100 + )); + $refund->send(); + $payment->delete(); + Session::flash('message', trans('texts.plan_refunded')); + \Log::info("Refunded Plan Payment: {$account->name} - {$user->email}"); + } else { + Session::flash('message', trans('texts.updated_plan')); + } + + $account->company->save(); + + } else { + $pending_change = array( + 'plan' => $plan, + 'term' => $plan == PLAN_FREE ? null : $term, + ); + } + } + + if (!empty($new_plan)) { + $percent_used = $planDetails['paid']->diff(date_create())->days / $planDetails['paid']->diff($planDetails['expires'])->days; + $old_plan_price = Account::$plan_prices[$planDetails['plan']][$planDetails['term']]; + $credit = $old_plan_price * (1 - $percent_used); + } + } else { + $new_plan = array( + 'plan' => $plan, + 'term' => $term, + ); + } + + if (!empty($pending_change) && empty($new_plan)) { + $account->company->pending_plan = $pending_change['plan']; + $account->company->pending_term = $pending_change['term']; + $account->company->save(); + + Session::flash('message', trans('texts.updated_plan')); + } + + if (!empty($new_plan)) { + $invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly)); + return Redirect::to('payment/'.$invitation->invitation_key); + } + + return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301); + } public function setTrashVisible($entityType, $visible) { @@ -235,9 +353,11 @@ class AccountController extends BaseController private function showAccountManagement() { + $account = Auth::user()->account; $data = [ - 'account' => Auth::user()->account, - 'title' => trans('texts.acount_management'), + 'account' => $account, + 'planDetails' => $account->getPlanDetails(), + 'title' => trans('texts.account_management'), ]; return View::make('accounts.management', $data); @@ -955,7 +1075,7 @@ class AccountController extends BaseController $user->registered = true; $user->save(); - $user->account->startTrial(); + $user->account->startTrial(PLAN_ENTERPRISE); if (Input::get('go_pro') == 'true') { Session::set(REQUESTED_PRO_PLAN, true); @@ -1027,12 +1147,12 @@ class AccountController extends BaseController return Redirect::to('/settings/'.ACCOUNT_USER_DETAILS)->with('message', trans('texts.confirmation_resent')); } - public function startTrial() + public function startTrial($plan) { $user = Auth::user(); - if ($user->isEligibleForTrial()) { - $user->account->startTrial(); + if ($user->isEligibleForTrial($plan)) { + $user->account->startTrial($plan); } return Redirect::back()->with('message', trans('texts.trial_success')); diff --git a/app/Http/routes.php b/app/Http/routes.php index 7c3f9c8739f9..7ebd29ecc759 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -188,7 +188,8 @@ Route::group([ Route::resource('users', 'UserController'); Route::post('users/bulk', 'UserController@bulk'); Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); - Route::get('start_trial', 'AccountController@startTrial'); + Route::get('start_trial/{plan}', 'AccountController@startTrial') + ->where(['plan'=>'pro|enterprise']); Route::get('restore_user/{user_id}', 'UserController@restoreUser'); Route::post('users/change_password', 'UserController@changePassword'); Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); @@ -212,6 +213,7 @@ Route::group([ Route::get('settings/charts_and_reports', 'ReportController@showReports'); Route::post('settings/charts_and_reports', 'ReportController@showReports'); + Route::post('settings/change_plan', 'AccountController@changePlan'); Route::post('settings/cancel_account', 'AccountController@cancelAccount'); Route::post('settings/company_details', 'AccountController@updateDetails'); Route::get('settings/{section?}', 'AccountController@showSection'); @@ -583,6 +585,10 @@ if (!defined('CONTACT_EMAIL')) { define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); define('PRO_PLAN_PRICE', 50); + define('PLAN_PRICE_PRO_MONTHLY', 5); + define('PLAN_PRICE_PRO_YEARLY', 50); + define('PLAN_PRICE_ENTERPRISE_MONTHLY', 10); + define('PLAN_PRICE_ENTERPRISE_YEARLY', 100); define('WHITE_LABEL_PRICE', 20); define('INVOICE_DESIGNS_PRICE', 10); @@ -645,6 +651,13 @@ if (!defined('CONTACT_EMAIL')) { define('RESELLER_REVENUE_SHARE', 'A'); define('RESELLER_LIMITED_USERS', 'B'); + + // These must be lowercase + define('PLAN_FREE', 'free'); + define('PLAN_PRO', 'pro'); + define('PLAN_ENTERPRISE', 'enterprise'); + define('PLAN_TERM_MONTHLY', 'month'); + define('PLAN_TERM_YEARLY', 'year'); $creditCards = [ 1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'], diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 439247f337c0..7213a2ef2b7d 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -440,7 +440,12 @@ class Utils return false; } - $dateTime = new DateTime($date); + if ($date instanceof DateTime) { + $dateTime = $date; + } else { + $dateTime = new DateTime($date); + } + $timestamp = $dateTime->getTimestamp(); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); @@ -961,38 +966,6 @@ class Utils return $entity1; } - public static function withinPastYear($date) - { - if (!$date || $date == '0000-00-00') { - return false; - } - - $today = new DateTime('now'); - $datePaid = DateTime::createFromFormat('Y-m-d', $date); - $interval = $today->diff($datePaid); - - return $interval->y == 0; - } - - public static function getInterval($date) - { - if (!$date || $date == '0000-00-00') { - return false; - } - - $today = new DateTime('now'); - $datePaid = DateTime::createFromFormat('Y-m-d', $date); - - return $today->diff($datePaid); - } - - public static function withinPastTwoWeeks($date) - { - $interval = Utils::getInterval($date); - - return $interval && $interval->d <= 14; - } - public static function addHttp($url) { if (!preg_match("~^(?:f|ht)tps?://~i", $url)) { diff --git a/app/Models/Account.php b/app/Models/Account.php index 92813865f2e1..1c871ae28fe6 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -18,6 +18,17 @@ class Account extends Eloquent { use PresentableTrait; use SoftDeletes; + + public static $plan_prices = array( + PLAN_PRO => array( + PLAN_TERM_MONTHLY => PLAN_PRICE_PRO_MONTHLY, + PLAN_TERM_YEARLY => PLAN_PRICE_PRO_YEARLY, + ), + PLAN_ENTERPRISE => array( + PLAN_TERM_MONTHLY => PLAN_PRICE_ENTERPRISE_MONTHLY, + PLAN_TERM_YEARLY => PLAN_PRICE_ENTERPRISE_YEARLY, + ), + ); protected $presenter = 'App\Ninja\Presenters\AccountPresenter'; protected $dates = ['deleted_at']; @@ -177,6 +188,11 @@ class Account extends Eloquent return $this->hasMany('App\Models\Payment','account_id','id')->withTrashed(); } + public function company() + { + return $this->belongsTo('App\Models\Company'); + } + public function setIndustryIdAttribute($value) { $this->attributes['industry_id'] = $value ?: null; @@ -762,17 +778,18 @@ class Account extends Eloquent return $this->account_key === NINJA_ACCOUNT_KEY; } - public function startTrial() + public function startTrial($plan) { if ( ! Utils::isNinja()) { return; } - $this->pro_plan_trial = date_create()->format('Y-m-d'); - $this->save(); + $this->company->trial_plan = $plan; + $this->company->trial_started = date_create()->format('Y-m-d'); + $this->company->save(); } - public function isPro() + public function isPro(&$plan_details = null) { if (!Utils::isNinjaProd()) { return true; @@ -782,14 +799,109 @@ class Account extends Eloquent return true; } - $datePaid = $this->pro_plan_paid; - $trialStart = $this->pro_plan_trial; + $plan_details = $this->getPlanDetails(); + + return $plan_details && $plan_details['active']; + } - if ($datePaid == NINJA_DATE) { + public function isEnterprise(&$plan_details = null) + { + if (!Utils::isNinjaProd()) { return true; } - return Utils::withinPastTwoWeeks($trialStart) || Utils::withinPastYear($datePaid); + if ($this->isNinjaAccount()) { + return true; + } + + $plan_details = $this->getPlanDetails(); + + return $plan_details && $plan_details['active'] && $plan_details['plan'] == PLAN_ENTERPRISE; + } + + public function getPlanDetails($include_trial = true) + { + if (!$this->company) { + return null; + } + + $plan = $this->company->plan; + $trial_plan = $this->company->trial_plan; + + if(!$plan && (!$trial_plan || !$include_trial)) { + return null; + } + + $trial_active = false; + if ($trial_plan && $include_trial) { + $trial_started = DateTime::createFromFormat('Y-m-d', $this->company->trial_started); + $trial_expires = clone $trial_started; + $trial_expires->modify('+2 weeks'); + + if ($trial_expires > date_create()) { + $trial_active = true; + } + } + + $plan_active = false; + if ($plan) { + if ($this->company->plan_expires == null && $this->company->plan_paid == NINJA_DATE) { + $plan_active = true; + $plan_expires = false; + } else { + $plan_expires = DateTime::createFromFormat('Y-m-d', $this->company->plan_expires); + if ($plan_expires > date_create()) { + $plan_active = true; + } + } + } + + // Should we show plan details or trial details? + if (($plan && !$trial_plan) || !$include_trial) { + $use_plan = true; + } elseif (!$plan && $trial_plan) { + $use_plan = false; + } else { + // There is both a plan and a trial + if (!empty($plan_active) && empty($trial_active)) { + $use_plan = true; + } elseif (empty($plan_active) && !empty($trial_active)) { + $use_plan = false; + } elseif (empty($plan_active) && empty($trial_active)) { + // Neither are active; use whichever expired most recently + $use_plan = $plan_expires >= $trial_expires; + } else { + // Both are active; use whichever is a better plan + if ($plan == PLAN_ENTERPRISE) { + $use_plan = true; + } elseif ($trial_plan == PLAN_ENTERPRISE) { + $use_plan = false; + } else { + // They're both the same; show the plan + $use_plan = true; + } + } + } + + if ($use_plan) { + return array( + 'trial' => false, + 'plan' => $plan, + 'started' => DateTime::createFromFormat('Y-m-d', $this->company->plan_started), + 'expires' => $plan_expires, + 'paid' => DateTime::createFromFormat('Y-m-d', $this->company->plan_paid), + 'term' => $this->company->plan_term, + 'active' => $plan_active, + ); + } else { + return array( + 'trial' => true, + 'plan' => $trial_plan, + 'started' => $trial_started, + 'expires' => $trial_expires, + 'active' => $trial_active, + ); + } } public function isTrial() @@ -797,35 +909,54 @@ class Account extends Eloquent if (!Utils::isNinjaProd()) { return false; } + + $plan_details = $this->getPlanDetails(); - if ($this->pro_plan_paid && $this->pro_plan_paid != '0000-00-00') { - return false; - } - - return Utils::withinPastTwoWeeks($this->pro_plan_trial); + return $plan_details && $plan_details['trial'] && $plan_details['active'];; } - public function isEligibleForTrial() + public function isEligibleForTrial($plan = null) { - return ! $this->pro_plan_trial || $this->pro_plan_trial == '0000-00-00'; + if (!$this->company->trial_plan) { + if ($plan) { + return $plan == PLAN_PRO || $plan == PLAN_ENTERPRISE; + } else { + return array(PLAN_PRO, PLAN_ENTERPRISE); + } + } + + if ($this->company->trial_plan == PLAN_PRO) { + if ($plan) { + return $plan != PLAN_PRO; + } else { + return array(PLAN_ENTERPRISE); + } + } + + return false; } public function getCountTrialDaysLeft() { - $interval = Utils::getInterval($this->pro_plan_trial); + $planDetails = $this->getPlanDetails(); + + if(!$planDetails || !$planDetails['trial']) { + return 0; + } + + $today = new DateTime('now'); + $interval = $today->diff($planDetails['expires']); return $interval ? 14 - $interval->d : 0; } public function getRenewalDate() { - if ($this->pro_plan_paid && $this->pro_plan_paid != '0000-00-00') { - $date = DateTime::createFromFormat('Y-m-d', $this->pro_plan_paid); - $date->modify('+1 year'); + $planDetails = $this->getPlanDetails(); + + if ($planDetails && $planDetails['active']) { + $date = $planDetails['expires']; $date = max($date, date_create()); - } elseif ($this->isTrial()) { - $date = date_create(); - $date->modify('+'.$this->getCountTrialDaysLeft().' day'); } else { $date = date_create(); } @@ -840,13 +971,10 @@ class Account extends Eloquent } if (Utils::isNinjaProd()) { - return self::isPro() && $this->pro_plan_paid != NINJA_DATE; + return self::isPro($plan_details) && $plan_details['expires']; } else { - if ($this->pro_plan_paid == NINJA_DATE) { - return true; - } - - return Utils::withinPastYear($this->pro_plan_paid); + $plan_details = $this->getPlanDetails(); + return $plan_details && $plan_details['active']; } } diff --git a/app/Models/Company.php b/app/Models/Company.php new file mode 100644 index 000000000000..1345db9e4bfb --- /dev/null +++ b/app/Models/Company.php @@ -0,0 +1,21 @@ +hasMany('App\Models\Account'); + } + + public function payment() + { + return $this->belongsTo('App\Models\Payment'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 1b2ae7815a36..c431dbb34484 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -122,9 +122,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return $this->account->isTrial(); } - public function isEligibleForTrial() + public function isEligibleForTrial($plan = null) { - return $this->account->isEligibleForTrial(); + return $this->account->isEligibleForTrial($plan); } public function maxInvoiceDesignId() diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index cd07a37b5db2..0a4fe424b6c8 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -205,21 +205,23 @@ class AccountRepository return $data; } - public function enableProPlan() + public function enablePlan($plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false) { - if (Auth::user()->isPro() && ! Auth::user()->isTrial()) { - return false; - } - $account = Auth::user()->account; $client = $this->getNinjaClient($account); - $invitation = $this->createNinjaInvoice($client, $account); + $invitation = $this->createNinjaInvoice($client, $account, $plan, $term, $credit, $pending_monthly); return $invitation; } - public function createNinjaInvoice($client, $clientAccount) + public function createNinjaInvoice($client, $clientAccount, $plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false) { + if ($credit < 0) { + $credit = 0; + } + + $plan_cost = Account::$plan_prices[$plan][$term]; + $account = $this->getNinjaAccount(); $lastInvoice = Invoice::withTrashed()->whereAccountId($account->id)->orderBy('public_id', 'DESC')->first(); $publicId = $lastInvoice ? ($lastInvoice->public_id + 1) : 1; @@ -230,19 +232,39 @@ class AccountRepository $invoice->client_id = $client->id; $invoice->invoice_number = $account->getNextInvoiceNumber($invoice); $invoice->invoice_date = $clientAccount->getRenewalDate(); - $invoice->amount = PRO_PLAN_PRICE; - $invoice->balance = PRO_PLAN_PRICE; + $invoice->amount = $invoice->balance = $plan_cost - $credit; $invoice->save(); - $item = new InvoiceItem(); - $item->account_id = $account->id; - $item->user_id = $account->users()->first()->id; - $item->public_id = $publicId; + if ($credit) { + $credit_item = InvoiceItem::createNew($invoice); + $credit_item->qty = 1; + $credit_item->cost = -$credit; + $credit_item->notes = trans('texts.plan_credit_description'); + $credit_item->product_key = trans('texts.plan_credit_product'); + $invoice->invoice_items()->save($credit_item); + } + + $item = InvoiceItem::createNew($invoice); $item->qty = 1; - $item->cost = PRO_PLAN_PRICE; - $item->notes = trans('texts.pro_plan_description'); - $item->product_key = trans('texts.pro_plan_product'); + $item->cost = $plan_cost; + $item->notes = trans("texts.{$plan}_plan_{$term}_description"); + + // Don't change this without updating the regex in PaymentService->createPayment() + $item->product_key = 'Plan - '.ucfirst($plan).' ('.ucfirst($term).')'; $invoice->invoice_items()->save($item); + + if ($pending_monthly) { + $term_end = $term == PLAN_MONTHLY ? date_create('+1 month') : date_create('+1 year'); + $pending_monthly_item = InvoiceItem::createNew($invoice); + $item->qty = 1; + $pending_monthly_item->cost = 0; + $pending_monthly_item->notes = trans("texts.plan_pending_monthly", array('date', Utils::dateToString($term_end))); + + // Don't change this without updating the text in PaymentService->createPayment() + $pending_monthly_item->product_key = 'Pending Monthly'; + $invoice->invoice_items()->save($pending_monthly_item); + } + $invitation = new Invitation(); $invitation->account_id = $account->id; @@ -355,7 +377,7 @@ class AccountRepository $user->last_name = $lastName; $user->registered = true; - $user->account->startTrial(); + $user->account->startTrial(PLAN_ENTERPRISE); } $user->oauth_provider_id = $providerId; diff --git a/app/Ninja/Repositories/ReferralRepository.php b/app/Ninja/Repositories/ReferralRepository.php index c847f3386b39..0a215d40cc42 100644 --- a/app/Ninja/Repositories/ReferralRepository.php +++ b/app/Ninja/Repositories/ReferralRepository.php @@ -13,13 +13,17 @@ class ReferralRepository $counts = [ 'free' => 0, - 'pro' => 0 + 'pro' => 0, + 'enterprise' => 0 ]; foreach ($accounts as $account) { $counts['free']++; - if (Utils::withinPastYear($account->pro_plan_paid)) { + if ($account->isPro()) { $counts['pro']++; + if ($account->isEnterprise()) { + $counts['enterprise']++; + } } } diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 1669d94d88d4..76e09256d2b4 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -216,17 +216,6 @@ class PaymentService extends BaseService { $invoice = $invitation->invoice; - // enable pro plan for hosted users - if ($invoice->account->account_key == NINJA_ACCOUNT_KEY && $invoice->amount == PRO_PLAN_PRICE) { - $account = Account::with('users')->find($invoice->client->public_id); - $account->pro_plan_paid = $account->getRenewalDate(); - $account->save(); - - // sync pro accounts - $user = $account->users()->first(); - $this->accountRepo->syncAccounts($user->id, $account->pro_plan_paid); - } - $payment = Payment::createNew($invitation); $payment->invitation_id = $invitation->id; $payment->account_gateway_id = $accountGateway->id; @@ -236,13 +225,45 @@ class PaymentService extends BaseService $payment->contact_id = $invitation->contact_id; $payment->transaction_reference = $ref; $payment->payment_date = date_create()->format('Y-m-d'); - + if ($payerId) { $payment->payer_id = $payerId; } $payment->save(); + // enable pro plan for hosted users + if ($invoice->account->account_key == NINJA_ACCOUNT_KEY) { + foreach ($invoice->invoice_items as $invoice_item) { + // Hacky, but invoices don't have meta fields to allow us to store this easily + if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) { + $plan = strtolower($matches[1]); + $term = strtolower($matches[2]); + } elseif ($invoice_item->product_key == 'Pending Monthly') { + $pending_monthly = true; + } + } + + if (!empty($plan)) { + $account = Account::with('users')->find($invoice->client->public_id); + $account->company->payment_id = $payment->id; + $account->company->plan = $plan; + $account->company->plan_term = $term; + $account->company->plan_paid = $account->company->plan_started = date_create()->format('Y-m-d'); + $account->company->plan_expires = date_create($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); + + if (!empty($pending_monthly)) { + $account->company->pending_plan = $plan; + $account->company->pending_term = PLAN_TERM_MONTHLY; + } else { + $account->company->pending_plan = null; + $account->company->pending_term = null; + } + + $account->company->save(); + } + } + return $payment; } diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php new file mode 100644 index 000000000000..ab6bf8892258 --- /dev/null +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -0,0 +1,111 @@ +increments('id'); + + $table->enum('plan', array('pro', 'enterprise'))->nullable(); + $table->enum('plan_term', array('month', 'year'))->nullable(); + $table->date('plan_started')->nullable(); + $table->date('plan_paid')->nullable(); + $table->date('plan_expires')->nullable(); + + $table->unsignedInteger('payment_id')->nullable(); + $table->foreign('payment_id')->references('id')->on('payments'); + + $table->date('trial_started')->nullable(); + $table->enum('trial_plan', array('pro', 'enterprise'))->nullable(); + + $table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable(); + $table->enum('pending_term', array('month', 'year'))->nullable(); + + // Used when a user has started changing a plan but hasn't finished paying yet + $table->enum('temp_pending_plan', array('pro', 'enterprise'))->nullable(); + $table->enum('temp_pending_term', array('month', 'year'))->nullable(); + + $table->timestamps(); + $table->softDeletes(); + }); + + Schema::table('accounts', function($table) + { + $table->unsignedInteger('company_id')->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + }); + + foreach (Account::all() as $account) { + $company = Company::create(); + if ($account->pro_plan_paid && $account->pro_plan_paid != '0000-00-00') { + $company->plan = 'pro'; + $company->plan_term = 'year'; + $company->plan_started = $account->pro_plan_paid; + $company->plan_paid = $account->pro_plan_paid; + + if ($company->plan_paid != NINJA_DATE) { + $expires = DateTime::createFromFormat('Y-m-d', $account->pro_plan_paid); + $expires->modify('+1 year'); + $company->plan_expires = $expires->format('Y-m-d'); + } + } + + if ($account->pro_plan_trial && $account->pro_plan_trial != '0000-00-00') { + $company->trial_started = $account->pro_plan_trial; + $company->trial_plan = 'pro'; + } + + $company->save(); + + $account->company_id = $company->id; + $account->save(); + } + + /*Schema::table('accounts', function($table) + { + $table->dropColumn('pro_plan_paid'); + $table->dropColumn('pro_plan_trial'); + });*/ + } + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + /*Schema::table('accounts', function($table) + { + $table->date('pro_plan_paid')->nullable(); + $table->date('pro_plan_trial')->nullable(); + });*/ + + foreach (Company::all() as $company) { + foreach ($company->accounts as $account) { + $account->pro_plan_paid = $company->plan_paid; + $account->pro_plan_trial = $company->trial_started; + $account->save(); + } + } + + Schema::table('accounts', function($table) + { + $table->dropForeign('accounts_company_id_foreign'); + $table->dropColumn('company_id'); + }); + + Schema::drop('companies'); + } +} \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 20c1ef7dae0a..f08be8ace67c 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -268,7 +268,6 @@ $LANG = array( 'erase_data' => 'This will permanently erase your data.', 'password' => 'Password', 'pro_plan_product' => 'Pro Plan', - 'pro_plan_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', 'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!

     
    Next Steps

    A payable invoice has been sent to the email address associated with your account. To unlock all of the awesome @@ -1127,13 +1126,40 @@ $LANG = array( 'enable_client_portal_dashboard' => 'Dashboard', 'enable_client_portal_dashboard_help' => 'Show/hide the dashboard page in the client portal.', + + // Plans 'account_management' => 'Account Management', 'plan_status' => 'Plan Status', + 'plan_upgrade' => 'Upgrade', + 'plan_change' => 'Change Plan', + 'pending_change_to' => 'Pending Change To', + 'plan' => 'Plan', + 'expires' => 'Expires', + 'expired' => 'Expired', + 'never' => 'never', 'plan_free' => 'Free', 'plan_pro' => 'Pro', - 'plan_enterprise' => 'Enterprise' - + 'plan_enterprise' => 'Enterprise', + 'plan_trial' => 'Trial', + 'plan_term' => 'Term', + 'plan_term_monthly' => 'Monthly', + 'plan_term_yearly' => 'Yearly', + 'plan_term_month' => 'Month', + 'plan_term_year' => 'Year', + 'plan_price_monthly' => '$:price/Month', + 'plan_price_yearly' => '$:price/Year', + 'updated_plan' => 'Updated plan settings', + + 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', + 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', + 'enterprise_plan_product' => 'Enterprise Plan', + 'enterprise_plan_year_description' => 'One year enrollment in the Invoice Ninja Enterprise Plan.', + 'enterprise_plan_month_description' => 'One month enrollment in the Invoice Ninja Enterprise Plan.', + 'plan_credit_product' => 'Credit', + 'plan_credit_description' => 'Credit for unused time', + 'plan_pending_monthly' => 'Will switch to monthly on :date', + 'plan_refunded' => 'A refund has been issued.' ); return $LANG; diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index bc131db70028..395610b07f28 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -7,21 +7,113 @@

    - +
    + + + {!! Former::close() !!} + @endif {!! Former::open('settings/cancel_account')->addClass('cancel-account') !!}
    @@ -54,17 +146,51 @@
    - {!! Former::close() !!} + {!! Former::close() !!}
    @stop \ No newline at end of file diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index 40b499fe8848..9a0185ea24ba 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -726,8 +726,8 @@

    {{ trans('texts.pro_plan_title') }}

    Only $50 Per Year - @if (Auth::user()->isEligibleForTrial()) - {{ trans('texts.trial_call_to_action') }} + @if (Auth::user()->isEligibleForTrial(PLAN_PRO)) + {{ trans('texts.trial_call_to_action') }} @else {{ trans('texts.pro_plan_call_to_action') }} @endif From 33b8566d18d39f8e1cf2dd2ad7b1856282e8e4ca Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 16 Apr 2016 19:14:43 -0400 Subject: [PATCH 087/258] Fix logo filesystem usage --- app/Http/Controllers/AccountController.php | 107 +++++++++++++++------ app/Models/Account.php | 11 ++- resources/views/accounts/details.blade.php | 4 +- 3 files changed, 87 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 3b9efbf8aeb1..6fe75a63363c 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -19,6 +19,7 @@ use App\Models\License; use App\Models\Invoice; use App\Models\User; use App\Models\Account; +use App\Models\Document; use App\Models\Gateway; use App\Models\InvoiceDesign; use App\Models\TaxRate; @@ -802,39 +803,77 @@ class AccountController extends BaseController $this->accountRepo->save($request->input(), $account); /* Logo image file */ - if ($file = Input::file('logo')) { + if ($uploaded = Input::file('logo')) { $path = Input::file('logo')->getRealPath(); - File::delete('logo/'.$account->account_key.'.jpg'); - File::delete('logo/'.$account->account_key.'.png'); + + $disk = $account->getLogoDisk(); + if ($account->hasLogo()) { + $disk->delete($account->logo); + } + + $extension = strtolower($uploaded->getClientOriginalExtension()); + if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){ + $documentType = Document::$extraExtensions[$extension]; + } + else{ + $documentType = $extension; + } - $mimeType = $file->getMimeType(); - - if ($mimeType == 'image/jpeg') { - $path = 'logo/'.$account->account_key.'.jpg'; - $file->move('logo/', $account->account_key.'.jpg'); - } elseif ($mimeType == 'image/png') { - $path = 'logo/'.$account->account_key.'.png'; - $file->move('logo/', $account->account_key.'.png'); + if(!in_array($documentType, array('jpeg', 'png', 'gif'))){ + Session::flash('warning', 'Unsupported file type'); } else { - if (extension_loaded('fileinfo')) { - $image = Image::make($path); - $image->resize(200, 120, function ($constraint) { - $constraint->aspectRatio(); - }); - $path = 'logo/'.$account->account_key.'.jpg'; - Image::canvas($image->width(), $image->height(), '#FFFFFF') - ->insert($image)->save($path); + $documentTypeData = Document::$types[$documentType]; + + $filePath = $uploaded->path(); + $size = filesize($filePath); + + if($size/1000 > MAX_DOCUMENT_SIZE){ + Session::flash('warning', 'File too large'); } else { - Session::flash('warning', 'Warning: To support gifs the fileinfo PHP extension needs to be enabled.'); + if ($documentType != 'gif') { + $account->logo = $account->account_key.'.'.$documentType; + + $imageSize = getimagesize($filePath); + $account->logo_width = $imageSize[0]; + $account->logo_height = $imageSize[1]; + $account->logo_size = $size; + + // make sure image isn't interlaced + if (extension_loaded('fileinfo')) { + $image = Image::make($path); + $image->interlace(false); + $imageStr = (string) $image->encode($documentType); + $disk->put($account->logo, $imageStr); + + $account->logo_size = strlen($imageStr); + } else { + $stream = fopen($filePath, 'r'); + $disk->getDriver()->putStream($account->logo, $stream, ['mimetype'=>$documentTypeData['mime']]); + fclose($stream); + } + } else { + if (extension_loaded('fileinfo')) { + $image = Image::make($path); + $image->resize(200, 120, function ($constraint) { + $constraint->aspectRatio(); + }); + + $account->logo = $account->account_key.'.png'; + $image = Image::canvas($image->width(), $image->height(), '#FFFFFF')->insert($image); + $imageStr = (string) $image->encode('png'); + $disk->put($account->logo, $imageStr); + + $account->logo_size = strlen($imageStr); + $account->logo_width = $image->width(); + $account->logo_height = $image->height(); + } else { + Session::flash('warning', 'Warning: To support gifs the fileinfo PHP extension needs to be enabled.'); + } + } } } - - // make sure image isn't interlaced - if (extension_loaded('fileinfo')) { - $img = Image::make($path); - $img->interlace(false); - $img->save(); - } + + $account->save(); } event(new UserSettingsChanged()); @@ -900,10 +939,18 @@ class AccountController extends BaseController public function removeLogo() { - File::delete('logo/'.Auth::user()->account->account_key.'.jpg'); - File::delete('logo/'.Auth::user()->account->account_key.'.png'); + $account = Auth::user()->account; + if ($account->hasLogo()) { + $account->getLogoDisk()->delete($account->logo); + + $account->logo = null; + $account->logo_size = null; + $account->logo_width = null; + $account->logo_height = null; + $account->save(); - Session::flash('message', trans('texts.removed_logo')); + Session::flash('message', trans('texts.removed_logo')); + } return Redirect::to('settings/'.ACCOUNT_COMPANY_DETAILS); } diff --git a/app/Models/Account.php b/app/Models/Account.php index 5fe2cccf7d7f..f50ed2cec1fa 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -6,9 +6,9 @@ use Session; use DateTime; use Event; use Cache; -use Document; use App; use File; +use App\Models\Document; use App\Events\UserSettingsChanged; use Illuminate\Support\Facades\Storage; use Illuminate\Database\Eloquent\SoftDeletes; @@ -426,7 +426,7 @@ class Account extends Eloquent return $disk->get($this->logo); } - public function getLogoURL() + public function getLogoURL($cachebuster = false) { if(!$this->hasLogo()){ return null; @@ -438,12 +438,17 @@ class Account extends Eloquent if($adapter instanceof \League\Flysystem\Adapter\Local) { // Stored locally $logo_url = str_replace(public_path(), url('/'), $adapter->applyPathPrefix($this->logo), $count); + + if ($cachebuster) { + $logo_url .= '?no_cache='.time(); + } + if($count == 1){ return str_replace(DIRECTORY_SEPARATOR, '/', $logo_url); } } - Document::getDirectFileUrl($this->logo, $this->getDisk()); + return Document::getDirectFileUrl($this->logo, $this->getLogoDisk()); } public function getToken($name) diff --git a/resources/views/accounts/details.blade.php b/resources/views/accounts/details.blade.php index c4da9bd868be..7601ff4eb34b 100644 --- a/resources/views/accounts/details.blade.php +++ b/resources/views/accounts/details.blade.php @@ -51,8 +51,8 @@
    From b4b8396c0fc4f7e384a3a93acc24ef145824bea8 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 16 Apr 2016 20:15:02 -0400 Subject: [PATCH 088/258] Fix documents migration --- database/migrations/2016_03_22_168362_add_documents.php | 1 + 1 file changed, 1 insertion(+) diff --git a/database/migrations/2016_03_22_168362_add_documents.php b/database/migrations/2016_03_22_168362_add_documents.php index b57e72da2079..3d0a44f20c25 100644 --- a/database/migrations/2016_03_22_168362_add_documents.php +++ b/database/migrations/2016_03_22_168362_add_documents.php @@ -63,6 +63,7 @@ class AddDocuments extends Migration { $table->dropColumn('logo_height'); $table->dropColumn('logo_size'); $table->dropColumn('invoice_embed_documents'); + $table->dropColumn('document_email_attachment'); }); Schema::dropIfExists('documents'); From 089349b591f91824cc7d8e1ac57aa8dbf9edf696 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 16 Apr 2016 20:15:17 -0400 Subject: [PATCH 089/258] Complete companies migration --- .../2016_04_16_103943_enterprise_plan.php | 110 +++++++++++++----- 1 file changed, 78 insertions(+), 32 deletions(-) diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index ab6bf8892258..0ff4417a2238 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -12,9 +12,9 @@ class EnterprisePlan extends Migration * * @return void */ - public function up() - { - Schema::create('companies', function($table) + public function up() { + + Schema::create('companies', function($table) { $table->increments('id'); @@ -47,38 +47,84 @@ class EnterprisePlan extends Migration $table->foreign('company_id')->references('id')->on('companies'); }); - foreach (Account::all() as $account) { - $company = Company::create(); - if ($account->pro_plan_paid && $account->pro_plan_paid != '0000-00-00') { - $company->plan = 'pro'; - $company->plan_term = 'year'; - $company->plan_started = $account->pro_plan_paid; - $company->plan_paid = $account->pro_plan_paid; - - if ($company->plan_paid != NINJA_DATE) { - $expires = DateTime::createFromFormat('Y-m-d', $account->pro_plan_paid); - $expires->modify('+1 year'); - $company->plan_expires = $expires->format('Y-m-d'); - } - } - - if ($account->pro_plan_trial && $account->pro_plan_trial != '0000-00-00') { - $company->trial_started = $account->pro_plan_trial; - $company->trial_plan = 'pro'; - } - - $company->save(); - - $account->company_id = $company->id; - $account->save(); + + $single_account_ids = \DB::table('users') + ->leftJoin('user_accounts', function ($join) { + $join->on('user_Accounts.user_id1', '=', 'users.id'); + $join->orOn('user_Accounts.user_id2', '=', 'users.id'); + $join->orOn('user_Accounts.user_id3', '=', 'users.id'); + $join->orOn('user_Accounts.user_id4', '=', 'users.id'); + $join->orOn('user_Accounts.user_id5', '=', 'users.id'); + }) + ->whereNull('user_accounts.id') + ->where(function ($query) { + $query->whereNull('users.public_id'); + $query->orWhere('users.public_id', '=', 0); + }) + ->lists('users.account_id'); + + $group_accounts = \DB::select( + 'SELECT u1.account_id as account1, u2.account_id as account2, u3.account_id as account3, u4.account_id as account4, u5.account_id as account5 FROM `user_accounts` +LEFT JOIN users u1 ON (u1.public_id IS NULL OR u1.public_id = 0) AND user_accounts.user_id1 = u1.id +LEFT JOIN users u2 ON (u2.public_id IS NULL OR u2.public_id = 0) AND user_accounts.user_id2 = u2.id +LEFT JOIN users u3 ON (u3.public_id IS NULL OR u3.public_id = 0) AND user_accounts.user_id3 = u3.id +LEFT JOIN users u4 ON (u4.public_id IS NULL OR u4.public_id = 0) AND user_accounts.user_id4 = u4.id +LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accounts.user_id5 = u5.id'); + + foreach (Account::find($single_account_ids) as $account) { + $this->upAccounts($account); } - /*Schema::table('accounts', function($table) + foreach ($group_accounts as $group_account) { + $this->upAccounts(null, Account::find(get_object_vars($group_account))); + } + + Schema::table('accounts', function($table) { $table->dropColumn('pro_plan_paid'); $table->dropColumn('pro_plan_trial'); - });*/ + }); } + + private function upAccounts($primaryAccount, $otherAccounts = array()) { + if(!$primaryAccount) { + $primaryAccount = $otherAccounts->first(); + } + + $company = Company::create(); + if ($primaryAccount->pro_plan_paid && $primaryAccount->pro_plan_paid != '0000-00-00') { + $company->plan = 'pro'; + $company->plan_term = 'year'; + $company->plan_started = $primaryAccount->pro_plan_paid; + $company->plan_paid = $primaryAccount->pro_plan_paid; + + if ($company->plan_paid != NINJA_DATE) { + $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); + $expires->modify('+1 year'); + $company->plan_expires = $expires->format('Y-m-d'); + } + } + + if ($primaryAccount->pro_plan_trial && $primaryAccount->pro_plan_trial != '0000-00-00') { + $company->trial_started = $primaryAccount->pro_plan_trial; + $company->trial_plan = 'pro'; + } + + $company->save(); + + $primaryAccount->company_id = $company->id; + $primaryAccount->save(); + + if (!empty($otherAccounts)) { + foreach ($otherAccounts as $account) { + if ($account && $account->id != $primaryAccount->id) { + $account->company_id = $company->id; + $account->save(); + } + } + } + } + /** * Reverse the migrations. * @@ -86,11 +132,11 @@ class EnterprisePlan extends Migration */ public function down() { - /*Schema::table('accounts', function($table) + Schema::table('accounts', function($table) { $table->date('pro_plan_paid')->nullable(); $table->date('pro_plan_trial')->nullable(); - });*/ + }); foreach (Company::all() as $company) { foreach ($company->accounts as $account) { @@ -106,6 +152,6 @@ class EnterprisePlan extends Migration $table->dropColumn('company_id'); }); - Schema::drop('companies'); + Schema::dropIfExists('companies'); } } \ No newline at end of file From 030d4ed4da5f31e7e8d4a71fb4493f0ee237fc3b Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 16 Apr 2016 22:09:01 -0400 Subject: [PATCH 090/258] Fix renewals; fix plan edge cases --- app/Console/Commands/SendRenewalInvoices.php | 36 ++++++++++++-------- app/Http/Controllers/AccountController.php | 14 ++++++-- app/Models/Account.php | 4 +-- app/Services/PaymentService.php | 29 +++++++++++++--- 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/app/Console/Commands/SendRenewalInvoices.php b/app/Console/Commands/SendRenewalInvoices.php index 0faf1304a964..64db2fad4fb1 100644 --- a/app/Console/Commands/SendRenewalInvoices.php +++ b/app/Console/Commands/SendRenewalInvoices.php @@ -5,7 +5,7 @@ use DateTime; use Illuminate\Console\Command; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; -use App\Models\Account; +use App\Models\Company; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Repositories\AccountRepository; @@ -30,24 +30,32 @@ class SendRenewalInvoices extends Command $today = new DateTime(); $sentTo = []; - // get all accounts with pro plans expiring in 10 days - $accounts = Account::whereRaw('datediff(curdate(), pro_plan_paid) = 355') + // get all accounts with plans expiring in 10 days + $companies = Company::whereRaw('datediff(plan_expires, curdate()) = 10') ->orderBy('id') ->get(); - $this->info(count($accounts).' accounts found'); + $this->info(count($companies).' companies found'); - foreach ($accounts as $account) { - // don't send multiple invoices to multi-company users - if ($userAccountId = $this->accountRepo->getUserAccountId($account)) { - if (isset($sentTo[$userAccountId])) { - continue; - } else { - $sentTo[$userAccountId] = true; - } + foreach ($companies as $company) { + if (!count($company->accounts)) { + continue; } - + + $account = $company->accounts->sortBy('id')->first(); + $plan = $company->plan; + $term = $company->plan_term; + + if ($company->pending_plan) { + $plan = $company->pending_plan; + $term = $company->pending_term; + } + + if ($plan == PLAN_FREE || !$plan || !$term ){ + continue; + } + $client = $this->accountRepo->getNinjaClient($account); - $invitation = $this->accountRepo->createNinjaInvoice($client, $account); + $invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, $term); // set the due date to 10 days from now $invoice = $invitation->invoice; diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 0845d9a572ae..c14193308a2c 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -170,7 +170,7 @@ class AccountController extends BaseController $refund_deadline = clone $planDetails['started']; $refund_deadline->modify('+30 days'); - if ($plan == PLAN_FREE && $refund_deadline > date_create()) { + if ($plan == PLAN_FREE && $refund_deadline >= date_create()) { // Refund $account->company->plan = null; $account->company->plan_term = null; @@ -207,7 +207,17 @@ class AccountController extends BaseController } if (!empty($new_plan)) { - $percent_used = $planDetails['paid']->diff(date_create())->days / $planDetails['paid']->diff($planDetails['expires'])->days; + $time_used = $planDetails['paid']->diff(date_create()); + $days_used = $time_used->days; + + if ($time_used->invert) { + // They paid in advance + $days_used *= -1; + } + + $days_total = $planDetails['paid']->diff($planDetails['expires'])->days; + + $percent_used = $days_used / $days_total; $old_plan_price = Account::$plan_prices[$planDetails['plan']][$planDetails['term']]; $credit = $old_plan_price * (1 - $percent_used); } diff --git a/app/Models/Account.php b/app/Models/Account.php index 5bd40b5637fe..852da13efec3 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -843,7 +843,7 @@ class Account extends Eloquent $trial_expires = clone $trial_started; $trial_expires->modify('+2 weeks'); - if ($trial_expires > date_create()) { + if ($trial_expires >= date_create()) { $trial_active = true; } } @@ -855,7 +855,7 @@ class Account extends Eloquent $plan_expires = false; } else { $plan_expires = DateTime::createFromFormat('Y-m-d', $this->company->plan_expires); - if ($plan_expires > date_create()) { + if ($plan_expires >= date_create()) { $plan_active = true; } } diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 76e09256d2b4..2f4b57f11ff0 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -244,14 +244,35 @@ class PaymentService extends BaseService } } - if (!empty($plan)) { + if (!empty($plan)) { $account = Account::with('users')->find($invoice->client->public_id); + + if( + $account->company->plan != $plan + || DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create('-7 days') + ) { + // Either this is a different plan, or the subscription expired more than a week ago + // Reset any grandfathering + $account->company->plan_started = date_create()->format('Y-m-d'); + } + + if ( + $account->company->plan == $plan + && $account->company->plan_term == $term + && DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create() + ) { + // This is a renewal; mark it paid as of when this term expires + $account->company->plan_paid = $account->company->plan_expires; + } else { + $account->company->plan_paid = date_create()->format('Y-m-d'); + } + $account->company->payment_id = $payment->id; $account->company->plan = $plan; $account->company->plan_term = $term; - $account->company->plan_paid = $account->company->plan_started = date_create()->format('Y-m-d'); - $account->company->plan_expires = date_create($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); - + $account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid) + ->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); + if (!empty($pending_monthly)) { $account->company->pending_plan = $plan; $account->company->pending_term = PLAN_TERM_MONTHLY; From 63b2c2c6b15916f659cd71ba07790209988225fe Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 16 Apr 2016 22:28:11 -0400 Subject: [PATCH 091/258] More intuitive account management page --- resources/lang/en/texts.php | 9 ++++- resources/views/accounts/management.blade.php | 38 +++++++++++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f08be8ace67c..7a6323ad7e38 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1133,10 +1133,15 @@ $LANG = array( 'plan_upgrade' => 'Upgrade', 'plan_change' => 'Change Plan', - 'pending_change_to' => 'Pending Change To', + 'pending_change_to' => 'Changes To', + 'plan_changes_to' => ':plan on :date', + 'plan_term_changes_to' => ':plan (:term) on :date', + 'cancel_plan_change' => 'Cancel Change', 'plan' => 'Plan', 'expires' => 'Expires', - 'expired' => 'Expired', + 'renews' => 'Renews', + 'plan_expired' => ':plan Plan Expired', + 'trial_expired' => ':plan Plan Trial Ended', 'never' => 'never', 'plan_free' => 'Free', 'plan_pro' => 'Pro', diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index 395610b07f28..ccaad182615a 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -34,10 +34,10 @@ @if ($planDetails && $planDetails['active'])
    @@ -56,18 +56,40 @@

    @if ($account->company->pending_plan == PLAN_FREE) - {{ trans('texts.plan_free') }} + {{ trans('texts.plan_changes_to', [ + 'plan'=>trans('texts.plan_free'), + 'date'=>Utils::dateToString($planDetails['expires']) + ]) }} @else - {{ trans('texts.plan_'.$account->company->pending_plan) }} - ({{ trans('texts.plan_term_'.$account->company->pending_term.'ly') }}) - @endif - {{ trans('texts.cancel') }} + {{ trans('texts.plan_term_changes_to', [ + 'plan'=>trans('texts.plan_'.$account->company->pending_plan), + 'term'=>trans('texts.plan_term_'.$account->company->pending_term.'ly'), + 'date'=>Utils::dateToString($planDetails['expires']) + ]) }} + @endif
    + {{ trans('texts.cancel_plan_change') }}

    @endif {!! Former::actions( Button::info(trans('texts.plan_change'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('edit'))) !!} @else + @if ($planDetails) +
    + +
    +

    + {{ Utils::dateToString($planDetails['expires']) }} +

    +
    +
    + @endif {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} @endif
    From f31ab2eef119b48403a18dd5cfaa6ce41a6bff08 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 17 Apr 2016 12:39:52 +0300 Subject: [PATCH 092/258] Enable setting contact's public_id as id --- app/Models/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Client.php b/app/Models/Client.php index 92232647117c..71970b7c9d48 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -146,7 +146,7 @@ class Client extends EntityModel public function addContact($data, $isPrimary = false) { - $publicId = isset($data['public_id']) ? $data['public_id'] : false; + $publicId = isset($data['public_id']) ? $data['public_id'] : (isset($data['id']) ? $data['id'] : false); if ($publicId && $publicId != '-1') { $contact = Contact::scope($publicId)->firstOrFail(); From 4b42cf700efd693b6aa227305b9e87529ff17e09 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 19:41:46 +1000 Subject: [PATCH 093/258] minor changes --- storage/templates/bold.js | 0 storage/templates/clean.js | 0 storage/templates/modern.js | 0 storage/templates/plain.js | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 storage/templates/bold.js mode change 100644 => 100755 storage/templates/clean.js mode change 100644 => 100755 storage/templates/modern.js mode change 100644 => 100755 storage/templates/plain.js diff --git a/storage/templates/bold.js b/storage/templates/bold.js old mode 100644 new mode 100755 diff --git a/storage/templates/clean.js b/storage/templates/clean.js old mode 100644 new mode 100755 diff --git a/storage/templates/modern.js b/storage/templates/modern.js old mode 100644 new mode 100755 diff --git a/storage/templates/plain.js b/storage/templates/plain.js old mode 100644 new mode 100755 From af5b5f4458a67897e31638190e7d6f4cf2e52f92 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 17 Apr 2016 13:08:58 +0300 Subject: [PATCH 094/258] Improve report name --- app/Http/Controllers/ReportController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index cfb9caaddef6..2d21e0b593c6 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -105,7 +105,7 @@ class ReportController extends BaseController $params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport)); if ($isExport) { - self::export($params['displayData'], $params['columns'], $params['reportTotals']); + self::export($reportType, $params['displayData'], $params['columns'], $params['reportTotals']); } } if ($enableChart) { @@ -514,11 +514,14 @@ class ReportController extends BaseController return $data; } - private function export($data, $columns, $totals) + private function export($reportType, $data, $columns, $totals) { $output = fopen('php://output', 'w') or Utils::fatalError(); + $reportType = trans("texts.{$reportType}s"); + $date = date('Y-m-d'); + header('Content-Type:application/csv'); - header('Content-Disposition:attachment;filename=ninja-report.csv'); + header("Content-Disposition:attachment;filename={$date}_Ninja_{$reportType}.csv"); Utils::exportData($output, $data, Utils::trans($columns)); From ed8abc5382f904aacad0038e83819ad93504c079 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 17 Apr 2016 13:13:37 +0300 Subject: [PATCH 095/258] Added more data to travis debug --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f76fe32447b1..9e8b983e795d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -87,6 +87,8 @@ after_script: - mysql -u root -e 'select * from clients;' ninja - mysql -u root -e 'select * from invoices;' ninja - mysql -u root -e 'select * from invoice_items;' ninja + - mysql -u root -e 'select * from payments;' ninja + - mysql -u root -e 'select * from credits;' ninja - cat storage/logs/laravel.log notifications: From b49938e36ddab14104f630264b81d5e2b30dc18b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 20:40:23 +1000 Subject: [PATCH 096/258] contacts array sorter --- app/Ninja/Repositories/ClientRepository.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 9df81663a5e5..f62014177e33 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -100,6 +100,10 @@ class ClientRepository extends BaseRepository $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; + $contacts = usort($contacts, function ($left, $right) { + return $left['priority'] - $right['priority']; + }); + foreach ($contacts as $contact) { $contact = $client->addContact($contact, $first); $contactIds[] = $contact->public_id; From b022ae0c773911280f738d91978e4f3a7ed3e489 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 20:42:30 +1000 Subject: [PATCH 097/258] contacts array sorter --- app/Ninja/Repositories/ClientRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index f62014177e33..1ef5322f05c8 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -100,7 +100,7 @@ class ClientRepository extends BaseRepository $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; - $contacts = usort($contacts, function ($left, $right) { + usort($contacts, function ($left, $right) { return $left['priority'] - $right['priority']; }); From edf7b6eb37e4205e2262c8383b85aacdb8695d89 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 20:42:51 +1000 Subject: [PATCH 098/258] contacts array sorter --- app/Ninja/Repositories/ClientRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 1ef5322f05c8..7d6a7dd4c1f7 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -101,7 +101,7 @@ class ClientRepository extends BaseRepository $contactIds = []; usort($contacts, function ($left, $right) { - return $left['priority'] - $right['priority']; + return $left['is_primary'] - $right['is_primary']; }); foreach ($contacts as $contact) { From 57c0a91de80e3b8c1f94fbe3fa1a69ac19733ab2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 20:45:24 +1000 Subject: [PATCH 099/258] contacts array sorter --- app/Ninja/Repositories/ClientRepository.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 7d6a7dd4c1f7..dc5488f9692b 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -8,6 +8,7 @@ use App\Models\Contact; use App\Models\Activity; use App\Events\ClientWasCreated; use App\Events\ClientWasUpdated; +use Illuminate\Support\Facades\Log; class ClientRepository extends BaseRepository { @@ -100,9 +101,13 @@ class ClientRepository extends BaseRepository $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; + Log::info($contacts); + usort($contacts, function ($left, $right) { return $left['is_primary'] - $right['is_primary']; }); + + Log::info($contacts); foreach ($contacts as $contact) { $contact = $client->addContact($contact, $first); From 7f6f34b3020b1bfb7355af3c1dc4296e64216bbd Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 20:50:06 +1000 Subject: [PATCH 100/258] contacts array sorter --- app/Ninja/Repositories/ClientRepository.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index dc5488f9692b..a30ce9c0672c 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -100,13 +100,11 @@ class ClientRepository extends BaseRepository $first = true; $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; - - Log::info($contacts); - - usort($contacts, function ($left, $right) { - return $left['is_primary'] - $right['is_primary']; - }); + usort($contacts, function ($left, $right) { + return $right['is_primary'] - $left['is_primary']; + }); + Log::info($contacts); foreach ($contacts as $contact) { From d19c43edced437babfebcf29ab1dd228b0e85d5d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 17 Apr 2016 20:53:53 +1000 Subject: [PATCH 101/258] contacts array sorter --- app/Ninja/Repositories/ClientRepository.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index a30ce9c0672c..46f16d0676e7 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -8,7 +8,6 @@ use App\Models\Contact; use App\Models\Activity; use App\Events\ClientWasCreated; use App\Events\ClientWasUpdated; -use Illuminate\Support\Facades\Log; class ClientRepository extends BaseRepository { @@ -100,13 +99,11 @@ class ClientRepository extends BaseRepository $first = true; $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; - + usort($contacts, function ($left, $right) { return $right['is_primary'] - $left['is_primary']; }); - Log::info($contacts); - foreach ($contacts as $contact) { $contact = $client->addContact($contact, $first); $contactIds[] = $contact->public_id; From 4248963d92278fc1de1cd30e9c329eeb421642bc Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 17 Apr 2016 14:03:37 +0300 Subject: [PATCH 102/258] Check if is_primary is set when sorting contacts --- app/Ninja/Repositories/ClientRepository.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 46f16d0676e7..d3f943a07001 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -100,8 +100,9 @@ class ClientRepository extends BaseRepository $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; + // If the primary is set ensure it's listed first usort($contacts, function ($left, $right) { - return $right['is_primary'] - $left['is_primary']; + return (isset($right['is_primary']) ? $right['is_primary'] : 0) - (isset($left['is_primary']) ? $left['is_primary'] : 0); }); foreach ($contacts as $contact) { From 454e9bc86144f4536806fc50a3e51ac789c575b4 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sun, 17 Apr 2016 12:55:03 -0400 Subject: [PATCH 103/258] Refactor getPlanDetails --- app/Http/Controllers/AccountController.php | 6 ++--- app/Models/Account.php | 26 +++++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index c14193308a2c..ee67700872a4 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -130,10 +130,10 @@ class AccountController extends BaseController $plan = Input::get('plan'); $term = Input::get('plan_term'); - $planDetails = $account->getPlanDetails(false); + $planDetails = $account->getPlanDetails(false, false); $credit = 0; - if ($planDetails['active']) { + if ($planDetails) { if ($planDetails['plan'] == PLAN_PRO && $plan == PLAN_ENTERPRISE) { // Upgrade from pro to enterprise if($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) { @@ -367,7 +367,7 @@ class AccountController extends BaseController $account = Auth::user()->account; $data = [ 'account' => $account, - 'planDetails' => $account->getPlanDetails(), + 'planDetails' => $account->getPlanDetails(true), 'title' => trans('texts.account_management'), ]; diff --git a/app/Models/Account.php b/app/Models/Account.php index 852da13efec3..a2a2ea90374c 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -806,7 +806,7 @@ class Account extends Eloquent $plan_details = $this->getPlanDetails(); - return $plan_details && $plan_details['active']; + return !empty($plan_details); } public function isEnterprise(&$plan_details = null) @@ -821,10 +821,10 @@ class Account extends Eloquent $plan_details = $this->getPlanDetails(); - return $plan_details && $plan_details['active'] && $plan_details['plan'] == PLAN_ENTERPRISE; + return $plan_details && $plan_details['plan'] == PLAN_ENTERPRISE; } - public function getPlanDetails($include_trial = true) + public function getPlanDetails($include_inactive = false, $include_trial = true) { if (!$this->company) { return null; @@ -861,6 +861,10 @@ class Account extends Eloquent } } + if (!$include_inactive && !$plan_active && !$trial_plan) { + return null; + } + // Should we show plan details or trial details? if (($plan && !$trial_plan) || !$include_trial) { $use_plan = true; @@ -872,10 +876,7 @@ class Account extends Eloquent $use_plan = true; } elseif (empty($plan_active) && !empty($trial_active)) { $use_plan = false; - } elseif (empty($plan_active) && empty($trial_active)) { - // Neither are active; use whichever expired most recently - $use_plan = $plan_expires >= $trial_expires; - } else { + } elseif (!empty($plan_active) && !empty($trial_active)) { // Both are active; use whichever is a better plan if ($plan == PLAN_ENTERPRISE) { $use_plan = true; @@ -885,6 +886,9 @@ class Account extends Eloquent // They're both the same; show the plan $use_plan = true; } + } else { + // Neither are active; use whichever expired most recently + $use_plan = $plan_expires >= $trial_expires; } } @@ -917,7 +921,7 @@ class Account extends Eloquent $plan_details = $this->getPlanDetails(); - return $plan_details && $plan_details['trial'] && $plan_details['active'];; + return $plan_details && $plan_details['trial']; } public function isEligibleForTrial($plan = null) @@ -943,7 +947,7 @@ class Account extends Eloquent public function getCountTrialDaysLeft() { - $planDetails = $this->getPlanDetails(); + $planDetails = $this->getPlanDetails(true); if(!$planDetails || !$planDetails['trial']) { return 0; @@ -959,7 +963,7 @@ class Account extends Eloquent { $planDetails = $this->getPlanDetails(); - if ($planDetails && $planDetails['active']) { + if ($planDetails) { $date = $planDetails['expires']; $date = max($date, date_create()); } else { @@ -979,7 +983,7 @@ class Account extends Eloquent return self::isPro($plan_details) && $plan_details['expires']; } else { $plan_details = $this->getPlanDetails(); - return $plan_details && $plan_details['active']; + return $plan_details; } } From 93ab0e430853d97879a47435eaffa45fd26d2c7f Mon Sep 17 00:00:00 2001 From: "rafael.sisweb" Date: Sun, 17 Apr 2016 21:36:22 -0400 Subject: [PATCH 104/258] Improve pt_BR translate new texts --- resources/lang/pt_BR/texts.php | 451 ++++++++++++++++----------------- 1 file changed, 224 insertions(+), 227 deletions(-) diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index 30fcfbf05c27..3b180e93d6ad 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -929,267 +929,264 @@ return array( 'client_portal' => 'Portal do Cliente', 'admin' => 'Admin', - 'disabled' => 'Disabilitado', + 'disabled' => 'Desabilitado', 'show_archived_users' => 'Mostrar usuários arquivados', 'notes' => 'Observações', 'invoice_will_create' => 'cliente será criado', 'invoices_will_create' => 'faturas serão criadas', 'failed_to_import' => 'A importação dos seguintes registros falhou', - 'publishable_key' => 'Publishable Key', - 'secret_key' => 'Secret Key', - 'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', + 'publishable_key' => 'Chave Publicável', + 'secret_key' => 'Chave Secreta', + 'missing_publishable_key' => 'Defina o sua chave publicável do Stripe para um processo de pagamento melhorado', - 'email_design' => 'Email Design', - 'due_by' => 'Due by :date', - 'enable_email_markup' => 'Enable Markup', - 'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', - 'template_help_title' => 'Templates Help', - 'template_help_1' => 'Available variables:', - 'email_design_id' => 'Email Style', - 'email_design_help' => 'Make your emails look more professional with HTML layouts', - 'plain' => 'Plain', - 'light' => 'Light', - 'dark' => 'Dark', + 'email_design' => 'Template de E-mail', + 'due_by' => 'Vencido em :date', + 'enable_email_markup' => 'Habilitar Marcação', + 'enable_email_markup_help' => 'Tornar mais fácil para os seus clientes efetuarem seus pagamentos, acrescentando marcação schema.org a seus e-mails.', + 'template_help_title' => 'Ajuda de Templates', + 'template_help_1' => 'Variáveis disponíveis:', + 'email_design_id' => 'Estilo de e-mails', + 'email_design_help' => 'Deixe seus e-mails mais profissionais com layouts HTML', + 'plain' => 'Plano', + 'light' => 'Claro', + 'dark' => 'Escuro', - 'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', - 'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', - 'invoice_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the invoice number.', - 'quote_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the quote number.', - 'custom_client_fields_helps' => 'Add a text input to the client create/edit page and display the label and value on the PDF.', - 'custom_account_fields_helps' => 'Add a label and value to the company details section of the PDF.', - 'custom_invoice_fields_helps' => 'Add a text input to the invoice create/edit page and display the label and value on the PDF.', - 'custom_invoice_charges_helps' => 'Add a text input to the invoice create/edit page and include the charge in the invoice subtotals.', - 'color_help' => 'Note: the primary color is also used in the client portal and custom email designs.', + 'industry_help' => 'Usado para fornecer comparações contra as médias das empresas de tamanho e indústria similar.', + 'subdomain_help' => 'Personalizar o link da fatura ou exibir a fatura em seu próprio site.', + 'invoice_number_help' => 'Especifique um prefixo ou usar um padrão personalizado para definir dinamicamente o número da fatura.', + 'quote_number_help' => 'Especifique um prefixo ou usar um padrão personalizado para definir dinamicamente o número do orçamento.', + 'custom_client_fields_helps' => 'Adicionar uma entrada de texto na página Criar/Editar Cliente e exibir no PDF.', + 'custom_account_fields_helps' => 'Adicionar um rótulo e um valor para a seção detalhes da empresa do PDF.', + 'custom_invoice_fields_helps' => 'Adicionar uma entrada de texto na página Criar/Editar Fatura e exibir no PDF.', + 'custom_invoice_charges_helps' => 'Adicionar uma entrada de texto na página Criar/Editar Fatura e incluir nos subtotais da fatura.', + 'color_help' => 'Nota: A cor primária também é utilizada nos projetos do portal do cliente e-mail personalizado.', - 'token_expired' => 'Validation token was expired. Please try again.', - 'invoice_link' => 'Invoice Link', - 'button_confirmation_message' => 'Click to confirm your email address.', - 'confirm' => 'Confirm', - 'email_preferences' => 'Email Preferences', - 'created_invoices' => 'Successfully created :count invoice(s)', - 'next_invoice_number' => 'The next invoice number is :number.', - 'next_quote_number' => 'The next quote number is :number.', + 'token_expired' => 'Token de acesso expirado. Tente novamente!', + 'invoice_link' => 'Link da Fatura', + 'button_confirmation_message' => 'Clique para confirmar seu endereço de e-mail.', + 'confirm' => 'Confirmar', + 'email_preferences' => 'Preferências de E-mails', + 'created_invoices' => ':count fatura(s) criadas com sucesso', + 'next_invoice_number' => 'O número da próxima fatura será :number.', + 'next_quote_number' => 'O número do próximo orçamento será :number.', - 'days_before' => 'days before', - 'days_after' => 'days after', - 'field_due_date' => 'due date', - 'field_invoice_date' => 'invoice date', - 'schedule' => 'Schedule', - 'email_designs' => 'Email Designs', - 'assigned_when_sent' => 'Assigned when sent', + 'days_before' => 'dias antes', + 'days_after' => 'dias depois', + 'field_due_date' => 'data de vencimento', + 'field_invoice_date' => 'data da fatura', + 'schedule' => 'Agendamento', + 'email_designs' => 'Design de E-mails', + 'assigned_when_sent' => 'Assinar quando enviar', - 'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', - 'white_label_purchase_link' => 'Purchase a white label license', + 'white_label_custom_css' => ':link apenas $'.WHITE_LABEL_PRICE.' para permitir um estilo personalizado e apoiar o nosso projecto.', + 'white_label_purchase_link' => 'Adquira uma licença white label', // Expense / vendor - 'expense' => 'Expense', - 'expenses' => 'Expenses', - 'new_expense' => 'Enter Expense', - 'enter_expense' => 'Enter Expense', - 'vendors' => 'Vendors', - 'new_vendor' => 'New Vendor', - 'payment_terms_net' => 'Net', - 'vendor' => 'Vendor', - 'edit_vendor' => 'Edit Vendor', - 'archive_vendor' => 'Archive Vendor', - 'delete_vendor' => 'Delete Vendor', - 'view_vendor' => 'View Vendor', - 'deleted_expense' => 'Successfully deleted expense', - 'archived_expense' => 'Successfully archived expense', - 'deleted_expenses' => 'Successfully deleted expenses', - 'archived_expenses' => 'Successfully archived expenses', + 'expense' => 'Despesa', + 'expenses' => 'Despesas', + 'new_expense' => 'Adicionar Despesa', + 'enter_expense' => 'Incluir Despesa', + 'vendors' => 'Fornecedor', + 'new_vendor' => 'Novo Fornecedor', + 'payment_terms_net' => 'Rede', + 'vendor' => 'Fornecedor', + 'edit_vendor' => 'Editar Fornecedor', + 'archive_vendor' => 'Arquivar Fornecedor', + 'delete_vendor' => 'Deletar Fornecedor', + 'view_vendor' => 'Visualizar Fornecedor', + 'deleted_expense' => 'Despesa excluída com sucesso', + 'archived_expense' => 'Despesa arquivada com sucesso', + 'deleted_expenses' => 'Despesas excluídas com sucesso', + 'archived_expenses' => 'Despesas arquivada com sucesso', // Expenses - 'expense_amount' => 'Expense Amount', - 'expense_balance' => 'Expense Balance', - 'expense_date' => 'Expense Date', - 'expense_should_be_invoiced' => 'Should this expense be invoiced?', - 'public_notes' => 'Public Notes', - 'invoice_amount' => 'Invoice Amount', - 'exchange_rate' => 'Exchange Rate', - 'yes' => 'Yes', - 'no' => 'No', - 'should_be_invoiced' => 'Should be invoiced', - 'view_expense' => 'View expense # :expense', - 'edit_expense' => 'Edit Expense', - 'archive_expense' => 'Archive Expense', - 'delete_expense' => 'Delete Expense', - 'view_expense_num' => 'Expense # :expense', - 'updated_expense' => 'Successfully updated expense', - 'created_expense' => 'Successfully created expense', - 'enter_expense' => 'Enter Expense', - 'view' => 'View', - 'restore_expense' => 'Restore Expense', - 'invoice_expense' => 'Invoice Expense', - 'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients', - 'expense_error_invoiced' => 'Expense has already been invoiced', - 'convert_currency' => 'Convert currency', + 'expense_amount' => 'Total de Despesas', + 'expense_balance' => 'Saldo de Despesas', + 'expense_date' => 'Data da Despesa', + 'expense_should_be_invoiced' => 'Esta despesa deve ser faturada?', + 'public_notes' => 'Notas Públicas', + 'invoice_amount' => 'Total da Fatura', + 'exchange_rate' => 'Taxa de Câmbio', + 'yes' => 'Sim', + 'no' => 'Não', + 'should_be_invoiced' => 'Deve ser Faturada', + 'view_expense' => 'Visualizar despesa # :expense', + 'edit_expense' => 'Editar Despesa', + 'archive_expense' => 'Arquivar Despesa', + 'delete_expense' => 'Deletar Despesa', + 'view_expense_num' => 'Despesa # :expense', + 'updated_expense' => 'Despesa atualizada com sucesso', + 'created_expense' => 'Despesa criada com sucesso', + 'enter_expense' => 'Incluir Despesa', + 'view' => 'Visualizar', + 'restore_expense' => 'Restaurar Despesa', + 'invoice_expense' => 'Faturar Despesa', + 'expense_error_multiple_clients' => 'Despesas não podem pertencer a clientes diferentes', + 'expense_error_invoiced' => 'Despeja já faturada', + 'convert_currency' => 'Converter moeda', // Payment terms - 'num_days' => 'Number of days', - 'create_payment_term' => 'Create Payment Term', - 'edit_payment_terms' => 'Edit Payment Term', - 'edit_payment_term' => 'Edit Payment Term', - 'archive_payment_term' => 'Archive Payment Term', + 'num_days' => 'Número de dias', + 'create_payment_term' => 'Criar Termo de Pagamento', + 'edit_payment_terms' => 'Editar Termos de Pagamento', + 'edit_payment_term' => 'Editar Termo de Pagamento', + 'archive_payment_term' => 'Arquivar Termo de Pagamento', // recurring due dates - 'recurring_due_dates' => 'Recurring Invoice Due Dates', - 'recurring_due_date_help' => '

    Automatically sets a due date for the invoice.

    -

    Invoices on a monthly or yearly cycle set to be due on or before the day they are created will be due the next month. Invoices set to be due on the 29th or 30th in months that don\'t have that day will be due the last day of the month.

    -

    Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.

    -

    For example:

    + 'recurring_due_dates' => 'Data de Vencimento das Faturas Recorrentes', + 'recurring_due_date_help' => '

    Definir automaticamente a data de vencimento da fatura.

    +

    Faturas em um ciclo mensal ou anual com vencimento anterior ou na data em que são criadas serão faturadas para o próximo mês. Faturas com vencimento no dia 29 ou 30 nos meses que não tem esse dia será faturada no último dia do mês..

    +

    Faturas em um clclo mensal com vencimento no dia da semana em que foi criada serão faturadas para a próxima semana.

    +

    Exemplo:

      -
    • Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.
    • -
    • Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month. -
    • -
    • Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of next month. -
    • -
    • Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today. -
    • +
    • Hoje é dia 15, vencimento no primeiro dia do mês. O Vencimento será no primeiro dia do próximo mês.
    • +
    • Hoje é dia 15, vencimento no último dia do mês. O Vencimento será no último dia do mês corrente
    • +
    • Hoje é dia 15, vencimento no dia 15. O venciemnto será no dia 15 do próximo mês.
    • +
    • Hoje é Sexta-Feira, vencimento na primeira sexta-feira. O venciemnto será na próxima sexta-feira, não hoje.
    ', - 'due' => 'Due', - 'next_due_on' => 'Due Next: :date', - 'use_client_terms' => 'Use client terms', - 'day_of_month' => ':ordinal day of month', - 'last_day_of_month' => 'Last day of month', - 'day_of_week_after' => ':ordinal :day after', - 'sunday' => 'Sunday', - 'monday' => 'Monday', - 'tuesday' => 'Tuesday', - 'wednesday' => 'Wednesday', - 'thursday' => 'Thursday', - 'friday' => 'Friday', - 'saturday' => 'Saturday', + 'due' => 'Vencimento', + 'next_due_on' => 'Próximo Vencimento: :date', + 'use_client_terms' => 'Usar condições do cliente', + 'day_of_month' => ':ordinal dia do mês ', + 'last_day_of_month' => 'Último dia do mês', + 'day_of_week_after' => ':ordinal :day depois', + 'sunday' => 'Domingo', + 'monday' => 'Segunda-Feira', + 'tuesday' => 'Terça-Feira', + 'wednesday' => 'Quarta-Feira', + 'thursday' => 'Quinta-Feira', + 'friday' => 'Sexta-Feira', + 'saturday' => 'Sábado', // Fonts - 'header_font_id' => 'Header Font', - 'body_font_id' => 'Body Font', - 'color_font_help' => 'Note: the primary color and fonts are also used in the client portal and custom email designs.', + 'header_font_id' => 'Fonte do Cabeçalho', + 'body_font_id' => 'Fonte dos Textos', + 'color_font_help' => 'Nota: A cor primária também é utilizada nos projetos do portal do cliente e-mail personalizado.', - 'live_preview' => 'Live Preview', - 'invalid_mail_config' => 'Unable to send email, please check that the mail settings are correct.', + 'live_preview' => 'Preview', + 'invalid_mail_config' => 'Falha ao enviar e-mail, verifique as configurações.', - 'invoice_message_button' => 'To view your invoice for :amount, click the button below.', - 'quote_message_button' => 'To view your quote for :amount, click the button below.', - 'payment_message_button' => 'Thank you for your payment of :amount.', - 'payment_type_direct_debit' => 'Direct Debit', - 'bank_accounts' => 'Bank Accounts', - 'add_bank_account' => 'Add Bank Account', - 'setup_account' => 'Setup Account', - 'import_expenses' => 'Import Expenses', - 'bank_id' => 'bank', - 'integration_type' => 'Integration Type', - 'updated_bank_account' => 'Successfully updated bank account', - 'edit_bank_account' => 'Edit Bank Account', - 'archive_bank_account' => 'Archive Bank Account', - 'archived_bank_account' => 'Successfully archived bank account', - 'created_bank_account' => 'Successfully created bank account', - 'validate_bank_account' => 'Validate Bank Account', - 'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and 400+ US banks.', - 'bank_password_help' => 'Note: your password is transmitted securely and never stored on our servers.', - 'bank_password_warning' => 'Warning: your password may be transmitted in plain text, consider enabling HTTPS.', - 'username' => 'Username', - 'account_number' => 'Account Number', - 'account_name' => 'Account Name', - 'bank_account_error' => 'Failed to retreive account details, please check your credentials.', - 'status_approved' => 'Approved', - 'quote_settings' => 'Quote Settings', - 'auto_convert_quote' => 'Auto convert quote', - 'auto_convert_quote_help' => 'Automatically convert a quote to an invoice when approved by a client.', - 'validate' => 'Validate', + 'invoice_message_button' => 'Para visualizar sua fatura de :amount, clique no botão abaixo.', + 'quote_message_button' => 'Para visualizar seu orçamento de :amount, clique no botão abaixo.', + 'payment_message_button' => 'Obrigado pelo seu pagamento de :amount.', + 'payment_type_direct_debit' => 'Débito', + 'bank_accounts' => 'Contas Bancárias', + 'add_bank_account' => 'Adicionar Conta Bancária', + 'setup_account' => 'Configurar Conta', + 'import_expenses' => 'Importar Despesas', + 'bank_id' => 'banco', + 'integration_type' => 'Tipo de Integração', + 'updated_bank_account' => 'Conta bancária atualizada com sucesso', + 'edit_bank_account' => 'Editar Conta Bancária', + 'archive_bank_account' => 'Arquivar Conta Bancária', + 'archived_bank_account' => 'Conta bancária arquivada com sucesso', + 'created_bank_account' => 'Conta bancária criada com sucesso', + 'validate_bank_account' => 'Validar Conta Bancária', + 'bank_accounts_help' => 'Conecte sua conta bancária para importar suas despesas e criar fornecedores. Suporte ao American Express e 400+ bancos americanos.', + 'bank_password_help' => 'Nota: sua senha é transferida de forma segura e não será armazenada em nossos servidores.', + 'bank_password_warning' => 'Atenção: sua senha será transferida de forma não segura, considere habilitar HTTPS.', + 'username' => 'Usuário', + 'account_number' => 'Conta', + 'account_name' => 'Nome da Conta', + 'bank_account_error' => 'Falha ao receber os detalhes da sua conta, verifique seus dados de acesso.', + 'status_approved' => 'Aprovado', + 'quote_settings' => 'Configuração de Orçamentos', + 'auto_convert_quote' => 'Auto converter orçamento', + 'auto_convert_quote_help' => 'Converter automaticamente um orçamento quando for aprovado pelo cliente.', + 'validate' => 'Validado', 'info' => 'Info', - 'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)', + 'imported_expenses' => ':count_vendors fornecedor(s) e :count_expenses despesa(s) importadas com sucesso', - 'iframe_url_help3' => 'Note: if you plan on accepting credit cards details we strongly recommend enabling HTTPS on your site.', - 'expense_error_multiple_currencies' => 'The expenses can\'t have different currencies.', - 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', + 'iframe_url_help3' => 'Nota: se o seu plano aceita detalhes do cartão de crédito recomendamos que seja habilitado o HTTPS em seu site.', + 'expense_error_multiple_currencies' => 'As despesas não podem ter diferentes moedas.', + 'expense_error_mismatch_currencies' => 'As configurações de moeda do cliente não coincide com a moeda nesta despesa.', 'trello_roadmap' => 'Trello Roadmap', - 'header_footer' => 'Header/Footer', - 'first_page' => 'first page', - 'all_pages' => 'all pages', - 'last_page' => 'last page', - 'all_pages_header' => 'Show header on', - 'all_pages_footer' => 'Show footer on', - 'invoice_currency' => 'Invoice Currency', - 'enable_https' => 'We strongly recommend using HTTPS to accept credit card details online.', - 'quote_issued_to' => 'Quote issued to', - 'show_currency_code' => 'Currency Code', - 'trial_message' => 'Your account will receive a free two week trial of our pro plan.', - 'trial_footer' => 'Your free trial lasts :count more days, :link to upgrade now.', - 'trial_footer_last_day' => 'This is the last day of your free trial, :link to upgrade now.', - 'trial_call_to_action' => 'Start Free Trial', - 'trial_success' => 'Successfully enabled two week free pro plan trial', - 'overdue' => 'Overdue', - 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', + 'header_footer' => 'Cabeçalho/Rodapé', + 'first_page' => 'primeira página', + 'all_pages' => 'todas as páginas', + 'last_page' => 'última página', + 'all_pages_header' => 'Mostrar cabeçalho on', + 'all_pages_footer' => 'Mostrar rodapé on', + 'invoice_currency' => 'Moeda da Fatura', + 'enable_https' => 'Recomendamos a utilização de HTTPS para receber os detalhes do cartão de crédito online.', + 'quote_issued_to' => 'Orçamento emitido para', + 'show_currency_code' => 'Código da Moeda', + 'trial_message' => 'Sua conta receberá duas semanas receberá duas semanas gratuitamente para testar nosso plano pro.', + 'trial_footer' => 'Seu período de teste expira em :count dias, :link para adquirir o plano pro.', + 'trial_footer_last_day' => 'Seu período de testes encerra hoje, :link para adiquirir o plano pro.', + 'trial_call_to_action' => 'Iniciar período de testes', + 'trial_success' => 'Duas semanas de testes foi habilitado com sucesso', + 'overdue' => 'Vencido', + 'white_label_text' => 'Adquira UM ano de licença white label por $'.WHITE_LABEL_PRICE.' para remover a marca Invoice Ninja do portal do cliente e ajudar nosso projeto.', - 'navigation' => 'Navigation', - 'list_invoices' => 'List Invoices', - 'list_clients' => 'List Clients', - 'list_quotes' => 'List Quotes', - 'list_tasks' => 'List Tasks', - 'list_expenses' => 'List Expenses', - 'list_recurring_invoices' => 'List Recurring Invoices', - 'list_payments' => 'List Payments', - 'list_credits' => 'List Credits', - 'tax_name' => 'Tax Name', - 'report_settings' => 'Report Settings', - 'search_hotkey' => 'shortcut is /', + 'navigation' => 'Navegação', + 'list_invoices' => 'Listar Faturas', + 'list_clients' => 'Listar Clientes', + 'list_quotes' => 'Listar Orçamentos', + 'list_tasks' => 'Listar Tarefas', + 'list_expenses' => 'Listar Despesas', + 'list_recurring_invoices' => 'Listar Faturas Recorrentes', + 'list_payments' => 'Listar Pagamentos', + 'list_credits' => 'Listar Créditos', + 'tax_name' => 'Nome da Taxa', + 'report_settings' => 'Configuração de Relatórios', + 'search_hotkey' => 'atalho /', - 'new_user' => 'New User', - 'new_product' => 'New Product', - 'new_tax_rate' => 'New Tax Rate', - 'invoiced_amount' => 'Invoiced Amount', - 'invoice_item_fields' => 'Invoice Item Fields', - 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', - 'recurring_invoice_number' => 'Recurring Invoice Number', - 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', - 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.', + 'new_user' => 'Novo Usuário', + 'new_product' => 'Novo Produto', + 'new_tax_rate' => 'Nova Taxa de Juro', + 'invoiced_amount' => 'Total Faturado', + 'invoice_item_fields' => 'Campos de Ítens da Fatura', + 'custom_invoice_item_fields_help' => 'Adicionar um campo ao adicionar um ítem na fatura e exibir no PDF.', + 'recurring_invoice_number' => 'Número da Fatura Recorrente', + 'recurring_invoice_number_prefix_help' => 'Informe um prefixo para a numeração das faturas recorrentes. O valor padrão é \'R\'.', + 'enable_client_portal' => 'Painel', + 'enable_client_portal_help' => 'Mostrar/Ocultar o painel no portal do cliente.', // Client Passwords - 'enable_portal_password'=>'Password protect invoices', - 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', - 'send_portal_password'=>'Generate password automatically', - 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + 'enable_portal_password'=>'Faturas protegidas por senha', + 'enable_portal_password_help'=>'Permite definir uma senha para cada contato. Se uma senha for definida, o contato deverá informar sua senha antes de visualizar a fatura.', + 'send_portal_password'=>'Gerar senha automaticamente', + 'send_portal_password_help'=>'Se uma senha não for definida, uma senha será gerada e enviada juntamente com a primeira fatura.', - 'expired' => 'Expired', - 'invalid_card_number' => 'The credit card number is not valid.', - 'invalid_expiry' => 'The expiration date is not valid.', - 'invalid_cvv' => 'The CVV is not valid.', - 'cost' => 'Cost', - 'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.', + 'expired' => 'Expireda', + 'invalid_card_number' => 'Cartão de Crédito inválido.', + 'invalid_expiry' => 'Data para expirar não é valida.', + 'invalid_cvv' => 'O código CVV não é válido.', + 'cost' => 'Custo', + 'create_invoice_for_sample' => 'Nota: cria sua primeira fatura para visualizar aqui.', // User Permissions - 'owner' => 'Owner', - 'administrator' => 'Administrator', - 'administrator_help' => 'Allow user to manage users, change settings and modify all records', - 'user_create_all' => 'Create clients, invoices, etc.', - 'user_view_all' => 'View all clients, invoices, etc.', - 'user_edit_all' => 'Edit all clients, invoices, etc.', - 'gateway_help_20' => ':link to sign up for Sage Pay.', - 'gateway_help_21' => ':link to sign up for Sage Pay.', - 'partial_due' => 'Partial Due', - 'restore_vendor' => 'Restore Vendor', - 'restored_vendor' => 'Successfully restored vendor', - 'restored_expense' => 'Successfully restored expense', - 'permissions' => 'Permissions', - 'create_all_help' => 'Allow user to create and modify records', - 'view_all_help' => 'Allow user to view records they didn\'t create', - 'edit_all_help' => 'Allow user to modify records they didn\'t create', - 'view_payment' => 'View Payment', + 'owner' => 'Proprietário', + 'administrator' => 'Administrador', + 'administrator_help' => 'Permite usuário gerenciar usuários, configurações e alterar todos os cadastros', + 'user_create_all' => 'Criar clientes, faturas, etc.', + 'user_view_all' => 'Visualizar todos os clientes, faturas, etc.', + 'user_edit_all' => 'Editar todos os clientes, faturas, etc.', + 'gateway_help_20' => ':link para habilitar Sage Pay.', + 'gateway_help_21' => ':link para habilitar Sage Pay.', + 'partial_due' => 'Vencimento Parcial', + 'restore_vendor' => 'Restaurar Fornecedor', + 'restored_vendor' => 'Fornecedor restarurado com sucesso', + 'restored_expense' => 'Despesa restaurada com sucesso', + 'permissions' => 'Permissões', + 'create_all_help' => 'Permite o usuário criar e alterar todos os regitros', + 'view_all_help' => 'Permite usuario visualizar regitros que ele não criou', + 'edit_all_help' => 'Permite usuario editar regitros que ele não criou', + 'view_payment' => 'Visualizar ', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December', + 'january' => 'Janeiro', + 'february' => 'Fevereiro', + 'march' => 'Março', + 'april' => 'Abril', + 'may' => 'Maio', + 'june' => 'Junho', + 'july' => 'Julho', + 'august' => 'Agosto', + 'september' => 'Setembro', + 'october' => 'Outubro', + 'november' => 'Novembro', + 'december' => 'Dezembro', -); +); \ No newline at end of file From 11019e5dc1429c8d9a8f622ddaca2ca97e87b8c7 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 18 Apr 2016 10:28:30 +0300 Subject: [PATCH 105/258] Improved HTML email margins --- resources/views/emails/master.blade.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/emails/master.blade.php b/resources/views/emails/master.blade.php index 5f5c3df90a2d..ee15ef001c3a 100644 --- a/resources/views/emails/master.blade.php +++ b/resources/views/emails/master.blade.php @@ -6,7 +6,7 @@ - @yield('markup') @@ -50,8 +50,8 @@ } -
    +
    From 4a8c02d5fd4ee12c016343eee95055311d5f3430 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 18 Apr 2016 12:58:21 +0300 Subject: [PATCH 106/258] Updated Omnipay --- composer.json | 2 +- composer.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index a1ee15eff291..9bb49326b1f8 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "anahkiasen/former": "4.0.*@dev", "barryvdh/laravel-debugbar": "~2.0", "chumper/datatable": "dev-develop#04ef2bf", - "omnipay/omnipay": "~2.3.0", + "omnipay/omnipay": "~2.3", "intervention/image": "dev-master", "webpatser/laravel-countries": "dev-master", "barryvdh/laravel-ide-helper": "dev-master", diff --git a/composer.lock b/composer.lock index 0c76e7216c49..606338333d7c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "2ab7ab9013e31d8a2f0dcf43b31beefa", - "content-hash": "188fba7fcc31b702098d5417bc0e63e2", + "hash": "cf82d2ddb25cb1a7d6b4867bcc8692b8", + "content-hash": "481a95753b873249aebceb99e7426421", "packages": [ { "name": "agmscode/omnipay-agms", @@ -127,7 +127,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/formers/former/zipball/d97f907741323b390f43954a90a227921ecc6b96", + "url": "https://api.github.com/repos/formers/former/zipball/78ae8c65b1f8134e2db1c9491c251c03638823ca", "reference": "d97f907741323b390f43954a90a227921ecc6b96", "shasum": "" }, @@ -4229,7 +4229,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/omnipay-bitpay/zipball/cf813f1d5436a1d2f942d3df6666695d1e2b5280", + "url": "https://api.github.com/repos/thephpleague/omnipay-bitpay/zipball/9cadfb7955bd361d1a00ac8f0570aee4c05c6bb4", "reference": "cf813f1d5436a1d2f942d3df6666695d1e2b5280", "shasum": "" }, From 0c27e22b51b11bc137fba74e511343645c9b6814 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 18 Apr 2016 20:17:02 +0300 Subject: [PATCH 107/258] Brought back error page in production --- app/Exceptions/Handler.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 76b2087ad89e..5333d90f9e3b 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -71,11 +71,10 @@ class Handler extends ExceptionHandler { } } - return parent::render($request, $e); - - /* // In production, except for maintenance mode, we'll show a custom error screen - if (Utils::isNinjaProd() && !Utils::isDownForMaintenance()) { + if (Utils::isNinjaProd() + && !Utils::isDownForMaintenance() + && !($e instanceof HttpResponseException)) { $data = [ 'error' => get_class($e), 'hideHeader' => true, @@ -85,6 +84,5 @@ class Handler extends ExceptionHandler { } else { return parent::render($request, $e); } - */ } } From 22f65e810828a3c37adfc8088f5d39ed96d24a05 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Mon, 18 Apr 2016 22:35:18 -0400 Subject: [PATCH 108/258] Finalize multi-plan support * Allow admins to change plan * Check features instead of plans * Support linking/unlinking accounts * Support creating/deleting accounts --- app/Console/Commands/SendReminders.php | 2 +- app/Http/Controllers/AccountController.php | 17 +-- app/Http/Controllers/Auth/AuthController.php | 3 + .../Controllers/ClientAuth/AuthController.php | 2 +- .../ClientAuth/PasswordController.php | 4 +- app/Http/Controllers/ClientController.php | 4 +- app/Http/Controllers/DocumentController.php | 2 +- app/Http/Controllers/InvoiceController.php | 18 ++- app/Http/Controllers/PaymentController.php | 2 +- .../Controllers/PublicClientController.php | 32 ++--- app/Http/Controllers/QuoteController.php | 4 +- app/Http/Controllers/ReportController.php | 4 +- app/Http/Controllers/TokenController.php | 2 +- app/Http/Controllers/UserController.php | 17 ++- app/Http/Controllers/VendorController.php | 4 +- app/Http/Middleware/ApiCheck.php | 2 +- app/Http/Middleware/Authenticate.php | 2 +- app/Http/Middleware/StartupCheck.php | 7 +- app/Http/routes.php | 33 +++++- app/Libraries/Utils.php | 5 + app/Listeners/InvoiceListener.php | 3 +- app/Models/Account.php | 112 ++++++++++++------ app/Models/Client.php | 2 +- app/Models/Invitation.php | 2 +- app/Models/Invoice.php | 2 +- app/Models/User.php | 13 +- app/Ninja/Mailers/ContactMailer.php | 4 +- app/Ninja/Repositories/AccountRepository.php | 108 ++++++++++------- app/Ninja/Repositories/NinjaRepository.php | 12 +- app/Ninja/Repositories/ReferralRepository.php | 15 +-- app/Providers/AppServiceProvider.php | 2 +- app/Services/ClientService.php | 6 +- app/Services/InvoiceService.php | 2 +- app/Services/VendorService.php | 4 +- .../2016_04_16_103943_enterprise_plan.php | 13 +- database/seeds/UserTableSeeder.php | 3 + public/built.js | 23 ++-- public/js/pdf.pdfmake.js | 20 ++-- resources/lang/en/texts.php | 9 +- resources/views/accounts/api_tokens.blade.php | 2 +- .../views/accounts/client_portal.blade.php | 4 +- .../views/accounts/customize_design.blade.php | 8 +- .../views/accounts/email_settings.blade.php | 2 +- .../views/accounts/invoice_design.blade.php | 14 ++- .../views/accounts/invoice_settings.blade.php | 2 +- resources/views/accounts/management.blade.php | 19 ++- .../templates_and_reminders.blade.php | 2 +- resources/views/accounts/token.blade.php | 2 +- .../views/accounts/user_management.blade.php | 2 +- resources/views/client.blade.php | 2 +- resources/views/clients/edit.blade.php | 42 +++++-- resources/views/clients/show.blade.php | 2 +- resources/views/expenses/edit.blade.php | 2 +- resources/views/header.blade.php | 4 +- resources/views/invoices/edit.blade.php | 22 ++-- resources/views/invoices/history.blade.php | 2 +- resources/views/invoices/pdf.blade.php | 2 +- resources/views/invoices/view.blade.php | 8 +- resources/views/list.blade.php | 2 +- .../views/reports/chart_builder.blade.php | 2 +- resources/views/reports/d3.blade.php | 2 +- resources/views/users/edit.blade.php | 2 + resources/views/vendors/edit.blade.php | 38 +++++- 63 files changed, 472 insertions(+), 238 deletions(-) diff --git a/app/Console/Commands/SendReminders.php b/app/Console/Commands/SendReminders.php index 3243ac016eaf..58c38a7d8163 100644 --- a/app/Console/Commands/SendReminders.php +++ b/app/Console/Commands/SendReminders.php @@ -36,7 +36,7 @@ class SendReminders extends Command $this->info(count($accounts).' accounts found'); foreach ($accounts as $account) { - if (!$account->isPro()) { + if (!$account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) { continue; } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index ee67700872a4..d5fe853a4c11 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -521,7 +521,7 @@ class AccountController extends BaseController $invoice->client = $client; $invoice->invoice_items = [$invoiceItem]; - //$invoice->documents = $account->isPro() ? [$document] : []; + //$invoice->documents = $account->hasFeature(FEATURE_DOCUMENTS) ? [$document] : []; $invoice->documents = []; $data['account'] = $account; @@ -643,7 +643,7 @@ class AccountController extends BaseController private function saveCustomizeDesign() { - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) { $account = Auth::user()->account; $account->custom_design = Input::get('custom_design'); $account->invoice_design_id = CUSTOM_DESIGN; @@ -658,7 +658,7 @@ class AccountController extends BaseController private function saveClientPortal() { // Only allowed for pro Invoice Ninja users or white labeled self-hosted users - if ((Utils::isNinja() && Auth::user()->account->isPro()) || Auth::user()->account->isWhiteLabel()) { + if (Auth::user()->account->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) { $input_css = Input::get('client_view_css'); if (Utils::isNinja()) { // Allow referencing the body element @@ -709,7 +709,7 @@ class AccountController extends BaseController private function saveEmailTemplates() { - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) { $account = Auth::user()->account; foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) { @@ -771,7 +771,7 @@ class AccountController extends BaseController private function saveEmailSettings() { - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_CUSTOM_EMAILS)) { $rules = []; $user = Auth::user(); $iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH)); @@ -814,7 +814,7 @@ class AccountController extends BaseController private function saveInvoiceSettings() { - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_INVOICE_SETTINGS)) { $rules = [ 'invoice_number_pattern' => 'has_counter', 'quote_number_pattern' => 'has_counter', @@ -894,7 +894,7 @@ class AccountController extends BaseController private function saveInvoiceDesign() { - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) { $account = Auth::user()->account; $account->hide_quantity = Input::get('hide_quantity') ? true : false; $account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false; @@ -1188,6 +1188,9 @@ class AccountController extends BaseController \Log::info("Canceled Account: {$account->name} - {$user->email}"); $this->accountRepo->unlinkAccount($account); + if ($account->company->accounts->count() == 1) { + $account->company->forceDelete(); + } $account->forceDelete(); Auth::logout(); diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index bd8813912d25..e599890c6a90 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -133,6 +133,9 @@ class AuthController extends Controller { if (Auth::check() && !Auth::user()->registered) { $account = Auth::user()->account; $this->accountRepo->unlinkAccount($account); + if ($account->company->accounts->count() == 1) { + $account->company->forceDelete(); + } $account->forceDelete(); } diff --git a/app/Http/Controllers/ClientAuth/AuthController.php b/app/Http/Controllers/ClientAuth/AuthController.php index c88c8a4b85ea..ed5f199ac255 100644 --- a/app/Http/Controllers/ClientAuth/AuthController.php +++ b/app/Http/Controllers/ClientAuth/AuthController.php @@ -33,7 +33,7 @@ class AuthController extends Controller { $client = $invoice->client; $account = $client->account; - $data['hideLogo'] = $account->isWhiteLabel(); + $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL); $data['clientViewCSS'] = $account->clientViewCSS(); $data['clientFontUrl'] = $account->getFontsUrl(); } diff --git a/app/Http/Controllers/ClientAuth/PasswordController.php b/app/Http/Controllers/ClientAuth/PasswordController.php index beefb016123e..35576b47ae39 100644 --- a/app/Http/Controllers/ClientAuth/PasswordController.php +++ b/app/Http/Controllers/ClientAuth/PasswordController.php @@ -50,7 +50,7 @@ class PasswordController extends Controller { $client = $invoice->client; $account = $client->account; - $data['hideLogo'] = $account->isWhiteLabel(); + $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL); $data['clientViewCSS'] = $account->clientViewCSS(); $data['clientFontUrl'] = $account->getFontsUrl(); } @@ -117,7 +117,7 @@ class PasswordController extends Controller { $client = $invoice->client; $account = $client->account; - $data['hideLogo'] = $account->isWhiteLabel(); + $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL); $data['clientViewCSS'] = $account->clientViewCSS(); $data['clientFontUrl'] = $account->getFontsUrl(); } diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 7f2fac3eed46..2992eb9a9550 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -114,7 +114,7 @@ class ClientController extends BaseController if(Task::canCreate()){ $actionLinks[] = ['label' => trans('texts.new_task'), 'url' => URL::to('/tasks/create/'.$client->public_id)]; } - if (Utils::isPro() && Invoice::canCreate()) { + if (Utils::hasFeature(FEATURE_QUOTES) && Invoice::canCreate()) { $actionLinks[] = ['label' => trans('texts.new_quote'), 'url' => URL::to('/quotes/create/'.$client->public_id)]; } @@ -201,7 +201,7 @@ class ClientController extends BaseController if (Auth::user()->account->isNinjaAccount()) { if ($account = Account::whereId($client->public_id)->first()) { - $data['proPlanPaid'] = $account['pro_plan_paid']; + $data['planDetails'] = $account->getPlanDetails(false, false); } } diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 8af0d560d679..15f3ced57fd2 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -114,7 +114,7 @@ class DocumentController extends BaseController public function postUpload() { - if (!Utils::isPro()) { + if (!Utils::hasFeature(FEATURE_DOCUMENTS)) { return; } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 794f09404a2d..774ed7a7a70c 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -132,7 +132,11 @@ class InvoiceController extends BaseController $invoice->start_date = Utils::fromSqlDate($invoice->start_date); $invoice->end_date = Utils::fromSqlDate($invoice->end_date); $invoice->last_sent_date = Utils::fromSqlDate($invoice->last_sent_date); - $invoice->is_pro = Auth::user()->isPro(); + $invoice->features = [ + 'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN), + 'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY), + 'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS), + ]; $actions = [ ['url' => 'javascript:onCloneClick()', 'label' => trans("texts.clone_{$entityType}")], @@ -573,7 +577,11 @@ class InvoiceController extends BaseController $invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country'); $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); - $invoice->is_pro = Auth::user()->isPro(); + $invoice->features = [ + 'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN), + 'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY), + 'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS), + ]; $invoice->is_quote = intval($invoice->is_quote); $activityTypeId = $invoice->is_quote ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE; @@ -591,7 +599,11 @@ class InvoiceController extends BaseController $backup = json_decode($activity->json_backup); $backup->invoice_date = Utils::fromSqlDate($backup->invoice_date); $backup->due_date = Utils::fromSqlDate($backup->due_date); - $backup->is_pro = Auth::user()->isPro(); + $invoice->features = [ + 'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN), + 'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY), + 'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS), + ]; $backup->is_quote = isset($backup->is_quote) && intval($backup->is_quote); $backup->account = $invoice->account->toArray(); diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 2f2cdd92cc7e..1f53b4a0bc54 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -191,7 +191,7 @@ class PaymentController extends BaseController 'currencyId' => $client->getCurrencyId(), 'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'), 'account' => $client->account, - 'hideLogo' => $account->isWhiteLabel(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), 'hideHeader' => $account->isNinjaAccount(), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index b7649471f051..32d6d53c3887 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -72,7 +72,11 @@ class PublicClientController extends BaseController $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); - $invoice->is_pro = $account->isPro(); + $invoice->features = [ + 'customize_invoice_design' => $account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN), + 'remove_created_by' => $account->hasFeature(FEATURE_REMOVE_CREATED_BY), + 'invoice_settings' => $account->hasFeature(FEATURE_INVOICE_SETTINGS), + ]; $invoice->invoice_fonts = $account->getFontsData(); if ($invoice->invoice_design_id == CUSTOM_DESIGN) { @@ -122,10 +126,10 @@ class PublicClientController extends BaseController 'account' => $account, 'showApprove' => $showApprove, 'showBreadcrumbs' => false, - 'hideLogo' => $account->isWhiteLabel(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), 'hideHeader' => $account->isNinjaAccount() || !$account->enable_client_portal, 'hideDashboard' => !$account->enable_client_portal_dashboard, - 'showDocuments' => $account->isPro(), + 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), 'invoice' => $invoice->hidePrivateFields(), @@ -140,7 +144,7 @@ class PublicClientController extends BaseController 'phantomjs' => Input::has('phantomjs'), ); - if($account->isPro() && $this->canCreateZip()){ + if($account->hasFeature(FEATURE_DOCUMENTS) && $this->canCreateZip()){ $zipDocs = $this->getInvoiceZipDocuments($invoice, $size); if(count($zipDocs) > 1){ @@ -220,8 +224,8 @@ class PublicClientController extends BaseController 'color' => $color, 'account' => $account, 'client' => $client, - 'hideLogo' => $account->isWhiteLabel(), - 'showDocuments' => $account->isPro(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), + 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), ]; @@ -273,9 +277,9 @@ class PublicClientController extends BaseController $data = [ 'color' => $color, - 'hideLogo' => $account->isWhiteLabel(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), 'hideDashboard' => !$account->enable_client_portal_dashboard, - 'showDocuments' => $account->isPro(), + 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), 'title' => trans('texts.invoices'), @@ -310,9 +314,9 @@ class PublicClientController extends BaseController $color = $account->primary_color ? $account->primary_color : '#0b4d78'; $data = [ 'color' => $color, - 'hideLogo' => $account->isWhiteLabel(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), 'hideDashboard' => !$account->enable_client_portal_dashboard, - 'showDocuments' => $account->isPro(), + 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), 'entityType' => ENTITY_PAYMENT, @@ -354,9 +358,9 @@ class PublicClientController extends BaseController $color = $account->primary_color ? $account->primary_color : '#0b4d78'; $data = [ 'color' => $color, - 'hideLogo' => $account->isWhiteLabel(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), 'hideDashboard' => !$account->enable_client_portal_dashboard, - 'showDocuments' => $account->isPro(), + 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), 'title' => trans('texts.quotes'), @@ -392,9 +396,9 @@ class PublicClientController extends BaseController $color = $account->primary_color ? $account->primary_color : '#0b4d78'; $data = [ 'color' => $color, - 'hideLogo' => $account->isWhiteLabel(), + 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL), 'hideDashboard' => !$account->enable_client_portal_dashboard, - 'showDocuments' => $account->isPro(), + 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS), 'clientViewCSS' => $account->clientViewCSS(), 'clientFontUrl' => $account->getFontsUrl(), 'title' => trans('texts.documents'), diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 20fdd484ba80..4aaa504bfd1b 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -47,7 +47,7 @@ class QuoteController extends BaseController public function index() { - if (!Utils::isPro()) { + if (!Utils::hasFeature(FEATURE_QUOTES)) { return Redirect::to('/invoices/create'); } @@ -84,7 +84,7 @@ class QuoteController extends BaseController return $response; } - if (!Utils::isPro()) { + if (!Utils::hasFeature(FEATURE_QUOTES)) { return Redirect::to('/invoices/create'); } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index cfb9caaddef6..a1ee3ea06688 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -21,7 +21,7 @@ class ReportController extends BaseController $message = ''; $fileName = storage_path().'/dataviz_sample.txt'; - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) { $account = Account::where('id', '=', Auth::user()->account->id) ->with(['clients.invoices.invoice_items', 'clients.contacts']) ->first(); @@ -99,7 +99,7 @@ class ReportController extends BaseController 'title' => trans('texts.charts_and_reports'), ]; - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) { if ($enableReport) { $isExport = $action == 'export'; $params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport)); diff --git a/app/Http/Controllers/TokenController.php b/app/Http/Controllers/TokenController.php index 8e255d6057be..aa5434a2694b 100644 --- a/app/Http/Controllers/TokenController.php +++ b/app/Http/Controllers/TokenController.php @@ -93,7 +93,7 @@ class TokenController extends BaseController */ public function save($tokenPublicId = false) { - if (Auth::user()->account->isPro()) { + if (Auth::user()->account->hasFeature(FEATURE_API)) { $rules = [ 'name' => 'required', ]; diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 2e3f675aa53e..6b3ab9ca5b58 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -164,7 +164,7 @@ class UserController extends BaseController */ public function save($userPublicId = false) { - if (Auth::user()->isPro() && ! Auth::user()->isTrial()) { + if (Auth::user()->hasFeature(FEATURE_USERS)) { $rules = [ 'first_name' => 'required', 'last_name' => 'required', @@ -190,8 +190,10 @@ class UserController extends BaseController $user->last_name = trim(Input::get('last_name')); $user->username = trim(Input::get('email')); $user->email = trim(Input::get('email')); - $user->is_admin = boolval(Input::get('is_admin')); - $user->permissions = Input::get('permissions'); + if (Auth::user()->hasFeature(FEATURE_USER_PERMISSIONS)) { + $user->is_admin = boolval(Input::get('is_admin')); + $user->permissions = Input::get('permissions'); + } } else { $lastUser = User::withTrashed()->where('account_id', '=', Auth::user()->account_id) ->orderBy('public_id', 'DESC')->first(); @@ -202,12 +204,14 @@ class UserController extends BaseController $user->last_name = trim(Input::get('last_name')); $user->username = trim(Input::get('email')); $user->email = trim(Input::get('email')); - $user->is_admin = boolval(Input::get('is_admin')); $user->registered = true; $user->password = str_random(RANDOM_KEY_LENGTH); $user->confirmation_code = str_random(RANDOM_KEY_LENGTH); $user->public_id = $lastUser->public_id + 1; - $user->permissions = Input::get('permissions'); + if (Auth::user()->hasFeature(FEATURE_USER_PERMISSIONS)) { + $user->is_admin = boolval(Input::get('is_admin')); + $user->permissions = Input::get('permissions'); + } } $user->save(); @@ -286,6 +290,9 @@ class UserController extends BaseController if (!Auth::user()->registered) { $account = Auth::user()->account; $this->accountRepo->unlinkAccount($account); + if ($account->company->accounts->count() == 1) { + $account->company->forceDelete(); + } $account->forceDelete(); } } diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 032f8d423d34..b337c1df29e7 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -175,8 +175,8 @@ class VendorController extends BaseController $data = array_merge($data, self::getViewModel()); if (Auth::user()->account->isNinjaAccount()) { - if ($account = Account::whereId($vendor->public_id)->first()) { - $data['proPlanPaid'] = $account['pro_plan_paid']; + if ($account = Account::whereId($client->public_id)->first()) { + $data['planDetails'] = $account->getPlanDetails(false, false); } } diff --git a/app/Http/Middleware/ApiCheck.php b/app/Http/Middleware/ApiCheck.php index 63e370cf4192..8b38c60fe2c5 100644 --- a/app/Http/Middleware/ApiCheck.php +++ b/app/Http/Middleware/ApiCheck.php @@ -47,7 +47,7 @@ class ApiCheck { return $next($request); } - if (!Utils::isPro() && !$loggingIn) { + if (!Utils::hasFeature(FEATURE_API) && !$loggingIn) { return Response::json('API requires pro plan', 403, $headers); } else { $key = Auth::check() ? Auth::user()->account->id : $request->getClientIp(); diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 3a49c762a94f..81fde62439e2 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -42,7 +42,7 @@ class Authenticate { // Does this account require portal passwords? $account = Account::whereId($account_id)->first(); - if($account && (!$account->enable_portal_password || !$account->isPro())){ + if($account && (!$account->enable_portal_password || !$account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD))){ $authenticated = true; } diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index 14344e10e956..be96ade97efe 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -141,9 +141,10 @@ class StartupCheck } } elseif ($productId == PRODUCT_WHITE_LABEL) { if ($data == 'valid') { - $account = Auth::user()->account; - $account->pro_plan_paid = date_create()->format('Y-m-d'); - $account->save(); + $company = Auth::user()->account->company; + $company->plan_paid = date_create()->format('Y-m-d'); + $company->plan = PLAN_WHITE_LABEL; + $company->save(); Session::flash('message', trans('texts.bought_white_label')); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 7ebd29ecc759..9df520ede465 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -555,7 +555,6 @@ if (!defined('CONTACT_EMAIL')) { define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com'); define('NINJA_VERSION', '2.5.1.3'); - define('NINJA_DATE', '2000-01-01'); define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); define('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'); @@ -656,9 +655,41 @@ if (!defined('CONTACT_EMAIL')) { define('PLAN_FREE', 'free'); define('PLAN_PRO', 'pro'); define('PLAN_ENTERPRISE', 'enterprise'); + define('PLAN_WHITE_LABEL', 'white_label'); define('PLAN_TERM_MONTHLY', 'month'); define('PLAN_TERM_YEARLY', 'year'); + + // Pro + define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design'); + define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by'); + define('FEATURE_DIFFERENT_DESIGNS', 'different_designs'); + define('FEATURE_EMAIL_TEMPLATES_REMINDERS', 'email_templates_reminders'); + define('FEATURE_INVOICE_SETTINGS', 'invoice_settings'); + define('FEATURE_CUSTOM_EMAILS', 'custom_emails'); + define('FEATURE_PDF_ATTACHMENT', 'pdf_attachment'); + define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs'); + define('FEATURE_QUOTES', 'quotes'); + define('FEATURE_REPORTS', 'reports'); + define('FEATURE_API', 'api'); + define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password'); + define('FEATURE_CUSTOM_URL', 'custom_url'); + + define('FEATURE_MORE_CLIENTS', 'more_clients'); // No trial allowed + + // Whitelabel + define('FEATURE_CLIENT_PORTAL_CSS', 'client_portal_css'); + define('FEATURE_WHITE_LABEL', 'feature_white_label'); + // Enterprise + define('FEATURE_DOCUMENTS', 'documents'); + + // No Trial allowed + define('FEATURE_USERS', 'users');// Grandfathered for old Pro users + define('FEATURE_USER_PERMISSIONS', 'user_permissions'); + + // Pro users who started paying on or before this date will be able to manage users + define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-05-15'); + $creditCards = [ 1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'], 2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'], diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 7213a2ef2b7d..e4ad99c6ce45 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -118,6 +118,11 @@ class Utils return Auth::check() && Auth::user()->isPro(); } + public static function hasFeature($feature) + { + return Auth::check() && Auth::user()->hasFeature($feature); + } + public static function isAdmin() { return Auth::check() && Auth::user()->is_admin; diff --git a/app/Listeners/InvoiceListener.php b/app/Listeners/InvoiceListener.php index 9e40bbbe6d6b..91f3de8cc213 100644 --- a/app/Listeners/InvoiceListener.php +++ b/app/Listeners/InvoiceListener.php @@ -14,10 +14,11 @@ class InvoiceListener { public function createdInvoice(InvoiceWasCreated $event) { - if (Utils::isPro()) { + if (Utils::hasFeature(FEATURE_DIFFERENT_DESIGNS)) { return; } + // Make sure the account has the same design set as the invoice does if (Auth::check()) { $invoice = $event->invoice; $account = Auth::user()->account; diff --git a/app/Models/Account.php b/app/Models/Account.php index a2a2ea90374c..1d280a3d8aea 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -533,7 +533,7 @@ class Account extends Eloquent public function getNumberPrefix($isQuote) { - if ( ! $this->isPro()) { + if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) { return ''; } @@ -542,7 +542,7 @@ class Account extends Eloquent public function hasNumberPattern($isQuote) { - if ( ! $this->isPro()) { + if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) { return false; } @@ -662,7 +662,7 @@ class Account extends Eloquent $default = $this->invoice_number_counter; $actual = Utils::parseInt($invoice->invoice_number); - if ( ! $this->isPro() && $default != $actual) { + if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $default != $actual) { $this->invoice_number_counter = $actual + 1; } else { $this->invoice_number_counter += 1; @@ -794,6 +794,63 @@ class Account extends Eloquent $this->company->save(); } + public function hasFeature($feature) + { + $planDetails = $this->getPlanDetails(); + $selfHost = !Utils::isNinjaProd(); + + switch ($feature) { + // Pro + case FEATURE_CUSTOMIZE_INVOICE_DESIGN: + case FEATURE_REMOVE_CREATED_BY: + case FEATURE_DIFFERENT_DESIGNS: + case FEATURE_EMAIL_TEMPLATES_REMINDERS: + case FEATURE_INVOICE_SETTINGS: + case FEATURE_CUSTOM_EMAILS: + case FEATURE_PDF_ATTACHMENT: + case FEATURE_MORE_INVOICE_DESIGNS: + case FEATURE_QUOTES: + case FEATURE_REPORTS: + case FEATURE_API: + case FEATURE_CLIENT_PORTAL_PASSWORD: + case FEATURE_CUSTOM_URL: + return $selfHost || !empty($planDetails); + + // Pro; No trial allowed, unless they're trialing enterprise with an active pro plan + case FEATURE_MORE_CLIENTS: + return $selfHost || !empty($planDetails) && (!$planDetails['trial'] || !empty($this->getPlanDetails(false, false))); + + // White Label + case FEATURE_WHITE_LABEL: + if ($this->isNinjaAccount() || (!$selfHost && $planDetails && !$plan_details['expires'])) { + return false; + } + // Fallthrough + case FEATURE_CLIENT_PORTAL_CSS: + return !empty($planDetails);// A plan is required even for self-hosted users + + // Enterprise + case FEATURE_DOCUMENTS: + return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE; + + // Enterprise; No Trial allowed; grandfathered for old pro users + case FEATURE_USERS:// Grandfathered for old Pro users + if($planDetails && $planDetails['trial']) { + // Do they have a non-trial plan? + $planDetails = $this->getPlanDetails(false, false); + } + + return $selfHost || !empty($planDetails) && ($planDetails['plan'] == PLAN_ENTERPRISE || $planDetails['started'] <= date_create(PRO_USERS_GRANDFATHER_DEADLINE)); + + // Enterprise; No Trial allowed + case FEATURE_USER_PERMISSIONS: + return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && !$planDetails['trial']; + + default: + return false; + } + } + public function isPro(&$plan_details = null) { if (!Utils::isNinjaProd()) { @@ -850,7 +907,7 @@ class Account extends Eloquent $plan_active = false; if ($plan) { - if ($this->company->plan_expires == null && $this->company->plan_paid == NINJA_DATE) { + if ($this->company->plan_expires == null) { $plan_active = true; $plan_expires = false; } else { @@ -861,7 +918,7 @@ class Account extends Eloquent } } - if (!$include_inactive && !$plan_active && !$trial_plan) { + if (!$include_inactive && !$plan_active && !$trial_active) { return null; } @@ -956,7 +1013,7 @@ class Account extends Eloquent $today = new DateTime('now'); $interval = $today->diff($planDetails['expires']); - return $interval ? 14 - $interval->d : 0; + return $interval ? $interval->d : 0; } public function getRenewalDate() @@ -973,20 +1030,6 @@ class Account extends Eloquent return $date->format('Y-m-d'); } - public function isWhiteLabel() - { - if ($this->isNinjaAccount()) { - return false; - } - - if (Utils::isNinjaProd()) { - return self::isPro($plan_details) && $plan_details['expires']; - } else { - $plan_details = $this->getPlanDetails(); - return $plan_details; - } - } - public function getLogoSize() { if(!$this->hasLogo()){ @@ -1063,7 +1106,7 @@ class Account extends Eloquent public function getEmailSubject($entityType) { - if ($this->isPro()) { + if ($this->hasFeature(FEATURE_CUSTOM_EMAILS)) { $field = "email_subject_{$entityType}"; $value = $this->$field; @@ -1083,7 +1126,7 @@ class Account extends Eloquent $template = "
    \$client,

    "; - if ($this->isPro() && $this->email_design_id != EMAIL_DESIGN_PLAIN) { + if ($this->hasFeature(FEATURE_CUSTOM_EMAILS) && $this->email_design_id != EMAIL_DESIGN_PLAIN) { $template .= "
    " . trans("texts.{$entityType}_message_button", ['amount' => '$amount']) . "

    " . "
    \$viewButton

    "; } else { @@ -1102,7 +1145,7 @@ class Account extends Eloquent { $template = false; - if ($this->isPro()) { + if ($this->hasFeature(FEATURE_CUSTOM_EMAILS)) { $field = "email_template_{$entityType}"; $template = $this->$field; } @@ -1198,7 +1241,7 @@ class Account extends Eloquent public function showCustomField($field, $entity = false) { - if ($this->isPro()) { + if ($this->hasFeature(FEATURE_INVOICE_SETTINGS)) { return $this->$field ? true : false; } @@ -1214,18 +1257,18 @@ class Account extends Eloquent public function attatchPDF() { - return $this->isPro() && $this->pdf_email_attachment; + return $this->hasFeaure(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment; } public function getEmailDesignId() { - return $this->isPro() ? $this->email_design_id : EMAIL_DESIGN_PLAIN; + return $this->hasFeature(FEATURE_CUSTOM_EMAILS) ? $this->email_design_id : EMAIL_DESIGN_PLAIN; } public function clientViewCSS(){ - $css = null; + $css = ''; - if ($this->isPro()) { + if ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) { $bodyFont = $this->getBodyFontCss(); $headerFont = $this->getHeaderFontCss(); @@ -1233,11 +1276,10 @@ class Account extends Eloquent if ($headerFont != $bodyFont) { $css .= 'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{'.$headerFont.'}'; } - - if ((Utils::isNinja() && $this->isPro()) || $this->isWhiteLabel()) { - // For self-hosted users, a white-label license is required for custom CSS - $css .= $this->client_view_css; - } + } + if ($this->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) { + // For self-hosted users, a white-label license is required for custom CSS + $css .= $this->client_view_css; } return $css; @@ -1270,11 +1312,11 @@ class Account extends Eloquent } public function getHeaderFontId() { - return ($this->isPro() && $this->header_font_id) ? $this->header_font_id : DEFAULT_HEADER_FONT; + return ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) && $this->header_font_id) ? $this->header_font_id : DEFAULT_HEADER_FONT; } public function getBodyFontId() { - return ($this->isPro() && $this->body_font_id) ? $this->body_font_id : DEFAULT_BODY_FONT; + return ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) && $this->body_font_id) ? $this->body_font_id : DEFAULT_BODY_FONT; } public function getHeaderFontName(){ diff --git a/app/Models/Client.php b/app/Models/Client.php index 92232647117c..10eca4781d88 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -155,7 +155,7 @@ class Client extends EntityModel $contact->send_invoice = true; } - if (!Utils::isPro() || $this->account->enable_portal_password){ + if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $this->account->enable_portal_password){ if(!empty($data['password']) && $data['password']!='-%unchanged%-'){ $contact->password = bcrypt($data['password']); } else if(empty($data['password'])){ diff --git a/app/Models/Invitation.php b/app/Models/Invitation.php index 687e9873457d..becd0197c93f 100644 --- a/app/Models/Invitation.php +++ b/app/Models/Invitation.php @@ -40,7 +40,7 @@ class Invitation extends EntityModel $url = SITE_URL; $iframe_url = $this->account->iframe_url; - if ($this->account->isPro()) { + if ($this->account->hasFeature(FEATURE_CUSTOM_URL)) { if ($iframe_url && !$forceOnsite) { return "{$iframe_url}?{$this->invitation_key}"; } elseif ($this->account->subdomain) { diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 4d9b82c79416..f158a3c83830 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -409,7 +409,7 @@ class Invoice extends EntityModel implements BalanceAffecting 'invoice_design', 'invoice_design_id', 'invoice_fonts', - 'is_pro', + 'features', 'is_quote', 'custom_value1', 'custom_value2', diff --git a/app/Models/User.php b/app/Models/User.php index c431dbb34484..32a4960a8482 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -112,9 +112,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return $this->account->isPro(); } + public function hasFeature($feature) + { + return $this->account->hasFeature($feature); + } + public function isPaidPro() { - return $this->isPro() && ! $this->isTrial(); + return $this->isPro($accountDetails) && !$accountDetails['trial']; } public function isTrial() @@ -129,7 +134,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon public function maxInvoiceDesignId() { - return $this->isPro() ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST); + return $this->hasFeature(FEATURE_MORE_INVOICE_DESIGNS) ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST); } public function getDisplayName() @@ -173,7 +178,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon public function getMaxNumClients() { - if ($this->isPro() && ! $this->isTrial()) { + if ($this->hasFeature(FEATURE_MORE_CLIENTS)) { return MAX_NUM_CLIENTS_PRO; } @@ -186,7 +191,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon public function getMaxNumVendors() { - if ($this->isPro() && ! $this->isTrial()) { + if ($this->hasFeature(FEATURE_MORE_CLIENTS)) { return MAX_NUM_VENDORS_PRO; } diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 229895067e42..4b176628ebf0 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -136,7 +136,7 @@ class ContactMailer extends Mailer 'amount' => $invoice->getRequestedAmount() ]; - if (empty($invitation->contact->password) && $account->isPro() && $account->enable_portal_password && $account->send_portal_password) { + if (empty($invitation->contact->password) && $account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password && $account->send_portal_password) { // The contact needs a password $variables['password'] = $password = $this->generatePassword(); $invitation->contact->password = bcrypt($password); @@ -291,7 +291,7 @@ class ContactMailer extends Mailer $passwordHTML = isset($data['password'])?'

    '.trans('texts.password').': '.$data['password'].'

    ':false; $documentsHTML = ''; - if($account->isPro() && $invoice->hasDocuments()){ + if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){ $documentsHTML .= trans('texts.email_documents_header').'

      '; foreach($invoice->documents as $document){ $documentsHTML .= '
    • '.HTML::entities($document->name).'
    • '; diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 0a4fe424b6c8..367e55007fe7 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -17,6 +17,7 @@ use App\Models\Client; use App\Models\Language; use App\Models\Contact; use App\Models\Account; +use App\Models\Company; use App\Models\User; use App\Models\UserAccount; use App\Models\AccountToken; @@ -25,9 +26,13 @@ class AccountRepository { public function create($firstName = '', $lastName = '', $email = '', $password = '') { + $company = new Company(); + $company->save(); + $account = new Account(); $account->ip = Request::getClientIp(); $account->account_key = str_random(RANDOM_KEY_LENGTH); + $account->company_id = $company->id; // Track referal code if ($referralCode = Session::get(SESSION_REFERRAL_CODE)) { @@ -493,10 +498,10 @@ class AccountRepository $item = new stdClass(); $item->id = $record->id; $item->user_id = $user->id; + $item->public_id = $user->public_id; $item->user_name = $user->getDisplayName(); $item->account_id = $user->account->id; $item->account_name = $user->account->getDisplayName(); - $item->pro_plan_paid = $user->account->pro_plan_paid; $item->logo_url = $user->account->hasLogo() ? $user->account->getLogoUrl() : null; $data[] = $item; } @@ -509,43 +514,6 @@ class AccountRepository return self::prepareUsersData($record); } - public function syncAccounts($userId, $proPlanPaid) { - $users = self::loadAccounts($userId); - self::syncUserAccounts($users, $proPlanPaid); - } - - public function syncUserAccounts($users, $proPlanPaid = false) { - if (!$users) { - return; - } - - if (!$proPlanPaid) { - foreach ($users as $user) { - if ($user->pro_plan_paid && $user->pro_plan_paid != '0000-00-00') { - $proPlanPaid = $user->pro_plan_paid; - break; - } - } - } - - if (!$proPlanPaid) { - return; - } - - $accountIds = []; - foreach ($users as $user) { - if ($user->pro_plan_paid != $proPlanPaid) { - $accountIds[] = $user->account_id; - } - } - - if (count($accountIds)) { - DB::table('accounts') - ->whereIn('id', $accountIds) - ->update(['pro_plan_paid' => $proPlanPaid]); - } - } - public function associateAccounts($userId1, $userId2) { $record = self::findUserAccounts($userId1, $userId2); @@ -564,8 +532,59 @@ class AccountRepository $record->save(); - $users = self::prepareUsersData($record); - self::syncUserAccounts($users); + $users = $this->getUserAccounts($record); + + // Pick the primary user + foreach ($users as $user) { + if (!$user->public_id) { + $useAsPrimary = false; + if(empty($primaryUser)) { + $useAsPrimary = true; + } + + $planDetails = $user->account->getPlanDetails(false, false); + $planLevel = 0; + + if ($planDetails) { + $planLevel = 1; + if ($planDetails['plan'] == PLAN_ENTERPRISE) { + $planLevel = 2; + } + + if (!$useAsPrimary && ( + $planLevel > $primaryUserPlanLevel + || ($planLevel == $primaryUserPlanLevel && $planDetails['expires'] > $primaryUserPlanExpires) + )) { + $useAsPrimary = true; + } + } + + if ($useAsPrimary) { + $primaryUser = $user; + $primaryUserPlanLevel = $planLevel; + if ($planDetails) { + $primaryUserPlanExpires = $planDetails['expires']; + } + } + } + } + + // Merge other companies into the primary user's company + if (!empty($primaryUser)) { + foreach ($users as $user) { + if ($user == $primaryUser || $user->public_id) { + continue; + } + + if ($user->account->company_id != $primaryUser->account->company_id) { + foreach ($user->account->company->accounts as $account) { + $account->company_id = $primaryUser->account->company_id; + $account->save(); + } + $user->account->company->forceDelete(); + } + } + } return $users; } @@ -585,6 +604,15 @@ class AccountRepository $userAccount->removeUserId($userId); $userAccount->save(); } + + $user = User::whereId($userId)->first(); + + if (!$user->public_id && $user->account->company->accounts->count() > 1) { + $company = Company::create(); + $company->save(); + $user->account->company_id = $company->id; + $user->account->save(); + } } public function findWithReminders() diff --git a/app/Ninja/Repositories/NinjaRepository.php b/app/Ninja/Repositories/NinjaRepository.php index 3f9c1fa4f19e..d1dc7abede6e 100644 --- a/app/Ninja/Repositories/NinjaRepository.php +++ b/app/Ninja/Repositories/NinjaRepository.php @@ -4,7 +4,7 @@ use App\Models\Account; class NinjaRepository { - public function updateProPlanPaid($clientPublicId, $proPlanPaid) + public function updatePlanDetails($clientPublicId, $data) { $account = Account::whereId($clientPublicId)->first(); @@ -12,7 +12,13 @@ class NinjaRepository return; } - $account->pro_plan_paid = $proPlanPaid; - $account->save(); + $company = $account->company; + $company->plan = !empty($data['plan']) && $data['plan'] != PLAN_FREE?$data['plan']:null; + $company->plan_term = !empty($data['plan_term'])?$data['plan_term']:null; + $company->plan_paid = !empty($data['plan_paid'])?$data['plan_paid']:null; + $company->plan_started = !empty($data['plan_started'])?$data['plan_started']:null; + $company->plan_expires = !empty($data['plan_expires'])?$data['plan_expires']:null; + + $company->save(); } } diff --git a/app/Ninja/Repositories/ReferralRepository.php b/app/Ninja/Repositories/ReferralRepository.php index 0a215d40cc42..f96475c72ddc 100644 --- a/app/Ninja/Repositories/ReferralRepository.php +++ b/app/Ninja/Repositories/ReferralRepository.php @@ -1,15 +1,13 @@ where('referral_user_id', $userId) - ->get(['id', 'pro_plan_paid']); + $accounts = Account::where('referral_user_id', $userId); $counts = [ 'free' => 0, @@ -19,9 +17,11 @@ class ReferralRepository foreach ($accounts as $account) { $counts['free']++; - if ($account->isPro()) { + $plan = $account->getPlanDetails(false, false); + + if ($plan) { $counts['pro']++; - if ($account->isEnterprise()) { + if ($plan['plan'] == PLAN_ENTERPRISE) { $counts['enterprise']++; } } @@ -29,7 +29,4 @@ class ReferralRepository return $counts; } - - - } \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d5f26393681f..4a55807e4f0f 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -68,7 +68,7 @@ class AppServiceProvider extends ServiceProvider { if(!empty($items))$items[] = '
    • '; $items[] = '
    • '.trans("texts.recurring_invoices").'
    • '; if(Invoice::canCreate())$items[] = '
    • '.trans("texts.new_recurring_invoice").'
    • '; - if (Auth::user()->isPro()) { + if (Auth::user()->hasFeature(FEATURE_QUOTES)) { $items[] = '
    • '; $items[] = '
    • '.trans("texts.quotes").'
    • '; if(Invoice::canCreate())$items[] = '
    • '.trans("texts.new_quote").'
    • '; diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index 662fc8eab9e8..f7a7dac57edb 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -32,8 +32,8 @@ class ClientService extends BaseService public function save($data) { - if (Auth::user()->account->isNinjaAccount() && isset($data['pro_plan_paid'])) { - $this->ninjaRepo->updateProPlanPaid($data['public_id'], $data['pro_plan_paid']); + if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { + $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } return $this->clientRepo->save($data); @@ -134,7 +134,7 @@ class ClientService extends BaseService return URL::to("quotes/create/{$model->public_id}"); }, function ($model) { - return Auth::user()->isPro() && Invoice::canCreate(); + return Auth::user()->hasFeature(FEATURE_QUOTES) && Invoice::canCreate(); } ], [ diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index c7c0be72acc0..43cdadd31d1c 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -100,7 +100,7 @@ class InvoiceService extends BaseService return null; } - if ($account->auto_convert_quote || ! $account->isPro()) { + if ($account->auto_convert_quote || ! $account->hasFeature(FEATURE_QUOTES)) { $invoice = $this->convertQuote($quote, $invitation); event(new QuoteInvitationWasApproved($quote, $invoice, $invitation)); diff --git a/app/Services/VendorService.php b/app/Services/VendorService.php index 6f9b4420d772..1118f23c5356 100644 --- a/app/Services/VendorService.php +++ b/app/Services/VendorService.php @@ -28,8 +28,8 @@ class VendorService extends BaseService public function save($data) { - if (Auth::user()->account->isNinjaAccount() && isset($data['pro_plan_paid'])) { - $this->ninjaRepo->updateProPlanPaid($data['public_id'], $data['pro_plan_paid']); + if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { + $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } return $this->vendorRepo->save($data); diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index 0ff4417a2238..fd180a5c5361 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -18,7 +18,7 @@ class EnterprisePlan extends Migration { $table->increments('id'); - $table->enum('plan', array('pro', 'enterprise'))->nullable(); + $table->enum('plan', array('pro', 'enterprise', 'white_label'))->nullable(); $table->enum('plan_term', array('month', 'year'))->nullable(); $table->date('plan_started')->nullable(); $table->date('plan_paid')->nullable(); @@ -33,10 +33,6 @@ class EnterprisePlan extends Migration $table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable(); $table->enum('pending_term', array('month', 'year'))->nullable(); - // Used when a user has started changing a plan but hasn't finished paying yet - $table->enum('temp_pending_plan', array('pro', 'enterprise'))->nullable(); - $table->enum('temp_pending_term', array('month', 'year'))->nullable(); - $table->timestamps(); $table->softDeletes(); }); @@ -98,11 +94,14 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun $company->plan_started = $primaryAccount->pro_plan_paid; $company->plan_paid = $primaryAccount->pro_plan_paid; - if ($company->plan_paid != NINJA_DATE) { + if (!Utils::isNinjaProd()) { + $company->plan = 'white_label'; + $company->plan_term = null; + } elseif ($company->plan_paid != '2000-01-01'/* NINJA_DATE*/) { $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); $expires->modify('+1 year'); $company->plan_expires = $expires->format('Y-m-d'); - } + } } if ($primaryAccount->pro_plan_trial && $primaryAccount->pro_plan_trial != '0000-00-00') { diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php index 2f27b9479304..c89cb45b20ba 100644 --- a/database/seeds/UserTableSeeder.php +++ b/database/seeds/UserTableSeeder.php @@ -13,10 +13,13 @@ class UserTableSeeder extends Seeder Eloquent::unguard(); + $company = Company::create(); + $account = Account::create([ //'name' => 'Test Account', 'account_key' => str_random(RANDOM_KEY_LENGTH), 'timezone_id' => 1, + 'company_id' => $company, ]); User::create([ diff --git a/public/built.js b/public/built.js index 327b7e5fb2ae..12771495d818 100644 --- a/public/built.js +++ b/public/built.js @@ -29449,9 +29449,6 @@ return a?b._ordinalParse:b._ordinalParseLenient}),R(["D","DD"],td),R("Do",functi //! github.com/moment/moment-timezone !function(a,b){"use strict";"function"==typeof define&&define.amd?define(["moment"],b):"object"==typeof exports?module.exports=b(require("moment")):b(a.moment)}(this,function(a){"use strict";function b(a){return a>96?a-87:a>64?a-29:a-48}function c(a){var c,d=0,e=a.split("."),f=e[0],g=e[1]||"",h=1,i=0,j=1;for(45===a.charCodeAt(0)&&(d=1,j=-1),d;dc;c++)a[c]=Math.round((a[c-1]||0)+6e4*a[c]);a[b-1]=1/0}function f(a,b){var c,d=[];for(c=0;cz||2===z&&6>A)&&q("Moment Timezone requires Moment.js >= 2.6.0. You are using Moment.js "+a.version+". See momentjs.com"),h.prototype={_set:function(a){this.name=a.name,this.abbrs=a.abbrs,this.untils=a.untils,this.offsets=a.offsets},_index:function(a){var b,c=+a,d=this.untils;for(b=0;be;e++)if(b=g[e],c=g[e+1],d=g[e?e-1:e],c>b&&r.moveAmbiguousForward?b=c:b>d&&r.moveInvalidForward&&(b=d),fz||2===z&&9>A)&&q("Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js "+a.version+"."),a.defaultZone=b?k(b):null,a};var C=a.momentProperties;return"[object Array]"===Object.prototype.toString.call(C)?(C.push("_z"),C.push("_a")):C&&(C._z=null),n({version:"2015g",zones:["Africa/Abidjan|LMT GMT|g.8 0|01|-2ldXH.Q","Africa/Accra|LMT GMT GHST|.Q 0 -k|012121212121212121212121212121212121212121212121|-26BbX.8 6tzX.8 MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE","Africa/Addis_Ababa|LMT EAT BEAT BEAUT|-2r.g -30 -2u -2J|01231|-1F3Cr.g 3Dzr.g okMu MFXJ","Africa/Algiers|PMT WET WEST CET CEST|-9.l 0 -10 -10 -20|0121212121212121343431312123431213|-2nco9.l cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 DA0 Imo0 rd0 De0 9Xz0 1fb0 1ap0 16K0 2yo0 mEp0 hwL0 jxA0 11A0 dDd0 17b0 11B0 1cN0 2Dy0 1cN0 1fB0 1cL0","Africa/Bangui|LMT WAT|-d.A -10|01|-22y0d.A","Africa/Bissau|LMT WAT GMT|12.k 10 0|012|-2ldWV.E 2xonV.E","Africa/Blantyre|LMT CAT|-2a.k -20|01|-2GJea.k","Africa/Cairo|EET EEST|-20 -30|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-1bIO0 vb0 1ip0 11z0 1iN0 1nz0 12p0 1pz0 10N0 1pz0 16p0 1jz0 s3d0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1WL0 rd0 1Rz0 wp0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1qL0 Xd0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1ny0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 WL0 1qN0 Rb0 1wp0 On0 1zd0 Lz0 1EN0 Fb0 c10 8n0 8Nd0 gL0 e10 mn0","Africa/Casablanca|LMT WET WEST CET|u.k 0 -10 -10|0121212121212121213121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2gMnt.E 130Lt.E rb0 Dd0 dVb0 b6p0 TX0 EoB0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4mn0 SyN0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 Rc0 11A0 e00 e00 U00 11A0 8o0 e00 11A0 11A0 5A0 e00 17c0 1fA0 1a00 1a00 1fA0 17c0 1io0 14o0 1lc0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1lc0 14o0 1fA0","Africa/Ceuta|WET WEST CET CEST|0 -10 -10 -20|010101010101010101010232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-25KN0 11z0 drd0 18o0 3I00 17c0 1fA0 1a00 1io0 1a00 1y7p0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4VB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Africa/El_Aaiun|LMT WAT WET WEST|Q.M 10 0 -10|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1rDz7.c 1GVA7.c 6L0 AL0 1Nd0 XX0 1Cp0 pz0 1cBB0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 Rc0 11A0 e00 e00 U00 11A0 8o0 e00 11A0 11A0 5A0 e00 17c0 1fA0 1a00 1a00 1fA0 17c0 1io0 14o0 1lc0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1lc0 14o0 1fA0","Africa/Johannesburg|SAST SAST SAST|-1u -20 -30|012121|-2GJdu 1Ajdu 1cL0 1cN0 1cL0","Africa/Juba|LMT CAT CAST EAT|-2a.8 -20 -30 -30|01212121212121212121212121212121213|-1yW2a.8 1zK0a.8 16L0 1iN0 17b0 1jd0 17b0 1ip0 17z0 1i10 17X0 1hB0 18n0 1hd0 19b0 1gp0 19z0 1iN0 17b0 1ip0 17z0 1i10 18n0 1hd0 18L0 1gN0 19b0 1gp0 19z0 1iN0 17z0 1i10 17X0 yGd0","Africa/Monrovia|MMT LRT GMT|H.8 I.u 0|012|-23Lzg.Q 29s01.m","Africa/Ndjamena|LMT WAT WAST|-10.c -10 -20|0121|-2le10.c 2J3c0.c Wn0","Africa/Tripoli|LMT CET CEST EET|-Q.I -10 -20 -20|012121213121212121212121213123123|-21JcQ.I 1hnBQ.I vx0 4iP0 xx0 4eN0 Bb0 7ip0 U0n0 A10 1db0 1cN0 1db0 1dd0 1db0 1eN0 1bb0 1e10 1cL0 1c10 1db0 1dd0 1db0 1cN0 1db0 1q10 fAn0 1ep0 1db0 AKq0 TA0 1o00","Africa/Tunis|PMT CET CEST|-9.l -10 -20|0121212121212121212121212121212121|-2nco9.l 18pa9.l 1qM0 DA0 3Tc0 11B0 1ze0 WM0 7z0 3d0 14L0 1cN0 1f90 1ar0 16J0 1gXB0 WM0 1rA0 11c0 nwo0 Ko0 1cM0 1cM0 1rA0 10M0 zuM0 10N0 1aN0 1qM0 WM0 1qM0 11A0 1o00","Africa/Windhoek|SWAT SAST SAST CAT WAT WAST|-1u -20 -30 -20 -10 -20|012134545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-2GJdu 1Ajdu 1cL0 1SqL0 9NA0 11D0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0","America/Adak|NST NWT NPT BST BDT AHST HST HDT|b0 a0 a0 b0 a0 a0 a0 90|012034343434343434343434343434343456767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17SX0 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Anchorage|CAT CAWT CAPT AHST AHDT YST AKST AKDT|a0 90 90 a0 90 90 90 80|012034343434343434343434343434343456767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17T00 8wX0 iA0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Anguilla|LMT AST|46.4 40|01|-2kNvR.U","America/Araguaina|LMT BRT BRST|3c.M 30 20|0121212121212121212121212121212121212121212121212121|-2glwL.c HdKL.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 ny10 Lz0","America/Argentina/Buenos_Aires|CMT ART ARST ART ARST|4g.M 40 30 30 20|0121212121212121212121212121212121212121213434343434343234343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 g0p0 10M0 j3c0 uL0 1qN0 WL0","America/Argentina/Catamarca|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343454343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 ako0 7B0 8zb0 uL0","America/Argentina/Cordoba|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343454343234343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 j3c0 uL0 1qN0 WL0","America/Argentina/Jujuy|CMT ART ARST ART ARST WART WARST|4g.M 40 30 30 20 40 30|01212121212121212121212121212121212121212134343456543432343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1ze0 TX0 1ld0 WK0 1wp0 TX0 g0p0 10M0 j3c0 uL0","America/Argentina/La_Rioja|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|01212121212121212121212121212121212121212134343434534343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 g0p0 10M0 ako0 7B0 8zb0 uL0","America/Argentina/Mendoza|CMT ART ARST ART ARST WART WARST|4g.M 40 30 30 20 40 30|0121212121212121212121212121212121212121213434345656543235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1u20 SL0 1vd0 Tb0 1wp0 TW0 g0p0 10M0 agM0 Op0 7TX0 uL0","America/Argentina/Rio_Gallegos|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343434343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 g0p0 10M0 ako0 7B0 8zb0 uL0","America/Argentina/Salta|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|01212121212121212121212121212121212121212134343434543432343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 j3c0 uL0","America/Argentina/San_Juan|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|01212121212121212121212121212121212121212134343434534343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 g0p0 10M0 ak00 m10 8lb0 uL0","America/Argentina/San_Luis|CMT ART ARST ART ARST WART WARST|4g.M 40 30 30 20 40 30|01212121212121212121212121212121212121212134343456536353465653|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 XX0 1q20 SL0 AN0 kin0 10M0 ak00 m10 8lb0 8L0 jd0 1qN0 WL0 1qN0","America/Argentina/Tucuman|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|012121212121212121212121212121212121212121343434345434323534343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 ako0 4N0 8BX0 uL0 1qN0 WL0","America/Argentina/Ushuaia|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343434343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 g0p0 10M0 ajA0 8p0 8zb0 uL0","America/Aruba|LMT ANT AST|4z.L 4u 40|012|-2kV7o.d 28KLS.d","America/Asuncion|AMT PYT PYT PYST|3O.E 40 30 30|012131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313|-1x589.k 1DKM9.k 3CL0 3Dd0 10L0 1pB0 10n0 1pB0 10n0 1pB0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1lB0 14n0 1dd0 1cL0 1fd0 WL0 1rd0 1aL0 1dB0 Xz0 1qp0 Xb0 1qN0 10L0 1rB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 WN0 1qL0 11B0 1nX0 1ip0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 TX0 1tB0 19X0 1a10 1fz0 1a10 1fz0 1cN0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0","America/Atikokan|CST CDT CWT CPT EST|60 50 50 50 50|0101234|-25TQ0 1in0 Rnb0 3je0 8x30 iw0","America/Bahia|LMT BRT BRST|2y.4 30 20|01212121212121212121212121212121212121212121212121212121212121|-2glxp.U HdLp.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 l5B0 Rb0","America/Bahia_Banderas|LMT MST CST PST MDT CDT|71 70 60 80 60 50|0121212131414141414141414141414141414152525252525252525252525252525252525252525252525252525252|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nW0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Barbados|LMT BMT AST ADT|3W.t 3W.t 40 30|01232323232|-1Q0I1.v jsM0 1ODC1.v IL0 1ip0 17b0 1ip0 17b0 1ld0 13b0","America/Belem|LMT BRT BRST|3d.U 30 20|012121212121212121212121212121|-2glwK.4 HdKK.4 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0","America/Belize|LMT CST CHDT CDT|5Q.M 60 5u 50|01212121212121212121212121212121212121212121212121213131|-2kBu7.c fPA7.c Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1f0Mu qn0 lxB0 mn0","America/Blanc-Sablon|AST ADT AWT APT|40 30 30 30|010230|-25TS0 1in0 UGp0 8x50 iu0","America/Boa_Vista|LMT AMT AMST|42.E 40 30|0121212121212121212121212121212121|-2glvV.k HdKV.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 smp0 WL0 1tB0 2L0","America/Bogota|BMT COT COST|4U.g 50 40|0121|-2eb73.I 38yo3.I 2en0","America/Boise|PST PDT MST MWT MPT MDT|80 70 70 60 60 60|0101023425252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-261q0 1nX0 11B0 1nX0 8C10 JCL0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 Dd0 1Kn0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Cambridge_Bay|zzz MST MWT MPT MDDT MDT CST CDT EST|0 70 60 60 50 60 60 50 50|0123141515151515151515151515151515151515151515678651515151515151515151515151515151515151515151515151515151515151515151515151|-21Jc0 RO90 8x20 ix0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11A0 1nX0 2K0 WQ0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Campo_Grande|LMT AMT AMST|3C.s 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2glwl.w HdLl.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0","America/Cancun|LMT CST EST EDT CDT|5L.4 60 50 40 50|0123232341414141414141414141414141414141412|-1UQG0 2q2o0 yLB0 1lb0 14p0 1lb0 14p0 Lz0 xB0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 Dd0","America/Caracas|CMT VET VET|4r.E 4u 40|0121|-2kV7w.k 28KM2.k 1IwOu","America/Cayenne|LMT GFT GFT|3t.k 40 30|012|-2mrwu.E 2gWou.E","America/Cayman|KMT EST EDT|57.b 50 40|0121212121212121212121212121212121212121212121|-2l1uQ.N 4duNQ.N 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Chicago|CST CDT EST CWT CPT|60 50 50 50 50|01010101010101010101010101010101010102010101010103401010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 1wp0 TX0 WN0 1qL0 1cN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 11B0 1Hz0 14p0 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Chihuahua|LMT MST CST CDT MDT|74.k 70 60 50 60|0121212323241414141414141414141414141414141414141414141414141414141414141414141414141414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Costa_Rica|SJMT CST CDT|5A.d 60 50|0121212121|-1Xd6n.L 2lu0n.L Db0 1Kp0 Db0 pRB0 15b0 1kp0 mL0","America/Creston|MST PST|70 80|010|-29DR0 43B0","America/Cuiaba|LMT AMT AMST|3I.k 40 30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2glwf.E HdLf.E 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 4a10 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0","America/Danmarkshavn|LMT WGT WGST GMT|1e.E 30 20 0|01212121212121212121212121212121213|-2a5WJ.k 2z5fJ.k 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 DC0","America/Dawson|YST YDT YWT YPT YDDT PST PDT|90 80 80 80 70 80 70|0101023040565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-25TN0 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 jrA0 fNd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Dawson_Creek|PST PDT PWT PPT MST|80 70 70 70 70|0102301010101010101010101010101010101010101010101010101014|-25TO0 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 ML0","America/Denver|MST MDT MWT MPT|70 60 60 60|01010101023010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261r0 1nX0 11B0 1nX0 11B0 1qL0 WN0 mn0 Ord0 8x20 ix0 LCN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Detroit|LMT CST EST EWT EPT EDT|5w.b 60 50 40 40 40|01234252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-2Cgir.N peqr.N 156L0 8x40 iv0 6fd0 11z0 Jy10 SL0 dnB0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Edmonton|LMT MST MDT MWT MPT|7x.Q 70 60 60 60|01212121212121341212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2yd4q.8 shdq.8 1in0 17d0 hz0 2dB0 1fz0 1a10 11z0 1qN0 WL0 1qN0 11z0 IGN0 8x20 ix0 3NB0 11z0 LFB0 1cL0 3Cp0 1cL0 66N0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Eirunepe|LMT ACT ACST AMT|4D.s 50 40 40|0121212121212121212121212121212131|-2glvk.w HdLk.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0 yTd0 d5X0","America/El_Salvador|LMT CST CDT|5U.M 60 50|012121|-1XiG3.c 2Fvc3.c WL0 1qN0 WL0","America/Ensenada|LMT MST PST PDT PWT PPT|7M.4 70 80 70 70 70|012123245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQE0 4PX0 8mM0 8lc0 SN0 1cL0 pHB0 83r0 zI0 5O10 1Rz0 cOP0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 BUp0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Fort_Nelson|PST PDT PWT PPT MST|80 70 70 70 70|01023010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010104|-25TO0 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0","America/Fort_Wayne|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|010101023010101010101010101040454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 QI10 Db0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 5Tz0 1o10 qLb0 1cL0 1cN0 1cL0 1qhd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Fortaleza|LMT BRT BRST|2y 30 20|0121212121212121212121212121212121212121|-2glxq HdLq 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 5z0 2mN0 On0","America/Glace_Bay|LMT AST ADT AWT APT|3X.M 40 30 30 30|012134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsI0.c CwO0.c 1in0 UGp0 8x50 iu0 iq10 11z0 Jg10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Godthab|LMT WGT WGST|3q.U 30 20|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a5Ux.4 2z5dx.4 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","America/Goose_Bay|NST NDT NST NDT NWT NPT AST ADT ADDT|3u.Q 2u.Q 3u 2u 2u 2u 40 30 20|010232323232323245232323232323232323232323232323232323232326767676767676767676767676767676767676767676768676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-25TSt.8 1in0 DXb0 2HbX.8 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 S10 g0u 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Grand_Turk|KMT EST EDT AST|57.b 50 40 40|0121212121212121212121212121212121212121212121212121212121212121212121212123|-2l1uQ.N 2HHBQ.N 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Guatemala|LMT CST CDT|62.4 60 50|0121212121|-24KhV.U 2efXV.U An0 mtd0 Nz0 ifB0 17b0 zDB0 11z0","America/Guayaquil|QMT ECT|5e 50|01|-1yVSK","America/Guyana|LMT GBGT GYT GYT GYT|3Q.E 3J 3J 30 40|01234|-2dvU7.k 24JzQ.k mlc0 Bxbf","America/Halifax|LMT AST ADT AWT APT|4e.o 40 30 30 30|0121212121212121212121212121212121212121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsHJ.A xzzJ.A 1db0 3I30 1in0 3HX0 IL0 1E10 ML0 1yN0 Pb0 1Bd0 Mn0 1Bd0 Rz0 1w10 Xb0 1w10 LX0 1w10 Xb0 1w10 Lz0 1C10 Jz0 1E10 OL0 1yN0 Un0 1qp0 Xb0 1qp0 11X0 1w10 Lz0 1HB0 LX0 1C10 FX0 1w10 Xb0 1qp0 Xb0 1BB0 LX0 1td0 Xb0 1qp0 Xb0 Rf0 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 6i10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Havana|HMT CST CDT|5t.A 50 40|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Meuu.o 72zu.o ML0 sld0 An0 1Nd0 Db0 1Nd0 An0 6Ep0 An0 1Nd0 An0 JDd0 Mn0 1Ap0 On0 1fd0 11X0 1qN0 WL0 1wp0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 14n0 1ld0 14L0 1kN0 15b0 1kp0 1cL0 1cN0 1fz0 1a10 1fz0 1fB0 11z0 14p0 1nX0 11B0 1nX0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 1a10 1in0 1a10 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 17c0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 11A0 6i00 Rc0 1wo0 U00 1tA0 Rc0 1wo0 U00 1wo0 U00 1zc0 U00 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0","America/Hermosillo|LMT MST CST PST MDT|7n.Q 70 60 80 60|0121212131414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0","America/Indiana/Knox|CST CDT CWT CPT EST|60 50 50 50 50|0101023010101010101010101010101010101040101010101010101010101010101010101010101010101010141010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 3Cn0 8wp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 z8o0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Marengo|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|0101023010101010101010104545454545414545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 dyN0 11z0 6fd0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1e6p0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Petersburg|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010104010101010101010101010141014545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 njX0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 3Fb0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 19co0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Tell_City|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010101010454541010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 g0p0 11z0 1o10 11z0 1qL0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 caL0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Vevay|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|010102304545454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 kPB0 Awn0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1lnd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Vincennes|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010101010454541014545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 g0p0 11z0 1o10 11z0 1qL0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 caL0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Winamac|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010101010101010454541054545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1za0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Inuvik|zzz PST PDDT MST MDT|0 80 60 70 60|0121343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-FnA0 tWU0 1fA0 wPe0 2pz0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Iqaluit|zzz EWT EPT EST EDDT EDT CST CDT|0 40 40 50 30 40 60 50|01234353535353535353535353535353535353535353567353535353535353535353535353535353535353535353535353535353535353535353535353|-16K00 7nX0 iv0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11C0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Jamaica|KMT EST EDT|57.b 50 40|0121212121212121212121|-2l1uQ.N 2uM1Q.N 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0","America/Juneau|PST PWT PPT PDT YDT YST AKST AKDT|80 70 70 70 80 90 90 80|01203030303030303030303030403030356767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17T20 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cM0 1cM0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Kentucky/Louisville|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|0101010102301010101010101010101010101454545454545414545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 3Fd0 Nb0 LPd0 11z0 RB0 8x30 iw0 Bb0 10N0 2bB0 8in0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 xz0 gso0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Kentucky/Monticello|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|0101023010101010101010101010101010101010101010101010101010101010101010101454545454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 SWp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/La_Paz|CMT BOST BOT|4w.A 3w.A 40|012|-1x37r.o 13b0","America/Lima|LMT PET PEST|58.A 50 40|0121212121212121|-2tyGP.o 1bDzP.o zX0 1aN0 1cL0 1cN0 1cL0 1PrB0 zX0 1O10 zX0 6Gp0 zX0 98p0 zX0","America/Los_Angeles|PST PDT PWT PPT|80 70 70 70|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261q0 1nX0 11B0 1nX0 SgN0 8x10 iy0 5Wp0 1Vb0 3dB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Maceio|LMT BRT BRST|2m.Q 30 20|012121212121212121212121212121212121212121|-2glxB.8 HdLB.8 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 8Q10 WL0 1tB0 5z0 2mN0 On0","America/Managua|MMT CST EST CDT|5J.c 60 50 50|0121313121213131|-1quie.M 1yAMe.M 4mn0 9Up0 Dz0 1K10 Dz0 s3F0 1KH0 DB0 9In0 k8p0 19X0 1o30 11y0","America/Manaus|LMT AMT AMST|40.4 40 30|01212121212121212121212121212121|-2glvX.U HdKX.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0","America/Martinique|FFMT AST ADT|44.k 40 30|0121|-2mPTT.E 2LPbT.E 19X0","America/Matamoros|LMT CST CDT|6E 60 50|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Mazatlan|LMT MST CST PST MDT|75.E 70 60 80 60|0121212131414141414141414141414141414141414141414141414141414141414141414141414141414141414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Menominee|CST CDT CWT CPT EST|60 50 50 50 50|01010230101041010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 LCN0 1fz0 6410 9Jb0 1cM0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Merida|LMT CST EST CDT|5W.s 60 50 50|0121313131313131313131313131313131313131313131313131313131313131313131313131313131313131|-1UQG0 2q2o0 2hz0 wu30 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Metlakatla|PST PWT PPT PDT|80 70 70 70|0120303030303030303030303030303030|-17T20 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0","America/Mexico_City|LMT MST CST CDT CWT|6A.A 70 60 50 50|012121232324232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 gEn0 TX0 3xd0 Jb0 6zB0 SL0 e5d0 17b0 1Pff0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Miquelon|LMT AST PMST PMDT|3I.E 40 30 20|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2mKkf.k 2LTAf.k gQ10 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Moncton|EST AST ADT AWT APT|50 40 30 30 30|012121212121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsH0 CwN0 1in0 zAo0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1K10 Lz0 1zB0 NX0 1u10 Wn0 S20 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 3Cp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14n1 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 ReX 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Monterrey|LMT CST CDT|6F.g 60 50|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Montevideo|MMT UYT UYHST UYST UYT UYHST|3I.I 3u 30 20 30 2u|012121212121212121212121213434343434345454543453434343434343434343434343434343434343434|-20UIf.g 8jzJ.g 1cLu 1dcu 1cLu 1dcu 1cLu ircu 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu WLu 1qMu WLu 1qMu 11zu 1o0u 11zu NAu 11bu 2iMu zWu Dq10 19X0 pd0 jz0 cm10 19X0 1fB0 1on0 11d0 1oL0 1nB0 1fzu 1aou 1fzu 1aou 1fzu 3nAu Jb0 3MN0 1SLu 4jzu 2PB0 Lb0 3Dd0 1pb0 ixd0 An0 1MN0 An0 1wp0 On0 1wp0 Rb0 1zd0 On0 1wp0 Rb0 s8p0 1fB0 1ip0 11z0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 11z0","America/Montreal|EST EDT EWT EPT|50 40 40 40|01010101010101010101010101010101010101010101012301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TR0 1in0 11Wu 1nzu 1fD0 WJ0 1wr0 Nb0 1Ap0 On0 1zd0 On0 1wp0 TX0 1tB0 TX0 1tB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 4kM0 8x40 iv0 1o10 11z0 1nX0 11z0 1o10 11z0 1o10 1qL0 11D0 1nX0 11B0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Nassau|LMT EST EDT|59.u 50 40|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2kNuO.u 26XdO.u 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/New_York|EST EDT EWT EPT|50 40 40 40|01010101010101010101010101010101010101010101010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 11B0 1qL0 1a10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x40 iv0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Nipigon|EST EDT EWT EPT|50 40 40 40|010123010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TR0 1in0 Rnb0 3je0 8x40 iv0 19yN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Nome|NST NWT NPT BST BDT YST AKST AKDT|b0 a0 a0 b0 a0 90 90 80|012034343434343434343434343434343456767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17SX0 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cl0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Noronha|LMT FNT FNST|29.E 20 10|0121212121212121212121212121212121212121|-2glxO.k HdKO.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0","America/North_Dakota/Beulah|MST MDT MWT MPT CST CDT|70 60 60 60 60 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101014545454545454545454545454545454545454545454545454545454|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/North_Dakota/Center|MST MDT MWT MPT CST CDT|70 60 60 60 60 50|010102301010101010101010101010101010101010101010101010101014545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/North_Dakota/New_Salem|MST MDT MWT MPT CST CDT|70 60 60 60 60 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101454545454545454545454545454545454545454545454545454545454545454545454|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Ojinaga|LMT MST CST CDT MDT|6V.E 70 60 50 60|0121212323241414141414141414141414141414141414141414141414141414141414141414141414141414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Panama|CMT EST|5j.A 50|01|-2uduE.o","America/Pangnirtung|zzz AST AWT APT ADDT ADT EDT EST CST CDT|0 40 30 30 20 30 40 50 60 50|012314151515151515151515151515151515167676767689767676767676767676767676767676767676767676767676767676767676767676767676767|-1XiM0 PnG0 8x50 iu0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1o00 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11C0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Paramaribo|LMT PMT PMT NEGT SRT SRT|3E.E 3E.Q 3E.A 3u 3u 30|012345|-2nDUj.k Wqo0.c qanX.I 1dmLN.o lzc0","America/Phoenix|MST MDT MWT|70 60 60|01010202010|-261r0 1nX0 11B0 1nX0 SgN0 4Al1 Ap0 1db0 SWqX 1cL0","America/Port-au-Prince|PPMT EST EDT|4N 50 40|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-28RHb 2FnMb 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14q0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 i6n0 1nX0 11B0 1nX0 d430 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Porto_Acre|LMT ACT ACST AMT|4v.c 50 40 40|01212121212121212121212121212131|-2glvs.M HdLs.M 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0 d5X0","America/Porto_Velho|LMT AMT AMST|4f.A 40 30|012121212121212121212121212121|-2glvI.o HdKI.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0","America/Puerto_Rico|AST AWT APT|40 30 30|0120|-17lU0 7XT0 iu0","America/Rainy_River|CST CDT CWT CPT|60 50 50 50|010123010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TQ0 1in0 Rnb0 3je0 8x30 iw0 19yN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Rankin_Inlet|zzz CST CDDT CDT EST|0 60 40 50 50|012131313131313131313131313131313131313131313431313131313131313131313131313131313131313131313131313131313131313131313131|-vDc0 keu0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Recife|LMT BRT BRST|2j.A 30 20|0121212121212121212121212121212121212121|-2glxE.o HdLE.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0","America/Regina|LMT MST MDT MWT MPT CST|6W.A 70 60 60 60 60|012121212121212121212121341212121212121212121212121215|-2AD51.o uHe1.o 1in0 s2L0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 66N0 1cL0 1cN0 19X0 1fB0 1cL0 1fB0 1cL0 1cN0 1cL0 M30 8x20 ix0 1ip0 1cL0 1ip0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 3NB0 1cL0 1cN0","America/Resolute|zzz CST CDDT CDT EST|0 60 40 50 50|012131313131313131313131313131313131313131313431313131313431313131313131313131313131313131313131313131313131313131313131|-SnA0 GWS0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Santa_Isabel|LMT MST PST PDT PWT PPT|7D.s 70 80 70 70 70|012123245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQE0 4PX0 8mM0 8lc0 SN0 1cL0 pHB0 83r0 zI0 5O10 1Rz0 cOP0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 BUp0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Santarem|LMT AMT AMST BRT|3C.M 40 30 30|0121212121212121212121212121213|-2glwl.c HdLl.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0","America/Santiago|SMT CLT CLT CLST CLST CLT|4G.K 50 40 40 30 30|01020313131313121242124242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424245|-2q2jh.e fJAh.e 5knG.K 1Vzh.e jRAG.K 1pbh.e 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 nHX0 op0 9Bz0 jb0 1oN0 ko0 Qeo0 WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","America/Santo_Domingo|SDMT EST EDT EHDT AST|4E 50 40 4u 40|01213131313131414|-1ttjk 1lJMk Mn0 6sp0 Lbu 1Cou yLu 1RAu wLu 1QMu xzu 1Q0u xXu 1PAu 13jB0 e00","America/Sao_Paulo|LMT BRT BRST|36.s 30 20|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2glwR.w HdKR.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 pTd0 PX0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0","America/Scoresbysund|LMT CGT CGST EGST EGT|1r.Q 20 10 0 10|0121343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-2a5Ww.8 2z5ew.8 1a00 1cK0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","America/Sitka|PST PWT PPT PDT YST AKST AKDT|80 70 70 70 90 90 80|01203030303030303030303030303030345656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-17T20 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/St_Johns|NST NDT NST NDT NWT NPT NDDT|3u.Q 2u.Q 3u 2u 2u 2u 1u|01010101010101010101010101010101010102323232323232324523232323232323232323232323232323232323232323232323232323232323232323232323232323232326232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-28oit.8 14L0 1nB0 1in0 1gm0 Dz0 1JB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1fB0 19X0 1fB0 19X0 10O0 eKX.8 19X0 1iq0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Swift_Current|LMT MST MDT MWT MPT CST|7b.k 70 60 60 60 60|012134121212121212121215|-2AD4M.E uHdM.E 1in0 UGp0 8x20 ix0 1o10 17b0 1ip0 11z0 1o10 11z0 1o10 11z0 isN0 1cL0 3Cp0 1cL0 1cN0 11z0 1qN0 WL0 pMp0","America/Tegucigalpa|LMT CST CDT|5M.Q 60 50|01212121|-1WGGb.8 2ETcb.8 WL0 1qN0 WL0 GRd0 AL0","America/Thule|LMT AST ADT|4z.8 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a5To.Q 31NBo.Q 1cL0 1cN0 1cL0 1fB0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Thunder_Bay|CST EST EWT EPT EDT|60 50 40 40 40|0123141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141|-2q5S0 1iaN0 8x40 iv0 XNB0 1cL0 1cN0 1fz0 1cN0 1cL0 3Cp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Vancouver|PST PDT PWT PPT|80 70 70 70|0102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TO0 1in0 UGp0 8x10 iy0 1o10 17b0 1ip0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Whitehorse|YST YDT YWT YPT YDDT PST PDT|90 80 80 80 70 80 70|0101023040565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-25TN0 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 3NA0 vrd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Winnipeg|CST CDT CWT CPT|60 50 50 50|010101023010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aIi0 WL0 3ND0 1in0 Jap0 Rb0 aCN0 8x30 iw0 1tB0 11z0 1ip0 11z0 1o10 11z0 1o10 11z0 1rd0 10L0 1op0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 1cL0 1cN0 11z0 6i10 WL0 6i10 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Yakutat|YST YWT YPT YDT AKST AKDT|90 80 80 80 90 80|01203030303030303030303030303030304545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-17T10 8x00 iz0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cn0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Yellowknife|zzz MST MWT MPT MDDT MDT|0 70 60 60 50 60|012314151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151|-1pdA0 hix0 8x20 ix0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Antarctica/Casey|zzz AWST CAST|0 -80 -b0|012121|-2q00 1DjS0 T90 40P0 KL0","Antarctica/Davis|zzz DAVT DAVT|0 -70 -50|01012121|-vyo0 iXt0 alj0 1D7v0 VB0 3Wn0 KN0","Antarctica/DumontDUrville|zzz PMT DDUT|0 -a0 -a0|0102|-U0o0 cfq0 bFm0","Antarctica/Macquarie|AEST AEDT zzz MIST|-a0 -b0 0 -b0|0102010101010101010101010101010101010101010101010101010101010101010101010101010101010101013|-29E80 19X0 4SL0 1ayy0 Lvs0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0","Antarctica/Mawson|zzz MAWT MAWT|0 -60 -50|012|-CEo0 2fyk0","Antarctica/McMurdo|NZMT NZST NZST NZDT|-bu -cu -c0 -d0|01020202020202020202020202023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-1GCVu Lz0 1tB0 11zu 1o0u 11zu 1o0u 11zu 1o0u 14nu 1lcu 14nu 1lcu 1lbu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1qLu WMu 1qLu 11Au 1n1bu IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Antarctica/Palmer|zzz ARST ART ART ARST CLT CLST CLT|0 30 40 30 20 40 30 30|012121212123435656565656565656565656565656565656565656565656565656565656565656567|-cao0 nD0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 jsN0 14N0 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","Antarctica/Rothera|zzz ROTT|0 30|01|gOo0","Antarctica/Syowa|zzz SYOT|0 -30|01|-vs00","Antarctica/Troll|zzz UTC CEST|0 0 -20|01212121212121212121212121212121212121212121212121212121212121212121|1puo0 hd0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Antarctica/Vostok|zzz VOST|0 -60|01|-tjA0","Arctic/Longyearbyen|CET CEST|-10 -20|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2awM0 Qm0 W6o0 5pf0 WM0 1fA0 1cM0 1cM0 1cM0 1cM0 wJc0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1qM0 WM0 zpc0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Aden|LMT AST|-36.Q -30|01|-TvD6.Q","Asia/Almaty|LMT ALMT ALMT ALMST|-57.M -50 -60 -70|0123232323232323232323232323232323232323232323232|-1Pc57.M eUo7.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 3Cl0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Amman|LMT EET EEST|-2n.I -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1yW2n.I 1HiMn.I KL0 1oN0 11b0 1oN0 11b0 1pd0 1dz0 1cp0 11b0 1op0 11b0 fO10 1db0 1e10 1cL0 1cN0 1cL0 1cN0 1fz0 1pd0 10n0 1ld0 14n0 1hB0 15b0 1ip0 19X0 1cN0 1cL0 1cN0 17b0 1ld0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1So0 y00 1fc0 1dc0 1co0 1dc0 1cM0 1cM0 1cM0 1o00 11A0 1lc0 17c0 1cM0 1cM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 4bX0 Dd0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0","Asia/Anadyr|LMT ANAT ANAT ANAST ANAST ANAST ANAT|-bN.U -c0 -d0 -e0 -d0 -c0 -b0|01232414141414141414141561414141414141414141414141414141414141561|-1PcbN.U eUnN.U 23CL0 1db0 1cN0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0","Asia/Aqtau|LMT FORT FORT SHET SHET SHEST AQTT AQTST AQTST AQTT|-3l.4 -40 -50 -50 -60 -60 -50 -60 -50 -40|012345353535353535353536767676898989898989898989896|-1Pc3l.4 eUnl.4 1jcL0 JDc0 1cL0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2UK0 Fz0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cN0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 RW0","Asia/Aqtobe|LMT AKTT AKTT AKTST AKTT AQTT AQTST|-3M.E -40 -50 -60 -60 -50 -60|01234323232323232323232565656565656565656565656565|-1Pc3M.E eUnM.E 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2UK0 Fz0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Ashgabat|LMT ASHT ASHT ASHST ASHST TMT TMT|-3R.w -40 -50 -60 -50 -40 -50|012323232323232323232324156|-1Pc3R.w eUnR.w 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 ba0 xC0","Asia/Baghdad|BMT AST ADT|-2V.A -30 -40|012121212121212121212121212121212121212121212121212121|-26BeV.A 2ACnV.A 11b0 1cp0 1dz0 1dd0 1db0 1cN0 1cp0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1de0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0","Asia/Bahrain|LMT GST AST|-3q.8 -40 -30|012|-21Jfq.8 27BXq.8","Asia/Baku|LMT BAKT BAKT BAKST BAKST AZST AZT AZT AZST|-3j.o -30 -40 -50 -40 -40 -30 -40 -50|0123232323232323232323245657878787878787878787878787878787878787878787878787878787878787878787878787878787878787|-1Pc3j.o 1jUoj.o WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 10K0 c30 1cJ0 1cL0 8wu0 1o00 11z0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Bangkok|BMT ICT|-6G.4 -70|01|-218SG.4","Asia/Beirut|EET EEST|-20 -30|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-21aq0 1on0 1410 1db0 19B0 1in0 1ip0 WL0 1lQp0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 q6N0 En0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1op0 11b0 dA10 17b0 1iN0 17b0 1iN0 17b0 1iN0 17b0 1vB0 SL0 1mp0 13z0 1iN0 17b0 1iN0 17b0 1jd0 12n0 1a10 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0","Asia/Bishkek|LMT FRUT FRUT FRUST FRUST KGT KGST KGT|-4W.o -50 -60 -70 -60 -50 -60 -60|01232323232323232323232456565656565656565656565656567|-1Pc4W.o eUnW.o 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 11c0 1tX0 17b0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1cPu 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 T8u","Asia/Brunei|LMT BNT BNT|-7D.E -7u -80|012|-1KITD.E gDc9.E","Asia/Calcutta|HMT BURT IST IST|-5R.k -6u -5u -6u|01232|-18LFR.k 1unn.k HB0 7zX0","Asia/Chita|LMT YAKT YAKT YAKST YAKST YAKT IRKT|-7x.Q -80 -90 -a0 -90 -a0 -80|012323232323232323232324123232323232323232323232323232323232323256|-21Q7x.Q pAnx.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Choibalsan|LMT ULAT ULAT CHOST CHOT CHOT CHOST|-7C -70 -80 -a0 -90 -80 -90|0123434343434343434343434343434343434343434343456565656565656565656565656565656565656565656565|-2APHC 2UkoC cKn0 1da0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 3Db0 h1f0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Chongqing|CST CDT|-80 -90|01010101010101010|-1c1I0 LX0 16p0 1jz0 1Myp0 Rb0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0","Asia/Colombo|MMT IST IHST IST LKT LKT|-5j.w -5u -60 -6u -6u -60|01231451|-2zOtj.w 1rFbN.w 1zzu 7Apu 23dz0 11zu n3cu","Asia/Dacca|HMT BURT IST DACT BDT BDST|-5R.k -6u -5u -60 -60 -70|01213454|-18LFR.k 1unn.k HB0 m6n0 LqMu 1x6n0 1i00","Asia/Damascus|LMT EET EEST|-2p.c -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-21Jep.c Hep.c 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1xRB0 11X0 1oN0 10L0 1pB0 11b0 1oN0 10L0 1mp0 13X0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 Nb0 1AN0 Nb0 bcp0 19X0 1gp0 19X0 3ld0 1xX0 Vd0 1Bz0 Sp0 1vX0 10p0 1dz0 1cN0 1cL0 1db0 1db0 1g10 1an0 1ap0 1db0 1fd0 1db0 1cN0 1db0 1dd0 1db0 1cp0 1dz0 1c10 1dX0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 19z0 1fB0 1qL0 11B0 1on0 Wp0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0","Asia/Dili|LMT TLT JST TLT WITA|-8m.k -80 -90 -90 -80|012343|-2le8m.k 1dnXm.k 8HA0 1ew00 Xld0","Asia/Dubai|LMT GST|-3F.c -40|01|-21JfF.c","Asia/Dushanbe|LMT DUST DUST DUSST DUSST TJT|-4z.c -50 -60 -70 -60 -50|0123232323232323232323245|-1Pc4z.c eUnz.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 14N0","Asia/Gaza|EET EET EEST IST IDT|-20 -30 -30 -20 -30|010101010102020202020202020202023434343434343434343434343430202020202020202020202020202020202020202020202020202020202020202020202020202020202020|-1c2q0 5Rb0 10r0 1px0 10N0 1pz0 16p0 1jB0 16p0 1jx0 pBd0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 npB0 11z0 1C10 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 11z0 1o10 14o0 1lA1 SKX 1xd1 MKX 1AN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0","Asia/Hebron|EET EET EEST IST IDT|-20 -30 -30 -20 -30|01010101010202020202020202020202343434343434343434343434343020202020202020202020202020202020202020202020202020202020202020202020202020202020202020|-1c2q0 5Rb0 10r0 1px0 10N0 1pz0 16p0 1jB0 16p0 1jx0 pBd0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 npB0 11z0 1C10 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 12L0 1mN0 14o0 1lc0 Tb0 1xd1 MKX bB0 cn0 1cN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0","Asia/Ho_Chi_Minh|LMT PLMT ICT IDT JST|-76.E -76.u -70 -80 -90|0123423232|-2yC76.E bK00.a 1h7b6.u 5lz0 18o0 3Oq0 k5b0 aW00 BAM0","Asia/Hong_Kong|LMT HKT HKST JST|-7A.G -80 -90 -90|0121312121212121212121212121212121212121212121212121212121212121212121|-2CFHA.G 1sEP6.G 1cL0 ylu 93X0 1qQu 1tX0 Rd0 1In0 NB0 1cL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1kL0 14N0 1nX0 U10 1tz0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 Rd0 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 17d0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1cL0 6fd0 14n0","Asia/Hovd|LMT HOVT HOVT HOVST|-66.A -60 -70 -80|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2APG6.A 2Uko6.A cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Irkutsk|IMT IRKT IRKT IRKST IRKST IRKT|-6V.5 -70 -80 -90 -80 -90|012323232323232323232324123232323232323232323232323232323232323252|-21zGV.5 pjXV.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Istanbul|IMT EET EEST TRST TRT|-1U.U -20 -30 -40 -30|012121212121212121212121212121212121212121212121212121234343434342121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ogNU.U dzzU.U 11b0 8tB0 1on0 1410 1db0 19B0 1in0 3Rd0 Un0 1oN0 11b0 zSp0 CL0 mN0 1Vz0 1gN0 1pz0 5Rd0 1fz0 1yp0 ML0 1kp0 17b0 1ip0 17b0 1fB0 19X0 1jB0 18L0 1ip0 17z0 qdd0 xX0 3S10 Tz0 dA10 11z0 1o10 11z0 1qN0 11z0 1ze0 11B0 WM0 1qO0 WI0 1nX0 1rB0 10L0 11B0 1in0 17d0 1in0 2pX0 19E0 1fU0 16Q0 1iI0 16Q0 1iI0 1Vd0 pb0 3Kp0 14o0 1df0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WO0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 Xc0 1qo0 WM0 1qM0 11A0 1o00 1200 1nA0 11A0 1tA0 U00 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Jakarta|BMT JAVT WIB JST WIB WIB|-77.c -7k -7u -90 -80 -70|01232425|-1Q0Tk luM0 mPzO 8vWu 6kpu 4PXu xhcu","Asia/Jayapura|LMT WIT ACST|-9m.M -90 -9u|0121|-1uu9m.M sMMm.M L4nu","Asia/Jerusalem|JMT IST IDT IDDT|-2k.E -20 -30 -40|01212121212132121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-26Bek.E SyMk.E 5Rb0 10r0 1px0 10N0 1pz0 16p0 1jB0 16p0 1jx0 3LB0 Em0 or0 1cn0 1dB0 16n0 10O0 1ja0 1tC0 14o0 1cM0 1a00 11A0 1Na0 An0 1MP0 AJ0 1Kp0 LC0 1oo0 Wl0 EQN0 Db0 1fB0 Rb0 npB0 11z0 1C10 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 1hB0 1dX0 1ep0 1aL0 1eN0 17X0 1nf0 11z0 1tB0 19W0 1e10 17b0 1ep0 1gL0 18N0 1fz0 1eN0 17b0 1gq0 1gn0 19d0 1dz0 1c10 17X0 1hB0 1gn0 19d0 1dz0 1c10 17X0 1kp0 1dz0 1c10 1aL0 1eN0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0","Asia/Kabul|AFT AFT|-40 -4u|01|-10Qs0","Asia/Kamchatka|LMT PETT PETT PETST PETST|-ay.A -b0 -c0 -d0 -c0|01232323232323232323232412323232323232323232323232323232323232412|-1SLKy.A ivXy.A 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0","Asia/Karachi|LMT IST IST KART PKT PKST|-4s.c -5u -6u -50 -50 -60|012134545454|-2xoss.c 1qOKW.c 7zX0 eup0 LqMu 1fy01 1cL0 dK0X 11b0 1610 1jX0","Asia/Kashgar|LMT XJT|-5O.k -60|01|-1GgtO.k","Asia/Kathmandu|LMT IST NPT|-5F.g -5u -5J|012|-21JhF.g 2EGMb.g","Asia/Khandyga|LMT YAKT YAKT YAKST YAKST VLAT VLAST VLAT YAKT|-92.d -80 -90 -a0 -90 -a0 -b0 -b0 -a0|01232323232323232323232412323232323232323232323232565656565656565782|-21Q92.d pAp2.d 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 qK0 yN0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0","Asia/Krasnoyarsk|LMT KRAT KRAT KRAST KRAST KRAT|-6b.q -60 -70 -80 -70 -80|012323232323232323232324123232323232323232323232323232323232323252|-21Hib.q prAb.q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Kuala_Lumpur|SMT MALT MALST MALT MALT JST MYT|-6T.p -70 -7k -7k -7u -90 -80|01234546|-2Bg6T.p 17anT.p 7hXE dM00 17bO 8Fyu 1so1u","Asia/Kuching|LMT BORT BORT BORTST JST MYT|-7l.k -7u -80 -8k -90 -80|01232323232323232425|-1KITl.k gDbP.k 6ynu AnE 1O0k AnE 1NAk AnE 1NAk AnE 1NAk AnE 1O0k AnE 1NAk AnE pAk 8Fz0 1so10","Asia/Macao|LMT MOT MOST CST|-7y.k -80 -90 -80|0121212121212121212121212121212121212121213|-2le7y.k 1XO34.k 1wn0 Rd0 1wn0 R9u 1wqu U10 1tz0 TVu 1tz0 17gu 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cJu 1cL0 1cN0 1fz0 1cN0 1cOu 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cJu 1cL0 1cN0 1fz0 1cN0 1cL0 KEp0","Asia/Magadan|LMT MAGT MAGT MAGST MAGST MAGT|-a3.c -a0 -b0 -c0 -b0 -c0|012323232323232323232324123232323232323232323232323232323232323251|-1Pca3.c eUo3.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Makassar|LMT MMT WITA JST|-7V.A -7V.A -80 -90|01232|-21JjV.A vfc0 myLV.A 8ML0","Asia/Manila|PHT PHST JST|-80 -90 -90|010201010|-1kJI0 AL0 cK10 65X0 mXB0 vX0 VK10 1db0","Asia/Nicosia|LMT EET EEST|-2d.s -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Vc2d.s 2a3cd.s 1cL0 1qp0 Xz0 19B0 19X0 1fB0 1db0 1cp0 1cL0 1fB0 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1o30 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Novokuznetsk|LMT KRAT KRAT KRAST KRAST NOVST NOVT NOVT|-5M.M -60 -70 -80 -70 -70 -60 -70|012323232323232323232324123232323232323232323232323232323232325672|-1PctM.M eULM.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0 8Hz0","Asia/Novosibirsk|LMT NOVT NOVT NOVST NOVST|-5v.E -60 -70 -80 -70|0123232323232323232323241232341414141414141414141414141414141414121|-21Qnv.E pAFv.E 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 ml0 Os0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Omsk|LMT OMST OMST OMSST OMSST OMST|-4R.u -50 -60 -70 -60 -70|012323232323232323232324123232323232323232323232323232323232323252|-224sR.u pMLR.u 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Oral|LMT URAT URAT URAST URAT URAST ORAT ORAST ORAT|-3p.o -40 -50 -60 -60 -50 -40 -50 -50|012343232323232323251516767676767676767676767676768|-1Pc3p.o eUnp.o 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 2UK0 Fz0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 RW0","Asia/Pontianak|LMT PMT WIB JST WIB WITA WIB|-7h.k -7h.k -7u -90 -80 -80 -70|012324256|-2ua7h.k XE00 munL.k 8Rau 6kpu 4PXu xhcu Wqnu","Asia/Pyongyang|LMT KST JCST JST KST|-8n -8u -90 -90 -90|012341|-2um8n 97XR 12FXu jdA0 2Onc0","Asia/Qyzylorda|LMT KIZT KIZT KIZST KIZT QYZT QYZT QYZST|-4l.Q -40 -50 -60 -60 -50 -60 -70|012343232323232323232325676767676767676767676767676|-1Pc4l.Q eUol.Q 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2UK0 dC0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Rangoon|RMT BURT JST MMT|-6o.E -6u -90 -6u|0123|-21Jio.E SmnS.E 7j9u","Asia/Sakhalin|LMT JCST JST SAKT SAKST SAKST SAKT|-9u.M -90 -90 -b0 -c0 -b0 -a0|0123434343434343434343435634343434343565656565656565656565656565636|-2AGVu.M 1iaMu.M je00 1qFa0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o10 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Samarkand|LMT SAMT SAMT SAMST TAST UZST UZT|-4r.R -40 -50 -60 -60 -60 -50|01234323232323232323232356|-1Pc4r.R eUor.R 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 11x0 bf0","Asia/Seoul|LMT KST JCST JST KST KDT KDT|-8r.Q -8u -90 -90 -90 -9u -a0|01234151515151515146464|-2um8r.Q 97XV.Q 12FXu jjA0 kKo0 2I0u OL0 1FB0 Rb0 1qN0 TX0 1tB0 TX0 1tB0 TX0 1tB0 TX0 2ap0 12FBu 11A0 1o00 11A0","Asia/Singapore|SMT MALT MALST MALT MALT JST SGT SGT|-6T.p -70 -7k -7k -7u -90 -7u -80|012345467|-2Bg6T.p 17anT.p 7hXE dM00 17bO 8Fyu Mspu DTA0","Asia/Srednekolymsk|LMT MAGT MAGT MAGST MAGST MAGT SRET|-ae.Q -a0 -b0 -c0 -b0 -c0 -b0|012323232323232323232324123232323232323232323232323232323232323256|-1Pcae.Q eUoe.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Taipei|JWST JST CST CDT|-80 -90 -80 -90|01232323232323232323232323232323232323232|-1iw80 joM0 1yo0 Tz0 1ip0 1jX0 1cN0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 10N0 1BX0 10p0 1pz0 10p0 1pz0 10p0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1BB0 ML0 1Bd0 ML0 uq10 1db0 1cN0 1db0 97B0 AL0","Asia/Tashkent|LMT TAST TAST TASST TASST UZST UZT|-4B.b -50 -60 -70 -60 -60 -50|01232323232323232323232456|-1Pc4B.b eUnB.b 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 11y0 bf0","Asia/Tbilisi|TBMT TBIT TBIT TBIST TBIST GEST GET GET GEST|-2X.b -30 -40 -50 -40 -40 -30 -40 -50|0123232323232323232323245656565787878787878787878567|-1Pc2X.b 1jUnX.b WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 3y0 19f0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cM0 1cL0 1fB0 3Nz0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 An0 Os0 WM0","Asia/Tehran|LMT TMT IRST IRST IRDT IRDT|-3p.I -3p.I -3u -40 -50 -4u|01234325252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-2btDp.I 1d3c0 1huLT.I TXu 1pz0 sN0 vAu 1cL0 1dB0 1en0 pNB0 UL0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 64p0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0","Asia/Thimbu|LMT IST BTT|-5W.A -5u -60|012|-Su5W.A 1BGMs.A","Asia/Tokyo|JCST JST JDT|-90 -90 -a0|0121212121|-1iw90 pKq0 QL0 1lB0 13X0 1zB0 NX0 1zB0 NX0","Asia/Ulaanbaatar|LMT ULAT ULAT ULAST|-77.w -70 -80 -90|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2APH7.w 2Uko7.w cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Ust-Nera|LMT YAKT YAKT MAGST MAGT MAGST MAGT MAGT VLAT VLAT|-9w.S -80 -90 -c0 -b0 -b0 -a0 -c0 -b0 -a0|0123434343434343434343456434343434343434343434343434343434343434789|-21Q9w.S pApw.S 23CL0 1d90 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0","Asia/Vladivostok|LMT VLAT VLAT VLAST VLAST VLAT|-8L.v -90 -a0 -b0 -a0 -b0|012323232323232323232324123232323232323232323232323232323232323252|-1SJIL.v itXL.v 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Yakutsk|LMT YAKT YAKT YAKST YAKST YAKT|-8C.W -80 -90 -a0 -90 -a0|012323232323232323232324123232323232323232323232323232323232323252|-21Q8C.W pAoC.W 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Yekaterinburg|LMT PMT SVET SVET SVEST SVEST YEKT YEKST YEKT|-42.x -3J.5 -40 -50 -60 -50 -50 -60 -60|0123434343434343434343435267676767676767676767676767676767676767686|-2ag42.x 7mQh.s qBvJ.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Yerevan|LMT YERT YERT YERST YERST AMST AMT AMT AMST|-2W -30 -40 -50 -40 -40 -30 -40 -50|0123232323232323232323245656565657878787878787878787878787878787|-1Pc2W 1jUnW WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1am0 2r0 1cJ0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 3Fb0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0","Atlantic/Azores|HMT AZOT AZOST AZOMT AZOT AZOST WET|1S.w 20 10 0 10 0 0|01212121212121212121212121212121212121212121232123212321232121212121212121212121212121212121212121454545454545454545454545454545456545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2ldW5.s aPX5.s Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 3Co0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Bermuda|LMT AST ADT|4j.i 40 30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1BnRE.G 1LTbE.G 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Atlantic/Canary|LMT CANT WET WEST|11.A 10 0 -10|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UtaW.o XPAW.o 1lAK0 1a10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Cape_Verde|LMT CVT CVST CVT|1y.4 20 10 10|01213|-2xomp.U 1qOMp.U 7zX0 1djf0","Atlantic/Faeroe|LMT WET WEST|r.4 0 -10|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2uSnw.U 2Wgow.U 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Madeira|FMT MADT MADST MADMT WET WEST|17.A 10 0 -10 0 -10|01212121212121212121212121212121212121212121232123212321232121212121212121212121212121212121212121454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2ldWQ.o aPWQ.o Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 3Co0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Reykjavik|LMT IST ISST GMT|1s 10 0 0|012121212121212121212121212121212121212121212121212121212121212121213|-2uWmw mfaw 1Bd0 ML0 1LB0 Cn0 1LB0 3fX0 C10 HrX0 1cO0 LB0 1EL0 LA0 1C00 Oo0 1wo0 Rc0 1wo0 Rc0 1wo0 Rc0 1zc0 Oo0 1zc0 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1lc0 14o0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 14o0","Atlantic/South_Georgia|GST|20|0|","Atlantic/Stanley|SMT FKT FKST FKT FKST|3P.o 40 30 30 20|0121212121212134343212121212121212121212121212121212121212121212121212|-2kJw8.A 12bA8.A 19X0 1fB0 19X0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 Cn0 1Cc10 WL0 1qL0 U10 1tz0 U10 1qM0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 U10 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qN0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 U10 1tz0 U10 1tz0 U10","Australia/ACT|AEST AEDT|-a0 -b0|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Adelaide|ACST ACDT|-9u -au|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lt xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 WM0 1qM0 Rc0 1zc0 U00 1tA0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Brisbane|AEST AEDT|-a0 -b0|01010101010101010|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0","Australia/Broken_Hill|ACST ACDT|-9u -au|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lt xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Currie|AEST AEDT|-a0 -b0|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-29E80 19X0 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Darwin|ACST ACDT|-9u -au|010101010|-293lt xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0","Australia/Eucla|ACWST ACWDT|-8J -9J|0101010101010101010|-293kI xcX 10jd0 yL0 1cN0 1cL0 1gSp0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0","Australia/Hobart|AEST AEDT|-a0 -b0|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-29E80 19X0 10jd0 yL0 1cN0 1cL0 1fB0 19X0 VfB0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/LHI|AEST LHST LHDT LHDT|-a0 -au -bu -b0|0121212121313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313|raC0 1zdu Rb0 1zd0 On0 1zd0 On0 1zd0 On0 1zd0 TXu 1qMu WLu 1tAu WLu 1tAu TXu 1tAu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu 11zu 1o0u 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 11Au 1nXu 1qMu 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu 11zu 1o0u WLu 1qMu 14nu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu","Australia/Lindeman|AEST AEDT|-a0 -b0|010101010101010101010|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0","Australia/Melbourne|AEST AEDT|-a0 -b0|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1qM0 11A0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Perth|AWST AWDT|-80 -90|0101010101010101010|-293jX xcX 10jd0 yL0 1cN0 1cL0 1gSp0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0","CET|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","CST6CDT|CST CDT CWT CPT|60 50 50 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Chile/EasterIsland|EMT EAST EASST EAST EASST EAST|7h.s 70 60 60 50 50|012121212121212121212121212123434343434343434343434343434343434343434343434343434343434343434345|-1uSgG.w 1s4IG.w WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","EET|EET EEST|-20 -30|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","EST|EST|50|0|","EST5EDT|EST EDT EWT EPT|50 40 40 40|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 SgN0 8x40 iv0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Eire|DMT IST GMT BST IST|p.l -y.D 0 -10 -10|01232323232324242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242|-2ax9y.D Rc0 1fzy.D 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 g5X0 14p0 1wn0 17d0 1io0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Etc/GMT+0|GMT|0|0|","Etc/GMT+1|GMT+1|10|0|","Etc/GMT+10|GMT+10|a0|0|","Etc/GMT+11|GMT+11|b0|0|","Etc/GMT+12|GMT+12|c0|0|","Etc/GMT+2|GMT+2|20|0|","Etc/GMT+3|GMT+3|30|0|","Etc/GMT+4|GMT+4|40|0|","Etc/GMT+5|GMT+5|50|0|","Etc/GMT+6|GMT+6|60|0|","Etc/GMT+7|GMT+7|70|0|","Etc/GMT+8|GMT+8|80|0|","Etc/GMT+9|GMT+9|90|0|","Etc/GMT-1|GMT-1|-10|0|","Etc/GMT-10|GMT-10|-a0|0|","Etc/GMT-11|GMT-11|-b0|0|","Etc/GMT-12|GMT-12|-c0|0|","Etc/GMT-13|GMT-13|-d0|0|","Etc/GMT-14|GMT-14|-e0|0|","Etc/GMT-2|GMT-2|-20|0|","Etc/GMT-3|GMT-3|-30|0|","Etc/GMT-4|GMT-4|-40|0|","Etc/GMT-5|GMT-5|-50|0|","Etc/GMT-6|GMT-6|-60|0|","Etc/GMT-7|GMT-7|-70|0|","Etc/GMT-8|GMT-8|-80|0|","Etc/GMT-9|GMT-9|-90|0|","Etc/UCT|UCT|0|0|","Etc/UTC|UTC|0|0|","Europe/Amsterdam|AMT NST NEST NET CEST CET|-j.w -1j.w -1k -k -20 -10|010101010101010101010101010101010101010101012323234545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-2aFcj.w 11b0 1iP0 11A0 1io0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1co0 1io0 1yo0 Pc0 1a00 1fA0 1Bc0 Mo0 1tc0 Uo0 1tA0 U00 1uo0 W00 1s00 VA0 1so0 Vc0 1sM0 UM0 1wo0 Rc0 1u00 Wo0 1rA0 W00 1s00 VA0 1sM0 UM0 1w00 fV0 BCX.w 1tA0 U00 1u00 Wo0 1sm0 601k WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Andorra|WET CET CEST|0 -10 -20|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-UBA0 1xIN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Athens|AMT EET EEST CEST CET|-1y.Q -20 -30 -20 -10|012123434121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a61x.Q CNbx.Q mn0 kU10 9b0 3Es0 Xa0 1fb0 1dd0 k3X0 Nz0 SCp0 1vc0 SO0 1cM0 1a00 1ao0 1fc0 1a10 1fG0 1cg0 1dX0 1bX0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Belfast|GMT BST BDST|0 -10 -20|0101010101010101010101010101010101010101010101010121212121210101210101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2axa0 Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Belgrade|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-19RC0 3IP0 WM0 1fA0 1cM0 1cM0 1rc0 Qo0 1vmo0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Berlin|CET CEST CEMT|-10 -20 -30|01010101010101210101210101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 kL0 Nc0 m10 WM0 1ao0 1cp0 dX0 jz0 Dd0 1io0 17c0 1fA0 1a00 1ehA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Bratislava|CET CEST|-10 -20|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 16M0 1lc0 1tA0 17A0 11c0 1io0 17c0 1io0 17c0 1fc0 1ao0 1bNc0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Brussels|WET CET CEST WEST|0 -10 -20 -10|0121212103030303030303030303030303030303030303030303212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ehc0 3zX0 11c0 1iO0 11A0 1o00 11A0 my0 Ic0 1qM0 Rc0 1EM0 UM0 1u00 10o0 1io0 1io0 17c0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a30 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 y00 5Wn0 WM0 1fA0 1cM0 16M0 1iM0 16M0 1C00 Uo0 1eeo0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Bucharest|BMT EET EEST|-1I.o -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1xApI.o 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Axc0 On0 1fA0 1a10 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Budapest|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1ip0 17b0 1op0 1tb0 Q2m0 3Ne0 WM0 1fA0 1cM0 1cM0 1oJ0 1dc0 1030 1fA0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1iM0 1fA0 8Ha0 Rb0 1wN0 Rb0 1BB0 Lz0 1C20 LB0 SNX0 1a10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Busingen|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-19Lc0 11A0 1o00 11A0 1xG10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Chisinau|CMT BMT EET EEST CEST CET MSK MSD|-1T -1I.o -20 -30 -20 -10 -30 -40|0123232323232323232345454676767676767676767623232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-26jdT wGMa.A 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 27A0 2en0 39g0 WM0 1fA0 1cM0 V90 1t7z0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1ty0 2bD0 1cM0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11D0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Copenhagen|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2azC0 Tz0 VuO0 60q0 WM0 1fA0 1cM0 1cM0 1cM0 S00 1HA0 Nc0 1C00 Dc0 1Nc0 Ao0 1h5A0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Gibraltar|GMT BST BDST CET CEST|0 -10 -20 -10 -20|010101010101010101010101010101010101010101010101012121212121010121010101010101010101034343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-2axa0 Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 10Jz0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Helsinki|HMT EET EEST|-1D.N -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1WuND.N OULD.N 1dA0 1xGq0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Kaliningrad|CET CEST CET CEST MSK MSD EEST EET FET|-10 -20 -20 -30 -30 -40 -30 -20 -30|0101010101010232454545454545454545454676767676767676767676767676767676767676787|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 Am0 Lb0 1en0 op0 1pNz0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1cJ0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Europe/Kiev|KMT EET MSK CEST CET MSD EEST|-22.4 -20 -30 -20 -10 -40 -30|0123434252525252525252525256161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161|-1Pc22.4 eUo2.4 rnz0 2Hg0 WM0 1fA0 da0 1v4m0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 Db0 3220 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cQ0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Lisbon|LMT WET WEST WEMT CET CEST|A.J 0 -10 -20 -10 -20|012121212121212121212121212121212121212121212321232123212321212121212121212121212121212121212121214121212121212121212121212121212124545454212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ldXn.f aPWn.f Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 3Co0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 pvy0 1cM0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Luxembourg|LMT CET CEST WET WEST WEST WET|-o.A -10 -20 0 -10 -20 -10|0121212134343434343434343434343434343434343434343434565651212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2DG0o.A t6mo.A TB0 1nX0 Up0 1o20 11A0 rW0 CM0 1qP0 R90 1EO0 UK0 1u20 10m0 1ip0 1in0 17e0 19W0 1fB0 1db0 1cp0 1in0 17d0 1fz0 1a10 1in0 1a10 1in0 17f0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 vA0 60L0 WM0 1fA0 1cM0 17c0 1io0 16M0 1C00 Uo0 1eeo0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Madrid|WET WEST WEMT CET CEST|0 -10 -20 -10 -20|01010101010101010101010121212121234343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-28dd0 11A0 1go0 19A0 1co0 1dA0 b1A0 18o0 3I00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 iyo0 Rc0 18o0 1hc0 1io0 1a00 14o0 5aL0 MM0 1vc0 17A0 1i00 1bc0 1eo0 17d0 1in0 17A0 6hA0 10N0 XIL0 1a10 1in0 17d0 19X0 1cN0 1fz0 1a10 1fX0 1cp0 1cO0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Malta|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2as10 M00 1cM0 1cM0 14o0 1o00 WM0 1qM0 17c0 1cM0 M3A0 5M20 WM0 1fA0 1cM0 1cM0 1cM0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 Lz0 1C10 Lz0 1EN0 Lz0 1C10 Lz0 1zd0 Oo0 1C00 On0 1cp0 1cM0 1lA0 Xc0 1qq0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1iN0 19z0 1fB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Minsk|MMT EET MSK CEST CET MSD EEST FET|-1O -20 -30 -20 -10 -40 -30 -30|012343432525252525252525252616161616161616161616161616161616161616172|-1Pc1O eUnO qNX0 3gQ0 WM0 1fA0 1cM0 Al0 1tsn0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 3Fc0 1cN0 1cK0 1cM0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hy0","Europe/Monaco|PMT WET WEST WEMT CET CEST|-9.l 0 -10 -20 -10 -20|01212121212121212121212121212121212121212121212121232323232345454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2nco9.l cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 1u00 10o0 1io0 1wo0 Rc0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Df0 2RV0 11z0 11B0 1ze0 WM0 1fA0 1cM0 1fa0 1aq0 16M0 1ekn0 1cL0 1fC0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Moscow|MMT MMT MST MDST MSD MSK MSM EET EEST MSK|-2u.h -2v.j -3v.j -4v.j -40 -30 -50 -20 -30 -40|012132345464575454545454545454545458754545454545454545454545454545454545454595|-2ag2u.h 2pyW.W 1bA0 11X0 GN0 1Hb0 c20 imv.j 3DA0 dz0 15A0 c10 2q10 iM10 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Europe/Paris|PMT WET WEST CEST CET WEMT|-9.l 0 -10 -20 -10 -20|0121212121212121212121212121212121212121212121212123434352543434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-2nco8.l cNb8.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 1u00 10o0 1io0 1wo0 Rc0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Df0 Ik0 5M30 WM0 1fA0 1cM0 Vx0 hB0 1aq0 16M0 1ekn0 1cL0 1fC0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Riga|RMT LST EET MSK CEST CET MSD EEST|-1A.y -2A.y -20 -30 -20 -10 -40 -30|010102345454536363636363636363727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272|-25TzA.y 11A0 1iM0 ko0 gWm0 yDXA.y 2bX0 3fE0 WM0 1fA0 1cM0 1cM0 4m0 1sLy0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1o00 11A0 1o00 11A0 1qM0 3oo0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Rome|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2as10 M00 1cM0 1cM0 14o0 1o00 WM0 1qM0 17c0 1cM0 M3A0 5M20 WM0 1fA0 1cM0 16K0 1iO0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 Lz0 1C10 Lz0 1EN0 Lz0 1C10 Lz0 1zd0 Oo0 1C00 On0 1C10 Lz0 1zd0 On0 1C10 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1zc0 Oo0 1fC0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Samara|LMT SAMT SAMT KUYT KUYST MSD MSK EEST KUYT SAMST SAMST|-3k.k -30 -40 -40 -50 -40 -30 -30 -30 -50 -40|012343434343434343435656782929292929292929292929292929292929292a12|-22WNk.k qHak.k bcn0 1Qqo0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cN0 8o0 14j0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0","Europe/Simferopol|SMT EET MSK CEST CET MSD EEST MSK|-2g -20 -30 -20 -10 -40 -30 -40|012343432525252525252525252161616525252616161616161616161616161616161616172|-1Pc2g eUog rEn0 2qs0 WM0 1fA0 1cM0 3V0 1u0L0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Q00 4eL0 1cL0 1cN0 1cL0 1cN0 dX0 WL0 1cN0 1cL0 1fB0 1o30 11B0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11z0 1nW0","Europe/Sofia|EET CET CEST EEST|-20 -10 -20 -30|01212103030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030|-168L0 WM0 1fA0 1cM0 1cM0 1cN0 1mKH0 1dd0 1fb0 1ap0 1fb0 1a20 1fy0 1a30 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Stockholm|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2azC0 TB0 2yDe0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Tallinn|TMT CET CEST EET MSK MSD EEST|-1D -10 -20 -20 -30 -40 -30|012103421212454545454545454546363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363|-26oND teD 11A0 1Ta0 4rXl KSLD 2FX0 2Jg0 WM0 1fA0 1cM0 18J0 1sTX0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o10 11A0 1qM0 5QM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Tirane|LMT CET CEST|-1j.k -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glBj.k 14pcj.k 5LC0 WM0 4M0 1fCK0 10n0 1op0 11z0 1pd0 11z0 1qN0 WL0 1qp0 Xb0 1qp0 Xb0 1qp0 11z0 1lB0 11z0 1qN0 11z0 1iN0 16n0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Uzhgorod|CET CEST MSK MSD EET EEST|-10 -20 -30 -40 -20 -30|010101023232323232323232320454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-1cqL0 6i00 WM0 1fA0 1cM0 1ml0 1Cp0 1r3W0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Q00 1Nf0 2pw0 1cL0 1cN0 1cL0 1cN0 1cL0 1cQ0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Vienna|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 3KM0 14o0 LA00 6i00 WM0 1fA0 1cM0 1cM0 1cM0 400 2qM0 1a00 1cM0 1cM0 1io0 17c0 1gHa0 19X0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Vilnius|WMT KMT CET EET MSK CEST MSD EEST|-1o -1z.A -10 -20 -30 -20 -40 -30|012324525254646464646464646464647373737373737352537373737373737373737373737373737373737373737373737373737373737373737373|-293do 6ILM.o 1Ooz.A zz0 Mfd0 29W0 3is0 WM0 1fA0 1cM0 LV0 1tgL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11B0 1o00 11A0 1qM0 8io0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Volgograd|LMT TSAT STAT STAT VOLT VOLST VOLST VOLT MSD MSK MSK|-2V.E -30 -30 -40 -40 -50 -40 -30 -40 -30 -40|0123454545454545454546767489898989898989898989898989898989898989a9|-21IqV.E cLXV.E cEM0 1gqn0 Lco0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 2pz0 1cJ0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Europe/Warsaw|WMT CET CEST EET EEST|-1o -10 -20 -20 -30|012121234312121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ctdo 1LXo 11d0 1iO0 11A0 1o00 11A0 1on0 11A0 6zy0 HWP0 5IM0 WM0 1fA0 1cM0 1dz0 1mL0 1en0 15B0 1aq0 1nA0 11A0 1io0 17c0 1fA0 1a00 iDX0 LA0 1cM0 1cM0 1C00 Oo0 1cM0 1cM0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1C00 LA0 uso0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Zaporozhye|CUT EET MSK CEST CET MSD EEST|-2k -20 -30 -20 -10 -40 -30|01234342525252525252525252526161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161|-1Pc2k eUok rdb0 2RE0 WM0 1fA0 8m0 1v9a0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cK0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cQ0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","HST|HST|a0|0|","Indian/Chagos|LMT IOT IOT|-4N.E -50 -60|012|-2xosN.E 3AGLN.E","Indian/Christmas|CXT|-70|0|","Indian/Cocos|CCT|-6u|0|","Indian/Kerguelen|zzz TFT|0 -50|01|-MG00","Indian/Mahe|LMT SCT|-3F.M -40|01|-2yO3F.M","Indian/Maldives|MMT MVT|-4S -50|01|-olgS","Indian/Mauritius|LMT MUT MUST|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0","Indian/Reunion|LMT RET|-3F.Q -40|01|-2mDDF.Q","Kwajalein|MHT KWAT MHT|-b0 c0 -c0|012|-AX0 W9X0","MET|MET MEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","MST|MST|70|0|","MST7MDT|MST MDT MWT MPT|70 60 60 60|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","NZ-CHAT|CHAST CHAST CHADT|-cf -cJ -dJ|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-WqAf 1adef IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","PST8PDT|PST PDT PWT PPT|80 70 70 70|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261q0 1nX0 11B0 1nX0 SgN0 8x10 iy0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Pacific/Apia|LMT WSST SST SDT WSDT WSST|bq.U bu b0 a0 -e0 -d0|01232345454545454545454545454545454545454545454545454545454|-2nDMx.4 1yW03.4 2rRbu 1ff0 1a00 CI0 AQ0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Pacific/Bougainville|PGT JST BST|-a0 -90 -b0|0102|-16Wy0 7CN0 2MQp0","Pacific/Chuuk|CHUT|-a0|0|","Pacific/Efate|LMT VUT VUST|-bd.g -b0 -c0|0121212121212121212121|-2l9nd.g 2Szcd.g 1cL0 1oN0 10L0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 Lz0 1Nd0 An0","Pacific/Enderbury|PHOT PHOT PHOT|c0 b0 -d0|012|nIc0 B8n0","Pacific/Fakaofo|TKT TKT|b0 -d0|01|1Gfn0","Pacific/Fiji|LMT FJT FJST|-bT.I -c0 -d0|0121212121212121212121212121212121212121212121212121212121212121|-2bUzT.I 3m8NT.I LA0 1EM0 IM0 nJc0 LA0 1o00 Rc0 1wo0 Ao0 1Nc0 Ao0 1Q00 xz0 1SN0 uM0 1SM0 uM0 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1VA0 s00 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1VA0 s00 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0","Pacific/Funafuti|TVT|-c0|0|","Pacific/Galapagos|LMT ECT GALT|5W.o 50 60|012|-1yVS1.A 2dTz1.A","Pacific/Gambier|LMT GAMT|8X.M 90|01|-2jof0.c","Pacific/Guadalcanal|LMT SBT|-aD.M -b0|01|-2joyD.M","Pacific/Guam|GST ChST|-a0 -a0|01|1fpq0","Pacific/Honolulu|HST HDT HST|au 9u a0|010102|-1thLu 8x0 lef0 8Pz0 46p0","Pacific/Kiritimati|LINT LINT LINT|aE a0 -e0|012|nIaE B8nk","Pacific/Kosrae|KOST KOST|-b0 -c0|010|-AX0 1bdz0","Pacific/Majuro|MHT MHT|-b0 -c0|01|-AX0","Pacific/Marquesas|LMT MART|9i 9u|01|-2joeG","Pacific/Midway|LMT NST BST SST|bm.M b0 b0 b0|0123|-2nDMB.c 2gVzB.c EyM0","Pacific/Nauru|LMT NRT JST NRT|-b7.E -bu -90 -c0|01213|-1Xdn7.E PvzB.E 5RCu 1ouJu","Pacific/Niue|NUT NUT NUT|bk bu b0|012|-KfME 17y0a","Pacific/Norfolk|NMT NFT NFST NFT|-bc -bu -cu -b0|01213|-Kgbc W01G On0 1COp0","Pacific/Noumea|LMT NCT NCST|-b5.M -b0 -c0|01212121|-2l9n5.M 2EqM5.M xX0 1PB0 yn0 HeP0 Ao0","Pacific/Palau|PWT|-90|0|","Pacific/Pitcairn|PNT PST|8u 80|01|18Vku","Pacific/Pohnpei|PONT|-b0|0|","Pacific/Port_Moresby|PGT|-a0|0|","Pacific/Rarotonga|CKT CKHST CKT|au 9u a0|012121212121212121212121212|lyWu IL0 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu","Pacific/Tahiti|LMT TAHT|9W.g a0|01|-2joe1.I","Pacific/Tarawa|GILT|-c0|0|","Pacific/Tongatapu|TOT TOT TOST|-ck -d0 -e0|01212121|-1aB0k 2n5dk 15A0 1wo0 xz0 1Q10 xz0","Pacific/Wake|WAKT|-c0|0|","Pacific/Wallis|WFT|-c0|0|","WET|WET WEST|0 -10|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00"], links:["Africa/Abidjan|Africa/Bamako","Africa/Abidjan|Africa/Banjul","Africa/Abidjan|Africa/Conakry","Africa/Abidjan|Africa/Dakar","Africa/Abidjan|Africa/Freetown","Africa/Abidjan|Africa/Lome","Africa/Abidjan|Africa/Nouakchott","Africa/Abidjan|Africa/Ouagadougou","Africa/Abidjan|Africa/Sao_Tome","Africa/Abidjan|Africa/Timbuktu","Africa/Abidjan|Atlantic/St_Helena","Africa/Addis_Ababa|Africa/Asmara","Africa/Addis_Ababa|Africa/Asmera","Africa/Addis_Ababa|Africa/Dar_es_Salaam","Africa/Addis_Ababa|Africa/Djibouti","Africa/Addis_Ababa|Africa/Kampala","Africa/Addis_Ababa|Africa/Mogadishu","Africa/Addis_Ababa|Africa/Nairobi","Africa/Addis_Ababa|Indian/Antananarivo","Africa/Addis_Ababa|Indian/Comoro","Africa/Addis_Ababa|Indian/Mayotte","Africa/Bangui|Africa/Brazzaville","Africa/Bangui|Africa/Douala","Africa/Bangui|Africa/Kinshasa","Africa/Bangui|Africa/Lagos","Africa/Bangui|Africa/Libreville","Africa/Bangui|Africa/Luanda","Africa/Bangui|Africa/Malabo","Africa/Bangui|Africa/Niamey","Africa/Bangui|Africa/Porto-Novo","Africa/Blantyre|Africa/Bujumbura","Africa/Blantyre|Africa/Gaborone","Africa/Blantyre|Africa/Harare","Africa/Blantyre|Africa/Kigali","Africa/Blantyre|Africa/Lubumbashi","Africa/Blantyre|Africa/Lusaka","Africa/Blantyre|Africa/Maputo","Africa/Cairo|Egypt","Africa/Johannesburg|Africa/Maseru","Africa/Johannesburg|Africa/Mbabane","Africa/Juba|Africa/Khartoum","Africa/Tripoli|Libya","America/Adak|America/Atka","America/Adak|US/Aleutian","America/Anchorage|US/Alaska","America/Anguilla|America/Antigua","America/Anguilla|America/Dominica","America/Anguilla|America/Grenada","America/Anguilla|America/Guadeloupe","America/Anguilla|America/Marigot","America/Anguilla|America/Montserrat","America/Anguilla|America/Port_of_Spain","America/Anguilla|America/St_Barthelemy","America/Anguilla|America/St_Kitts","America/Anguilla|America/St_Lucia","America/Anguilla|America/St_Thomas","America/Anguilla|America/St_Vincent","America/Anguilla|America/Tortola","America/Anguilla|America/Virgin","America/Argentina/Buenos_Aires|America/Buenos_Aires","America/Argentina/Catamarca|America/Argentina/ComodRivadavia","America/Argentina/Catamarca|America/Catamarca","America/Argentina/Cordoba|America/Cordoba","America/Argentina/Cordoba|America/Rosario","America/Argentina/Jujuy|America/Jujuy","America/Argentina/Mendoza|America/Mendoza","America/Aruba|America/Curacao","America/Aruba|America/Kralendijk","America/Aruba|America/Lower_Princes","America/Atikokan|America/Coral_Harbour","America/Chicago|US/Central","America/Denver|America/Shiprock","America/Denver|Navajo","America/Denver|US/Mountain","America/Detroit|US/Michigan","America/Edmonton|Canada/Mountain","America/Ensenada|America/Tijuana","America/Ensenada|Mexico/BajaNorte","America/Fort_Wayne|America/Indiana/Indianapolis","America/Fort_Wayne|America/Indianapolis","America/Fort_Wayne|US/East-Indiana","America/Halifax|Canada/Atlantic","America/Havana|Cuba","America/Indiana/Knox|America/Knox_IN","America/Indiana/Knox|US/Indiana-Starke","America/Jamaica|Jamaica","America/Kentucky/Louisville|America/Louisville","America/Los_Angeles|US/Pacific","America/Los_Angeles|US/Pacific-New","America/Manaus|Brazil/West","America/Mazatlan|Mexico/BajaSur","America/Mexico_City|Mexico/General","America/Montreal|America/Toronto","America/Montreal|Canada/Eastern","America/New_York|US/Eastern","America/Noronha|Brazil/DeNoronha","America/Phoenix|US/Arizona","America/Porto_Acre|America/Rio_Branco","America/Porto_Acre|Brazil/Acre","America/Regina|Canada/East-Saskatchewan","America/Regina|Canada/Saskatchewan","America/Santiago|Chile/Continental","America/Sao_Paulo|Brazil/East","America/St_Johns|Canada/Newfoundland","America/Vancouver|Canada/Pacific","America/Whitehorse|Canada/Yukon","America/Winnipeg|Canada/Central","Antarctica/McMurdo|Antarctica/South_Pole","Antarctica/McMurdo|NZ","Antarctica/McMurdo|Pacific/Auckland","Arctic/Longyearbyen|Atlantic/Jan_Mayen","Arctic/Longyearbyen|Europe/Oslo","Asia/Aden|Asia/Kuwait","Asia/Aden|Asia/Riyadh","Asia/Ashgabat|Asia/Ashkhabad","Asia/Bahrain|Asia/Qatar","Asia/Bangkok|Asia/Phnom_Penh","Asia/Bangkok|Asia/Vientiane","Asia/Calcutta|Asia/Kolkata","Asia/Chongqing|Asia/Chungking","Asia/Chongqing|Asia/Harbin","Asia/Chongqing|Asia/Shanghai","Asia/Chongqing|PRC","Asia/Dacca|Asia/Dhaka","Asia/Dubai|Asia/Muscat","Asia/Ho_Chi_Minh|Asia/Saigon","Asia/Hong_Kong|Hongkong","Asia/Istanbul|Europe/Istanbul","Asia/Istanbul|Turkey","Asia/Jerusalem|Asia/Tel_Aviv","Asia/Jerusalem|Israel","Asia/Kashgar|Asia/Urumqi","Asia/Kathmandu|Asia/Katmandu","Asia/Macao|Asia/Macau","Asia/Makassar|Asia/Ujung_Pandang","Asia/Nicosia|Europe/Nicosia","Asia/Seoul|ROK","Asia/Singapore|Singapore","Asia/Taipei|ROC","Asia/Tehran|Iran","Asia/Thimbu|Asia/Thimphu","Asia/Tokyo|Japan","Asia/Ulaanbaatar|Asia/Ulan_Bator","Atlantic/Faeroe|Atlantic/Faroe","Atlantic/Reykjavik|Iceland","Australia/ACT|Australia/Canberra","Australia/ACT|Australia/NSW","Australia/ACT|Australia/Sydney","Australia/Adelaide|Australia/South","Australia/Brisbane|Australia/Queensland","Australia/Broken_Hill|Australia/Yancowinna","Australia/Darwin|Australia/North","Australia/Hobart|Australia/Tasmania","Australia/LHI|Australia/Lord_Howe","Australia/Melbourne|Australia/Victoria","Australia/Perth|Australia/West","Chile/EasterIsland|Pacific/Easter","Eire|Europe/Dublin","Etc/GMT+0|Etc/GMT","Etc/GMT+0|Etc/GMT-0","Etc/GMT+0|Etc/GMT0","Etc/GMT+0|Etc/Greenwich","Etc/GMT+0|GMT","Etc/GMT+0|GMT+0","Etc/GMT+0|GMT-0","Etc/GMT+0|GMT0","Etc/GMT+0|Greenwich","Etc/UCT|UCT","Etc/UTC|Etc/Universal","Etc/UTC|Etc/Zulu","Etc/UTC|UTC","Etc/UTC|Universal","Etc/UTC|Zulu","Europe/Belfast|Europe/Guernsey","Europe/Belfast|Europe/Isle_of_Man","Europe/Belfast|Europe/Jersey","Europe/Belfast|Europe/London","Europe/Belfast|GB","Europe/Belfast|GB-Eire","Europe/Belgrade|Europe/Ljubljana","Europe/Belgrade|Europe/Podgorica","Europe/Belgrade|Europe/Sarajevo","Europe/Belgrade|Europe/Skopje","Europe/Belgrade|Europe/Zagreb","Europe/Bratislava|Europe/Prague","Europe/Busingen|Europe/Vaduz","Europe/Busingen|Europe/Zurich","Europe/Chisinau|Europe/Tiraspol","Europe/Helsinki|Europe/Mariehamn","Europe/Lisbon|Portugal","Europe/Moscow|W-SU","Europe/Rome|Europe/San_Marino","Europe/Rome|Europe/Vatican","Europe/Warsaw|Poland","Kwajalein|Pacific/Kwajalein","NZ-CHAT|Pacific/Chatham","Pacific/Chuuk|Pacific/Truk","Pacific/Chuuk|Pacific/Yap","Pacific/Guam|Pacific/Saipan","Pacific/Honolulu|Pacific/Johnston","Pacific/Honolulu|US/Hawaii","Pacific/Midway|Pacific/Pago_Pago","Pacific/Midway|Pacific/Samoa","Pacific/Midway|US/Samoa","Pacific/Pohnpei|Pacific/Ponape"]}),a}); -(function(){"use strict";function e(e){return"function"==typeof e||"object"==typeof e&&null!==e}function t(e){return"function"==typeof e}function r(e){return"object"==typeof e&&null!==e}function n(e){q=e}function o(e){Z=e}function i(){return function(){process.nextTick(l)}}function a(){return function(){I(l)}}function s(){var e=0,t=new Y(l),r=document.createTextNode("");return t.observe(r,{characterData:!0}),function(){r.data=e=++e%2}}function u(){var e=new MessageChannel;return e.port1.onmessage=l,function(){e.port2.postMessage(0)}}function c(){return function(){setTimeout(l,1)}}function l(){for(var e=0;B>e;e+=2){var t=K[e],r=K[e+1];t(r),K[e]=void 0,K[e+1]=void 0}B=0}function f(){try{var e=require,t=e("vertx");return I=t.runOnLoop||t.runOnContext,a()}catch(r){return c()}}function p(){}function h(){return new TypeError("You cannot resolve a promise with itself")}function g(){return new TypeError("A promises callback cannot return that same promise.")}function d(e){try{return e.then}catch(t){return te.error=t,te}}function m(e,t,r,n){try{e.call(t,r,n)}catch(o){return o}}function y(e,t,r){Z(function(e){var n=!1,o=m(r,t,function(r){n||(n=!0,t!==r?b(e,r):A(e,r))},function(t){n||(n=!0,C(e,t))},"Settle: "+(e._label||" unknown promise"));!n&&o&&(n=!0,C(e,o))},e)}function v(e,t){t._state===W?A(e,t._result):t._state===ee?C(e,t._result):S(t,void 0,function(t){b(e,t)},function(t){C(e,t)})}function _(e,r){if(r.constructor===e.constructor)v(e,r);else{var n=d(r);n===te?C(e,te.error):void 0===n?A(e,r):t(n)?y(e,r,n):A(e,r)}}function b(t,r){t===r?C(t,h()):e(r)?_(t,r):A(t,r)}function w(e){e._onerror&&e._onerror(e._result),O(e)}function A(e,t){e._state===Q&&(e._result=t,e._state=W,0!==e._subscribers.length&&Z(O,e))}function C(e,t){e._state===Q&&(e._state=ee,e._result=t,Z(w,e))}function S(e,t,r,n){var o=e._subscribers,i=o.length;e._onerror=null,o[i]=t,o[i+W]=r,o[i+ee]=n,0===i&&e._state&&Z(O,e)}function O(e){var t=e._subscribers,r=e._state;if(0!==t.length){for(var n,o,i=e._result,a=0;aa;a++)S(n.resolve(e[a]),void 0,t,r);return o}function L(e){var t=this;if(e&&"object"==typeof e&&e.constructor===t)return e;var r=new t(p);return b(r,e),r}function P(e){var t=this,r=new t(p);return C(r,e),r}function F(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function R(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function $(e){this._id=ue++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==e&&(t(e)||F(),this instanceof $||R(),j(this,e))}function G(){var e;if("undefined"!=typeof global)e=global;else if("undefined"!=typeof self)e=self;else try{e=Function("return this")()}catch(t){throw new Error("polyfill failed because global object is unavailable in this environment")}var r=e.Promise;(!r||"[object Promise]"!==Object.prototype.toString.call(r.resolve())||r.cast)&&(e.Promise=ce)}var U;U=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)};var I,q,J,D=U,B=0,Z=({}.toString,function(e,t){K[B]=e,K[B+1]=t,B+=2,2===B&&(q?q(l):J())}),z="undefined"!=typeof window?window:void 0,H=z||{},Y=H.MutationObserver||H.WebKitMutationObserver,X="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,K=new Array(1e3);J=X?i():Y?s():V?u():void 0===z&&"function"==typeof require?f():c();var Q=void 0,W=1,ee=2,te=new E,re=new E;M.prototype._validateInput=function(e){return D(e)},M.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},M.prototype._init=function(){this._result=new Array(this.length)};var ne=M;M.prototype._enumerate=function(){for(var e=this,t=e.length,r=e.promise,n=e._input,o=0;r._state===Q&&t>o;o++)e._eachEntry(n[o],o)},M.prototype._eachEntry=function(e,t){var n=this,o=n._instanceConstructor;r(e)?e.constructor===o&&e._state!==Q?(e._onerror=null,n._settledAt(e._state,t,e._result)):n._willSettleAt(o.resolve(e),t):(n._remaining--,n._result[t]=e)},M.prototype._settledAt=function(e,t,r){var n=this,o=n.promise;o._state===Q&&(n._remaining--,e===ee?C(o,r):n._result[t]=r),0===n._remaining&&A(o,n._result)},M.prototype._willSettleAt=function(e,t){var r=this;S(e,void 0,function(e){r._settledAt(W,t,e)},function(e){r._settledAt(ee,t,e)})};var oe=k,ie=T,ae=L,se=P,ue=0,ce=$;$.all=oe,$.race=ie,$.resolve=ae,$.reject=se,$._setScheduler=n,$._setAsap=o,$._asap=Z,$.prototype={constructor:$,then:function(e,t){var r=this,n=r._state;if(n===W&&!e||n===ee&&!t)return this;var o=new this.constructor(p),i=r._result;if(n){var a=arguments[n-1];Z(function(){x(n,o,a,i)})}else S(r,o,e,t);return o},"catch":function(e){return this.then(null,e)}};var le=G,fe={Promise:ce,polyfill:le};"function"==typeof define&&define.amd?define(function(){return fe}):"undefined"!=typeof module&&module.exports?module.exports=fe:"undefined"!=typeof this&&(this.ES6Promise=fe),le()}).call(this),function(){function e(t,n){function i(e){if(i[e]!==m)return i[e];var t;if("bug-string-char-index"==e)t="a"!="a"[0];else if("json"==e)t=i("json-stringify")&&i("json-parse");else{var r,o='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if("json-stringify"==e){var u=n.stringify,l="function"==typeof u&&_;if(l){(r=function(){return 1}).toJSON=r;try{l="0"===u(0)&&"0"===u(new a)&&'""'==u(new s)&&u(v)===m&&u(m)===m&&u()===m&&"1"===u(r)&&"[1]"==u([r])&&"[null]"==u([m])&&"null"==u(null)&&"[null,null,null]"==u([m,v,null])&&u({a:[r,!0,!1,null,"\x00\b\n\f\r "]})==o&&"1"===u(null,r)&&"[\n 1,\n 2\n]"==u([1,2],null,1)&&'"-271821-04-20T00:00:00.000Z"'==u(new c(-864e13))&&'"+275760-09-13T00:00:00.000Z"'==u(new c(864e13))&&'"-000001-01-01T00:00:00.000Z"'==u(new c(-621987552e5))&&'"1969-12-31T23:59:59.999Z"'==u(new c(-1))}catch(f){l=!1}}t=l}if("json-parse"==e){var p=n.parse;if("function"==typeof p)try{if(0===p("0")&&!p(!1)){r=p(o);var h=5==r.a.length&&1===r.a[0];if(h){try{h=!p('" "')}catch(f){}if(h)try{h=1!==p("01")}catch(f){}if(h)try{h=1!==p("1.")}catch(f){}}}}catch(f){h=!1}t=h}}return i[e]=!!t}t||(t=o.Object()),n||(n=o.Object());var a=t.Number||o.Number,s=t.String||o.String,u=t.Object||o.Object,c=t.Date||o.Date,l=t.SyntaxError||o.SyntaxError,f=t.TypeError||o.TypeError,p=t.Math||o.Math,h=t.JSON||o.JSON;"object"==typeof h&&h&&(n.stringify=h.stringify,n.parse=h.parse);var g,d,m,y=u.prototype,v=y.toString,_=new c(-0xc782b5b800cec);try{_=-109252==_.getUTCFullYear()&&0===_.getUTCMonth()&&1===_.getUTCDate()&&10==_.getUTCHours()&&37==_.getUTCMinutes()&&6==_.getUTCSeconds()&&708==_.getUTCMilliseconds()}catch(b){}if(!i("json")){var w="[object Function]",A="[object Date]",C="[object Number]",S="[object String]",O="[object Array]",E="[object Boolean]",N=i("bug-string-char-index");if(!_)var x=p.floor,j=[0,31,59,90,120,151,181,212,243,273,304,334],M=function(e,t){return j[t]+365*(e-1970)+x((e-1969+(t=+(t>1)))/4)-x((e-1901+t)/100)+x((e-1601+t)/400)};if((g=y.hasOwnProperty)||(g=function(e){var t,r={};return(r.__proto__=null,r.__proto__={toString:1},r).toString!=v?g=function(e){var t=this.__proto__,r=e in(this.__proto__=null,this);return this.__proto__=t,r}:(t=r.constructor,g=function(e){var r=(this.constructor||t).prototype;return e in this&&!(e in r&&this[e]===r[e])}),r=null,g.call(this,e)}),d=function(e,t){var n,o,i,a=0;(n=function(){this.valueOf=0}).prototype.valueOf=0,o=new n;for(i in o)g.call(o,i)&&a++;return n=o=null,a?d=2==a?function(e,t){var r,n={},o=v.call(e)==w;for(r in e)o&&"prototype"==r||g.call(n,r)||!(n[r]=1)||!g.call(e,r)||t(r)}:function(e,t){var r,n,o=v.call(e)==w;for(r in e)o&&"prototype"==r||!g.call(e,r)||(n="constructor"===r)||t(r);(n||g.call(e,r="constructor"))&&t(r)}:(o=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"],d=function(e,t){var n,i,a=v.call(e)==w,s=!a&&"function"!=typeof e.constructor&&r[typeof e.hasOwnProperty]&&e.hasOwnProperty||g;for(n in e)a&&"prototype"==n||!s.call(e,n)||t(n);for(i=o.length;n=o[--i];s.call(e,n)&&t(n));}),d(e,t)},!i("json-stringify")){var k={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"},T="000000",L=function(e,t){return(T+(t||0)).slice(-e)},P="\\u00",F=function(e){for(var t='"',r=0,n=e.length,o=!N||n>10,i=o&&(N?e.split(""):e);n>r;r++){var a=e.charCodeAt(r);switch(a){case 8:case 9:case 10:case 12:case 13:case 34:case 92:t+=k[a];break;default:if(32>a){t+=P+L(2,a.toString(16));break}t+=o?i[r]:e.charAt(r)}}return t+'"'},R=function(e,t,r,n,o,i,a){var s,u,c,l,p,h,y,_,b,w,N,j,k,T,P,$;try{s=t[e]}catch(G){}if("object"==typeof s&&s)if(u=v.call(s),u!=A||g.call(s,"toJSON"))"function"==typeof s.toJSON&&(u!=C&&u!=S&&u!=O||g.call(s,"toJSON"))&&(s=s.toJSON(e));else if(s>-1/0&&1/0>s){if(M){for(p=x(s/864e5),c=x(p/365.2425)+1970-1;M(c+1,0)<=p;c++);for(l=x((p-M(c,0))/30.42);M(c,l+1)<=p;l++);p=1+p-M(c,l),h=(s%864e5+864e5)%864e5,y=x(h/36e5)%24,_=x(h/6e4)%60,b=x(h/1e3)%60,w=h%1e3}else c=s.getUTCFullYear(),l=s.getUTCMonth(),p=s.getUTCDate(),y=s.getUTCHours(),_=s.getUTCMinutes(),b=s.getUTCSeconds(),w=s.getUTCMilliseconds();s=(0>=c||c>=1e4?(0>c?"-":"+")+L(6,0>c?-c:c):L(4,c))+"-"+L(2,l+1)+"-"+L(2,p)+"T"+L(2,y)+":"+L(2,_)+":"+L(2,b)+"."+L(3,w)+"Z"}else s=null;if(r&&(s=r.call(t,e,s)),null===s)return"null";if(u=v.call(s),u==E)return""+s;if(u==C)return s>-1/0&&1/0>s?""+s:"null";if(u==S)return F(""+s);if("object"==typeof s){for(T=a.length;T--;)if(a[T]===s)throw f();if(a.push(s),N=[],P=i,i+=o,u==O){for(k=0,T=s.length;T>k;k++)j=R(k,s,r,n,o,i,a),N.push(j===m?"null":j);$=N.length?o?"[\n"+i+N.join(",\n"+i)+"\n"+P+"]":"["+N.join(",")+"]":"[]"}else d(n||s,function(e){var t=R(e,s,r,n,o,i,a);t!==m&&N.push(F(e)+":"+(o?" ":"")+t)}),$=N.length?o?"{\n"+i+N.join(",\n"+i)+"\n"+P+"}":"{"+N.join(",")+"}":"{}";return a.pop(),$}};n.stringify=function(e,t,n){var o,i,a,s;if(r[typeof t]&&t)if((s=v.call(t))==w)i=t;else if(s==O){a={};for(var u,c=0,l=t.length;l>c;u=t[c++],s=v.call(u),(s==S||s==C)&&(a[u]=1));}if(n)if((s=v.call(n))==C){if((n-=n%1)>0)for(o="",n>10&&(n=10);o.length$;)switch(o=i.charCodeAt($)){case 9:case 10:case 13:case 32:$++;break;case 123:case 125:case 91:case 93:case 58:case 44:return e=N?i.charAt($):i[$],$++,e;case 34:for(e="@",$++;a>$;)if(o=i.charCodeAt($),32>o)q();else if(92==o)switch(o=i.charCodeAt(++$)){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:e+=I[o],$++;break;case 117:for(t=++$,r=$+4;r>$;$++)o=i.charCodeAt($),o>=48&&57>=o||o>=97&&102>=o||o>=65&&70>=o||q();e+=U("0x"+i.slice(t,$));break;default:q()}else{if(34==o)break;for(o=i.charCodeAt($),t=$;o>=32&&92!=o&&34!=o;)o=i.charCodeAt(++$);e+=i.slice(t,$)}if(34==i.charCodeAt($))return $++,e;q();default:if(t=$,45==o&&(n=!0,o=i.charCodeAt(++$)),o>=48&&57>=o){for(48==o&&(o=i.charCodeAt($+1),o>=48&&57>=o)&&q(),n=!1;a>$&&(o=i.charCodeAt($),o>=48&&57>=o);$++);if(46==i.charCodeAt($)){for(r=++$;a>r&&(o=i.charCodeAt(r),o>=48&&57>=o);r++);r==$&&q(),$=r}if(o=i.charCodeAt($),101==o||69==o){for(o=i.charCodeAt(++$),(43==o||45==o)&&$++,r=$;a>r&&(o=i.charCodeAt(r),o>=48&&57>=o);r++);r==$&&q(),$=r}return+i.slice(t,$)}if(n&&q(),"true"==i.slice($,$+4))return $+=4,!0;if("false"==i.slice($,$+5))return $+=5,!1;if("null"==i.slice($,$+4))return $+=4,null;q()}return"$"},D=function(e){var t,r;if("$"==e&&q(),"string"==typeof e){if("@"==(N?e.charAt(0):e[0]))return e.slice(1);if("["==e){for(t=[];e=J(),"]"!=e;r||(r=!0))r&&(","==e?(e=J(),"]"==e&&q()):q()),","==e&&q(),t.push(D(e));return t}if("{"==e){for(t={};e=J(),"}"!=e;r||(r=!0))r&&(","==e?(e=J(),"}"==e&&q()):q()),(","==e||"string"!=typeof e||"@"!=(N?e.charAt(0):e[0])||":"!=J())&&q(),t[e.slice(1)]=D(J());return t}q()}return e},B=function(e,t,r){var n=Z(e,t,r);n===m?delete e[t]:e[t]=n},Z=function(e,t,r){var n,o=e[t];if("object"==typeof o&&o)if(v.call(o)==O)for(n=o.length;n--;)B(o,n,r);else d(o,function(e){B(o,e,r)});return r.call(e,t,o)};n.parse=function(e,t){var r,n;return $=0,G=""+e,r=D(J()),"$"!=J()&&q(),$=G=null,t&&v.call(t)==w?Z((n={},n[""]=r,n),"",t):r}}}return n.runInContext=e,n}var t="function"==typeof define&&define.amd,r={"function":!0,object:!0},n=r[typeof exports]&&exports&&!exports.nodeType&&exports,o=r[typeof window]&&window||this,i=n&&r[typeof module]&&module&&!module.nodeType&&"object"==typeof global&&global;if(!i||i.global!==i&&i.window!==i&&i.self!==i||(o=i),n&&!t)e(o,n);else{var a=o.JSON,s=o.JSON3,u=!1,c=e(o,o.JSON3={noConflict:function(){return u||(u=!0,o.JSON=a,o.JSON3=s,a=s=null),c}});o.JSON={parse:c.parse,stringify:c.stringify}}t&&define(function(){return c})}.call(this),"undefined"==typeof Promise&&ES6Promise.polyfill(),Function.prototype.bind||(Function.prototype.bind=function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),r=this,n=function(){},o=function(){return r.apply(this instanceof n&&e?this:e,t.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,o.prototype=new n,o}),Array.prototype.map||(Array.prototype.map=function(e,t){if(void 0===this||null===this)throw new TypeError("this is null or not defined");var r,n=Object(this),o=n.length>>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");arguments.length>1&&(r=t);for(var i=new Array(o),a=0;o>a;){var s,u;a in n&&(s=n[a],u=e.call(r,s,a,n),i[a]=u),a++}return i}),Array.prototype.filter||(Array.prototype.filter=function(e){if(void 0===this||null===this)throw new TypeError("this is null or not defined");var t=Object(this),r=t.length>>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(var n=[],o=arguments.length>=2?arguments[1]:void 0,i=0;r>i;i++)if(i in t){var a=t[i];e.call(o,a,i,t)&&n.push(a)}return n}),Array.prototype.forEach||(Array.prototype.forEach=function(e,t){var r,n;if(null===this||void 0===this)throw new TypeError(" this is null or not defined");var o=Object(this),i=o.length>>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(arguments.length>1&&(r=t),n=0;i>n;){var a;n in o&&(a=o[n],e.call(r,a,n,o)),n++}}),!function(e,t){"use strict";"function"==typeof define&&define.amd?define("stackframe",[],t):"object"==typeof exports?module.exports=t():e.StackFrame=t()}(this,function(){"use strict";function e(e){return!isNaN(parseFloat(e))&&isFinite(e)}function t(e,t,r,n,o){void 0!==e&&this.setFunctionName(e),void 0!==t&&this.setArgs(t),void 0!==r&&this.setFileName(r),void 0!==n&&this.setLineNumber(n),void 0!==o&&this.setColumnNumber(o)}return t.prototype={getFunctionName:function(){return this.functionName},setFunctionName:function(e){this.functionName=String(e)},getArgs:function(){return this.args},setArgs:function(e){if("[object Array]"!==Object.prototype.toString.call(e))throw new TypeError("Args must be an Array");this.args=e},getFileName:function(){return this.fileName},setFileName:function(e){this.fileName=String(e)},getLineNumber:function(){return this.lineNumber},setLineNumber:function(t){if(!e(t))throw new TypeError("Line Number must be a Number");this.lineNumber=Number(t)},getColumnNumber:function(){return this.columnNumber},setColumnNumber:function(t){if(!e(t))throw new TypeError("Column Number must be a Number");this.columnNumber=Number(t)},toString:function(){var t=this.getFunctionName()||"{anonymous}",r="("+(this.getArgs()||[]).join(",")+")",n=this.getFileName()?"@"+this.getFileName():"",o=e(this.getLineNumber())?":"+this.getLineNumber():"",i=e(this.getColumnNumber())?":"+this.getColumnNumber():"";return t+r+n+o+i}},t});var SourceMap=function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){var n;n=function(e,t,n){function o(e){var t=e;"string"==typeof e&&(t=JSON.parse(e.replace(/^\)\]\}'/,"")));var r=i.getArg(t,"version"),n=i.getArg(t,"sources"),o=i.getArg(t,"names",[]),a=i.getArg(t,"sourceRoot",null),u=i.getArg(t,"sourcesContent",null),c=i.getArg(t,"mappings"),l=i.getArg(t,"file",null);if(r!=this._version)throw new Error("Unsupported version: "+r);n=n.map(i.normalize),this._names=s.fromArray(o,!0),this._sources=s.fromArray(n,!0),this.sourceRoot=a,this.sourcesContent=u,this._mappings=c,this.file=l}var i=r(1),a=r(2),s=r(3).ArraySet,u=r(4);o.fromSourceMap=function(e){var t=Object.create(o.prototype);return t._names=s.fromArray(e._names.toArray(),!0),t._sources=s.fromArray(e._sources.toArray(),!0),t.sourceRoot=e._sourceRoot,t.sourcesContent=e._generateSourcesContent(t._sources.toArray(),t.sourceRoot),t.file=e._file,t.__generatedMappings=e._mappings.toArray().slice(),t.__originalMappings=e._mappings.toArray().slice().sort(i.compareByOriginalPositions),t},o.prototype._version=3,Object.defineProperty(o.prototype,"sources",{get:function(){return this._sources.toArray().map(function(e){return null!=this.sourceRoot?i.join(this.sourceRoot,e):e},this)}}),o.prototype.__generatedMappings=null,Object.defineProperty(o.prototype,"_generatedMappings",{get:function(){return this.__generatedMappings||(this.__generatedMappings=[],this.__originalMappings=[],this._parseMappings(this._mappings,this.sourceRoot)),this.__generatedMappings}}),o.prototype.__originalMappings=null,Object.defineProperty(o.prototype,"_originalMappings",{get:function(){return this.__originalMappings||(this.__generatedMappings=[],this.__originalMappings=[],this._parseMappings(this._mappings,this.sourceRoot)),this.__originalMappings}}),o.prototype._nextCharIsMappingSeparator=function(e){var t=e.charAt(0);return";"===t||","===t},o.prototype._parseMappings=function(e,t){for(var r,n=1,o=0,a=0,s=0,c=0,l=0,f=e,p={};f.length>0;)if(";"===f.charAt(0))n++,f=f.slice(1),o=0;else if(","===f.charAt(0))f=f.slice(1);else{if(r={},r.generatedLine=n,u.decode(f,p),r.generatedColumn=o+p.value,o=r.generatedColumn,f=p.rest,f.length>0&&!this._nextCharIsMappingSeparator(f)){if(u.decode(f,p),r.source=this._sources.at(c+p.value),c+=p.value,f=p.rest,0===f.length||this._nextCharIsMappingSeparator(f))throw new Error("Found a source, but no line and column");if(u.decode(f,p),r.originalLine=a+p.value,a=r.originalLine,r.originalLine+=1,f=p.rest,0===f.length||this._nextCharIsMappingSeparator(f))throw new Error("Found a source and line, but no column");u.decode(f,p),r.originalColumn=s+p.value,s=r.originalColumn,f=p.rest,f.length>0&&!this._nextCharIsMappingSeparator(f)&&(u.decode(f,p),r.name=this._names.at(l+p.value),l+=p.value,f=p.rest)}this.__generatedMappings.push(r),"number"==typeof r.originalLine&&this.__originalMappings.push(r)}this.__generatedMappings.sort(i.compareByGeneratedPositions),this.__originalMappings.sort(i.compareByOriginalPositions)},o.prototype._findMapping=function(e,t,r,n,o){if(e[r]<=0)throw new TypeError("Line must be greater than or equal to 1, got "+e[r]);if(e[n]<0)throw new TypeError("Column must be greater than or equal to 0, got "+e[n]);return a.search(e,t,o)},o.prototype.computeColumnSpans=function(){for(var e=0;e=0){var n=this._generatedMappings[r];if(n.generatedLine===t.generatedLine){var o=i.getArg(n,"source",null);return null!=o&&null!=this.sourceRoot&&(o=i.join(this.sourceRoot,o)),{source:o,line:i.getArg(n,"originalLine",null),column:i.getArg(n,"originalColumn",null),name:i.getArg(n,"name",null)}}}return{source:null,line:null,column:null,name:null}},o.prototype.sourceContentFor=function(e){if(!this.sourcesContent)return null;if(null!=this.sourceRoot&&(e=i.relative(this.sourceRoot,e)),this._sources.has(e))return this.sourcesContent[this._sources.indexOf(e)];var t;if(null!=this.sourceRoot&&(t=i.urlParse(this.sourceRoot))){var r=e.replace(/^file:\/\//,"");if("file"==t.scheme&&this._sources.has(r))return this.sourcesContent[this._sources.indexOf(r)];if((!t.path||"/"==t.path)&&this._sources.has("/"+e))return this.sourcesContent[this._sources.indexOf("/"+e)]}throw new Error('"'+e+'" is not in the SourceMap.')},o.prototype.generatedPositionFor=function(e){var t={source:i.getArg(e,"source"),originalLine:i.getArg(e,"line"),originalColumn:i.getArg(e,"column")};null!=this.sourceRoot&&(t.source=i.relative(this.sourceRoot,t.source));var r=this._findMapping(t,this._originalMappings,"originalLine","originalColumn",i.compareByOriginalPositions);if(r>=0){var n=this._originalMappings[r];return{line:i.getArg(n,"generatedLine",null),column:i.getArg(n,"generatedColumn",null),lastColumn:i.getArg(n,"lastGeneratedColumn",null)}}return{line:null,column:null,lastColumn:null}},o.prototype.allGeneratedPositionsFor=function(e){var t={source:i.getArg(e,"source"),originalLine:i.getArg(e,"line"),originalColumn:1/0};null!=this.sourceRoot&&(t.source=i.relative(this.sourceRoot,t.source));var r=[],n=this._findMapping(t,this._originalMappings,"originalLine","originalColumn",i.compareByOriginalPositions);if(n>=0)for(var o=this._originalMappings[n];o&&o.originalLine===t.originalLine;)r.push({line:i.getArg(o,"generatedLine",null),column:i.getArg(o,"generatedColumn",null),lastColumn:i.getArg(o,"lastGeneratedColumn",null)}),o=this._originalMappings[--n];return r.reverse()},o.GENERATED_ORDER=1,o.ORIGINAL_ORDER=2,o.prototype.eachMapping=function(e,t,r){var n,a=t||null,s=r||o.GENERATED_ORDER;switch(s){case o.GENERATED_ORDER:n=this._generatedMappings;break;case o.ORIGINAL_ORDER:n=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;n.map(function(e){var t=e.source;return null!=t&&null!=u&&(t=i.join(u,t)),{source:t,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name}}).forEach(e,a)},t.SourceMapConsumer=o}.call(t,r,t,e),!(void 0!==n&&(e.exports=n))},function(e,t,r){var n;n=function(e,t,r){function n(e,t,r){if(t in e)return e[t];if(3===arguments.length)return r;throw new Error('"'+t+'" is a required argument.')}function o(e){var t=e.match(g);return t?{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}:null}function i(e){var t="";return e.scheme&&(t+=e.scheme+":"),t+="//",e.auth&&(t+=e.auth+"@"),e.host&&(t+=e.host),e.port&&(t+=":"+e.port),e.path&&(t+=e.path),t}function a(e){var t=e,r=o(e);if(r){if(!r.path)return e;t=r.path}for(var n,a="/"===t.charAt(0),s=t.split(/\/+/),u=0,c=s.length-1;c>=0;c--)n=s[c],"."===n?s.splice(c,1):".."===n?u++:u>0&&(""===n?(s.splice(c+1,u),u=0):(s.splice(c,2),u--));return t=s.join("/"),""===t&&(t=a?"/":"."),r?(r.path=t,i(r)):t}function s(e,t){""===e&&(e="."),""===t&&(t=".");var r=o(t),n=o(e);if(n&&(e=n.path||"/"),r&&!r.scheme)return n&&(r.scheme=n.scheme),i(r);if(r||t.match(d))return t;if(n&&!n.host&&!n.path)return n.host=t,i(n);var s="/"===t.charAt(0)?t:a(e.replace(/\/+$/,"")+"/"+t);return n?(n.path=s,i(n)):s}function u(e,t){""===e&&(e="."),e=e.replace(/\/$/,"");var r=o(e);return"/"==t.charAt(0)&&r&&"/"==r.path?t.slice(1):0===t.indexOf(e+"/")?t.substr(e.length+1):t}function c(e){return"$"+e}function l(e){return e.substr(1)}function f(e,t){var r=e||"",n=t||"";return(r>n)-(n>r)}function p(e,t,r){var n;return(n=f(e.source,t.source))?n:(n=e.originalLine-t.originalLine)?n:(n=e.originalColumn-t.originalColumn,n||r?n:(n=f(e.name,t.name))?n:(n=e.generatedLine-t.generatedLine,n?n:e.generatedColumn-t.generatedColumn))}function h(e,t,r){var n;return(n=e.generatedLine-t.generatedLine)?n:(n=e.generatedColumn-t.generatedColumn,n||r?n:(n=f(e.source,t.source))?n:(n=e.originalLine-t.originalLine)?n:(n=e.originalColumn-t.originalColumn,n?n:f(e.name,t.name)))}t.getArg=n;var g=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/,d=/^data:.+\,.+$/;t.urlParse=o,t.urlGenerate=i,t.normalize=a,t.join=s,t.relative=u,t.toSetString=c,t.fromSetString=l,t.compareByOriginalPositions=p,t.compareByGeneratedPositions=h}.call(t,r,t,e),!(void 0!==n&&(e.exports=n))},function(e,t,r){var n;n=function(e,t,r){function n(e,t,r,o,i){var a=Math.floor((t-e)/2)+e,s=i(r,o[a],!0);return 0===s?a:s>0?t-a>1?n(a,t,r,o,i):a:a-e>1?n(e,a,r,o,i):0>e?-1:e}t.search=function(e,t,r){return 0===t.length?-1:n(-1,t.length,e,t,r)}}.call(t,r,t,e),!(void 0!==n&&(e.exports=n))},function(e,t,r){var n;n=function(e,t,n){function o(){this._array=[],this._set={}}var i=r(1);o.fromArray=function(e,t){for(var r=new o,n=0,i=e.length;i>n;n++)r.add(e[n],t);return r},o.prototype.add=function(e,t){var r=this.has(e),n=this._array.length;(!r||t)&&this._array.push(e),r||(this._set[i.toSetString(e)]=n)},o.prototype.has=function(e){return Object.prototype.hasOwnProperty.call(this._set,i.toSetString(e))},o.prototype.indexOf=function(e){if(this.has(e))return this._set[i.toSetString(e)];throw new Error('"'+e+'" is not in the set.')},o.prototype.at=function(e){if(e>=0&&ee?(-e<<1)+1:(e<<1)+0}function i(e){var t=1===(1&e),r=e>>1;return t?-r:r}var a=r(5),s=5,u=1<>>=s,n>0&&(t|=l),r+=a.encode(t);while(n>0);return r},t.decode=function(e,t){var r,n,o=0,u=e.length,f=0,p=0;do{if(o>=u)throw new Error("Expected more digits in base 64 VLQ value.");n=a.decode(e.charAt(o++)),r=!!(n&l),n&=c,f+=n<=200&&o.status<400)return t(o.responseText);n(new Error("Unable to retrieve "+e))}},o.send()}function o(e,t,r){for(var n,o,i,a=/function\s+([^(]*?)\s*\(([^)]*)\)/,s=/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/,u=/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/,c=e.split("\n"),l="",f=Math.min(t,20),p=0;f>p;++p)if(n=c[t-p-1],i=n.indexOf("//"),i>=0&&(n=n.substr(0,i)),n){if(l=n+l,o=s.exec(l),o&&o[1])return o[1];if(o=a.exec(l),o&&o[1])return o[1];if(o=u.exec(l),o&&o[1])return o[1]}return void 0}function i(){if("function"!=typeof Object.defineProperty||"function"!=typeof Object.create)throw new Error("Unable to consume source maps in older browsers")}function a(e){if("object"!=typeof e)throw new TypeError("Given StackFrame is not an object");if("string"!=typeof e.fileName)throw new TypeError("Given file name is not a String");if("number"!=typeof e.lineNumber||e.lineNumber%1!==0||e.lineNumber<1)throw new TypeError("Given line number must be a positive integer");if("number"!=typeof e.columnNumber||e.columnNumber%1!==0||e.columnNumber<0)throw new TypeError("Given column number must be a non-negative integer");return!0}function s(e){var t=/\/\/[#@] ?sourceMappingURL=([^\s'"]+)$/.exec(e);if(t&&t[1])return t[1];throw new Error("sourceMappingURL not found")}function u(r,n,o,i){var a=new e.SourceMapConsumer(r).originalPositionFor({line:o,column:i});return new t(a.name,n,a.source,a.line,a.column)}return function c(e){return this instanceof c?(e=e||{},this.sourceCache=e.sourceCache||{},this.ajax=n,this._atob=function(e){if(window&&window.atob)return window.atob(e);if("undefined"!=typeof Buffer)return new Buffer(e,"base64").toString("utf-8");throw new Error("No base64 decoder available")},this._get=function(t){return new Promise(function(r,n){var o="data:"===t.substr(0,5);if(this.sourceCache[t])r(this.sourceCache[t]);else if(e.offline&&!o)n(new Error("Cannot make network requests in offline mode"));else if(o){var i="application/json;base64";if(t.substr(5,i.length)!==i)n(new Error("The encoding of the inline sourcemap is not supported"));else{var a="data:".length+i.length+",".length,s=t.substr(a),u=this._atob(s);this.sourceCache[t]=u,r(u)}}else this.ajax(t,function(e){this.sourceCache[t]=e,r(e)}.bind(this),n)}.bind(this))},this.pinpoint=function(e){return new Promise(function(t,r){this.getMappedLocation(e).then(function(e){function r(){t(e)}this.findFunctionName(e).then(t,r)["catch"](r)}.bind(this),r)}.bind(this))},this.findFunctionName=function(e){return new Promise(function(r,n){a(e),this._get(e.fileName).then(function(n){var i=o(n,e.lineNumber,e.columnNumber);r(new t(i,e.args,e.fileName,e.lineNumber,e.columnNumber))},n)}.bind(this))},void(this.getMappedLocation=function(e){return new Promise(function(t,r){i(),a(e);var n=e.fileName;this._get(n).then(function(o){var i=s(o);"/"!==i[0]&&(i=n.substring(0,n.lastIndexOf("/")+1)+i),this._get(i).then(function(r){var n=e.lineNumber,o=e.columnNumber;t(u(r,e.args,n,o))},r)["catch"](r)}.bind(this),r)["catch"](r)}.bind(this))})):new c(e)}}),function(e,t){"use strict";"function"==typeof define&&define.amd?define("stack-generator",["stackframe"],t):"object"==typeof exports?module.exports=t(require("stackframe")):e.StackGenerator=t(e.StackFrame)}(this,function(e){return{backtrace:function(t){var r=[],n=10;"object"==typeof t&&"number"==typeof t.maxStackSize&&(n=t.maxStackSize);for(var o=arguments.callee;o&&r.length-1&&e.message.split("\n").length>e.stacktrace.split("\n").length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(t){for(var r=/Line (\d+).*script (?:in )?(\S+)/i,n=t.message.split("\n"),o=[],i=2,a=n.length;a>i;i+=2){var s=r.exec(n[i]);s&&o.push(new e(void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera10:function(t){for(var r=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,n=t.stacktrace.split("\n"),o=[],i=0,a=n.length;a>i;i+=2){var s=r.exec(n[i]);s&&o.push(new e(s[3]||void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera11:function(r){return r.stack.split("\n").filter(function(e){return!!e.match(t)&&!e.match(/^Error created at/)},this).map(function(t){var r,n=t.split("@"),o=this.extractLocation(n.pop()),i=n.shift()||"",a=i.replace(//,"$2").replace(/\([^\)]*\)/g,"")||void 0;i.match(/\(([^\)]*)\)/)&&(r=i.replace(/^[^\(]+\(([^\)]*)\)$/,"$1"));var s=void 0===r||"[arguments not available]"===r?void 0:r.split(",");return new e(a,s,o[0],o[1],o[2],t)},this)}}}),function(e,t){"use strict";"function"==typeof define&&define.amd?define("stacktrace",["error-stack-parser","stack-generator","stacktrace-gps"],t):"object"==typeof exports?module.exports=t(require("error-stack-parser"),require("stack-generator"),require("stacktrace-gps")):e.StackTrace=t(e.ErrorStackParser,e.StackGenerator,e.StackTraceGPS)}(this,function(e,t,r){function n(e,t){var r={};return[e,t].forEach(function(e){for(var t in e)e.hasOwnProperty(t)&&(r[t]=e[t]);return r}),r}function o(e){return e.stack||e["opera#sourceloc"]}var i={filter:function(e){return-1===(e.functionName||"").indexOf("StackTrace$$")&&-1===(e.functionName||"").indexOf("ErrorStackParser$$")&&-1===(e.functionName||"").indexOf("StackTraceGPS$$")&&-1===(e.functionName||"").indexOf("StackGenerator$$")}};return{get:function(e){try{throw new Error}catch(t){return o(t)?this.fromError(t,e):this.generateArtificially(e)}},fromError:function(t,o){return o=n(i,o),new Promise(function(n){var i=e.parse(t);"function"==typeof o.filter&&(i=i.filter(o.filter)),n(Promise.all(i.map(function(e){return new Promise(function(t){function n(r){t(e)}new r(o).pinpoint(e).then(t,n)["catch"](n)})})))}.bind(this))},generateArtificially:function(e){e=n(i,e);var r=t.backtrace(e);return"function"==typeof e.filter&&(r=r.filter(e.filter)),Promise.resolve(r)},instrument:function(e,t,r,n){if("function"!=typeof e)throw new Error("Cannot instrument non-function object");if("function"==typeof e.__stacktraceOriginalFn)return e;var i=function(){try{this.get().then(t,r)["catch"](r),e.apply(n||this,arguments)}catch(i){throw o(i)&&this.fromError(i).then(t,r)["catch"](r),i}}.bind(this);return i.__stacktraceOriginalFn=e,i},deinstrument:function(e){if("function"!=typeof e)throw new Error("Cannot de-instrument non-function object");return"function"==typeof e.__stacktraceOriginalFn?e.__stacktraceOriginalFn:e},report:function(e,t){return new Promise(function(r,n){var o=new XMLHttpRequest;o.onerror=n,o.onreadystatechange=function(){4===o.readyState&&(o.status>=200&&o.status<400?r(o.responseText):n(new Error("POST to "+t+" failed with status: "+o.status)))},o.open("post",t),o.setRequestHeader("Content-Type","application/json"),o.send(JSON.stringify({stack:e}))})}}}); -//# sourceMappingURL=vendor/stacktrace-js/dist/stacktrace-with-polyfills.min.js.map /** * @license * Fuse - Lightweight fuzzy-search @@ -31059,7 +31056,7 @@ function GetPdfMake(invoice, javascript, callback) { } // determine whether or not to show the header/footer - if (invoice.is_pro) { + if (invoice.features.customize_invoice_design) { if (key === 'header') { return function(page, pages) { return page === 1 || invoice.account.all_pages_header == '1' ? val : ''; @@ -31090,7 +31087,7 @@ function GetPdfMake(invoice, javascript, callback) { // Add ninja logo to the footer var dd = JSON.parse(javascript, jsonCallBack); var designId = invoice.invoice_design_id; - if (!invoice.is_pro) { + if (!invoice.features.remove_created_by) { if (designId == NINJA.TEMPLATES.CLEAN || designId == NINJA.TEMPLATES.NORMAL) { dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, 0, 0, 0]}) } else if (designId == NINJA.TEMPLATES.BOLD) { @@ -31273,10 +31270,10 @@ NINJA.invoiceColumns = function(invoice) columns.push("*") - if (invoice.is_pro && account.custom_invoice_item_label1) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { columns.push("10%"); } - if (invoice.is_pro && account.custom_invoice_item_label2) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label2) { columns.push("10%"); } @@ -31296,7 +31293,7 @@ NINJA.invoiceColumns = function(invoice) NINJA.invoiceFooter = function(invoice) { - if (!invoice.is_pro && invoice.invoice_design_id == 3) { + if (!invoice.features.invoice_settings && invoice.invoice_design_id == 3) { return invoice.invoice_footer ? invoice.invoice_footer.substring(0, 200) : ' '; } else { return invoice.invoice_footer || ' '; @@ -31328,10 +31325,10 @@ NINJA.invoiceLines = function(invoice) { grid[0].push({text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']}); - if (invoice.is_pro && account.custom_invoice_item_label1) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { grid[0].push({text: account.custom_invoice_item_label1, style: ['tableHeader', 'custom1TableHeader']}); } - if (invoice.is_pro && account.custom_invoice_item_label2) { + if (invoice.features.invoice_ettings && account.custom_invoice_item_label2) { grid[0].push({text: account.custom_invoice_item_label2, style: ['tableHeader', 'custom2TableHeader']}); } @@ -31388,10 +31385,10 @@ NINJA.invoiceLines = function(invoice) { row.push({style:["productKey", rowStyle], text:productKey || ' '}); // product key can be blank when selecting from a datalist } row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]}); - if (invoice.is_pro && account.custom_invoice_item_label1) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { row.push({style:["customValue1", rowStyle], text:item.custom_value1 || ' '}); } - if (invoice.is_pro && account.custom_invoice_item_label2) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label2) { row.push({style:["customValue2", rowStyle], text:item.custom_value2 || ' '}); } row.push({style:["cost", rowStyle], text:cost}); @@ -31558,7 +31555,7 @@ NINJA.accountAddress = function(invoice) { {text: account.country ? account.country.name : ''}, ]; - if (invoice.is_pro) { + if (invoice.features.invoice_settings) { data.push({text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false}); data.push({text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false}); } diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index afabd47f8875..153b81019eda 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -55,7 +55,7 @@ function GetPdfMake(invoice, javascript, callback) { } // determine whether or not to show the header/footer - if (invoice.is_pro) { + if (invoice.features.customize_invoice_design) { if (key === 'header') { return function(page, pages) { return page === 1 || invoice.account.all_pages_header == '1' ? val : ''; @@ -86,7 +86,7 @@ function GetPdfMake(invoice, javascript, callback) { // Add ninja logo to the footer var dd = JSON.parse(javascript, jsonCallBack); var designId = invoice.invoice_design_id; - if (!invoice.is_pro) { + if (!invoice.features.remove_created_by) { if (designId == NINJA.TEMPLATES.CLEAN || designId == NINJA.TEMPLATES.NORMAL) { dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, 0, 0, 0]}) } else if (designId == NINJA.TEMPLATES.BOLD) { @@ -269,10 +269,10 @@ NINJA.invoiceColumns = function(invoice) columns.push("*") - if (invoice.is_pro && account.custom_invoice_item_label1) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { columns.push("10%"); } - if (invoice.is_pro && account.custom_invoice_item_label2) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label2) { columns.push("10%"); } @@ -292,7 +292,7 @@ NINJA.invoiceColumns = function(invoice) NINJA.invoiceFooter = function(invoice) { - if (!invoice.is_pro && invoice.invoice_design_id == 3) { + if (!invoice.features.invoice_settings && invoice.invoice_design_id == 3) { return invoice.invoice_footer ? invoice.invoice_footer.substring(0, 200) : ' '; } else { return invoice.invoice_footer || ' '; @@ -324,10 +324,10 @@ NINJA.invoiceLines = function(invoice) { grid[0].push({text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']}); - if (invoice.is_pro && account.custom_invoice_item_label1) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { grid[0].push({text: account.custom_invoice_item_label1, style: ['tableHeader', 'custom1TableHeader']}); } - if (invoice.is_pro && account.custom_invoice_item_label2) { + if (invoice.features.invoice_ettings && account.custom_invoice_item_label2) { grid[0].push({text: account.custom_invoice_item_label2, style: ['tableHeader', 'custom2TableHeader']}); } @@ -384,10 +384,10 @@ NINJA.invoiceLines = function(invoice) { row.push({style:["productKey", rowStyle], text:productKey || ' '}); // product key can be blank when selecting from a datalist } row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]}); - if (invoice.is_pro && account.custom_invoice_item_label1) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { row.push({style:["customValue1", rowStyle], text:item.custom_value1 || ' '}); } - if (invoice.is_pro && account.custom_invoice_item_label2) { + if (invoice.features.invoice_settings && account.custom_invoice_item_label2) { row.push({style:["customValue2", rowStyle], text:item.custom_value2 || ' '}); } row.push({style:["cost", rowStyle], text:cost}); @@ -554,7 +554,7 @@ NINJA.accountAddress = function(invoice) { {text: account.country ? account.country.name : ''}, ]; - if (invoice.is_pro) { + if (invoice.features.invoice_settings) { data.push({text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false}); data.push({text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false}); } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 7a6323ad7e38..1f0467d1ce46 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1142,10 +1142,12 @@ $LANG = array( 'renews' => 'Renews', 'plan_expired' => ':plan Plan Expired', 'trial_expired' => ':plan Plan Trial Ended', - 'never' => 'never', + 'never' => 'Never', 'plan_free' => 'Free', 'plan_pro' => 'Pro', 'plan_enterprise' => 'Enterprise', + 'plan_white_label' => 'Self Hosted (White labeled)', + 'plan_free_self_hosted' => 'Self Hosted (Free)', 'plan_trial' => 'Trial', 'plan_term' => 'Term', 'plan_term_monthly' => 'Monthly', @@ -1155,6 +1157,11 @@ $LANG = array( 'plan_price_monthly' => '$:price/Month', 'plan_price_yearly' => '$:price/Year', 'updated_plan' => 'Updated plan settings', + 'plan_paid' => 'Term Started', + 'plan_started' => 'Plan Started', + 'plan_expires' => 'Plan Expires', + + 'white_label_button' => 'White Label', 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', diff --git a/resources/views/accounts/api_tokens.blade.php b/resources/views/accounts/api_tokens.blade.php index 0b338c80268a..9c328e6b2643 100644 --- a/resources/views/accounts/api_tokens.blade.php +++ b/resources/views/accounts/api_tokens.blade.php @@ -9,7 +9,7 @@ @if (Utils::isNinja()) {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} @endif - @if (Utils::isPro()) + @if (Utils::hasFeature(FEATURE_API)) {!! Button::primary(trans('texts.add_token'))->asLinkTo(URL::to('/tokens/create'))->appendIcon(Icon::create('plus-sign')) !!} @endif diff --git a/resources/views/accounts/client_portal.blade.php b/resources/views/accounts/client_portal.blade.php index 8c4171b0f7a2..710858ae9817 100644 --- a/resources/views/accounts/client_portal.blade.php +++ b/resources/views/accounts/client_portal.blade.php @@ -18,7 +18,7 @@ {!! Former::populateField('enable_portal_password', intval($enable_portal_password)) !!} {!! Former::populateField('send_portal_password', intval($send_portal_password)) !!} -@if (!Utils::isNinja() && !Auth::user()->account->isWhiteLabel()) +@if (!Utils::isNinja() && !Auth::user()->account->hasFeature(FEATURE_WHITE_LABEL))
      {!! trans('texts.white_label_custom_css', ['link'=>''.trans('texts.white_label_purchase_link').'']) !!} @@ -59,6 +59,7 @@
      + @if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_CSS))

      {!! trans('texts.custom_css') !!}

      @@ -74,6 +75,7 @@ ->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!}
      + @endif diff --git a/resources/views/accounts/customize_design.blade.php b/resources/views/accounts/customize_design.blade.php index 439ef1d4acef..9c9fff51121c 100644 --- a/resources/views/accounts/customize_design.blade.php +++ b/resources/views/accounts/customize_design.blade.php @@ -45,7 +45,11 @@ var customDesign = origCustomDesign = {!! $customDesign ?: 'JSON.parse(invoiceDesigns[0].javascript);' !!}; function getPDFString(cb, force) { - invoice.is_pro = {!! Auth::user()->isPro() ? 'true' : 'false' !!}; + invoice.features = { + customize_invoice_design:{{ Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }}, + remove_created_by:{{ Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }}, + invoice_settings:{{ Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }} + }; invoice.account.hide_quantity = {!! Auth::user()->account->hide_quantity ? 'true' : 'false' !!}; invoice.account.hide_paid_to_date = {!! Auth::user()->account->hide_paid_to_date ? 'true' : 'false' !!}; invoice.invoice_design_id = {!! Auth::user()->account->invoice_design_id !!}; @@ -194,7 +198,7 @@ @endif diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php index 4af8e7a58f20..0e434b3d07d4 100644 --- a/resources/views/clients/show.blade.php +++ b/resources/views/clients/show.blade.php @@ -230,7 +230,7 @@ @endif - @if (Utils::isPro() && $hasQuotes) + @if (Utils::hasFeature(FEATURE_QUOTES) && $hasQuotes)
      {!! Datatable::table() diff --git a/resources/views/expenses/edit.blade.php b/resources/views/expenses/edit.blade.php index 2b999114f5ac..49e3b9e379d3 100644 --- a/resources/views/expenses/edit.blade.php +++ b/resources/views/expenses/edit.blade.php @@ -362,7 +362,7 @@ } } - @if (Auth::user()->account->isPro()) + @if (Auth::user()->account->hasFeature(FEATURE_DOCUMENTS)) function handleDocumentAdded(file){ if(file.mock)return; file.index = model.documents().length; diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index 9a0185ea24ba..50999b8cc1d1 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -431,7 +431,7 @@
    @@ -330,7 +330,7 @@ ') !!} - @if ($account->isPro()) + @if ($account->hasFeature(FEATURE_DOCUMENTS))
    @@ -499,7 +499,7 @@ @endif - @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) + @if (!Utils::hasFeature(FEATURE_MORE_INVOICE_DESIGNS) || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!} @else {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!} @@ -571,7 +571,7 @@ - @if (Auth::user()->isPro()) + @if (Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS)) @if ($account->custom_client_label1) {!! Former::text('client[custom_value1]') ->label($account->custom_client_label1) @@ -626,7 +626,7 @@ ->addClass('client-email') !!} {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', attr: {name: 'client[contacts][' + \$index() + '][phone]'}") !!} - @if ($account->isPro() && $account->enable_portal_password) + @if ($account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password) {!! Former::password('password')->data_bind("value: (typeof password=='function'?password():null)?'-%unchanged%-':'', valueUpdate: 'afterkeydown', attr: {name: 'client[contacts][' + \$index() + '][password]'}") !!} @endif @@ -966,7 +966,7 @@ applyComboboxListeners(); - @if (Auth::user()->account->isPro()) + @if (Auth::user()->account->hasFeature(FEATURE_DOCUMENTS)) $('.main-form').submit(function(){ if($('#document-upload .dropzone .fallback input').val())$(this).attr('enctype', 'multipart/form-data') else $(this).removeAttr('enctype') @@ -1062,7 +1062,11 @@ var model = ko.toJS(window.model); if(!model)return; var invoice = model.invoice; - invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }}; + invoice.features = { + customize_invoice_design:{{ Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }}, + remove_created_by:{{ Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }}, + invoice_settings:{{ Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }} + }; invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }}; invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true}); @@ -1375,7 +1379,7 @@ model.invoice().invoice_number(number); } - @if ($account->isPro()) + @if ($account->hasFeature(FEATURE_DOCUMENTS)) function handleDocumentAdded(file){ if(file.mock)return; file.index = model.invoice().documents().length; @@ -1399,7 +1403,7 @@ @endif - @if ($account->isPro() && $account->invoice_embed_documents) + @if ($account->hasFeature(FEATURE_DOCUMENTS) && $account->invoice_embed_documents) @foreach ($invoice->documents as $document) @if($document->isPDFEmbeddable()) diff --git a/resources/views/invoices/history.blade.php b/resources/views/invoices/history.blade.php index baa5c925aadb..18fedecc6b27 100644 --- a/resources/views/invoices/history.blade.php +++ b/resources/views/invoices/history.blade.php @@ -57,7 +57,7 @@ @include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800]) - @if (Utils::isPro() && $invoice->account->invoice_embed_documents) + @if (Utils::hasFeature(FEATURE_DOCUMENTS) && $invoice->account->invoice_embed_documents) @foreach ($invoice->documents as $document) @if($document->isPDFEmbeddable()) diff --git a/resources/views/invoices/pdf.blade.php b/resources/views/invoices/pdf.blade.php index 1c8389affeba..81349390ad39 100644 --- a/resources/views/invoices/pdf.blade.php +++ b/resources/views/invoices/pdf.blade.php @@ -84,7 +84,7 @@ @endif var NINJA = NINJA || {}; - @if ($account->isPro()) + @if ($account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) NINJA.primaryColor = "{{ $account->primary_color }}"; NINJA.secondaryColor = "{{ $account->secondary_color }}"; NINJA.fontSize = {{ $account->font_size }}; diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index 08f5cd9827cd..730094e659af 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -65,7 +65,7 @@
    @endif - @if ($account->isPro() && $account->invoice_embed_documents) + @if ($account->hasFeature(FEATURE_DOCUMENTS) && $account->invoice_embed_documents) @foreach ($invoice->documents as $document) @if($document->isPDFEmbeddable()) @@ -82,7 +82,11 @@ @endif From 9f5fe33059ecaff9ed52482ab04a7ac6f8fdbe6e Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 19 Apr 2016 10:51:57 +0300 Subject: [PATCH 109/258] Added page size and live preview settings --- app/Http/Controllers/AccountController.php | 59 +++++++++++++-- app/Models/Account.php | 11 --- app/Models/Invoice.php | 3 +- .../2016_04_18_174135_add_page_size.php | 72 +++++++++++++++++++ database/seeds/FontsSeeder.php | 1 - public/built.js | 3 +- public/js/pdf.pdfmake.js | 3 +- resources/lang/en/texts.php | 10 +-- .../views/accounts/invoice_design.blade.php | 30 +++++--- resources/views/invoices/edit.blade.php | 10 +-- 10 files changed, 162 insertions(+), 40 deletions(-) create mode 100644 database/migrations/2016_04_18_174135_add_page_size.php diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 6fe75a63363c..407db691b75e 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -389,6 +389,58 @@ class AccountController extends BaseController $data['invoiceDesigns'] = InvoiceDesign::getDesigns(); $data['invoiceFonts'] = Cache::get('fonts'); $data['section'] = $section; + + $pageSizes = [ + 'A0', + 'A1', + 'A2', + 'A3', + 'A4', + 'A5', + 'A6', + 'A7', + 'A8', + 'A9', + 'A10', + 'B0', + 'B1', + 'B2', + 'B3', + 'B4', + 'B5', + 'B6', + 'B7', + 'B8', + 'B9', + 'B10', + 'C0', + 'C1', + 'C2', + 'C3', + 'C4', + 'C5', + 'C6', + 'C7', + 'C8', + 'C9', + 'C10', + 'RA0', + 'RA1', + 'RA2', + 'RA3', + 'RA4', + 'SRA0', + 'SRA1', + 'SRA2', + 'SRA3', + 'SRA4', + 'Executive', + 'Folio', + 'Legal', + 'Letter', + 'Tabloid', + ]; + $data['pageSizes'] = array_combine($pageSizes, $pageSizes); $design = false; foreach ($data['invoiceDesigns'] as $item) { @@ -764,10 +816,9 @@ class AccountController extends BaseController $account->primary_color = Input::get('primary_color'); $account->secondary_color = Input::get('secondary_color'); $account->invoice_design_id = Input::get('invoice_design_id'); - - if (Input::has('font_size')) { - $account->font_size = intval(Input::get('font_size')); - } + $account->font_size = intval(Input::get('font_size')); + $account->page_size = Input::get('page_size'); + $account->live_preview = Input::get('live_preview') ? true : false; $labels = []; foreach (['item', 'description', 'unit_cost', 'quantity', 'line_total', 'terms', 'balance_due', 'partial_due'] as $field) { diff --git a/app/Models/Account.php b/app/Models/Account.php index f50ed2cec1fa..351901d3fa6c 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -1110,17 +1110,6 @@ class Account extends Eloquent return $css; } - public function hasLargeFont() - { - foreach (['chinese', 'japanese'] as $language) { - if (stripos($this->getBodyFontName(), $language) || stripos($this->getHeaderFontName(), $language)) { - return true; - } - } - - return false; - } - public function getFontsUrl($protocol = ''){ $bodyFont = $this->getHeaderFontId(); $headerFont = $this->getBodyFontId(); diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 4d9b82c79416..c40f22d34ad3 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -474,7 +474,8 @@ class Invoice extends EntityModel implements BalanceAffecting 'custom_invoice_text_label2', 'custom_invoice_item_label1', 'custom_invoice_item_label2', - 'invoice_embed_documents' + 'invoice_embed_documents', + 'page_size', ]); foreach ($this->invoice_items as $invoiceItem) { diff --git a/database/migrations/2016_04_18_174135_add_page_size.php b/database/migrations/2016_04_18_174135_add_page_size.php new file mode 100644 index 000000000000..d5bbb86f64bc --- /dev/null +++ b/database/migrations/2016_04_18_174135_add_page_size.php @@ -0,0 +1,72 @@ +string('page_size')->default('A4'); + $table->boolean('live_preview')->default(true); + $table->smallInteger('invoice_number_padding')->default(4); + }); + + Schema::table('fonts', function ($table) { + $table->dropColumn('is_early_access'); + }); + + Schema::create('expense_categories', function($table) + { + $table->increments('id'); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('account_id')->index(); + $table->timestamps(); + $table->softDeletes(); + + $table->string('name')->nullable(); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + $table->unsignedInteger('public_id')->index(); + $table->unique( array('account_id','public_id') ); + }); + + Schema::table('expenses', function ($table) { + $table->unsignedInteger('expense_category_id')->nullable()->index(); + + $table->foreign('expense_category_id')->references('id')->on('expense_categories')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accounts', function ($table) { + $table->dropColumn('page_size'); + $table->dropColumn('live_preview'); + $table->dropColumn('invoice_number_padding'); + }); + + Schema::table('fonts', function ($table) { + $table->boolean('is_early_access'); + }); + + Schema::dropIfExists('expense_categories'); + + Schema::table('expenses', function ($table) { + $table->dropColumn('expense_category_id'); + }); + } +} diff --git a/database/seeds/FontsSeeder.php b/database/seeds/FontsSeeder.php index 6f27ffa0be02..de67f441ced5 100644 --- a/database/seeds/FontsSeeder.php +++ b/database/seeds/FontsSeeder.php @@ -246,7 +246,6 @@ class FontsSeeder extends Seeder foreach ($fonts as $font) { if (!DB::table('fonts')->where('name', '=', $font['name'])->get()) { - $font['is_early_access'] = false; Font::create($font); } } diff --git a/public/built.js b/public/built.js index 327b7e5fb2ae..2547d7804ce2 100644 --- a/public/built.js +++ b/public/built.js @@ -31100,7 +31100,8 @@ function GetPdfMake(invoice, javascript, callback) { } } - + // set page size + dd.pageSize = invoice.account.page_size; pdfMake.fonts = {} fonts = window.invoiceFonts || invoice.invoice_fonts; diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index afabd47f8875..09d01f7470e2 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -96,7 +96,8 @@ function GetPdfMake(invoice, javascript, callback) { } } - + // set page size + dd.pageSize = invoice.account.page_size; pdfMake.fonts = {} fonts = window.invoiceFonts || invoice.invoice_fonts; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 799cf6cbaab2..748a2eb3fb0a 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -976,9 +976,9 @@ $LANG = array( 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', 'trello_roadmap' => 'Trello Roadmap', 'header_footer' => 'Header/Footer', - 'first_page' => 'first page', - 'all_pages' => 'all pages', - 'last_page' => 'last page', + 'first_page' => 'First page', + 'all_pages' => 'All pages', + 'last_page' => 'Last page', 'all_pages_header' => 'Show header on', 'all_pages_footer' => 'Show footer on', 'invoice_currency' => 'Invoice Currency', @@ -1126,7 +1126,9 @@ $LANG = array( 'enable_client_portal_help' => 'Show/hide the client portal.', 'enable_client_portal_dashboard' => 'Dashboard', 'enable_client_portal_dashboard_help' => 'Show/hide the dashboard page in the client portal.', - + + 'live_preview' => 'Live Preview', + 'page_size' => 'Page Size', ); return $LANG; diff --git a/resources/views/accounts/invoice_design.blade.php b/resources/views/accounts/invoice_design.blade.php index 9576afbf1d92..e7d21e685ffc 100644 --- a/resources/views/accounts/invoice_design.blade.php +++ b/resources/views/accounts/invoice_design.blade.php @@ -51,16 +51,14 @@ invoice.account.invoice_embed_documents = $('#invoice_embed_documents').is(":checked"); invoice.account.hide_paid_to_date = $('#hide_paid_to_date').is(":checked"); invoice.invoice_design_id = $('#invoice_design_id').val(); + invoice.account.page_size = $('#page_size option:selected').text(); NINJA.primaryColor = $('#primary_color').val(); NINJA.secondaryColor = $('#secondary_color').val(); NINJA.fontSize = parseInt($('#font_size').val()); - @if (Auth::user()->isPro()) - NINJA.headerFont = $('#header_font_id option:selected').text(); - NINJA.bodyFont = $('#body_font_id option:selected').text(); - @else - NINJA.headerFont = NINJA.bodyFont = 'Roboto'; - @endif + NINJA.headerFont = $('#header_font_id option:selected').text(); + NINJA.bodyFont = $('#body_font_id option:selected').text(); + var fields = [ 'item', 'description', @@ -112,7 +110,16 @@
    {!! Former::open()->addClass('warn-on-exit')->onchange('if(!window.loadingFonts)refreshPDF()') !!} - {!! Former::populate($account) !!} + + {!! Former::populateField('invoice_design_id', $account->invoice_design_id) !!} + {!! Former::populateField('body_font_id', $account->body_font_id) !!} + {!! Former::populateField('header_font_id', $account->header_font_id) !!} + {!! Former::populateField('live_preview', intval($account->live_preview)) !!} + {!! Former::populateField('font_size', $account->font_size) !!} + {!! Former::populateField('page_size', $account->page_size) !!} + {!! Former::populateField('invoice_embed_documents', intval($account->invoice_embed_documents)) !!} + {!! Former::populateField('primary_color', $account->primary_color) !!} + {!! Former::populateField('secondary_color', $account->secondary_color) !!} {!! Former::populateField('hide_quantity', intval($account->hide_quantity)) !!} {!! Former::populateField('hide_paid_to_date', intval($account->hide_paid_to_date)) !!} {!! Former::populateField('all_pages_header', intval($account->all_pages_header)) !!} @@ -156,13 +163,17 @@ {!! Former::select('header_font_id') ->fromQuery($invoiceFonts, 'name', 'id') !!} + {!! Former::checkbox('live_preview')->text(trans('texts.enable')) !!} +
    - {{ Former::setOption('TwitterBootstrap3.labelWidths.large', 6) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.small', 6) }} - + + {!! Former::select('page_size') + ->options($pageSizes) !!} + {!! Former::text('font_size') ->type('number') ->min('0') @@ -171,6 +182,7 @@ {!! Former::text('primary_color') !!} {!! Former::text('secondary_color') !!} + {{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }} diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 5c70a5be1d17..838a5de1746a 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -493,12 +493,6 @@ {!! Former::text('pdfupload') !!}
    - @if ($account->hasLargeFont()) - - @endif - @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!} @else @@ -1097,8 +1091,8 @@ window.generatedPDF = false; function getPDFString(cb, force) { - @if ($account->hasLargeFont()) - if (!$('#livePreview').is(':checked') && window.generatedPDF) { + @if (!$account->live_preview) + if (window.generatedPDF) { return; } @endif From cd52faa3bf0de5d811d59a94223093fe4d5e5184 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 19 Apr 2016 14:12:02 +0300 Subject: [PATCH 110/258] Automatically disable live preview when using a large font --- app/Http/Controllers/AccountController.php | 12 ++++++++++++ resources/lang/en/texts.php | 1 + 2 files changed, 13 insertions(+) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 407db691b75e..074e03d9e38d 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -820,6 +820,18 @@ class AccountController extends BaseController $account->page_size = Input::get('page_size'); $account->live_preview = Input::get('live_preview') ? true : false; + // Automatically disable live preview when using a large font + $fonts = Cache::get('fonts')->filter(function($font) use ($account) { + if ($font->google_font) { + return false; + } + return $font->id == $account->header_font_id || $font->id == $account->body_font_id; + }); + if ($account->live_preview && count($fonts)) { + $account->live_preview = false; + Session::flash('warning', trans('texts.live_preview_disabled')); + } + $labels = []; foreach (['item', 'description', 'unit_cost', 'quantity', 'line_total', 'terms', 'balance_due', 'partial_due'] as $field) { $labels[$field] = Input::get("labels_{$field}"); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 748a2eb3fb0a..94f7734a74cd 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1129,6 +1129,7 @@ $LANG = array( 'live_preview' => 'Live Preview', 'page_size' => 'Page Size', + 'live_preview_disabled' => 'Live preview has been disabled to support selected font', ); return $LANG; From 32d5a25f61db9edbb72dfb3af23c7e3759bd589c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 19 Apr 2016 14:26:42 +0300 Subject: [PATCH 111/258] Enabled specifying invoice padding --- app/Http/Controllers/AccountController.php | 1 + app/Models/Account.php | 4 ++-- resources/lang/en/texts.php | 2 ++ resources/views/accounts/invoice_settings.blade.php | 9 +++++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 074e03d9e38d..099f252ca3b8 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -753,6 +753,7 @@ class AccountController extends BaseController $account->custom_invoice_item_label1 = trim(Input::get('custom_invoice_item_label1')); $account->custom_invoice_item_label2 = trim(Input::get('custom_invoice_item_label2')); + $account->invoice_number_padding = Input::get('invoice_number_padding'); $account->invoice_number_counter = Input::get('invoice_number_counter'); $account->quote_number_prefix = Input::get('quote_number_prefix'); $account->share_counter = Input::get('share_counter') ? true : false; diff --git a/app/Models/Account.php b/app/Models/Account.php index 351901d3fa6c..8d1261d92884 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -551,7 +551,7 @@ class Account extends Eloquent $replace = [date('Y')]; $search[] = '{$counter}'; - $replace[] = str_pad($this->getCounter($invoice->is_quote), 4, '0', STR_PAD_LEFT); + $replace[] = str_pad($this->getCounter($invoice->is_quote), $this->invoice_number_padding, '0', STR_PAD_LEFT); if (strstr($pattern, '{$userId}')) { $search[] = '{$userId}'; @@ -617,7 +617,7 @@ class Account extends Eloquent // confirm the invoice number isn't already taken do { - $number = $prefix . str_pad($counter, 4, '0', STR_PAD_LEFT); + $number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT); $check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first(); $counter++; $counterOffset++; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 94f7734a74cd..88009352202a 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1130,6 +1130,8 @@ $LANG = array( 'live_preview' => 'Live Preview', 'page_size' => 'Page Size', 'live_preview_disabled' => 'Live preview has been disabled to support selected font', + 'invoice_number_padding' => 'Padding', + ); return $LANG; diff --git a/resources/views/accounts/invoice_settings.blade.php b/resources/views/accounts/invoice_settings.blade.php index 09b730737e0d..ed8498143b09 100644 --- a/resources/views/accounts/invoice_settings.blade.php +++ b/resources/views/accounts/invoice_settings.blade.php @@ -59,12 +59,13 @@ {!! Former::text('invoice_number_prefix') ->addGroupClass('invoice-prefix') - ->label(' ') !!} + ->label(trans('texts.prefix')) !!} {!! Former::text('invoice_number_pattern') ->appendIcon('question-sign') ->addGroupClass('invoice-pattern') - ->label(' ') + ->label(trans('texts.pattern')) ->addGroupClass('number-pattern') !!} + {!! Former::text('invoice_number_padding') !!} {!! Former::text('invoice_number_counter') ->label(trans('texts.counter')) ->help(trans('texts.invoice_number_help') . ' ' . @@ -84,12 +85,12 @@ {!! Former::text('quote_number_prefix') ->addGroupClass('quote-prefix') - ->label(' ') !!} + ->label(trans('texts.prefix')) !!} {!! Former::text('quote_number_pattern') ->appendIcon('question-sign') ->addGroupClass('quote-pattern') ->addGroupClass('number-pattern') - ->label(' ') !!} + ->label(trans('texts.pattern')) !!} {!! Former::text('quote_number_counter') ->label(trans('texts.counter')) ->addGroupClass('pad-checkbox') From 639a3c321a22ab5e23855da21210ac25d4ac91a2 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 19 Apr 2016 10:26:29 -0400 Subject: [PATCH 112/258] Restrict documents to non-trial; allow resellers to override features --- app/Models/Account.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index 1d280a3d8aea..efbfd5b20470 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -799,6 +799,14 @@ class Account extends Eloquent $planDetails = $this->getPlanDetails(); $selfHost = !Utils::isNinjaProd(); + if (!$selfHost && function_exists('ninja_account_features')) { + $result = ninja_account_features($this, $feature); + + if ($result != null) { + return $result; + } + } + switch ($feature) { // Pro case FEATURE_CUSTOMIZE_INVOICE_DESIGN: @@ -829,10 +837,6 @@ class Account extends Eloquent case FEATURE_CLIENT_PORTAL_CSS: return !empty($planDetails);// A plan is required even for self-hosted users - // Enterprise - case FEATURE_DOCUMENTS: - return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE; - // Enterprise; No Trial allowed; grandfathered for old pro users case FEATURE_USERS:// Grandfathered for old Pro users if($planDetails && $planDetails['trial']) { @@ -843,6 +847,7 @@ class Account extends Eloquent return $selfHost || !empty($planDetails) && ($planDetails['plan'] == PLAN_ENTERPRISE || $planDetails['started'] <= date_create(PRO_USERS_GRANDFATHER_DEADLINE)); // Enterprise; No Trial allowed + case FEATURE_DOCUMENTS: case FEATURE_USER_PERMISSIONS: return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && !$planDetails['trial']; From 0fd39e5be01617631f094aa3aa3c2a35174d3c14 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 19 Apr 2016 10:28:27 -0400 Subject: [PATCH 113/258] Only allow pro trials --- app/Http/Controllers/AccountController.php | 2 +- app/Http/routes.php | 2 +- app/Ninja/Repositories/AccountRepository.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index d5fe853a4c11..75f6819dad5e 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -1132,7 +1132,7 @@ class AccountController extends BaseController $user->registered = true; $user->save(); - $user->account->startTrial(PLAN_ENTERPRISE); + $user->account->startTrial(PLAN_PRO); if (Input::get('go_pro') == 'true') { Session::set(REQUESTED_PRO_PLAN, true); diff --git a/app/Http/routes.php b/app/Http/routes.php index 9df520ede465..11594109fcb0 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -189,7 +189,7 @@ Route::group([ Route::post('users/bulk', 'UserController@bulk'); Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); Route::get('start_trial/{plan}', 'AccountController@startTrial') - ->where(['plan'=>'pro|enterprise']); + ->where(['plan'=>'pro']); Route::get('restore_user/{user_id}', 'UserController@restoreUser'); Route::post('users/change_password', 'UserController@changePassword'); Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 367e55007fe7..e8fe2d041843 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -382,7 +382,7 @@ class AccountRepository $user->last_name = $lastName; $user->registered = true; - $user->account->startTrial(PLAN_ENTERPRISE); + $user->account->startTrial(PLAN_PRO); } $user->oauth_provider_id = $providerId; From b8ad27963e2b594a0c5ce23d92e5fe50b7c47652 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 19 Apr 2016 10:30:25 -0400 Subject: [PATCH 114/258] Fix table name in migration --- .../migrations/2016_04_16_103943_enterprise_plan.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index fd180a5c5361..6145d052c8c9 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -46,11 +46,11 @@ class EnterprisePlan extends Migration $single_account_ids = \DB::table('users') ->leftJoin('user_accounts', function ($join) { - $join->on('user_Accounts.user_id1', '=', 'users.id'); - $join->orOn('user_Accounts.user_id2', '=', 'users.id'); - $join->orOn('user_Accounts.user_id3', '=', 'users.id'); - $join->orOn('user_Accounts.user_id4', '=', 'users.id'); - $join->orOn('user_Accounts.user_id5', '=', 'users.id'); + $join->on('user_accounts.user_id1', '=', 'users.id'); + $join->orOn('user_accounts.user_id2', '=', 'users.id'); + $join->orOn('user_accounts.user_id3', '=', 'users.id'); + $join->orOn('user_accounts.user_id4', '=', 'users.id'); + $join->orOn('user_accounts.user_id5', '=', 'users.id'); }) ->whereNull('user_accounts.id') ->where(function ($query) { From d07fec5d629407b5b2bf34c0062e310cdb5a1eb8 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 19 Apr 2016 21:46:18 +0300 Subject: [PATCH 115/258] Fix for #816 preg_match() error on settings update --- app/Http/Controllers/AppController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 1fc3f7588873..9dcf243fbe02 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -178,9 +178,12 @@ class AppController extends BaseController $config = ''; foreach ($_ENV as $key => $val) { - if (preg_match('/\s/',$val)) { - $val = "'{$val}'"; - } + if (is_array($val)) { + continue; + } + if (preg_match('/\s/', $val)) { + $val = "'{$val}'"; + } $config .= "{$key}={$val}\n"; } From d6608f863c5cdfa6e92c84ed35702c44f7fe8a9f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 19 Apr 2016 23:01:44 +0300 Subject: [PATCH 116/258] Syntax fix --- resources/views/invoices/view.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index 730094e659af..13d83c3746cf 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -84,7 +84,7 @@ window.invoice = {!! $invoice->toJson() !!}; invoice.features = { customize_invoice_design:{{ $invoice->client->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }}, - remove_created_by:{{ $invoice->client->account)->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }}, + remove_created_by:{{ $invoice->client->account->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }}, invoice_settings:{{ $invoice->client->account->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }} }; invoice.is_quote = {{ $invoice->is_quote ? 'true' : 'false' }}; From c67564823b6abb9267291668f34710fbfffcc344 Mon Sep 17 00:00:00 2001 From: DraperStudio Date: Tue, 19 Apr 2016 22:02:46 +0200 Subject: [PATCH 117/258] Use initialize method to set configuration --- app/Services/PaymentService.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 2f4b57f11ff0..61af6cd2336c 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -39,18 +39,7 @@ class PaymentService extends BaseService public function createGateway($accountGateway) { $gateway = Omnipay::create($accountGateway->gateway->provider); - $config = $accountGateway->getConfig(); - - foreach ($config as $key => $val) { - if (!$val) { - continue; - } - - $function = "set".ucfirst($key); - if (method_exists($gateway, $function)) { - $gateway->$function($val); - } - } + $gateway->initialize((array) $accountGateway->getConfig()); if ($accountGateway->isGateway(GATEWAY_DWOLLA)) { if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) { From d37794d402f957e11e4cec0aacad19c74b696915 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 19 Apr 2016 23:28:25 +0300 Subject: [PATCH 118/258] Fix for tests --- database/seeds/UserTableSeeder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php index c89cb45b20ba..3f8b70e17eba 100644 --- a/database/seeds/UserTableSeeder.php +++ b/database/seeds/UserTableSeeder.php @@ -2,6 +2,7 @@ use App\Models\User; use App\Models\Account; +use App\Models\Company; use App\Models\Affiliate; class UserTableSeeder extends Seeder From eb91a3cd4242d259323ee7ed181dbcd56ee32326 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 19 Apr 2016 16:31:50 -0400 Subject: [PATCH 119/258] Fix typos --- app/Models/Account.php | 4 ++-- resources/views/invoices/view.blade.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index efbfd5b20470..3f8ca4810cb3 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -830,7 +830,7 @@ class Account extends Eloquent // White Label case FEATURE_WHITE_LABEL: - if ($this->isNinjaAccount() || (!$selfHost && $planDetails && !$plan_details['expires'])) { + if ($this->isNinjaAccount() || (!$selfHost && $planDetails && !$planDetails['expires'])) { return false; } // Fallthrough @@ -1262,7 +1262,7 @@ class Account extends Eloquent public function attatchPDF() { - return $this->hasFeaure(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment; + return $this->hasFeature(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment; } public function getEmailDesignId() diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index 730094e659af..13d83c3746cf 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -84,7 +84,7 @@ window.invoice = {!! $invoice->toJson() !!}; invoice.features = { customize_invoice_design:{{ $invoice->client->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }}, - remove_created_by:{{ $invoice->client->account)->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }}, + remove_created_by:{{ $invoice->client->account->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }}, invoice_settings:{{ $invoice->client->account->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }} }; invoice.is_quote = {{ $invoice->is_quote ? 'true' : 'false' }}; From 5c4b75cf76811529c2f291f034094a83c99fc891 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 20 Apr 2016 00:02:02 +0300 Subject: [PATCH 120/258] Fix for tests --- database/seeds/UserTableSeeder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php index 3f8b70e17eba..1ec659bf389e 100644 --- a/database/seeds/UserTableSeeder.php +++ b/database/seeds/UserTableSeeder.php @@ -20,7 +20,7 @@ class UserTableSeeder extends Seeder //'name' => 'Test Account', 'account_key' => str_random(RANDOM_KEY_LENGTH), 'timezone_id' => 1, - 'company_id' => $company, + 'company_id' => $company->id, ]); User::create([ From 9281cd5ba21b5e33a83d1aa551dd9d2920e49a50 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 20 Apr 2016 11:04:54 +0300 Subject: [PATCH 121/258] Fix for default quote terms --- app/Ninja/Repositories/InvoiceRepository.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 845bb0082d79..6b41db461a31 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -284,7 +284,14 @@ class InvoiceRepository extends BaseRepository $invoice->end_date = null; } - $invoice->terms = (isset($data['terms']) && trim($data['terms'])) ? trim($data['terms']) : (!$publicId && $account->invoice_terms ? $account->invoice_terms : ''); + if (isset($data['terms']) && trim($data['terms'])) { + $invoice->terms = trim($data['terms']); + } elseif ($isNew && $account->{"{$entityType}_terms"}) { + $invoice->terms = $account->{"{$entityType}_terms"}; + } else { + $invoice->terms = ''; + } + $invoice->invoice_footer = (isset($data['invoice_footer']) && trim($data['invoice_footer'])) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : ''); $invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null; From d87552c90e412374e9ad97632548b7088ea2e864 Mon Sep 17 00:00:00 2001 From: DraperStudio Date: Wed, 20 Apr 2016 13:37:57 +0200 Subject: [PATCH 122/258] Use object_get to retrieve a config field --- app/Models/AccountGateway.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/Models/AccountGateway.php b/app/Models/AccountGateway.php index a4027db6ef36..5e951285349e 100644 --- a/app/Models/AccountGateway.php +++ b/app/Models/AccountGateway.php @@ -60,13 +60,7 @@ class AccountGateway extends EntityModel public function getConfigField($field) { - $config = $this->getConfig(); - - if (!$field || !property_exists($config, $field)) { - return false; - } - - return $config->$field; + return object_get($this->getConfig(), $field, false); } public function getPublishableStripeKey() From b5b3fa56ac9364af9613f8b2c289dffa61a37386 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Wed, 20 Apr 2016 09:50:13 -0400 Subject: [PATCH 123/258] Don't try to migrate nonexistent accounts --- database/migrations/2016_04_16_103943_enterprise_plan.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index 6145d052c8c9..2b3656703220 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -87,6 +87,10 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun $primaryAccount = $otherAccounts->first(); } + if (empty($primaryAccount)) { + return; + } + $company = Company::create(); if ($primaryAccount->pro_plan_paid && $primaryAccount->pro_plan_paid != '0000-00-00') { $company->plan = 'pro'; From 71b6b6eb87932e2cdb15a83fc17949138d5e121f Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Wed, 20 Apr 2016 11:47:38 -0400 Subject: [PATCH 124/258] More robust migration; avoids timeout failures --- .../2016_04_16_103943_enterprise_plan.php | 181 ++++++++++++------ 1 file changed, 121 insertions(+), 60 deletions(-) diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index 2b3656703220..8ca5632ad358 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -13,36 +13,45 @@ class EnterprisePlan extends Migration * @return void */ public function up() { + $timeout = ini_get('max_execution_time'); + if ($timeout == 0) { + $timeout = 600; + } + $timeout = max($timeout - 10, $timeout * .9); + $startTime = time(); - Schema::create('companies', function($table) - { - $table->increments('id'); - - $table->enum('plan', array('pro', 'enterprise', 'white_label'))->nullable(); - $table->enum('plan_term', array('month', 'year'))->nullable(); - $table->date('plan_started')->nullable(); - $table->date('plan_paid')->nullable(); - $table->date('plan_expires')->nullable(); - - $table->unsignedInteger('payment_id')->nullable(); - $table->foreign('payment_id')->references('id')->on('payments'); - - $table->date('trial_started')->nullable(); - $table->enum('trial_plan', array('pro', 'enterprise'))->nullable(); - - $table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable(); - $table->enum('pending_term', array('month', 'year'))->nullable(); - - $table->timestamps(); - $table->softDeletes(); - }); - - Schema::table('accounts', function($table) - { - $table->unsignedInteger('company_id')->nullable(); - $table->foreign('company_id')->references('id')->on('companies'); - }); + if (!Schema::hasTable('companies')) { + Schema::create('companies', function($table) + { + $table->increments('id'); + + $table->enum('plan', array('pro', 'enterprise', 'white_label'))->nullable(); + $table->enum('plan_term', array('month', 'year'))->nullable(); + $table->date('plan_started')->nullable(); + $table->date('plan_paid')->nullable(); + $table->date('plan_expires')->nullable(); + + $table->unsignedInteger('payment_id')->nullable(); + $table->foreign('payment_id')->references('id')->on('payments'); + + $table->date('trial_started')->nullable(); + $table->enum('trial_plan', array('pro', 'enterprise'))->nullable(); + + $table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable(); + $table->enum('pending_term', array('month', 'year'))->nullable(); + + $table->timestamps(); + $table->softDeletes(); + }); + } + if (!Schema::hasColumn('accounts', 'company_id')) { + Schema::table('accounts', function($table) + { + $table->unsignedInteger('company_id')->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + }); + } $single_account_ids = \DB::table('users') ->leftJoin('user_accounts', function ($join) { @@ -52,34 +61,54 @@ class EnterprisePlan extends Migration $join->orOn('user_accounts.user_id4', '=', 'users.id'); $join->orOn('user_accounts.user_id5', '=', 'users.id'); }) + ->leftJoin('accounts', 'accounts.id', '=', 'users.account_id') ->whereNull('user_accounts.id') + ->whereNull('accounts.company_id') ->where(function ($query) { $query->whereNull('users.public_id'); $query->orWhere('users.public_id', '=', 0); }) ->lists('users.account_id'); + if (count($single_account_ids)) { + foreach (Account::find($single_account_ids) as $account) { + $this->upAccounts($account); + $this->checkTimeout($timeout, $startTime); + } + } + $group_accounts = \DB::select( 'SELECT u1.account_id as account1, u2.account_id as account2, u3.account_id as account3, u4.account_id as account4, u5.account_id as account5 FROM `user_accounts` -LEFT JOIN users u1 ON (u1.public_id IS NULL OR u1.public_id = 0) AND user_accounts.user_id1 = u1.id -LEFT JOIN users u2 ON (u2.public_id IS NULL OR u2.public_id = 0) AND user_accounts.user_id2 = u2.id -LEFT JOIN users u3 ON (u3.public_id IS NULL OR u3.public_id = 0) AND user_accounts.user_id3 = u3.id -LEFT JOIN users u4 ON (u4.public_id IS NULL OR u4.public_id = 0) AND user_accounts.user_id4 = u4.id -LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accounts.user_id5 = u5.id'); - - foreach (Account::find($single_account_ids) as $account) { - $this->upAccounts($account); + LEFT JOIN users u1 ON (u1.public_id IS NULL OR u1.public_id = 0) AND user_accounts.user_id1 = u1.id + LEFT JOIN users u2 ON (u2.public_id IS NULL OR u2.public_id = 0) AND user_accounts.user_id2 = u2.id + LEFT JOIN users u3 ON (u3.public_id IS NULL OR u3.public_id = 0) AND user_accounts.user_id3 = u3.id + LEFT JOIN users u4 ON (u4.public_id IS NULL OR u4.public_id = 0) AND user_accounts.user_id4 = u4.id + LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accounts.user_id5 = u5.id + LEFT JOIN accounts a1 ON a1.id = u1.account_id + LEFT JOIN accounts a2 ON a2.id = u2.account_id + LEFT JOIN accounts a3 ON a3.id = u3.account_id + LEFT JOIN accounts a4 ON a4.id = u4.account_id + LEFT JOIN accounts a5 ON a5.id = u5.account_id + WHERE (a1.id IS NOT NULL AND a1.company_id IS NULL) + OR (a2.id IS NOT NULL AND a2.company_id IS NULL) + OR (a3.id IS NOT NULL AND a3.company_id IS NULL) + OR (a4.id IS NOT NULL AND a4.company_id IS NULL) + OR (a5.id IS NOT NULL AND a5.company_id IS NULL)'); + + if (count($group_accounts)) { + foreach ($group_accounts as $group_account) { + $this->upAccounts(null, Account::find(get_object_vars($group_account))); + $this->checkTimeout($timeout, $startTime); + } } - foreach ($group_accounts as $group_account) { - $this->upAccounts(null, Account::find(get_object_vars($group_account))); + if (Schema::hasColumn('accounts', 'pro_plan_paid')) { + Schema::table('accounts', function($table) + { + $table->dropColumn('pro_plan_paid'); + $table->dropColumn('pro_plan_trial'); + }); } - - Schema::table('accounts', function($table) - { - $table->dropColumn('pro_plan_paid'); - $table->dropColumn('pro_plan_trial'); - }); } private function upAccounts($primaryAccount, $otherAccounts = array()) { @@ -128,6 +157,12 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun } } + protected function checkTimeout($timeout, $startTime) { + if (time() - $startTime >= $timeout) { + exit('Migration reached time limit; please run again to continue'); + } + } + /** * Reverse the migrations. * @@ -135,25 +170,51 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun */ public function down() { - Schema::table('accounts', function($table) - { - $table->date('pro_plan_paid')->nullable(); - $table->date('pro_plan_trial')->nullable(); - }); + $timeout = ini_get('max_execution_time'); + if ($timeout == 0) { + $timeout = 600; + } + $timeout = max($timeout - 10, $timeout * .9); + $startTime = time(); - foreach (Company::all() as $company) { - foreach ($company->accounts as $account) { - $account->pro_plan_paid = $company->plan_paid; - $account->pro_plan_trial = $company->trial_started; - $account->save(); + if (!Schema::hasColumn('accounts', 'pro_plan_paid')) { + Schema::table('accounts', function($table) + { + $table->date('pro_plan_paid')->nullable(); + $table->date('pro_plan_trial')->nullable(); + }); + } + + $company_ids = \DB::table('companies') + ->leftJoin('accounts', 'accounts.company_id', '=', 'companies.id') + ->whereNull('accounts.pro_plan_paid') + ->whereNull('accounts.pro_plan_trial') + ->where(function ($query) { + $query->whereNotNull('companies.plan_paid'); + $query->orWhereNotNull('companies.trial_started'); + }) + ->lists('companies.id'); + + $company_ids = array_unique($company_ids); + + if (count($company_ids)) { + foreach (Company::find($company_ids) as $company) { + foreach ($company->accounts as $account) { + $account->pro_plan_paid = $company->plan_paid; + $account->pro_plan_trial = $company->trial_started; + $account->save(); + } + $this->checkTimeout($timeout, $startTime); } } - - Schema::table('accounts', function($table) - { - $table->dropForeign('accounts_company_id_foreign'); - $table->dropColumn('company_id'); - }); + + if (Schema::hasColumn('accounts', 'company_id')) { + Schema::table('accounts', function($table) + { + $table->dropForeign('accounts_company_id_foreign'); + $table->dropColumn('company_id'); + }); + } Schema::dropIfExists('companies'); } From 9d27b68fb6df0dbaf9490e5fbdfb4caa33c098fd Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Wed, 20 Apr 2016 16:53:56 -0400 Subject: [PATCH 125/258] Fix refunds; support using stored card; change proPlanModel link --- app/Http/Controllers/AccountController.php | 6 +++--- resources/views/accounts/management.blade.php | 5 +++++ resources/views/header.blade.php | 16 +++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 75f6819dad5e..af2fdd3df656 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -165,7 +165,7 @@ class AccountController extends BaseController $account->company->save(); Session::flash('message', trans('texts.updated_plan')); } - } else { + } elseif (!empty($planDetails['started'])) { // Downgrade $refund_deadline = clone $planDetails['started']; $refund_deadline->modify('+30 days'); @@ -186,7 +186,7 @@ class AccountController extends BaseController $gateway = $this->paymentService->createGateway($payment->account_gateway); $refund = $gateway->refund(array( 'transactionReference' => $payment->transaction_reference, - 'amount' => $payment->amount * 100 + 'amount' => $payment->amount )); $refund->send(); $payment->delete(); @@ -238,7 +238,7 @@ class AccountController extends BaseController if (!empty($new_plan)) { $invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly)); - return Redirect::to('payment/'.$invitation->invitation_key); + return Redirect::to('view/'.$invitation->invitation_key); } return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301); diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index 0f74a04e829f..cf9917deb005 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -220,6 +220,11 @@ } $('#plan_term, #plan').change(updatePlanModal); updatePlanModal(); + + if(window.location.hash) { + var hash = window.location.hash; + $(hash).modal('toggle'); + } }); @stop \ No newline at end of file diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index 50999b8cc1d1..bcf8df4ccf16 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -189,15 +189,13 @@ function submitProPlan() { fbq('track', 'AddPaymentInfo'); trackEvent('/account', '/submit_pro_plan/' + NINJA.proPlanFeature); - if (NINJA.isRegistered) { - $.ajax({ - type: 'POST', - url: '{{ URL::to('account/go_pro') }}', - success: function(result) { - NINJA.formIsChanged = false; - window.location = '/payment/' + result; - } - }); + if (NINJA.isRegistered) { + if (window.showChangePlan) { + $('#proPlanModal').modal('hide'); + showChangePlan(); + } else { + window.location = '/settings/account_management#changePlanModel'; + } } else { $('#proPlanModal').modal('hide'); $('#go_pro').val('true'); From b27505f63c6504c61b37c5df12bcc80523991004 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 21 Apr 2016 13:01:36 +0300 Subject: [PATCH 126/258] Fix for saving invoice client with id/public_id --- app/Console/Kernel.php | 1 + app/Services/InvoiceService.php | 16 ++++++++-------- .../2016_04_18_174135_add_page_size.php | 5 +++-- public/built.js | 5 ++++- public/js/script.js | 2 +- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index af5bbe7bc9d2..38a5bf4950ef 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel 'App\Console\Commands\RemoveOrphanedDocuments', 'App\Console\Commands\ResetData', 'App\Console\Commands\CheckData', + 'App\Console\Commands\PruneData', 'App\Console\Commands\SendRenewalInvoices', 'App\Console\Commands\ChargeRenewalInvoices', 'App\Console\Commands\SendReminders', diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index 43cdadd31d1c..9222e794dc17 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -33,17 +33,17 @@ class InvoiceService extends BaseService public function save($data, $checkSubPermissions = false) { if (isset($data['client'])) { - $can_save_client = !$checkSubPermissions; - if(!$can_save_client){ - if(empty($data['client']['public_id']) || $data['client']['public_id']=='-1'){ - $can_save_client = Client::canCreate(); - } - else{ - $can_save_client = Client::wherePublicId($data['client']['public_id'])->first()->canEdit(); + $canSaveClient = !$checkSubPermissions; + if( ! $canSaveClient){ + $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); + if (empty($clientPublicId) || $clientPublicId == '-1') { + $canSaveClient = Client::canCreate(); + } else { + $canSaveClient = Client::scope($clientPublicId)->first()->canEdit(); } } - if($can_save_client){ + if ($canSaveClient) { $client = $this->clientRepo->save($data['client']); $data['client_id'] = $client->id; } diff --git a/database/migrations/2016_04_18_174135_add_page_size.php b/database/migrations/2016_04_18_174135_add_page_size.php index d5bbb86f64bc..0094c84335b5 100644 --- a/database/migrations/2016_04_18_174135_add_page_size.php +++ b/database/migrations/2016_04_18_174135_add_page_size.php @@ -63,10 +63,11 @@ class AddPageSize extends Migration $table->boolean('is_early_access'); }); - Schema::dropIfExists('expense_categories'); - Schema::table('expenses', function ($table) { + $table->dropForeign('expenses_expense_category_id_foreign'); $table->dropColumn('expense_category_id'); }); + + Schema::dropIfExists('expense_categories'); } } diff --git a/public/built.js b/public/built.js index d7dd051b99a0..0522b23e67c3 100644 --- a/public/built.js +++ b/public/built.js @@ -29449,6 +29449,9 @@ return a?b._ordinalParse:b._ordinalParseLenient}),R(["D","DD"],td),R("Do",functi //! github.com/moment/moment-timezone !function(a,b){"use strict";"function"==typeof define&&define.amd?define(["moment"],b):"object"==typeof exports?module.exports=b(require("moment")):b(a.moment)}(this,function(a){"use strict";function b(a){return a>96?a-87:a>64?a-29:a-48}function c(a){var c,d=0,e=a.split("."),f=e[0],g=e[1]||"",h=1,i=0,j=1;for(45===a.charCodeAt(0)&&(d=1,j=-1),d;dc;c++)a[c]=Math.round((a[c-1]||0)+6e4*a[c]);a[b-1]=1/0}function f(a,b){var c,d=[];for(c=0;cz||2===z&&6>A)&&q("Moment Timezone requires Moment.js >= 2.6.0. You are using Moment.js "+a.version+". See momentjs.com"),h.prototype={_set:function(a){this.name=a.name,this.abbrs=a.abbrs,this.untils=a.untils,this.offsets=a.offsets},_index:function(a){var b,c=+a,d=this.untils;for(b=0;be;e++)if(b=g[e],c=g[e+1],d=g[e?e-1:e],c>b&&r.moveAmbiguousForward?b=c:b>d&&r.moveInvalidForward&&(b=d),fz||2===z&&9>A)&&q("Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js "+a.version+"."),a.defaultZone=b?k(b):null,a};var C=a.momentProperties;return"[object Array]"===Object.prototype.toString.call(C)?(C.push("_z"),C.push("_a")):C&&(C._z=null),n({version:"2015g",zones:["Africa/Abidjan|LMT GMT|g.8 0|01|-2ldXH.Q","Africa/Accra|LMT GMT GHST|.Q 0 -k|012121212121212121212121212121212121212121212121|-26BbX.8 6tzX.8 MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE 1BAk MnE 1C0k MnE 1BAk MnE 1BAk MnE","Africa/Addis_Ababa|LMT EAT BEAT BEAUT|-2r.g -30 -2u -2J|01231|-1F3Cr.g 3Dzr.g okMu MFXJ","Africa/Algiers|PMT WET WEST CET CEST|-9.l 0 -10 -10 -20|0121212121212121343431312123431213|-2nco9.l cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 DA0 Imo0 rd0 De0 9Xz0 1fb0 1ap0 16K0 2yo0 mEp0 hwL0 jxA0 11A0 dDd0 17b0 11B0 1cN0 2Dy0 1cN0 1fB0 1cL0","Africa/Bangui|LMT WAT|-d.A -10|01|-22y0d.A","Africa/Bissau|LMT WAT GMT|12.k 10 0|012|-2ldWV.E 2xonV.E","Africa/Blantyre|LMT CAT|-2a.k -20|01|-2GJea.k","Africa/Cairo|EET EEST|-20 -30|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-1bIO0 vb0 1ip0 11z0 1iN0 1nz0 12p0 1pz0 10N0 1pz0 16p0 1jz0 s3d0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1WL0 rd0 1Rz0 wp0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1qL0 Xd0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1ny0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 WL0 1qN0 Rb0 1wp0 On0 1zd0 Lz0 1EN0 Fb0 c10 8n0 8Nd0 gL0 e10 mn0","Africa/Casablanca|LMT WET WEST CET|u.k 0 -10 -10|0121212121212121213121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2gMnt.E 130Lt.E rb0 Dd0 dVb0 b6p0 TX0 EoB0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4mn0 SyN0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 Rc0 11A0 e00 e00 U00 11A0 8o0 e00 11A0 11A0 5A0 e00 17c0 1fA0 1a00 1a00 1fA0 17c0 1io0 14o0 1lc0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1lc0 14o0 1fA0","Africa/Ceuta|WET WEST CET CEST|0 -10 -10 -20|010101010101010101010232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-25KN0 11z0 drd0 18o0 3I00 17c0 1fA0 1a00 1io0 1a00 1y7p0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4VB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Africa/El_Aaiun|LMT WAT WET WEST|Q.M 10 0 -10|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1rDz7.c 1GVA7.c 6L0 AL0 1Nd0 XX0 1Cp0 pz0 1cBB0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 Rc0 11A0 e00 e00 U00 11A0 8o0 e00 11A0 11A0 5A0 e00 17c0 1fA0 1a00 1a00 1fA0 17c0 1io0 14o0 1lc0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1lc0 14o0 1fA0","Africa/Johannesburg|SAST SAST SAST|-1u -20 -30|012121|-2GJdu 1Ajdu 1cL0 1cN0 1cL0","Africa/Juba|LMT CAT CAST EAT|-2a.8 -20 -30 -30|01212121212121212121212121212121213|-1yW2a.8 1zK0a.8 16L0 1iN0 17b0 1jd0 17b0 1ip0 17z0 1i10 17X0 1hB0 18n0 1hd0 19b0 1gp0 19z0 1iN0 17b0 1ip0 17z0 1i10 18n0 1hd0 18L0 1gN0 19b0 1gp0 19z0 1iN0 17z0 1i10 17X0 yGd0","Africa/Monrovia|MMT LRT GMT|H.8 I.u 0|012|-23Lzg.Q 29s01.m","Africa/Ndjamena|LMT WAT WAST|-10.c -10 -20|0121|-2le10.c 2J3c0.c Wn0","Africa/Tripoli|LMT CET CEST EET|-Q.I -10 -20 -20|012121213121212121212121213123123|-21JcQ.I 1hnBQ.I vx0 4iP0 xx0 4eN0 Bb0 7ip0 U0n0 A10 1db0 1cN0 1db0 1dd0 1db0 1eN0 1bb0 1e10 1cL0 1c10 1db0 1dd0 1db0 1cN0 1db0 1q10 fAn0 1ep0 1db0 AKq0 TA0 1o00","Africa/Tunis|PMT CET CEST|-9.l -10 -20|0121212121212121212121212121212121|-2nco9.l 18pa9.l 1qM0 DA0 3Tc0 11B0 1ze0 WM0 7z0 3d0 14L0 1cN0 1f90 1ar0 16J0 1gXB0 WM0 1rA0 11c0 nwo0 Ko0 1cM0 1cM0 1rA0 10M0 zuM0 10N0 1aN0 1qM0 WM0 1qM0 11A0 1o00","Africa/Windhoek|SWAT SAST SAST CAT WAT WAST|-1u -20 -30 -20 -10 -20|012134545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-2GJdu 1Ajdu 1cL0 1SqL0 9NA0 11D0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0","America/Adak|NST NWT NPT BST BDT AHST HST HDT|b0 a0 a0 b0 a0 a0 a0 90|012034343434343434343434343434343456767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17SX0 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Anchorage|CAT CAWT CAPT AHST AHDT YST AKST AKDT|a0 90 90 a0 90 90 90 80|012034343434343434343434343434343456767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17T00 8wX0 iA0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Anguilla|LMT AST|46.4 40|01|-2kNvR.U","America/Araguaina|LMT BRT BRST|3c.M 30 20|0121212121212121212121212121212121212121212121212121|-2glwL.c HdKL.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 ny10 Lz0","America/Argentina/Buenos_Aires|CMT ART ARST ART ARST|4g.M 40 30 30 20|0121212121212121212121212121212121212121213434343434343234343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 g0p0 10M0 j3c0 uL0 1qN0 WL0","America/Argentina/Catamarca|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343454343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 ako0 7B0 8zb0 uL0","America/Argentina/Cordoba|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343454343234343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 j3c0 uL0 1qN0 WL0","America/Argentina/Jujuy|CMT ART ARST ART ARST WART WARST|4g.M 40 30 30 20 40 30|01212121212121212121212121212121212121212134343456543432343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1ze0 TX0 1ld0 WK0 1wp0 TX0 g0p0 10M0 j3c0 uL0","America/Argentina/La_Rioja|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|01212121212121212121212121212121212121212134343434534343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 g0p0 10M0 ako0 7B0 8zb0 uL0","America/Argentina/Mendoza|CMT ART ARST ART ARST WART WARST|4g.M 40 30 30 20 40 30|0121212121212121212121212121212121212121213434345656543235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1u20 SL0 1vd0 Tb0 1wp0 TW0 g0p0 10M0 agM0 Op0 7TX0 uL0","America/Argentina/Rio_Gallegos|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343434343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 g0p0 10M0 ako0 7B0 8zb0 uL0","America/Argentina/Salta|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|01212121212121212121212121212121212121212134343434543432343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 j3c0 uL0","America/Argentina/San_Juan|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|01212121212121212121212121212121212121212134343434534343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 g0p0 10M0 ak00 m10 8lb0 uL0","America/Argentina/San_Luis|CMT ART ARST ART ARST WART WARST|4g.M 40 30 30 20 40 30|01212121212121212121212121212121212121212134343456536353465653|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 XX0 1q20 SL0 AN0 kin0 10M0 ak00 m10 8lb0 8L0 jd0 1qN0 WL0 1qN0","America/Argentina/Tucuman|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|012121212121212121212121212121212121212121343434345434323534343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 g0p0 10M0 ako0 4N0 8BX0 uL0 1qN0 WL0","America/Argentina/Ushuaia|CMT ART ARST ART ARST WART|4g.M 40 30 30 20 40|0121212121212121212121212121212121212121213434343434343235343|-20UHH.c pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 g0p0 10M0 ajA0 8p0 8zb0 uL0","America/Aruba|LMT ANT AST|4z.L 4u 40|012|-2kV7o.d 28KLS.d","America/Asuncion|AMT PYT PYT PYST|3O.E 40 30 30|012131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313|-1x589.k 1DKM9.k 3CL0 3Dd0 10L0 1pB0 10n0 1pB0 10n0 1pB0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1lB0 14n0 1dd0 1cL0 1fd0 WL0 1rd0 1aL0 1dB0 Xz0 1qp0 Xb0 1qN0 10L0 1rB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 WN0 1qL0 11B0 1nX0 1ip0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 TX0 1tB0 19X0 1a10 1fz0 1a10 1fz0 1cN0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0","America/Atikokan|CST CDT CWT CPT EST|60 50 50 50 50|0101234|-25TQ0 1in0 Rnb0 3je0 8x30 iw0","America/Bahia|LMT BRT BRST|2y.4 30 20|01212121212121212121212121212121212121212121212121212121212121|-2glxp.U HdLp.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 l5B0 Rb0","America/Bahia_Banderas|LMT MST CST PST MDT CDT|71 70 60 80 60 50|0121212131414141414141414141414141414152525252525252525252525252525252525252525252525252525252|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nW0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Barbados|LMT BMT AST ADT|3W.t 3W.t 40 30|01232323232|-1Q0I1.v jsM0 1ODC1.v IL0 1ip0 17b0 1ip0 17b0 1ld0 13b0","America/Belem|LMT BRT BRST|3d.U 30 20|012121212121212121212121212121|-2glwK.4 HdKK.4 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0","America/Belize|LMT CST CHDT CDT|5Q.M 60 5u 50|01212121212121212121212121212121212121212121212121213131|-2kBu7.c fPA7.c Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1f0Mu qn0 lxB0 mn0","America/Blanc-Sablon|AST ADT AWT APT|40 30 30 30|010230|-25TS0 1in0 UGp0 8x50 iu0","America/Boa_Vista|LMT AMT AMST|42.E 40 30|0121212121212121212121212121212121|-2glvV.k HdKV.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 smp0 WL0 1tB0 2L0","America/Bogota|BMT COT COST|4U.g 50 40|0121|-2eb73.I 38yo3.I 2en0","America/Boise|PST PDT MST MWT MPT MDT|80 70 70 60 60 60|0101023425252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-261q0 1nX0 11B0 1nX0 8C10 JCL0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 Dd0 1Kn0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Cambridge_Bay|zzz MST MWT MPT MDDT MDT CST CDT EST|0 70 60 60 50 60 60 50 50|0123141515151515151515151515151515151515151515678651515151515151515151515151515151515151515151515151515151515151515151515151|-21Jc0 RO90 8x20 ix0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11A0 1nX0 2K0 WQ0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Campo_Grande|LMT AMT AMST|3C.s 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2glwl.w HdLl.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0","America/Cancun|LMT CST EST EDT CDT|5L.4 60 50 40 50|0123232341414141414141414141414141414141412|-1UQG0 2q2o0 yLB0 1lb0 14p0 1lb0 14p0 Lz0 xB0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 Dd0","America/Caracas|CMT VET VET|4r.E 4u 40|0121|-2kV7w.k 28KM2.k 1IwOu","America/Cayenne|LMT GFT GFT|3t.k 40 30|012|-2mrwu.E 2gWou.E","America/Cayman|KMT EST EDT|57.b 50 40|0121212121212121212121212121212121212121212121|-2l1uQ.N 4duNQ.N 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Chicago|CST CDT EST CWT CPT|60 50 50 50 50|01010101010101010101010101010101010102010101010103401010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 1wp0 TX0 WN0 1qL0 1cN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 11B0 1Hz0 14p0 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Chihuahua|LMT MST CST CDT MDT|74.k 70 60 50 60|0121212323241414141414141414141414141414141414141414141414141414141414141414141414141414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Costa_Rica|SJMT CST CDT|5A.d 60 50|0121212121|-1Xd6n.L 2lu0n.L Db0 1Kp0 Db0 pRB0 15b0 1kp0 mL0","America/Creston|MST PST|70 80|010|-29DR0 43B0","America/Cuiaba|LMT AMT AMST|3I.k 40 30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2glwf.E HdLf.E 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 4a10 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0","America/Danmarkshavn|LMT WGT WGST GMT|1e.E 30 20 0|01212121212121212121212121212121213|-2a5WJ.k 2z5fJ.k 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 DC0","America/Dawson|YST YDT YWT YPT YDDT PST PDT|90 80 80 80 70 80 70|0101023040565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-25TN0 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 jrA0 fNd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Dawson_Creek|PST PDT PWT PPT MST|80 70 70 70 70|0102301010101010101010101010101010101010101010101010101014|-25TO0 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 ML0","America/Denver|MST MDT MWT MPT|70 60 60 60|01010101023010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261r0 1nX0 11B0 1nX0 11B0 1qL0 WN0 mn0 Ord0 8x20 ix0 LCN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Detroit|LMT CST EST EWT EPT EDT|5w.b 60 50 40 40 40|01234252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-2Cgir.N peqr.N 156L0 8x40 iv0 6fd0 11z0 Jy10 SL0 dnB0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Edmonton|LMT MST MDT MWT MPT|7x.Q 70 60 60 60|01212121212121341212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2yd4q.8 shdq.8 1in0 17d0 hz0 2dB0 1fz0 1a10 11z0 1qN0 WL0 1qN0 11z0 IGN0 8x20 ix0 3NB0 11z0 LFB0 1cL0 3Cp0 1cL0 66N0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Eirunepe|LMT ACT ACST AMT|4D.s 50 40 40|0121212121212121212121212121212131|-2glvk.w HdLk.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0 yTd0 d5X0","America/El_Salvador|LMT CST CDT|5U.M 60 50|012121|-1XiG3.c 2Fvc3.c WL0 1qN0 WL0","America/Ensenada|LMT MST PST PDT PWT PPT|7M.4 70 80 70 70 70|012123245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQE0 4PX0 8mM0 8lc0 SN0 1cL0 pHB0 83r0 zI0 5O10 1Rz0 cOP0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 BUp0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Fort_Nelson|PST PDT PWT PPT MST|80 70 70 70 70|01023010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010104|-25TO0 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0","America/Fort_Wayne|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|010101023010101010101010101040454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 QI10 Db0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 5Tz0 1o10 qLb0 1cL0 1cN0 1cL0 1qhd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Fortaleza|LMT BRT BRST|2y 30 20|0121212121212121212121212121212121212121|-2glxq HdLq 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 5z0 2mN0 On0","America/Glace_Bay|LMT AST ADT AWT APT|3X.M 40 30 30 30|012134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsI0.c CwO0.c 1in0 UGp0 8x50 iu0 iq10 11z0 Jg10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Godthab|LMT WGT WGST|3q.U 30 20|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a5Ux.4 2z5dx.4 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","America/Goose_Bay|NST NDT NST NDT NWT NPT AST ADT ADDT|3u.Q 2u.Q 3u 2u 2u 2u 40 30 20|010232323232323245232323232323232323232323232323232323232326767676767676767676767676767676767676767676768676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-25TSt.8 1in0 DXb0 2HbX.8 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 S10 g0u 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Grand_Turk|KMT EST EDT AST|57.b 50 40 40|0121212121212121212121212121212121212121212121212121212121212121212121212123|-2l1uQ.N 2HHBQ.N 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Guatemala|LMT CST CDT|62.4 60 50|0121212121|-24KhV.U 2efXV.U An0 mtd0 Nz0 ifB0 17b0 zDB0 11z0","America/Guayaquil|QMT ECT|5e 50|01|-1yVSK","America/Guyana|LMT GBGT GYT GYT GYT|3Q.E 3J 3J 30 40|01234|-2dvU7.k 24JzQ.k mlc0 Bxbf","America/Halifax|LMT AST ADT AWT APT|4e.o 40 30 30 30|0121212121212121212121212121212121212121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsHJ.A xzzJ.A 1db0 3I30 1in0 3HX0 IL0 1E10 ML0 1yN0 Pb0 1Bd0 Mn0 1Bd0 Rz0 1w10 Xb0 1w10 LX0 1w10 Xb0 1w10 Lz0 1C10 Jz0 1E10 OL0 1yN0 Un0 1qp0 Xb0 1qp0 11X0 1w10 Lz0 1HB0 LX0 1C10 FX0 1w10 Xb0 1qp0 Xb0 1BB0 LX0 1td0 Xb0 1qp0 Xb0 Rf0 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 6i10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Havana|HMT CST CDT|5t.A 50 40|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Meuu.o 72zu.o ML0 sld0 An0 1Nd0 Db0 1Nd0 An0 6Ep0 An0 1Nd0 An0 JDd0 Mn0 1Ap0 On0 1fd0 11X0 1qN0 WL0 1wp0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 14n0 1ld0 14L0 1kN0 15b0 1kp0 1cL0 1cN0 1fz0 1a10 1fz0 1fB0 11z0 14p0 1nX0 11B0 1nX0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 1a10 1in0 1a10 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 17c0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 11A0 6i00 Rc0 1wo0 U00 1tA0 Rc0 1wo0 U00 1wo0 U00 1zc0 U00 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0","America/Hermosillo|LMT MST CST PST MDT|7n.Q 70 60 80 60|0121212131414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0","America/Indiana/Knox|CST CDT CWT CPT EST|60 50 50 50 50|0101023010101010101010101010101010101040101010101010101010101010101010101010101010101010141010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 3Cn0 8wp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 z8o0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Marengo|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|0101023010101010101010104545454545414545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 dyN0 11z0 6fd0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1e6p0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Petersburg|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010104010101010101010101010141014545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 njX0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 3Fb0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 19co0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Tell_City|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010101010454541010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 g0p0 11z0 1o10 11z0 1qL0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 caL0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Vevay|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|010102304545454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 kPB0 Awn0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1lnd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Vincennes|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010101010454541014545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 g0p0 11z0 1o10 11z0 1qL0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 caL0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Indiana/Winamac|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|01010230101010101010101010101010101010454541054545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1za0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Inuvik|zzz PST PDDT MST MDT|0 80 60 70 60|0121343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-FnA0 tWU0 1fA0 wPe0 2pz0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Iqaluit|zzz EWT EPT EST EDDT EDT CST CDT|0 40 40 50 30 40 60 50|01234353535353535353535353535353535353535353567353535353535353535353535353535353535353535353535353535353535353535353535353|-16K00 7nX0 iv0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11C0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Jamaica|KMT EST EDT|57.b 50 40|0121212121212121212121|-2l1uQ.N 2uM1Q.N 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0","America/Juneau|PST PWT PPT PDT YDT YST AKST AKDT|80 70 70 70 80 90 90 80|01203030303030303030303030403030356767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17T20 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cM0 1cM0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Kentucky/Louisville|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|0101010102301010101010101010101010101454545454545414545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 3Fd0 Nb0 LPd0 11z0 RB0 8x30 iw0 Bb0 10N0 2bB0 8in0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 xz0 gso0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Kentucky/Monticello|CST CDT CWT CPT EST EDT|60 50 50 50 50 40|0101023010101010101010101010101010101010101010101010101010101010101010101454545454545454545454545454545454545454545454545454545454545454545454545454|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 SWp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/La_Paz|CMT BOST BOT|4w.A 3w.A 40|012|-1x37r.o 13b0","America/Lima|LMT PET PEST|58.A 50 40|0121212121212121|-2tyGP.o 1bDzP.o zX0 1aN0 1cL0 1cN0 1cL0 1PrB0 zX0 1O10 zX0 6Gp0 zX0 98p0 zX0","America/Los_Angeles|PST PDT PWT PPT|80 70 70 70|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261q0 1nX0 11B0 1nX0 SgN0 8x10 iy0 5Wp0 1Vb0 3dB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Maceio|LMT BRT BRST|2m.Q 30 20|012121212121212121212121212121212121212121|-2glxB.8 HdLB.8 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 8Q10 WL0 1tB0 5z0 2mN0 On0","America/Managua|MMT CST EST CDT|5J.c 60 50 50|0121313121213131|-1quie.M 1yAMe.M 4mn0 9Up0 Dz0 1K10 Dz0 s3F0 1KH0 DB0 9In0 k8p0 19X0 1o30 11y0","America/Manaus|LMT AMT AMST|40.4 40 30|01212121212121212121212121212121|-2glvX.U HdKX.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0","America/Martinique|FFMT AST ADT|44.k 40 30|0121|-2mPTT.E 2LPbT.E 19X0","America/Matamoros|LMT CST CDT|6E 60 50|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Mazatlan|LMT MST CST PST MDT|75.E 70 60 80 60|0121212131414141414141414141414141414141414141414141414141414141414141414141414141414141414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Menominee|CST CDT CWT CPT EST|60 50 50 50 50|01010230101041010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 LCN0 1fz0 6410 9Jb0 1cM0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Merida|LMT CST EST CDT|5W.s 60 50 50|0121313131313131313131313131313131313131313131313131313131313131313131313131313131313131|-1UQG0 2q2o0 2hz0 wu30 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Metlakatla|PST PWT PPT PDT|80 70 70 70|0120303030303030303030303030303030|-17T20 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0","America/Mexico_City|LMT MST CST CDT CWT|6A.A 70 60 50 50|012121232324232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 gEn0 TX0 3xd0 Jb0 6zB0 SL0 e5d0 17b0 1Pff0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Miquelon|LMT AST PMST PMDT|3I.E 40 30 20|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2mKkf.k 2LTAf.k gQ10 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Moncton|EST AST ADT AWT APT|50 40 30 30 30|012121212121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsH0 CwN0 1in0 zAo0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1K10 Lz0 1zB0 NX0 1u10 Wn0 S20 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 3Cp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14n1 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 ReX 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Monterrey|LMT CST CDT|6F.g 60 50|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Montevideo|MMT UYT UYHST UYST UYT UYHST|3I.I 3u 30 20 30 2u|012121212121212121212121213434343434345454543453434343434343434343434343434343434343434|-20UIf.g 8jzJ.g 1cLu 1dcu 1cLu 1dcu 1cLu ircu 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu WLu 1qMu WLu 1qMu 11zu 1o0u 11zu NAu 11bu 2iMu zWu Dq10 19X0 pd0 jz0 cm10 19X0 1fB0 1on0 11d0 1oL0 1nB0 1fzu 1aou 1fzu 1aou 1fzu 3nAu Jb0 3MN0 1SLu 4jzu 2PB0 Lb0 3Dd0 1pb0 ixd0 An0 1MN0 An0 1wp0 On0 1wp0 Rb0 1zd0 On0 1wp0 Rb0 s8p0 1fB0 1ip0 11z0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 11z0","America/Montreal|EST EDT EWT EPT|50 40 40 40|01010101010101010101010101010101010101010101012301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TR0 1in0 11Wu 1nzu 1fD0 WJ0 1wr0 Nb0 1Ap0 On0 1zd0 On0 1wp0 TX0 1tB0 TX0 1tB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 4kM0 8x40 iv0 1o10 11z0 1nX0 11z0 1o10 11z0 1o10 1qL0 11D0 1nX0 11B0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Nassau|LMT EST EDT|59.u 50 40|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2kNuO.u 26XdO.u 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/New_York|EST EDT EWT EPT|50 40 40 40|01010101010101010101010101010101010101010101010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 11B0 1qL0 1a10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x40 iv0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Nipigon|EST EDT EWT EPT|50 40 40 40|010123010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TR0 1in0 Rnb0 3je0 8x40 iv0 19yN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Nome|NST NWT NPT BST BDT YST AKST AKDT|b0 a0 a0 b0 a0 90 90 80|012034343434343434343434343434343456767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-17SX0 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cl0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Noronha|LMT FNT FNST|29.E 20 10|0121212121212121212121212121212121212121|-2glxO.k HdKO.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0","America/North_Dakota/Beulah|MST MDT MWT MPT CST CDT|70 60 60 60 60 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101014545454545454545454545454545454545454545454545454545454|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/North_Dakota/Center|MST MDT MWT MPT CST CDT|70 60 60 60 60 50|010102301010101010101010101010101010101010101010101010101014545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/North_Dakota/New_Salem|MST MDT MWT MPT CST CDT|70 60 60 60 60 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101454545454545454545454545454545454545454545454545454545454545454545454|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Ojinaga|LMT MST CST CDT MDT|6V.E 70 60 50 60|0121212323241414141414141414141414141414141414141414141414141414141414141414141414141414141|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Panama|CMT EST|5j.A 50|01|-2uduE.o","America/Pangnirtung|zzz AST AWT APT ADDT ADT EDT EST CST CDT|0 40 30 30 20 30 40 50 60 50|012314151515151515151515151515151515167676767689767676767676767676767676767676767676767676767676767676767676767676767676767|-1XiM0 PnG0 8x50 iu0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1o00 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11C0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Paramaribo|LMT PMT PMT NEGT SRT SRT|3E.E 3E.Q 3E.A 3u 3u 30|012345|-2nDUj.k Wqo0.c qanX.I 1dmLN.o lzc0","America/Phoenix|MST MDT MWT|70 60 60|01010202010|-261r0 1nX0 11B0 1nX0 SgN0 4Al1 Ap0 1db0 SWqX 1cL0","America/Port-au-Prince|PPMT EST EDT|4N 50 40|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-28RHb 2FnMb 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14q0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 i6n0 1nX0 11B0 1nX0 d430 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Porto_Acre|LMT ACT ACST AMT|4v.c 50 40 40|01212121212121212121212121212131|-2glvs.M HdLs.M 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0 d5X0","America/Porto_Velho|LMT AMT AMST|4f.A 40 30|012121212121212121212121212121|-2glvI.o HdKI.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0","America/Puerto_Rico|AST AWT APT|40 30 30|0120|-17lU0 7XT0 iu0","America/Rainy_River|CST CDT CWT CPT|60 50 50 50|010123010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TQ0 1in0 Rnb0 3je0 8x30 iw0 19yN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Rankin_Inlet|zzz CST CDDT CDT EST|0 60 40 50 50|012131313131313131313131313131313131313131313431313131313131313131313131313131313131313131313131313131313131313131313131|-vDc0 keu0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Recife|LMT BRT BRST|2j.A 30 20|0121212121212121212121212121212121212121|-2glxE.o HdLE.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0","America/Regina|LMT MST MDT MWT MPT CST|6W.A 70 60 60 60 60|012121212121212121212121341212121212121212121212121215|-2AD51.o uHe1.o 1in0 s2L0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 66N0 1cL0 1cN0 19X0 1fB0 1cL0 1fB0 1cL0 1cN0 1cL0 M30 8x20 ix0 1ip0 1cL0 1ip0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 3NB0 1cL0 1cN0","America/Resolute|zzz CST CDDT CDT EST|0 60 40 50 50|012131313131313131313131313131313131313131313431313131313431313131313131313131313131313131313131313131313131313131313131|-SnA0 GWS0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Santa_Isabel|LMT MST PST PDT PWT PPT|7D.s 70 80 70 70 70|012123245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQE0 4PX0 8mM0 8lc0 SN0 1cL0 pHB0 83r0 zI0 5O10 1Rz0 cOP0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 BUp0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Santarem|LMT AMT AMST BRT|3C.M 40 30 30|0121212121212121212121212121213|-2glwl.c HdLl.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0","America/Santiago|SMT CLT CLT CLST CLST CLT|4G.K 50 40 40 30 30|01020313131313121242124242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424245|-2q2jh.e fJAh.e 5knG.K 1Vzh.e jRAG.K 1pbh.e 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 nHX0 op0 9Bz0 jb0 1oN0 ko0 Qeo0 WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","America/Santo_Domingo|SDMT EST EDT EHDT AST|4E 50 40 4u 40|01213131313131414|-1ttjk 1lJMk Mn0 6sp0 Lbu 1Cou yLu 1RAu wLu 1QMu xzu 1Q0u xXu 1PAu 13jB0 e00","America/Sao_Paulo|LMT BRT BRST|36.s 30 20|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2glwR.w HdKR.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 pTd0 PX0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10 Lz0 1C10 On0 1zd0 Rb0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0","America/Scoresbysund|LMT CGT CGST EGST EGT|1r.Q 20 10 0 10|0121343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-2a5Ww.8 2z5ew.8 1a00 1cK0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","America/Sitka|PST PWT PPT PDT YST AKST AKDT|80 70 70 70 90 90 80|01203030303030303030303030303030345656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-17T20 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/St_Johns|NST NDT NST NDT NWT NPT NDDT|3u.Q 2u.Q 3u 2u 2u 2u 1u|01010101010101010101010101010101010102323232323232324523232323232323232323232323232323232323232323232323232323232323232323232323232323232326232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-28oit.8 14L0 1nB0 1in0 1gm0 Dz0 1JB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1fB0 19X0 1fB0 19X0 10O0 eKX.8 19X0 1iq0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Swift_Current|LMT MST MDT MWT MPT CST|7b.k 70 60 60 60 60|012134121212121212121215|-2AD4M.E uHdM.E 1in0 UGp0 8x20 ix0 1o10 17b0 1ip0 11z0 1o10 11z0 1o10 11z0 isN0 1cL0 3Cp0 1cL0 1cN0 11z0 1qN0 WL0 pMp0","America/Tegucigalpa|LMT CST CDT|5M.Q 60 50|01212121|-1WGGb.8 2ETcb.8 WL0 1qN0 WL0 GRd0 AL0","America/Thule|LMT AST ADT|4z.8 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a5To.Q 31NBo.Q 1cL0 1cN0 1cL0 1fB0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Thunder_Bay|CST EST EWT EPT EDT|60 50 40 40 40|0123141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141|-2q5S0 1iaN0 8x40 iv0 XNB0 1cL0 1cN0 1fz0 1cN0 1cL0 3Cp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Vancouver|PST PDT PWT PPT|80 70 70 70|0102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-25TO0 1in0 UGp0 8x10 iy0 1o10 17b0 1ip0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Whitehorse|YST YDT YWT YPT YDDT PST PDT|90 80 80 80 70 80 70|0101023040565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-25TN0 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 3NA0 vrd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Winnipeg|CST CDT CWT CPT|60 50 50 50|010101023010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aIi0 WL0 3ND0 1in0 Jap0 Rb0 aCN0 8x30 iw0 1tB0 11z0 1ip0 11z0 1o10 11z0 1o10 11z0 1rd0 10L0 1op0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 1cL0 1cN0 11z0 6i10 WL0 6i10 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Yakutat|YST YWT YPT YDT AKST AKDT|90 80 80 80 90 80|01203030303030303030303030303030304545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-17T10 8x00 iz0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cn0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Yellowknife|zzz MST MWT MPT MDDT MDT|0 70 60 60 50 60|012314151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151|-1pdA0 hix0 8x20 ix0 LCL0 1fA0 zgO0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Antarctica/Casey|zzz AWST CAST|0 -80 -b0|012121|-2q00 1DjS0 T90 40P0 KL0","Antarctica/Davis|zzz DAVT DAVT|0 -70 -50|01012121|-vyo0 iXt0 alj0 1D7v0 VB0 3Wn0 KN0","Antarctica/DumontDUrville|zzz PMT DDUT|0 -a0 -a0|0102|-U0o0 cfq0 bFm0","Antarctica/Macquarie|AEST AEDT zzz MIST|-a0 -b0 0 -b0|0102010101010101010101010101010101010101010101010101010101010101010101010101010101010101013|-29E80 19X0 4SL0 1ayy0 Lvs0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0","Antarctica/Mawson|zzz MAWT MAWT|0 -60 -50|012|-CEo0 2fyk0","Antarctica/McMurdo|NZMT NZST NZST NZDT|-bu -cu -c0 -d0|01020202020202020202020202023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-1GCVu Lz0 1tB0 11zu 1o0u 11zu 1o0u 11zu 1o0u 14nu 1lcu 14nu 1lcu 1lbu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1qLu WMu 1qLu 11Au 1n1bu IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Antarctica/Palmer|zzz ARST ART ART ARST CLT CLST CLT|0 30 40 30 20 40 30 30|012121212123435656565656565656565656565656565656565656565656565656565656565656567|-cao0 nD0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 jsN0 14N0 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","Antarctica/Rothera|zzz ROTT|0 30|01|gOo0","Antarctica/Syowa|zzz SYOT|0 -30|01|-vs00","Antarctica/Troll|zzz UTC CEST|0 0 -20|01212121212121212121212121212121212121212121212121212121212121212121|1puo0 hd0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Antarctica/Vostok|zzz VOST|0 -60|01|-tjA0","Arctic/Longyearbyen|CET CEST|-10 -20|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2awM0 Qm0 W6o0 5pf0 WM0 1fA0 1cM0 1cM0 1cM0 1cM0 wJc0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1qM0 WM0 zpc0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Aden|LMT AST|-36.Q -30|01|-TvD6.Q","Asia/Almaty|LMT ALMT ALMT ALMST|-57.M -50 -60 -70|0123232323232323232323232323232323232323232323232|-1Pc57.M eUo7.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 3Cl0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Amman|LMT EET EEST|-2n.I -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1yW2n.I 1HiMn.I KL0 1oN0 11b0 1oN0 11b0 1pd0 1dz0 1cp0 11b0 1op0 11b0 fO10 1db0 1e10 1cL0 1cN0 1cL0 1cN0 1fz0 1pd0 10n0 1ld0 14n0 1hB0 15b0 1ip0 19X0 1cN0 1cL0 1cN0 17b0 1ld0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1So0 y00 1fc0 1dc0 1co0 1dc0 1cM0 1cM0 1cM0 1o00 11A0 1lc0 17c0 1cM0 1cM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 4bX0 Dd0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0","Asia/Anadyr|LMT ANAT ANAT ANAST ANAST ANAST ANAT|-bN.U -c0 -d0 -e0 -d0 -c0 -b0|01232414141414141414141561414141414141414141414141414141414141561|-1PcbN.U eUnN.U 23CL0 1db0 1cN0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0","Asia/Aqtau|LMT FORT FORT SHET SHET SHEST AQTT AQTST AQTST AQTT|-3l.4 -40 -50 -50 -60 -60 -50 -60 -50 -40|012345353535353535353536767676898989898989898989896|-1Pc3l.4 eUnl.4 1jcL0 JDc0 1cL0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2UK0 Fz0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cN0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 RW0","Asia/Aqtobe|LMT AKTT AKTT AKTST AKTT AQTT AQTST|-3M.E -40 -50 -60 -60 -50 -60|01234323232323232323232565656565656565656565656565|-1Pc3M.E eUnM.E 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2UK0 Fz0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Ashgabat|LMT ASHT ASHT ASHST ASHST TMT TMT|-3R.w -40 -50 -60 -50 -40 -50|012323232323232323232324156|-1Pc3R.w eUnR.w 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 ba0 xC0","Asia/Baghdad|BMT AST ADT|-2V.A -30 -40|012121212121212121212121212121212121212121212121212121|-26BeV.A 2ACnV.A 11b0 1cp0 1dz0 1dd0 1db0 1cN0 1cp0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1de0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0","Asia/Bahrain|LMT GST AST|-3q.8 -40 -30|012|-21Jfq.8 27BXq.8","Asia/Baku|LMT BAKT BAKT BAKST BAKST AZST AZT AZT AZST|-3j.o -30 -40 -50 -40 -40 -30 -40 -50|0123232323232323232323245657878787878787878787878787878787878787878787878787878787878787878787878787878787878787|-1Pc3j.o 1jUoj.o WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 10K0 c30 1cJ0 1cL0 8wu0 1o00 11z0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Bangkok|BMT ICT|-6G.4 -70|01|-218SG.4","Asia/Beirut|EET EEST|-20 -30|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-21aq0 1on0 1410 1db0 19B0 1in0 1ip0 WL0 1lQp0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 q6N0 En0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1op0 11b0 dA10 17b0 1iN0 17b0 1iN0 17b0 1iN0 17b0 1vB0 SL0 1mp0 13z0 1iN0 17b0 1iN0 17b0 1jd0 12n0 1a10 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0","Asia/Bishkek|LMT FRUT FRUT FRUST FRUST KGT KGST KGT|-4W.o -50 -60 -70 -60 -50 -60 -60|01232323232323232323232456565656565656565656565656567|-1Pc4W.o eUnW.o 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 11c0 1tX0 17b0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1cPu 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 T8u","Asia/Brunei|LMT BNT BNT|-7D.E -7u -80|012|-1KITD.E gDc9.E","Asia/Calcutta|HMT BURT IST IST|-5R.k -6u -5u -6u|01232|-18LFR.k 1unn.k HB0 7zX0","Asia/Chita|LMT YAKT YAKT YAKST YAKST YAKT IRKT|-7x.Q -80 -90 -a0 -90 -a0 -80|012323232323232323232324123232323232323232323232323232323232323256|-21Q7x.Q pAnx.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Choibalsan|LMT ULAT ULAT CHOST CHOT CHOT CHOST|-7C -70 -80 -a0 -90 -80 -90|0123434343434343434343434343434343434343434343456565656565656565656565656565656565656565656565|-2APHC 2UkoC cKn0 1da0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 3Db0 h1f0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Chongqing|CST CDT|-80 -90|01010101010101010|-1c1I0 LX0 16p0 1jz0 1Myp0 Rb0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0","Asia/Colombo|MMT IST IHST IST LKT LKT|-5j.w -5u -60 -6u -6u -60|01231451|-2zOtj.w 1rFbN.w 1zzu 7Apu 23dz0 11zu n3cu","Asia/Dacca|HMT BURT IST DACT BDT BDST|-5R.k -6u -5u -60 -60 -70|01213454|-18LFR.k 1unn.k HB0 m6n0 LqMu 1x6n0 1i00","Asia/Damascus|LMT EET EEST|-2p.c -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-21Jep.c Hep.c 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1xRB0 11X0 1oN0 10L0 1pB0 11b0 1oN0 10L0 1mp0 13X0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 Nb0 1AN0 Nb0 bcp0 19X0 1gp0 19X0 3ld0 1xX0 Vd0 1Bz0 Sp0 1vX0 10p0 1dz0 1cN0 1cL0 1db0 1db0 1g10 1an0 1ap0 1db0 1fd0 1db0 1cN0 1db0 1dd0 1db0 1cp0 1dz0 1c10 1dX0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 19z0 1fB0 1qL0 11B0 1on0 Wp0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0","Asia/Dili|LMT TLT JST TLT WITA|-8m.k -80 -90 -90 -80|012343|-2le8m.k 1dnXm.k 8HA0 1ew00 Xld0","Asia/Dubai|LMT GST|-3F.c -40|01|-21JfF.c","Asia/Dushanbe|LMT DUST DUST DUSST DUSST TJT|-4z.c -50 -60 -70 -60 -50|0123232323232323232323245|-1Pc4z.c eUnz.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 14N0","Asia/Gaza|EET EET EEST IST IDT|-20 -30 -30 -20 -30|010101010102020202020202020202023434343434343434343434343430202020202020202020202020202020202020202020202020202020202020202020202020202020202020|-1c2q0 5Rb0 10r0 1px0 10N0 1pz0 16p0 1jB0 16p0 1jx0 pBd0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 npB0 11z0 1C10 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 11z0 1o10 14o0 1lA1 SKX 1xd1 MKX 1AN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0","Asia/Hebron|EET EET EEST IST IDT|-20 -30 -30 -20 -30|01010101010202020202020202020202343434343434343434343434343020202020202020202020202020202020202020202020202020202020202020202020202020202020202020|-1c2q0 5Rb0 10r0 1px0 10N0 1pz0 16p0 1jB0 16p0 1jx0 pBd0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 npB0 11z0 1C10 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 12L0 1mN0 14o0 1lc0 Tb0 1xd1 MKX bB0 cn0 1cN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0","Asia/Ho_Chi_Minh|LMT PLMT ICT IDT JST|-76.E -76.u -70 -80 -90|0123423232|-2yC76.E bK00.a 1h7b6.u 5lz0 18o0 3Oq0 k5b0 aW00 BAM0","Asia/Hong_Kong|LMT HKT HKST JST|-7A.G -80 -90 -90|0121312121212121212121212121212121212121212121212121212121212121212121|-2CFHA.G 1sEP6.G 1cL0 ylu 93X0 1qQu 1tX0 Rd0 1In0 NB0 1cL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1kL0 14N0 1nX0 U10 1tz0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 Rd0 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 17d0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1cL0 6fd0 14n0","Asia/Hovd|LMT HOVT HOVT HOVST|-66.A -60 -70 -80|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2APG6.A 2Uko6.A cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Irkutsk|IMT IRKT IRKT IRKST IRKST IRKT|-6V.5 -70 -80 -90 -80 -90|012323232323232323232324123232323232323232323232323232323232323252|-21zGV.5 pjXV.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Istanbul|IMT EET EEST TRST TRT|-1U.U -20 -30 -40 -30|012121212121212121212121212121212121212121212121212121234343434342121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ogNU.U dzzU.U 11b0 8tB0 1on0 1410 1db0 19B0 1in0 3Rd0 Un0 1oN0 11b0 zSp0 CL0 mN0 1Vz0 1gN0 1pz0 5Rd0 1fz0 1yp0 ML0 1kp0 17b0 1ip0 17b0 1fB0 19X0 1jB0 18L0 1ip0 17z0 qdd0 xX0 3S10 Tz0 dA10 11z0 1o10 11z0 1qN0 11z0 1ze0 11B0 WM0 1qO0 WI0 1nX0 1rB0 10L0 11B0 1in0 17d0 1in0 2pX0 19E0 1fU0 16Q0 1iI0 16Q0 1iI0 1Vd0 pb0 3Kp0 14o0 1df0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WO0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 Xc0 1qo0 WM0 1qM0 11A0 1o00 1200 1nA0 11A0 1tA0 U00 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Jakarta|BMT JAVT WIB JST WIB WIB|-77.c -7k -7u -90 -80 -70|01232425|-1Q0Tk luM0 mPzO 8vWu 6kpu 4PXu xhcu","Asia/Jayapura|LMT WIT ACST|-9m.M -90 -9u|0121|-1uu9m.M sMMm.M L4nu","Asia/Jerusalem|JMT IST IDT IDDT|-2k.E -20 -30 -40|01212121212132121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-26Bek.E SyMk.E 5Rb0 10r0 1px0 10N0 1pz0 16p0 1jB0 16p0 1jx0 3LB0 Em0 or0 1cn0 1dB0 16n0 10O0 1ja0 1tC0 14o0 1cM0 1a00 11A0 1Na0 An0 1MP0 AJ0 1Kp0 LC0 1oo0 Wl0 EQN0 Db0 1fB0 Rb0 npB0 11z0 1C10 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 1hB0 1dX0 1ep0 1aL0 1eN0 17X0 1nf0 11z0 1tB0 19W0 1e10 17b0 1ep0 1gL0 18N0 1fz0 1eN0 17b0 1gq0 1gn0 19d0 1dz0 1c10 17X0 1hB0 1gn0 19d0 1dz0 1c10 17X0 1kp0 1dz0 1c10 1aL0 1eN0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0","Asia/Kabul|AFT AFT|-40 -4u|01|-10Qs0","Asia/Kamchatka|LMT PETT PETT PETST PETST|-ay.A -b0 -c0 -d0 -c0|01232323232323232323232412323232323232323232323232323232323232412|-1SLKy.A ivXy.A 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0","Asia/Karachi|LMT IST IST KART PKT PKST|-4s.c -5u -6u -50 -50 -60|012134545454|-2xoss.c 1qOKW.c 7zX0 eup0 LqMu 1fy01 1cL0 dK0X 11b0 1610 1jX0","Asia/Kashgar|LMT XJT|-5O.k -60|01|-1GgtO.k","Asia/Kathmandu|LMT IST NPT|-5F.g -5u -5J|012|-21JhF.g 2EGMb.g","Asia/Khandyga|LMT YAKT YAKT YAKST YAKST VLAT VLAST VLAT YAKT|-92.d -80 -90 -a0 -90 -a0 -b0 -b0 -a0|01232323232323232323232412323232323232323232323232565656565656565782|-21Q92.d pAp2.d 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 qK0 yN0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0","Asia/Krasnoyarsk|LMT KRAT KRAT KRAST KRAST KRAT|-6b.q -60 -70 -80 -70 -80|012323232323232323232324123232323232323232323232323232323232323252|-21Hib.q prAb.q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Kuala_Lumpur|SMT MALT MALST MALT MALT JST MYT|-6T.p -70 -7k -7k -7u -90 -80|01234546|-2Bg6T.p 17anT.p 7hXE dM00 17bO 8Fyu 1so1u","Asia/Kuching|LMT BORT BORT BORTST JST MYT|-7l.k -7u -80 -8k -90 -80|01232323232323232425|-1KITl.k gDbP.k 6ynu AnE 1O0k AnE 1NAk AnE 1NAk AnE 1NAk AnE 1O0k AnE 1NAk AnE pAk 8Fz0 1so10","Asia/Macao|LMT MOT MOST CST|-7y.k -80 -90 -80|0121212121212121212121212121212121212121213|-2le7y.k 1XO34.k 1wn0 Rd0 1wn0 R9u 1wqu U10 1tz0 TVu 1tz0 17gu 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cJu 1cL0 1cN0 1fz0 1cN0 1cOu 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cJu 1cL0 1cN0 1fz0 1cN0 1cL0 KEp0","Asia/Magadan|LMT MAGT MAGT MAGST MAGST MAGT|-a3.c -a0 -b0 -c0 -b0 -c0|012323232323232323232324123232323232323232323232323232323232323251|-1Pca3.c eUo3.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Makassar|LMT MMT WITA JST|-7V.A -7V.A -80 -90|01232|-21JjV.A vfc0 myLV.A 8ML0","Asia/Manila|PHT PHST JST|-80 -90 -90|010201010|-1kJI0 AL0 cK10 65X0 mXB0 vX0 VK10 1db0","Asia/Nicosia|LMT EET EEST|-2d.s -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Vc2d.s 2a3cd.s 1cL0 1qp0 Xz0 19B0 19X0 1fB0 1db0 1cp0 1cL0 1fB0 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1o30 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Novokuznetsk|LMT KRAT KRAT KRAST KRAST NOVST NOVT NOVT|-5M.M -60 -70 -80 -70 -70 -60 -70|012323232323232323232324123232323232323232323232323232323232325672|-1PctM.M eULM.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0 8Hz0","Asia/Novosibirsk|LMT NOVT NOVT NOVST NOVST|-5v.E -60 -70 -80 -70|0123232323232323232323241232341414141414141414141414141414141414121|-21Qnv.E pAFv.E 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 ml0 Os0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Omsk|LMT OMST OMST OMSST OMSST OMST|-4R.u -50 -60 -70 -60 -70|012323232323232323232324123232323232323232323232323232323232323252|-224sR.u pMLR.u 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Oral|LMT URAT URAT URAST URAT URAST ORAT ORAST ORAT|-3p.o -40 -50 -60 -60 -50 -40 -50 -50|012343232323232323251516767676767676767676767676768|-1Pc3p.o eUnp.o 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 2UK0 Fz0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 RW0","Asia/Pontianak|LMT PMT WIB JST WIB WITA WIB|-7h.k -7h.k -7u -90 -80 -80 -70|012324256|-2ua7h.k XE00 munL.k 8Rau 6kpu 4PXu xhcu Wqnu","Asia/Pyongyang|LMT KST JCST JST KST|-8n -8u -90 -90 -90|012341|-2um8n 97XR 12FXu jdA0 2Onc0","Asia/Qyzylorda|LMT KIZT KIZT KIZST KIZT QYZT QYZT QYZST|-4l.Q -40 -50 -60 -60 -50 -60 -70|012343232323232323232325676767676767676767676767676|-1Pc4l.Q eUol.Q 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2UK0 dC0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Rangoon|RMT BURT JST MMT|-6o.E -6u -90 -6u|0123|-21Jio.E SmnS.E 7j9u","Asia/Sakhalin|LMT JCST JST SAKT SAKST SAKST SAKT|-9u.M -90 -90 -b0 -c0 -b0 -a0|0123434343434343434343435634343434343565656565656565656565656565636|-2AGVu.M 1iaMu.M je00 1qFa0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o10 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Samarkand|LMT SAMT SAMT SAMST TAST UZST UZT|-4r.R -40 -50 -60 -60 -60 -50|01234323232323232323232356|-1Pc4r.R eUor.R 23CL0 1db0 1cM0 1dc0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 11x0 bf0","Asia/Seoul|LMT KST JCST JST KST KDT KDT|-8r.Q -8u -90 -90 -90 -9u -a0|01234151515151515146464|-2um8r.Q 97XV.Q 12FXu jjA0 kKo0 2I0u OL0 1FB0 Rb0 1qN0 TX0 1tB0 TX0 1tB0 TX0 1tB0 TX0 2ap0 12FBu 11A0 1o00 11A0","Asia/Singapore|SMT MALT MALST MALT MALT JST SGT SGT|-6T.p -70 -7k -7k -7u -90 -7u -80|012345467|-2Bg6T.p 17anT.p 7hXE dM00 17bO 8Fyu Mspu DTA0","Asia/Srednekolymsk|LMT MAGT MAGT MAGST MAGST MAGT SRET|-ae.Q -a0 -b0 -c0 -b0 -c0 -b0|012323232323232323232324123232323232323232323232323232323232323256|-1Pcae.Q eUoe.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Taipei|JWST JST CST CDT|-80 -90 -80 -90|01232323232323232323232323232323232323232|-1iw80 joM0 1yo0 Tz0 1ip0 1jX0 1cN0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 10N0 1BX0 10p0 1pz0 10p0 1pz0 10p0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1BB0 ML0 1Bd0 ML0 uq10 1db0 1cN0 1db0 97B0 AL0","Asia/Tashkent|LMT TAST TAST TASST TASST UZST UZT|-4B.b -50 -60 -70 -60 -60 -50|01232323232323232323232456|-1Pc4B.b eUnB.b 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 11y0 bf0","Asia/Tbilisi|TBMT TBIT TBIT TBIST TBIST GEST GET GET GEST|-2X.b -30 -40 -50 -40 -40 -30 -40 -50|0123232323232323232323245656565787878787878787878567|-1Pc2X.b 1jUnX.b WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 3y0 19f0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cM0 1cL0 1fB0 3Nz0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 An0 Os0 WM0","Asia/Tehran|LMT TMT IRST IRST IRDT IRDT|-3p.I -3p.I -3u -40 -50 -4u|01234325252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-2btDp.I 1d3c0 1huLT.I TXu 1pz0 sN0 vAu 1cL0 1dB0 1en0 pNB0 UL0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 64p0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0","Asia/Thimbu|LMT IST BTT|-5W.A -5u -60|012|-Su5W.A 1BGMs.A","Asia/Tokyo|JCST JST JDT|-90 -90 -a0|0121212121|-1iw90 pKq0 QL0 1lB0 13X0 1zB0 NX0 1zB0 NX0","Asia/Ulaanbaatar|LMT ULAT ULAT ULAST|-77.w -70 -80 -90|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2APH7.w 2Uko7.w cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Ust-Nera|LMT YAKT YAKT MAGST MAGT MAGST MAGT MAGT VLAT VLAT|-9w.S -80 -90 -c0 -b0 -b0 -a0 -c0 -b0 -a0|0123434343434343434343456434343434343434343434343434343434343434789|-21Q9w.S pApw.S 23CL0 1d90 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0","Asia/Vladivostok|LMT VLAT VLAT VLAST VLAST VLAT|-8L.v -90 -a0 -b0 -a0 -b0|012323232323232323232324123232323232323232323232323232323232323252|-1SJIL.v itXL.v 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Yakutsk|LMT YAKT YAKT YAKST YAKST YAKT|-8C.W -80 -90 -a0 -90 -a0|012323232323232323232324123232323232323232323232323232323232323252|-21Q8C.W pAoC.W 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Yekaterinburg|LMT PMT SVET SVET SVEST SVEST YEKT YEKST YEKT|-42.x -3J.5 -40 -50 -60 -50 -50 -60 -60|0123434343434343434343435267676767676767676767676767676767676767686|-2ag42.x 7mQh.s qBvJ.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Asia/Yerevan|LMT YERT YERT YERST YERST AMST AMT AMT AMST|-2W -30 -40 -50 -40 -40 -30 -40 -50|0123232323232323232323245656565657878787878787878787878787878787|-1Pc2W 1jUnW WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1am0 2r0 1cJ0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 3Fb0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0","Atlantic/Azores|HMT AZOT AZOST AZOMT AZOT AZOST WET|1S.w 20 10 0 10 0 0|01212121212121212121212121212121212121212121232123212321232121212121212121212121212121212121212121454545454545454545454545454545456545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2ldW5.s aPX5.s Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 3Co0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Bermuda|LMT AST ADT|4j.i 40 30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1BnRE.G 1LTbE.G 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Atlantic/Canary|LMT CANT WET WEST|11.A 10 0 -10|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UtaW.o XPAW.o 1lAK0 1a10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Cape_Verde|LMT CVT CVST CVT|1y.4 20 10 10|01213|-2xomp.U 1qOMp.U 7zX0 1djf0","Atlantic/Faeroe|LMT WET WEST|r.4 0 -10|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2uSnw.U 2Wgow.U 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Madeira|FMT MADT MADST MADMT WET WEST|17.A 10 0 -10 0 -10|01212121212121212121212121212121212121212121232123212321232121212121212121212121212121212121212121454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2ldWQ.o aPWQ.o Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 3Co0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Reykjavik|LMT IST ISST GMT|1s 10 0 0|012121212121212121212121212121212121212121212121212121212121212121213|-2uWmw mfaw 1Bd0 ML0 1LB0 Cn0 1LB0 3fX0 C10 HrX0 1cO0 LB0 1EL0 LA0 1C00 Oo0 1wo0 Rc0 1wo0 Rc0 1wo0 Rc0 1zc0 Oo0 1zc0 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1lc0 14o0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 14o0","Atlantic/South_Georgia|GST|20|0|","Atlantic/Stanley|SMT FKT FKST FKT FKST|3P.o 40 30 30 20|0121212121212134343212121212121212121212121212121212121212121212121212|-2kJw8.A 12bA8.A 19X0 1fB0 19X0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 Cn0 1Cc10 WL0 1qL0 U10 1tz0 U10 1qM0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 U10 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qN0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 U10 1tz0 U10 1tz0 U10","Australia/ACT|AEST AEDT|-a0 -b0|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Adelaide|ACST ACDT|-9u -au|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lt xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 WM0 1qM0 Rc0 1zc0 U00 1tA0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Brisbane|AEST AEDT|-a0 -b0|01010101010101010|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0","Australia/Broken_Hill|ACST ACDT|-9u -au|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lt xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Currie|AEST AEDT|-a0 -b0|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-29E80 19X0 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Darwin|ACST ACDT|-9u -au|010101010|-293lt xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0","Australia/Eucla|ACWST ACWDT|-8J -9J|0101010101010101010|-293kI xcX 10jd0 yL0 1cN0 1cL0 1gSp0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0","Australia/Hobart|AEST AEDT|-a0 -b0|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-29E80 19X0 10jd0 yL0 1cN0 1cL0 1fB0 19X0 VfB0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/LHI|AEST LHST LHDT LHDT|-a0 -au -bu -b0|0121212121313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313|raC0 1zdu Rb0 1zd0 On0 1zd0 On0 1zd0 On0 1zd0 TXu 1qMu WLu 1tAu WLu 1tAu TXu 1tAu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu 11zu 1o0u 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 11Au 1nXu 1qMu 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu 11zu 1o0u WLu 1qMu 14nu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu","Australia/Lindeman|AEST AEDT|-a0 -b0|010101010101010101010|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0","Australia/Melbourne|AEST AEDT|-a0 -b0|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|-293lX xcX 10jd0 yL0 1cN0 1cL0 1fB0 19X0 17c10 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1qM0 11A0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Perth|AWST AWDT|-80 -90|0101010101010101010|-293jX xcX 10jd0 yL0 1cN0 1cL0 1gSp0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0","CET|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","CST6CDT|CST CDT CWT CPT|60 50 50 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Chile/EasterIsland|EMT EAST EASST EAST EASST EAST|7h.s 70 60 60 50 50|012121212121212121212121212123434343434343434343434343434343434343434343434343434343434343434345|-1uSgG.w 1s4IG.w WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","EET|EET EEST|-20 -30|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","EST|EST|50|0|","EST5EDT|EST EDT EWT EPT|50 40 40 40|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 SgN0 8x40 iv0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Eire|DMT IST GMT BST IST|p.l -y.D 0 -10 -10|01232323232324242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242|-2ax9y.D Rc0 1fzy.D 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 g5X0 14p0 1wn0 17d0 1io0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Etc/GMT+0|GMT|0|0|","Etc/GMT+1|GMT+1|10|0|","Etc/GMT+10|GMT+10|a0|0|","Etc/GMT+11|GMT+11|b0|0|","Etc/GMT+12|GMT+12|c0|0|","Etc/GMT+2|GMT+2|20|0|","Etc/GMT+3|GMT+3|30|0|","Etc/GMT+4|GMT+4|40|0|","Etc/GMT+5|GMT+5|50|0|","Etc/GMT+6|GMT+6|60|0|","Etc/GMT+7|GMT+7|70|0|","Etc/GMT+8|GMT+8|80|0|","Etc/GMT+9|GMT+9|90|0|","Etc/GMT-1|GMT-1|-10|0|","Etc/GMT-10|GMT-10|-a0|0|","Etc/GMT-11|GMT-11|-b0|0|","Etc/GMT-12|GMT-12|-c0|0|","Etc/GMT-13|GMT-13|-d0|0|","Etc/GMT-14|GMT-14|-e0|0|","Etc/GMT-2|GMT-2|-20|0|","Etc/GMT-3|GMT-3|-30|0|","Etc/GMT-4|GMT-4|-40|0|","Etc/GMT-5|GMT-5|-50|0|","Etc/GMT-6|GMT-6|-60|0|","Etc/GMT-7|GMT-7|-70|0|","Etc/GMT-8|GMT-8|-80|0|","Etc/GMT-9|GMT-9|-90|0|","Etc/UCT|UCT|0|0|","Etc/UTC|UTC|0|0|","Europe/Amsterdam|AMT NST NEST NET CEST CET|-j.w -1j.w -1k -k -20 -10|010101010101010101010101010101010101010101012323234545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-2aFcj.w 11b0 1iP0 11A0 1io0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1co0 1io0 1yo0 Pc0 1a00 1fA0 1Bc0 Mo0 1tc0 Uo0 1tA0 U00 1uo0 W00 1s00 VA0 1so0 Vc0 1sM0 UM0 1wo0 Rc0 1u00 Wo0 1rA0 W00 1s00 VA0 1sM0 UM0 1w00 fV0 BCX.w 1tA0 U00 1u00 Wo0 1sm0 601k WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Andorra|WET CET CEST|0 -10 -20|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-UBA0 1xIN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Athens|AMT EET EEST CEST CET|-1y.Q -20 -30 -20 -10|012123434121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a61x.Q CNbx.Q mn0 kU10 9b0 3Es0 Xa0 1fb0 1dd0 k3X0 Nz0 SCp0 1vc0 SO0 1cM0 1a00 1ao0 1fc0 1a10 1fG0 1cg0 1dX0 1bX0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Belfast|GMT BST BDST|0 -10 -20|0101010101010101010101010101010101010101010101010121212121210101210101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2axa0 Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Belgrade|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-19RC0 3IP0 WM0 1fA0 1cM0 1cM0 1rc0 Qo0 1vmo0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Berlin|CET CEST CEMT|-10 -20 -30|01010101010101210101210101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 kL0 Nc0 m10 WM0 1ao0 1cp0 dX0 jz0 Dd0 1io0 17c0 1fA0 1a00 1ehA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Bratislava|CET CEST|-10 -20|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 16M0 1lc0 1tA0 17A0 11c0 1io0 17c0 1io0 17c0 1fc0 1ao0 1bNc0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Brussels|WET CET CEST WEST|0 -10 -20 -10|0121212103030303030303030303030303030303030303030303212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ehc0 3zX0 11c0 1iO0 11A0 1o00 11A0 my0 Ic0 1qM0 Rc0 1EM0 UM0 1u00 10o0 1io0 1io0 17c0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a30 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 y00 5Wn0 WM0 1fA0 1cM0 16M0 1iM0 16M0 1C00 Uo0 1eeo0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Bucharest|BMT EET EEST|-1I.o -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1xApI.o 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Axc0 On0 1fA0 1a10 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Budapest|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1ip0 17b0 1op0 1tb0 Q2m0 3Ne0 WM0 1fA0 1cM0 1cM0 1oJ0 1dc0 1030 1fA0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1iM0 1fA0 8Ha0 Rb0 1wN0 Rb0 1BB0 Lz0 1C20 LB0 SNX0 1a10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Busingen|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-19Lc0 11A0 1o00 11A0 1xG10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Chisinau|CMT BMT EET EEST CEST CET MSK MSD|-1T -1I.o -20 -30 -20 -10 -30 -40|0123232323232323232345454676767676767676767623232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-26jdT wGMa.A 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 27A0 2en0 39g0 WM0 1fA0 1cM0 V90 1t7z0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1ty0 2bD0 1cM0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11D0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Copenhagen|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2azC0 Tz0 VuO0 60q0 WM0 1fA0 1cM0 1cM0 1cM0 S00 1HA0 Nc0 1C00 Dc0 1Nc0 Ao0 1h5A0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Gibraltar|GMT BST BDST CET CEST|0 -10 -20 -10 -20|010101010101010101010101010101010101010101010101012121212121010121010101010101010101034343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-2axa0 Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 10Jz0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Helsinki|HMT EET EEST|-1D.N -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1WuND.N OULD.N 1dA0 1xGq0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Kaliningrad|CET CEST CET CEST MSK MSD EEST EET FET|-10 -20 -20 -30 -30 -40 -30 -20 -30|0101010101010232454545454545454545454676767676767676767676767676767676767676787|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 Am0 Lb0 1en0 op0 1pNz0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1cJ0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Europe/Kiev|KMT EET MSK CEST CET MSD EEST|-22.4 -20 -30 -20 -10 -40 -30|0123434252525252525252525256161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161|-1Pc22.4 eUo2.4 rnz0 2Hg0 WM0 1fA0 da0 1v4m0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 Db0 3220 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cQ0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Lisbon|LMT WET WEST WEMT CET CEST|A.J 0 -10 -20 -10 -20|012121212121212121212121212121212121212121212321232123212321212121212121212121212121212121212121214121212121212121212121212121212124545454212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ldXn.f aPWn.f Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 3Co0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 pvy0 1cM0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Luxembourg|LMT CET CEST WET WEST WEST WET|-o.A -10 -20 0 -10 -20 -10|0121212134343434343434343434343434343434343434343434565651212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2DG0o.A t6mo.A TB0 1nX0 Up0 1o20 11A0 rW0 CM0 1qP0 R90 1EO0 UK0 1u20 10m0 1ip0 1in0 17e0 19W0 1fB0 1db0 1cp0 1in0 17d0 1fz0 1a10 1in0 1a10 1in0 17f0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 vA0 60L0 WM0 1fA0 1cM0 17c0 1io0 16M0 1C00 Uo0 1eeo0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Madrid|WET WEST WEMT CET CEST|0 -10 -20 -10 -20|01010101010101010101010121212121234343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-28dd0 11A0 1go0 19A0 1co0 1dA0 b1A0 18o0 3I00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 iyo0 Rc0 18o0 1hc0 1io0 1a00 14o0 5aL0 MM0 1vc0 17A0 1i00 1bc0 1eo0 17d0 1in0 17A0 6hA0 10N0 XIL0 1a10 1in0 17d0 19X0 1cN0 1fz0 1a10 1fX0 1cp0 1cO0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Malta|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2as10 M00 1cM0 1cM0 14o0 1o00 WM0 1qM0 17c0 1cM0 M3A0 5M20 WM0 1fA0 1cM0 1cM0 1cM0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 Lz0 1C10 Lz0 1EN0 Lz0 1C10 Lz0 1zd0 Oo0 1C00 On0 1cp0 1cM0 1lA0 Xc0 1qq0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1iN0 19z0 1fB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Minsk|MMT EET MSK CEST CET MSD EEST FET|-1O -20 -30 -20 -10 -40 -30 -30|012343432525252525252525252616161616161616161616161616161616161616172|-1Pc1O eUnO qNX0 3gQ0 WM0 1fA0 1cM0 Al0 1tsn0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 3Fc0 1cN0 1cK0 1cM0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hy0","Europe/Monaco|PMT WET WEST WEMT CET CEST|-9.l 0 -10 -20 -10 -20|01212121212121212121212121212121212121212121212121232323232345454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2nco9.l cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 1u00 10o0 1io0 1wo0 Rc0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Df0 2RV0 11z0 11B0 1ze0 WM0 1fA0 1cM0 1fa0 1aq0 16M0 1ekn0 1cL0 1fC0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Moscow|MMT MMT MST MDST MSD MSK MSM EET EEST MSK|-2u.h -2v.j -3v.j -4v.j -40 -30 -50 -20 -30 -40|012132345464575454545454545454545458754545454545454545454545454545454545454595|-2ag2u.h 2pyW.W 1bA0 11X0 GN0 1Hb0 c20 imv.j 3DA0 dz0 15A0 c10 2q10 iM10 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rU0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Europe/Paris|PMT WET WEST CEST CET WEMT|-9.l 0 -10 -20 -10 -20|0121212121212121212121212121212121212121212121212123434352543434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-2nco8.l cNb8.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 1u00 10o0 1io0 1wo0 Rc0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Df0 Ik0 5M30 WM0 1fA0 1cM0 Vx0 hB0 1aq0 16M0 1ekn0 1cL0 1fC0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Riga|RMT LST EET MSK CEST CET MSD EEST|-1A.y -2A.y -20 -30 -20 -10 -40 -30|010102345454536363636363636363727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272|-25TzA.y 11A0 1iM0 ko0 gWm0 yDXA.y 2bX0 3fE0 WM0 1fA0 1cM0 1cM0 4m0 1sLy0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1o00 11A0 1o00 11A0 1qM0 3oo0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Rome|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2as10 M00 1cM0 1cM0 14o0 1o00 WM0 1qM0 17c0 1cM0 M3A0 5M20 WM0 1fA0 1cM0 16K0 1iO0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 Lz0 1C10 Lz0 1EN0 Lz0 1C10 Lz0 1zd0 Oo0 1C00 On0 1C10 Lz0 1zd0 On0 1C10 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1zc0 Oo0 1fC0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Samara|LMT SAMT SAMT KUYT KUYST MSD MSK EEST KUYT SAMST SAMST|-3k.k -30 -40 -40 -50 -40 -30 -30 -30 -50 -40|012343434343434343435656782929292929292929292929292929292929292a12|-22WNk.k qHak.k bcn0 1Qqo0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cN0 8o0 14j0 1cL0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qN0 WM0","Europe/Simferopol|SMT EET MSK CEST CET MSD EEST MSK|-2g -20 -30 -20 -10 -40 -30 -40|012343432525252525252525252161616525252616161616161616161616161616161616172|-1Pc2g eUog rEn0 2qs0 WM0 1fA0 1cM0 3V0 1u0L0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Q00 4eL0 1cL0 1cN0 1cL0 1cN0 dX0 WL0 1cN0 1cL0 1fB0 1o30 11B0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11z0 1nW0","Europe/Sofia|EET CET CEST EEST|-20 -10 -20 -30|01212103030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030|-168L0 WM0 1fA0 1cM0 1cM0 1cN0 1mKH0 1dd0 1fb0 1ap0 1fb0 1a20 1fy0 1a30 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Stockholm|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2azC0 TB0 2yDe0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Tallinn|TMT CET CEST EET MSK MSD EEST|-1D -10 -20 -20 -30 -40 -30|012103421212454545454545454546363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363|-26oND teD 11A0 1Ta0 4rXl KSLD 2FX0 2Jg0 WM0 1fA0 1cM0 18J0 1sTX0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o10 11A0 1qM0 5QM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Tirane|LMT CET CEST|-1j.k -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glBj.k 14pcj.k 5LC0 WM0 4M0 1fCK0 10n0 1op0 11z0 1pd0 11z0 1qN0 WL0 1qp0 Xb0 1qp0 Xb0 1qp0 11z0 1lB0 11z0 1qN0 11z0 1iN0 16n0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Uzhgorod|CET CEST MSK MSD EET EEST|-10 -20 -30 -40 -20 -30|010101023232323232323232320454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-1cqL0 6i00 WM0 1fA0 1cM0 1ml0 1Cp0 1r3W0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Q00 1Nf0 2pw0 1cL0 1cN0 1cL0 1cN0 1cL0 1cQ0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Vienna|CET CEST|-10 -20|0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 3KM0 14o0 LA00 6i00 WM0 1fA0 1cM0 1cM0 1cM0 400 2qM0 1a00 1cM0 1cM0 1io0 17c0 1gHa0 19X0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Vilnius|WMT KMT CET EET MSK CEST MSD EEST|-1o -1z.A -10 -20 -30 -20 -40 -30|012324525254646464646464646464647373737373737352537373737373737373737373737373737373737373737373737373737373737373737373|-293do 6ILM.o 1Ooz.A zz0 Mfd0 29W0 3is0 WM0 1fA0 1cM0 LV0 1tgL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11B0 1o00 11A0 1qM0 8io0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Volgograd|LMT TSAT STAT STAT VOLT VOLST VOLST VOLT MSD MSK MSK|-2V.E -30 -30 -40 -40 -50 -40 -30 -40 -30 -40|0123454545454545454546767489898989898989898989898989898989898989a9|-21IqV.E cLXV.E cEM0 1gqn0 Lco0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 2pz0 1cJ0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0","Europe/Warsaw|WMT CET CEST EET EEST|-1o -10 -20 -20 -30|012121234312121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2ctdo 1LXo 11d0 1iO0 11A0 1o00 11A0 1on0 11A0 6zy0 HWP0 5IM0 WM0 1fA0 1cM0 1dz0 1mL0 1en0 15B0 1aq0 1nA0 11A0 1io0 17c0 1fA0 1a00 iDX0 LA0 1cM0 1cM0 1C00 Oo0 1cM0 1cM0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1C00 LA0 uso0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Zaporozhye|CUT EET MSK CEST CET MSD EEST|-2k -20 -30 -20 -10 -40 -30|01234342525252525252525252526161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161|-1Pc2k eUok rdb0 2RE0 WM0 1fA0 8m0 1v9a0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cK0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cQ0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","HST|HST|a0|0|","Indian/Chagos|LMT IOT IOT|-4N.E -50 -60|012|-2xosN.E 3AGLN.E","Indian/Christmas|CXT|-70|0|","Indian/Cocos|CCT|-6u|0|","Indian/Kerguelen|zzz TFT|0 -50|01|-MG00","Indian/Mahe|LMT SCT|-3F.M -40|01|-2yO3F.M","Indian/Maldives|MMT MVT|-4S -50|01|-olgS","Indian/Mauritius|LMT MUT MUST|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0","Indian/Reunion|LMT RET|-3F.Q -40|01|-2mDDF.Q","Kwajalein|MHT KWAT MHT|-b0 c0 -c0|012|-AX0 W9X0","MET|MET MEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","MST|MST|70|0|","MST7MDT|MST MDT MWT MPT|70 60 60 60|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","NZ-CHAT|CHAST CHAST CHADT|-cf -cJ -dJ|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-WqAf 1adef IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","PST8PDT|PST PDT PWT PPT|80 70 70 70|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261q0 1nX0 11B0 1nX0 SgN0 8x10 iy0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Pacific/Apia|LMT WSST SST SDT WSDT WSST|bq.U bu b0 a0 -e0 -d0|01232345454545454545454545454545454545454545454545454545454|-2nDMx.4 1yW03.4 2rRbu 1ff0 1a00 CI0 AQ0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Pacific/Bougainville|PGT JST BST|-a0 -90 -b0|0102|-16Wy0 7CN0 2MQp0","Pacific/Chuuk|CHUT|-a0|0|","Pacific/Efate|LMT VUT VUST|-bd.g -b0 -c0|0121212121212121212121|-2l9nd.g 2Szcd.g 1cL0 1oN0 10L0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 Lz0 1Nd0 An0","Pacific/Enderbury|PHOT PHOT PHOT|c0 b0 -d0|012|nIc0 B8n0","Pacific/Fakaofo|TKT TKT|b0 -d0|01|1Gfn0","Pacific/Fiji|LMT FJT FJST|-bT.I -c0 -d0|0121212121212121212121212121212121212121212121212121212121212121|-2bUzT.I 3m8NT.I LA0 1EM0 IM0 nJc0 LA0 1o00 Rc0 1wo0 Ao0 1Nc0 Ao0 1Q00 xz0 1SN0 uM0 1SM0 uM0 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1VA0 s00 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1VA0 s00 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0","Pacific/Funafuti|TVT|-c0|0|","Pacific/Galapagos|LMT ECT GALT|5W.o 50 60|012|-1yVS1.A 2dTz1.A","Pacific/Gambier|LMT GAMT|8X.M 90|01|-2jof0.c","Pacific/Guadalcanal|LMT SBT|-aD.M -b0|01|-2joyD.M","Pacific/Guam|GST ChST|-a0 -a0|01|1fpq0","Pacific/Honolulu|HST HDT HST|au 9u a0|010102|-1thLu 8x0 lef0 8Pz0 46p0","Pacific/Kiritimati|LINT LINT LINT|aE a0 -e0|012|nIaE B8nk","Pacific/Kosrae|KOST KOST|-b0 -c0|010|-AX0 1bdz0","Pacific/Majuro|MHT MHT|-b0 -c0|01|-AX0","Pacific/Marquesas|LMT MART|9i 9u|01|-2joeG","Pacific/Midway|LMT NST BST SST|bm.M b0 b0 b0|0123|-2nDMB.c 2gVzB.c EyM0","Pacific/Nauru|LMT NRT JST NRT|-b7.E -bu -90 -c0|01213|-1Xdn7.E PvzB.E 5RCu 1ouJu","Pacific/Niue|NUT NUT NUT|bk bu b0|012|-KfME 17y0a","Pacific/Norfolk|NMT NFT NFST NFT|-bc -bu -cu -b0|01213|-Kgbc W01G On0 1COp0","Pacific/Noumea|LMT NCT NCST|-b5.M -b0 -c0|01212121|-2l9n5.M 2EqM5.M xX0 1PB0 yn0 HeP0 Ao0","Pacific/Palau|PWT|-90|0|","Pacific/Pitcairn|PNT PST|8u 80|01|18Vku","Pacific/Pohnpei|PONT|-b0|0|","Pacific/Port_Moresby|PGT|-a0|0|","Pacific/Rarotonga|CKT CKHST CKT|au 9u a0|012121212121212121212121212|lyWu IL0 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu","Pacific/Tahiti|LMT TAHT|9W.g a0|01|-2joe1.I","Pacific/Tarawa|GILT|-c0|0|","Pacific/Tongatapu|TOT TOT TOST|-ck -d0 -e0|01212121|-1aB0k 2n5dk 15A0 1wo0 xz0 1Q10 xz0","Pacific/Wake|WAKT|-c0|0|","Pacific/Wallis|WFT|-c0|0|","WET|WET WEST|0 -10|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00"], links:["Africa/Abidjan|Africa/Bamako","Africa/Abidjan|Africa/Banjul","Africa/Abidjan|Africa/Conakry","Africa/Abidjan|Africa/Dakar","Africa/Abidjan|Africa/Freetown","Africa/Abidjan|Africa/Lome","Africa/Abidjan|Africa/Nouakchott","Africa/Abidjan|Africa/Ouagadougou","Africa/Abidjan|Africa/Sao_Tome","Africa/Abidjan|Africa/Timbuktu","Africa/Abidjan|Atlantic/St_Helena","Africa/Addis_Ababa|Africa/Asmara","Africa/Addis_Ababa|Africa/Asmera","Africa/Addis_Ababa|Africa/Dar_es_Salaam","Africa/Addis_Ababa|Africa/Djibouti","Africa/Addis_Ababa|Africa/Kampala","Africa/Addis_Ababa|Africa/Mogadishu","Africa/Addis_Ababa|Africa/Nairobi","Africa/Addis_Ababa|Indian/Antananarivo","Africa/Addis_Ababa|Indian/Comoro","Africa/Addis_Ababa|Indian/Mayotte","Africa/Bangui|Africa/Brazzaville","Africa/Bangui|Africa/Douala","Africa/Bangui|Africa/Kinshasa","Africa/Bangui|Africa/Lagos","Africa/Bangui|Africa/Libreville","Africa/Bangui|Africa/Luanda","Africa/Bangui|Africa/Malabo","Africa/Bangui|Africa/Niamey","Africa/Bangui|Africa/Porto-Novo","Africa/Blantyre|Africa/Bujumbura","Africa/Blantyre|Africa/Gaborone","Africa/Blantyre|Africa/Harare","Africa/Blantyre|Africa/Kigali","Africa/Blantyre|Africa/Lubumbashi","Africa/Blantyre|Africa/Lusaka","Africa/Blantyre|Africa/Maputo","Africa/Cairo|Egypt","Africa/Johannesburg|Africa/Maseru","Africa/Johannesburg|Africa/Mbabane","Africa/Juba|Africa/Khartoum","Africa/Tripoli|Libya","America/Adak|America/Atka","America/Adak|US/Aleutian","America/Anchorage|US/Alaska","America/Anguilla|America/Antigua","America/Anguilla|America/Dominica","America/Anguilla|America/Grenada","America/Anguilla|America/Guadeloupe","America/Anguilla|America/Marigot","America/Anguilla|America/Montserrat","America/Anguilla|America/Port_of_Spain","America/Anguilla|America/St_Barthelemy","America/Anguilla|America/St_Kitts","America/Anguilla|America/St_Lucia","America/Anguilla|America/St_Thomas","America/Anguilla|America/St_Vincent","America/Anguilla|America/Tortola","America/Anguilla|America/Virgin","America/Argentina/Buenos_Aires|America/Buenos_Aires","America/Argentina/Catamarca|America/Argentina/ComodRivadavia","America/Argentina/Catamarca|America/Catamarca","America/Argentina/Cordoba|America/Cordoba","America/Argentina/Cordoba|America/Rosario","America/Argentina/Jujuy|America/Jujuy","America/Argentina/Mendoza|America/Mendoza","America/Aruba|America/Curacao","America/Aruba|America/Kralendijk","America/Aruba|America/Lower_Princes","America/Atikokan|America/Coral_Harbour","America/Chicago|US/Central","America/Denver|America/Shiprock","America/Denver|Navajo","America/Denver|US/Mountain","America/Detroit|US/Michigan","America/Edmonton|Canada/Mountain","America/Ensenada|America/Tijuana","America/Ensenada|Mexico/BajaNorte","America/Fort_Wayne|America/Indiana/Indianapolis","America/Fort_Wayne|America/Indianapolis","America/Fort_Wayne|US/East-Indiana","America/Halifax|Canada/Atlantic","America/Havana|Cuba","America/Indiana/Knox|America/Knox_IN","America/Indiana/Knox|US/Indiana-Starke","America/Jamaica|Jamaica","America/Kentucky/Louisville|America/Louisville","America/Los_Angeles|US/Pacific","America/Los_Angeles|US/Pacific-New","America/Manaus|Brazil/West","America/Mazatlan|Mexico/BajaSur","America/Mexico_City|Mexico/General","America/Montreal|America/Toronto","America/Montreal|Canada/Eastern","America/New_York|US/Eastern","America/Noronha|Brazil/DeNoronha","America/Phoenix|US/Arizona","America/Porto_Acre|America/Rio_Branco","America/Porto_Acre|Brazil/Acre","America/Regina|Canada/East-Saskatchewan","America/Regina|Canada/Saskatchewan","America/Santiago|Chile/Continental","America/Sao_Paulo|Brazil/East","America/St_Johns|Canada/Newfoundland","America/Vancouver|Canada/Pacific","America/Whitehorse|Canada/Yukon","America/Winnipeg|Canada/Central","Antarctica/McMurdo|Antarctica/South_Pole","Antarctica/McMurdo|NZ","Antarctica/McMurdo|Pacific/Auckland","Arctic/Longyearbyen|Atlantic/Jan_Mayen","Arctic/Longyearbyen|Europe/Oslo","Asia/Aden|Asia/Kuwait","Asia/Aden|Asia/Riyadh","Asia/Ashgabat|Asia/Ashkhabad","Asia/Bahrain|Asia/Qatar","Asia/Bangkok|Asia/Phnom_Penh","Asia/Bangkok|Asia/Vientiane","Asia/Calcutta|Asia/Kolkata","Asia/Chongqing|Asia/Chungking","Asia/Chongqing|Asia/Harbin","Asia/Chongqing|Asia/Shanghai","Asia/Chongqing|PRC","Asia/Dacca|Asia/Dhaka","Asia/Dubai|Asia/Muscat","Asia/Ho_Chi_Minh|Asia/Saigon","Asia/Hong_Kong|Hongkong","Asia/Istanbul|Europe/Istanbul","Asia/Istanbul|Turkey","Asia/Jerusalem|Asia/Tel_Aviv","Asia/Jerusalem|Israel","Asia/Kashgar|Asia/Urumqi","Asia/Kathmandu|Asia/Katmandu","Asia/Macao|Asia/Macau","Asia/Makassar|Asia/Ujung_Pandang","Asia/Nicosia|Europe/Nicosia","Asia/Seoul|ROK","Asia/Singapore|Singapore","Asia/Taipei|ROC","Asia/Tehran|Iran","Asia/Thimbu|Asia/Thimphu","Asia/Tokyo|Japan","Asia/Ulaanbaatar|Asia/Ulan_Bator","Atlantic/Faeroe|Atlantic/Faroe","Atlantic/Reykjavik|Iceland","Australia/ACT|Australia/Canberra","Australia/ACT|Australia/NSW","Australia/ACT|Australia/Sydney","Australia/Adelaide|Australia/South","Australia/Brisbane|Australia/Queensland","Australia/Broken_Hill|Australia/Yancowinna","Australia/Darwin|Australia/North","Australia/Hobart|Australia/Tasmania","Australia/LHI|Australia/Lord_Howe","Australia/Melbourne|Australia/Victoria","Australia/Perth|Australia/West","Chile/EasterIsland|Pacific/Easter","Eire|Europe/Dublin","Etc/GMT+0|Etc/GMT","Etc/GMT+0|Etc/GMT-0","Etc/GMT+0|Etc/GMT0","Etc/GMT+0|Etc/Greenwich","Etc/GMT+0|GMT","Etc/GMT+0|GMT+0","Etc/GMT+0|GMT-0","Etc/GMT+0|GMT0","Etc/GMT+0|Greenwich","Etc/UCT|UCT","Etc/UTC|Etc/Universal","Etc/UTC|Etc/Zulu","Etc/UTC|UTC","Etc/UTC|Universal","Etc/UTC|Zulu","Europe/Belfast|Europe/Guernsey","Europe/Belfast|Europe/Isle_of_Man","Europe/Belfast|Europe/Jersey","Europe/Belfast|Europe/London","Europe/Belfast|GB","Europe/Belfast|GB-Eire","Europe/Belgrade|Europe/Ljubljana","Europe/Belgrade|Europe/Podgorica","Europe/Belgrade|Europe/Sarajevo","Europe/Belgrade|Europe/Skopje","Europe/Belgrade|Europe/Zagreb","Europe/Bratislava|Europe/Prague","Europe/Busingen|Europe/Vaduz","Europe/Busingen|Europe/Zurich","Europe/Chisinau|Europe/Tiraspol","Europe/Helsinki|Europe/Mariehamn","Europe/Lisbon|Portugal","Europe/Moscow|W-SU","Europe/Rome|Europe/San_Marino","Europe/Rome|Europe/Vatican","Europe/Warsaw|Poland","Kwajalein|Pacific/Kwajalein","NZ-CHAT|Pacific/Chatham","Pacific/Chuuk|Pacific/Truk","Pacific/Chuuk|Pacific/Yap","Pacific/Guam|Pacific/Saipan","Pacific/Honolulu|Pacific/Johnston","Pacific/Honolulu|US/Hawaii","Pacific/Midway|Pacific/Pago_Pago","Pacific/Midway|Pacific/Samoa","Pacific/Midway|US/Samoa","Pacific/Pohnpei|Pacific/Ponape"]}),a}); +(function(){"use strict";function e(e){return"function"==typeof e||"object"==typeof e&&null!==e}function t(e){return"function"==typeof e}function r(e){return"object"==typeof e&&null!==e}function n(e){q=e}function o(e){Z=e}function i(){return function(){process.nextTick(l)}}function a(){return function(){I(l)}}function s(){var e=0,t=new Y(l),r=document.createTextNode("");return t.observe(r,{characterData:!0}),function(){r.data=e=++e%2}}function u(){var e=new MessageChannel;return e.port1.onmessage=l,function(){e.port2.postMessage(0)}}function c(){return function(){setTimeout(l,1)}}function l(){for(var e=0;B>e;e+=2){var t=K[e],r=K[e+1];t(r),K[e]=void 0,K[e+1]=void 0}B=0}function f(){try{var e=require,t=e("vertx");return I=t.runOnLoop||t.runOnContext,a()}catch(r){return c()}}function p(){}function h(){return new TypeError("You cannot resolve a promise with itself")}function g(){return new TypeError("A promises callback cannot return that same promise.")}function d(e){try{return e.then}catch(t){return te.error=t,te}}function m(e,t,r,n){try{e.call(t,r,n)}catch(o){return o}}function y(e,t,r){Z(function(e){var n=!1,o=m(r,t,function(r){n||(n=!0,t!==r?b(e,r):A(e,r))},function(t){n||(n=!0,C(e,t))},"Settle: "+(e._label||" unknown promise"));!n&&o&&(n=!0,C(e,o))},e)}function v(e,t){t._state===W?A(e,t._result):t._state===ee?C(e,t._result):S(t,void 0,function(t){b(e,t)},function(t){C(e,t)})}function _(e,r){if(r.constructor===e.constructor)v(e,r);else{var n=d(r);n===te?C(e,te.error):void 0===n?A(e,r):t(n)?y(e,r,n):A(e,r)}}function b(t,r){t===r?C(t,h()):e(r)?_(t,r):A(t,r)}function w(e){e._onerror&&e._onerror(e._result),O(e)}function A(e,t){e._state===Q&&(e._result=t,e._state=W,0!==e._subscribers.length&&Z(O,e))}function C(e,t){e._state===Q&&(e._state=ee,e._result=t,Z(w,e))}function S(e,t,r,n){var o=e._subscribers,i=o.length;e._onerror=null,o[i]=t,o[i+W]=r,o[i+ee]=n,0===i&&e._state&&Z(O,e)}function O(e){var t=e._subscribers,r=e._state;if(0!==t.length){for(var n,o,i=e._result,a=0;aa;a++)S(n.resolve(e[a]),void 0,t,r);return o}function L(e){var t=this;if(e&&"object"==typeof e&&e.constructor===t)return e;var r=new t(p);return b(r,e),r}function P(e){var t=this,r=new t(p);return C(r,e),r}function F(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function R(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function $(e){this._id=ue++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==e&&(t(e)||F(),this instanceof $||R(),j(this,e))}function G(){var e;if("undefined"!=typeof global)e=global;else if("undefined"!=typeof self)e=self;else try{e=Function("return this")()}catch(t){throw new Error("polyfill failed because global object is unavailable in this environment")}var r=e.Promise;(!r||"[object Promise]"!==Object.prototype.toString.call(r.resolve())||r.cast)&&(e.Promise=ce)}var U;U=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)};var I,q,J,D=U,B=0,Z=({}.toString,function(e,t){K[B]=e,K[B+1]=t,B+=2,2===B&&(q?q(l):J())}),z="undefined"!=typeof window?window:void 0,H=z||{},Y=H.MutationObserver||H.WebKitMutationObserver,X="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,K=new Array(1e3);J=X?i():Y?s():V?u():void 0===z&&"function"==typeof require?f():c();var Q=void 0,W=1,ee=2,te=new E,re=new E;M.prototype._validateInput=function(e){return D(e)},M.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},M.prototype._init=function(){this._result=new Array(this.length)};var ne=M;M.prototype._enumerate=function(){for(var e=this,t=e.length,r=e.promise,n=e._input,o=0;r._state===Q&&t>o;o++)e._eachEntry(n[o],o)},M.prototype._eachEntry=function(e,t){var n=this,o=n._instanceConstructor;r(e)?e.constructor===o&&e._state!==Q?(e._onerror=null,n._settledAt(e._state,t,e._result)):n._willSettleAt(o.resolve(e),t):(n._remaining--,n._result[t]=e)},M.prototype._settledAt=function(e,t,r){var n=this,o=n.promise;o._state===Q&&(n._remaining--,e===ee?C(o,r):n._result[t]=r),0===n._remaining&&A(o,n._result)},M.prototype._willSettleAt=function(e,t){var r=this;S(e,void 0,function(e){r._settledAt(W,t,e)},function(e){r._settledAt(ee,t,e)})};var oe=k,ie=T,ae=L,se=P,ue=0,ce=$;$.all=oe,$.race=ie,$.resolve=ae,$.reject=se,$._setScheduler=n,$._setAsap=o,$._asap=Z,$.prototype={constructor:$,then:function(e,t){var r=this,n=r._state;if(n===W&&!e||n===ee&&!t)return this;var o=new this.constructor(p),i=r._result;if(n){var a=arguments[n-1];Z(function(){x(n,o,a,i)})}else S(r,o,e,t);return o},"catch":function(e){return this.then(null,e)}};var le=G,fe={Promise:ce,polyfill:le};"function"==typeof define&&define.amd?define(function(){return fe}):"undefined"!=typeof module&&module.exports?module.exports=fe:"undefined"!=typeof this&&(this.ES6Promise=fe),le()}).call(this),function(){function e(t,n){function i(e){if(i[e]!==m)return i[e];var t;if("bug-string-char-index"==e)t="a"!="a"[0];else if("json"==e)t=i("json-stringify")&&i("json-parse");else{var r,o='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if("json-stringify"==e){var u=n.stringify,l="function"==typeof u&&_;if(l){(r=function(){return 1}).toJSON=r;try{l="0"===u(0)&&"0"===u(new a)&&'""'==u(new s)&&u(v)===m&&u(m)===m&&u()===m&&"1"===u(r)&&"[1]"==u([r])&&"[null]"==u([m])&&"null"==u(null)&&"[null,null,null]"==u([m,v,null])&&u({a:[r,!0,!1,null,"\x00\b\n\f\r "]})==o&&"1"===u(null,r)&&"[\n 1,\n 2\n]"==u([1,2],null,1)&&'"-271821-04-20T00:00:00.000Z"'==u(new c(-864e13))&&'"+275760-09-13T00:00:00.000Z"'==u(new c(864e13))&&'"-000001-01-01T00:00:00.000Z"'==u(new c(-621987552e5))&&'"1969-12-31T23:59:59.999Z"'==u(new c(-1))}catch(f){l=!1}}t=l}if("json-parse"==e){var p=n.parse;if("function"==typeof p)try{if(0===p("0")&&!p(!1)){r=p(o);var h=5==r.a.length&&1===r.a[0];if(h){try{h=!p('" "')}catch(f){}if(h)try{h=1!==p("01")}catch(f){}if(h)try{h=1!==p("1.")}catch(f){}}}}catch(f){h=!1}t=h}}return i[e]=!!t}t||(t=o.Object()),n||(n=o.Object());var a=t.Number||o.Number,s=t.String||o.String,u=t.Object||o.Object,c=t.Date||o.Date,l=t.SyntaxError||o.SyntaxError,f=t.TypeError||o.TypeError,p=t.Math||o.Math,h=t.JSON||o.JSON;"object"==typeof h&&h&&(n.stringify=h.stringify,n.parse=h.parse);var g,d,m,y=u.prototype,v=y.toString,_=new c(-0xc782b5b800cec);try{_=-109252==_.getUTCFullYear()&&0===_.getUTCMonth()&&1===_.getUTCDate()&&10==_.getUTCHours()&&37==_.getUTCMinutes()&&6==_.getUTCSeconds()&&708==_.getUTCMilliseconds()}catch(b){}if(!i("json")){var w="[object Function]",A="[object Date]",C="[object Number]",S="[object String]",O="[object Array]",E="[object Boolean]",N=i("bug-string-char-index");if(!_)var x=p.floor,j=[0,31,59,90,120,151,181,212,243,273,304,334],M=function(e,t){return j[t]+365*(e-1970)+x((e-1969+(t=+(t>1)))/4)-x((e-1901+t)/100)+x((e-1601+t)/400)};if((g=y.hasOwnProperty)||(g=function(e){var t,r={};return(r.__proto__=null,r.__proto__={toString:1},r).toString!=v?g=function(e){var t=this.__proto__,r=e in(this.__proto__=null,this);return this.__proto__=t,r}:(t=r.constructor,g=function(e){var r=(this.constructor||t).prototype;return e in this&&!(e in r&&this[e]===r[e])}),r=null,g.call(this,e)}),d=function(e,t){var n,o,i,a=0;(n=function(){this.valueOf=0}).prototype.valueOf=0,o=new n;for(i in o)g.call(o,i)&&a++;return n=o=null,a?d=2==a?function(e,t){var r,n={},o=v.call(e)==w;for(r in e)o&&"prototype"==r||g.call(n,r)||!(n[r]=1)||!g.call(e,r)||t(r)}:function(e,t){var r,n,o=v.call(e)==w;for(r in e)o&&"prototype"==r||!g.call(e,r)||(n="constructor"===r)||t(r);(n||g.call(e,r="constructor"))&&t(r)}:(o=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"],d=function(e,t){var n,i,a=v.call(e)==w,s=!a&&"function"!=typeof e.constructor&&r[typeof e.hasOwnProperty]&&e.hasOwnProperty||g;for(n in e)a&&"prototype"==n||!s.call(e,n)||t(n);for(i=o.length;n=o[--i];s.call(e,n)&&t(n));}),d(e,t)},!i("json-stringify")){var k={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"},T="000000",L=function(e,t){return(T+(t||0)).slice(-e)},P="\\u00",F=function(e){for(var t='"',r=0,n=e.length,o=!N||n>10,i=o&&(N?e.split(""):e);n>r;r++){var a=e.charCodeAt(r);switch(a){case 8:case 9:case 10:case 12:case 13:case 34:case 92:t+=k[a];break;default:if(32>a){t+=P+L(2,a.toString(16));break}t+=o?i[r]:e.charAt(r)}}return t+'"'},R=function(e,t,r,n,o,i,a){var s,u,c,l,p,h,y,_,b,w,N,j,k,T,P,$;try{s=t[e]}catch(G){}if("object"==typeof s&&s)if(u=v.call(s),u!=A||g.call(s,"toJSON"))"function"==typeof s.toJSON&&(u!=C&&u!=S&&u!=O||g.call(s,"toJSON"))&&(s=s.toJSON(e));else if(s>-1/0&&1/0>s){if(M){for(p=x(s/864e5),c=x(p/365.2425)+1970-1;M(c+1,0)<=p;c++);for(l=x((p-M(c,0))/30.42);M(c,l+1)<=p;l++);p=1+p-M(c,l),h=(s%864e5+864e5)%864e5,y=x(h/36e5)%24,_=x(h/6e4)%60,b=x(h/1e3)%60,w=h%1e3}else c=s.getUTCFullYear(),l=s.getUTCMonth(),p=s.getUTCDate(),y=s.getUTCHours(),_=s.getUTCMinutes(),b=s.getUTCSeconds(),w=s.getUTCMilliseconds();s=(0>=c||c>=1e4?(0>c?"-":"+")+L(6,0>c?-c:c):L(4,c))+"-"+L(2,l+1)+"-"+L(2,p)+"T"+L(2,y)+":"+L(2,_)+":"+L(2,b)+"."+L(3,w)+"Z"}else s=null;if(r&&(s=r.call(t,e,s)),null===s)return"null";if(u=v.call(s),u==E)return""+s;if(u==C)return s>-1/0&&1/0>s?""+s:"null";if(u==S)return F(""+s);if("object"==typeof s){for(T=a.length;T--;)if(a[T]===s)throw f();if(a.push(s),N=[],P=i,i+=o,u==O){for(k=0,T=s.length;T>k;k++)j=R(k,s,r,n,o,i,a),N.push(j===m?"null":j);$=N.length?o?"[\n"+i+N.join(",\n"+i)+"\n"+P+"]":"["+N.join(",")+"]":"[]"}else d(n||s,function(e){var t=R(e,s,r,n,o,i,a);t!==m&&N.push(F(e)+":"+(o?" ":"")+t)}),$=N.length?o?"{\n"+i+N.join(",\n"+i)+"\n"+P+"}":"{"+N.join(",")+"}":"{}";return a.pop(),$}};n.stringify=function(e,t,n){var o,i,a,s;if(r[typeof t]&&t)if((s=v.call(t))==w)i=t;else if(s==O){a={};for(var u,c=0,l=t.length;l>c;u=t[c++],s=v.call(u),(s==S||s==C)&&(a[u]=1));}if(n)if((s=v.call(n))==C){if((n-=n%1)>0)for(o="",n>10&&(n=10);o.length$;)switch(o=i.charCodeAt($)){case 9:case 10:case 13:case 32:$++;break;case 123:case 125:case 91:case 93:case 58:case 44:return e=N?i.charAt($):i[$],$++,e;case 34:for(e="@",$++;a>$;)if(o=i.charCodeAt($),32>o)q();else if(92==o)switch(o=i.charCodeAt(++$)){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:e+=I[o],$++;break;case 117:for(t=++$,r=$+4;r>$;$++)o=i.charCodeAt($),o>=48&&57>=o||o>=97&&102>=o||o>=65&&70>=o||q();e+=U("0x"+i.slice(t,$));break;default:q()}else{if(34==o)break;for(o=i.charCodeAt($),t=$;o>=32&&92!=o&&34!=o;)o=i.charCodeAt(++$);e+=i.slice(t,$)}if(34==i.charCodeAt($))return $++,e;q();default:if(t=$,45==o&&(n=!0,o=i.charCodeAt(++$)),o>=48&&57>=o){for(48==o&&(o=i.charCodeAt($+1),o>=48&&57>=o)&&q(),n=!1;a>$&&(o=i.charCodeAt($),o>=48&&57>=o);$++);if(46==i.charCodeAt($)){for(r=++$;a>r&&(o=i.charCodeAt(r),o>=48&&57>=o);r++);r==$&&q(),$=r}if(o=i.charCodeAt($),101==o||69==o){for(o=i.charCodeAt(++$),(43==o||45==o)&&$++,r=$;a>r&&(o=i.charCodeAt(r),o>=48&&57>=o);r++);r==$&&q(),$=r}return+i.slice(t,$)}if(n&&q(),"true"==i.slice($,$+4))return $+=4,!0;if("false"==i.slice($,$+5))return $+=5,!1;if("null"==i.slice($,$+4))return $+=4,null;q()}return"$"},D=function(e){var t,r;if("$"==e&&q(),"string"==typeof e){if("@"==(N?e.charAt(0):e[0]))return e.slice(1);if("["==e){for(t=[];e=J(),"]"!=e;r||(r=!0))r&&(","==e?(e=J(),"]"==e&&q()):q()),","==e&&q(),t.push(D(e));return t}if("{"==e){for(t={};e=J(),"}"!=e;r||(r=!0))r&&(","==e?(e=J(),"}"==e&&q()):q()),(","==e||"string"!=typeof e||"@"!=(N?e.charAt(0):e[0])||":"!=J())&&q(),t[e.slice(1)]=D(J());return t}q()}return e},B=function(e,t,r){var n=Z(e,t,r);n===m?delete e[t]:e[t]=n},Z=function(e,t,r){var n,o=e[t];if("object"==typeof o&&o)if(v.call(o)==O)for(n=o.length;n--;)B(o,n,r);else d(o,function(e){B(o,e,r)});return r.call(e,t,o)};n.parse=function(e,t){var r,n;return $=0,G=""+e,r=D(J()),"$"!=J()&&q(),$=G=null,t&&v.call(t)==w?Z((n={},n[""]=r,n),"",t):r}}}return n.runInContext=e,n}var t="function"==typeof define&&define.amd,r={"function":!0,object:!0},n=r[typeof exports]&&exports&&!exports.nodeType&&exports,o=r[typeof window]&&window||this,i=n&&r[typeof module]&&module&&!module.nodeType&&"object"==typeof global&&global;if(!i||i.global!==i&&i.window!==i&&i.self!==i||(o=i),n&&!t)e(o,n);else{var a=o.JSON,s=o.JSON3,u=!1,c=e(o,o.JSON3={noConflict:function(){return u||(u=!0,o.JSON=a,o.JSON3=s,a=s=null),c}});o.JSON={parse:c.parse,stringify:c.stringify}}t&&define(function(){return c})}.call(this),"undefined"==typeof Promise&&ES6Promise.polyfill(),Function.prototype.bind||(Function.prototype.bind=function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),r=this,n=function(){},o=function(){return r.apply(this instanceof n&&e?this:e,t.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,o.prototype=new n,o}),Array.prototype.map||(Array.prototype.map=function(e,t){if(void 0===this||null===this)throw new TypeError("this is null or not defined");var r,n=Object(this),o=n.length>>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");arguments.length>1&&(r=t);for(var i=new Array(o),a=0;o>a;){var s,u;a in n&&(s=n[a],u=e.call(r,s,a,n),i[a]=u),a++}return i}),Array.prototype.filter||(Array.prototype.filter=function(e){if(void 0===this||null===this)throw new TypeError("this is null or not defined");var t=Object(this),r=t.length>>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(var n=[],o=arguments.length>=2?arguments[1]:void 0,i=0;r>i;i++)if(i in t){var a=t[i];e.call(o,a,i,t)&&n.push(a)}return n}),Array.prototype.forEach||(Array.prototype.forEach=function(e,t){var r,n;if(null===this||void 0===this)throw new TypeError(" this is null or not defined");var o=Object(this),i=o.length>>>0;if("function"!=typeof e)throw new TypeError(e+" is not a function");for(arguments.length>1&&(r=t),n=0;i>n;){var a;n in o&&(a=o[n],e.call(r,a,n,o)),n++}}),!function(e,t){"use strict";"function"==typeof define&&define.amd?define("stackframe",[],t):"object"==typeof exports?module.exports=t():e.StackFrame=t()}(this,function(){"use strict";function e(e){return!isNaN(parseFloat(e))&&isFinite(e)}function t(e,t,r,n,o){void 0!==e&&this.setFunctionName(e),void 0!==t&&this.setArgs(t),void 0!==r&&this.setFileName(r),void 0!==n&&this.setLineNumber(n),void 0!==o&&this.setColumnNumber(o)}return t.prototype={getFunctionName:function(){return this.functionName},setFunctionName:function(e){this.functionName=String(e)},getArgs:function(){return this.args},setArgs:function(e){if("[object Array]"!==Object.prototype.toString.call(e))throw new TypeError("Args must be an Array");this.args=e},getFileName:function(){return this.fileName},setFileName:function(e){this.fileName=String(e)},getLineNumber:function(){return this.lineNumber},setLineNumber:function(t){if(!e(t))throw new TypeError("Line Number must be a Number");this.lineNumber=Number(t)},getColumnNumber:function(){return this.columnNumber},setColumnNumber:function(t){if(!e(t))throw new TypeError("Column Number must be a Number");this.columnNumber=Number(t)},toString:function(){var t=this.getFunctionName()||"{anonymous}",r="("+(this.getArgs()||[]).join(",")+")",n=this.getFileName()?"@"+this.getFileName():"",o=e(this.getLineNumber())?":"+this.getLineNumber():"",i=e(this.getColumnNumber())?":"+this.getColumnNumber():"";return t+r+n+o+i}},t});var SourceMap=function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){var n;n=function(e,t,n){function o(e){var t=e;"string"==typeof e&&(t=JSON.parse(e.replace(/^\)\]\}'/,"")));var r=i.getArg(t,"version"),n=i.getArg(t,"sources"),o=i.getArg(t,"names",[]),a=i.getArg(t,"sourceRoot",null),u=i.getArg(t,"sourcesContent",null),c=i.getArg(t,"mappings"),l=i.getArg(t,"file",null);if(r!=this._version)throw new Error("Unsupported version: "+r);n=n.map(i.normalize),this._names=s.fromArray(o,!0),this._sources=s.fromArray(n,!0),this.sourceRoot=a,this.sourcesContent=u,this._mappings=c,this.file=l}var i=r(1),a=r(2),s=r(3).ArraySet,u=r(4);o.fromSourceMap=function(e){var t=Object.create(o.prototype);return t._names=s.fromArray(e._names.toArray(),!0),t._sources=s.fromArray(e._sources.toArray(),!0),t.sourceRoot=e._sourceRoot,t.sourcesContent=e._generateSourcesContent(t._sources.toArray(),t.sourceRoot),t.file=e._file,t.__generatedMappings=e._mappings.toArray().slice(),t.__originalMappings=e._mappings.toArray().slice().sort(i.compareByOriginalPositions),t},o.prototype._version=3,Object.defineProperty(o.prototype,"sources",{get:function(){return this._sources.toArray().map(function(e){return null!=this.sourceRoot?i.join(this.sourceRoot,e):e},this)}}),o.prototype.__generatedMappings=null,Object.defineProperty(o.prototype,"_generatedMappings",{get:function(){return this.__generatedMappings||(this.__generatedMappings=[],this.__originalMappings=[],this._parseMappings(this._mappings,this.sourceRoot)),this.__generatedMappings}}),o.prototype.__originalMappings=null,Object.defineProperty(o.prototype,"_originalMappings",{get:function(){return this.__originalMappings||(this.__generatedMappings=[],this.__originalMappings=[],this._parseMappings(this._mappings,this.sourceRoot)),this.__originalMappings}}),o.prototype._nextCharIsMappingSeparator=function(e){var t=e.charAt(0);return";"===t||","===t},o.prototype._parseMappings=function(e,t){for(var r,n=1,o=0,a=0,s=0,c=0,l=0,f=e,p={};f.length>0;)if(";"===f.charAt(0))n++,f=f.slice(1),o=0;else if(","===f.charAt(0))f=f.slice(1);else{if(r={},r.generatedLine=n,u.decode(f,p),r.generatedColumn=o+p.value,o=r.generatedColumn,f=p.rest,f.length>0&&!this._nextCharIsMappingSeparator(f)){if(u.decode(f,p),r.source=this._sources.at(c+p.value),c+=p.value,f=p.rest,0===f.length||this._nextCharIsMappingSeparator(f))throw new Error("Found a source, but no line and column");if(u.decode(f,p),r.originalLine=a+p.value,a=r.originalLine,r.originalLine+=1,f=p.rest,0===f.length||this._nextCharIsMappingSeparator(f))throw new Error("Found a source and line, but no column");u.decode(f,p),r.originalColumn=s+p.value,s=r.originalColumn,f=p.rest,f.length>0&&!this._nextCharIsMappingSeparator(f)&&(u.decode(f,p),r.name=this._names.at(l+p.value),l+=p.value,f=p.rest)}this.__generatedMappings.push(r),"number"==typeof r.originalLine&&this.__originalMappings.push(r)}this.__generatedMappings.sort(i.compareByGeneratedPositions),this.__originalMappings.sort(i.compareByOriginalPositions)},o.prototype._findMapping=function(e,t,r,n,o){if(e[r]<=0)throw new TypeError("Line must be greater than or equal to 1, got "+e[r]);if(e[n]<0)throw new TypeError("Column must be greater than or equal to 0, got "+e[n]);return a.search(e,t,o)},o.prototype.computeColumnSpans=function(){for(var e=0;e=0){var n=this._generatedMappings[r];if(n.generatedLine===t.generatedLine){var o=i.getArg(n,"source",null);return null!=o&&null!=this.sourceRoot&&(o=i.join(this.sourceRoot,o)),{source:o,line:i.getArg(n,"originalLine",null),column:i.getArg(n,"originalColumn",null),name:i.getArg(n,"name",null)}}}return{source:null,line:null,column:null,name:null}},o.prototype.sourceContentFor=function(e){if(!this.sourcesContent)return null;if(null!=this.sourceRoot&&(e=i.relative(this.sourceRoot,e)),this._sources.has(e))return this.sourcesContent[this._sources.indexOf(e)];var t;if(null!=this.sourceRoot&&(t=i.urlParse(this.sourceRoot))){var r=e.replace(/^file:\/\//,"");if("file"==t.scheme&&this._sources.has(r))return this.sourcesContent[this._sources.indexOf(r)];if((!t.path||"/"==t.path)&&this._sources.has("/"+e))return this.sourcesContent[this._sources.indexOf("/"+e)]}throw new Error('"'+e+'" is not in the SourceMap.')},o.prototype.generatedPositionFor=function(e){var t={source:i.getArg(e,"source"),originalLine:i.getArg(e,"line"),originalColumn:i.getArg(e,"column")};null!=this.sourceRoot&&(t.source=i.relative(this.sourceRoot,t.source));var r=this._findMapping(t,this._originalMappings,"originalLine","originalColumn",i.compareByOriginalPositions);if(r>=0){var n=this._originalMappings[r];return{line:i.getArg(n,"generatedLine",null),column:i.getArg(n,"generatedColumn",null),lastColumn:i.getArg(n,"lastGeneratedColumn",null)}}return{line:null,column:null,lastColumn:null}},o.prototype.allGeneratedPositionsFor=function(e){var t={source:i.getArg(e,"source"),originalLine:i.getArg(e,"line"),originalColumn:1/0};null!=this.sourceRoot&&(t.source=i.relative(this.sourceRoot,t.source));var r=[],n=this._findMapping(t,this._originalMappings,"originalLine","originalColumn",i.compareByOriginalPositions);if(n>=0)for(var o=this._originalMappings[n];o&&o.originalLine===t.originalLine;)r.push({line:i.getArg(o,"generatedLine",null),column:i.getArg(o,"generatedColumn",null),lastColumn:i.getArg(o,"lastGeneratedColumn",null)}),o=this._originalMappings[--n];return r.reverse()},o.GENERATED_ORDER=1,o.ORIGINAL_ORDER=2,o.prototype.eachMapping=function(e,t,r){var n,a=t||null,s=r||o.GENERATED_ORDER;switch(s){case o.GENERATED_ORDER:n=this._generatedMappings;break;case o.ORIGINAL_ORDER:n=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;n.map(function(e){var t=e.source;return null!=t&&null!=u&&(t=i.join(u,t)),{source:t,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name}}).forEach(e,a)},t.SourceMapConsumer=o}.call(t,r,t,e),!(void 0!==n&&(e.exports=n))},function(e,t,r){var n;n=function(e,t,r){function n(e,t,r){if(t in e)return e[t];if(3===arguments.length)return r;throw new Error('"'+t+'" is a required argument.')}function o(e){var t=e.match(g);return t?{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}:null}function i(e){var t="";return e.scheme&&(t+=e.scheme+":"),t+="//",e.auth&&(t+=e.auth+"@"),e.host&&(t+=e.host),e.port&&(t+=":"+e.port),e.path&&(t+=e.path),t}function a(e){var t=e,r=o(e);if(r){if(!r.path)return e;t=r.path}for(var n,a="/"===t.charAt(0),s=t.split(/\/+/),u=0,c=s.length-1;c>=0;c--)n=s[c],"."===n?s.splice(c,1):".."===n?u++:u>0&&(""===n?(s.splice(c+1,u),u=0):(s.splice(c,2),u--));return t=s.join("/"),""===t&&(t=a?"/":"."),r?(r.path=t,i(r)):t}function s(e,t){""===e&&(e="."),""===t&&(t=".");var r=o(t),n=o(e);if(n&&(e=n.path||"/"),r&&!r.scheme)return n&&(r.scheme=n.scheme),i(r);if(r||t.match(d))return t;if(n&&!n.host&&!n.path)return n.host=t,i(n);var s="/"===t.charAt(0)?t:a(e.replace(/\/+$/,"")+"/"+t);return n?(n.path=s,i(n)):s}function u(e,t){""===e&&(e="."),e=e.replace(/\/$/,"");var r=o(e);return"/"==t.charAt(0)&&r&&"/"==r.path?t.slice(1):0===t.indexOf(e+"/")?t.substr(e.length+1):t}function c(e){return"$"+e}function l(e){return e.substr(1)}function f(e,t){var r=e||"",n=t||"";return(r>n)-(n>r)}function p(e,t,r){var n;return(n=f(e.source,t.source))?n:(n=e.originalLine-t.originalLine)?n:(n=e.originalColumn-t.originalColumn,n||r?n:(n=f(e.name,t.name))?n:(n=e.generatedLine-t.generatedLine,n?n:e.generatedColumn-t.generatedColumn))}function h(e,t,r){var n;return(n=e.generatedLine-t.generatedLine)?n:(n=e.generatedColumn-t.generatedColumn,n||r?n:(n=f(e.source,t.source))?n:(n=e.originalLine-t.originalLine)?n:(n=e.originalColumn-t.originalColumn,n?n:f(e.name,t.name)))}t.getArg=n;var g=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/,d=/^data:.+\,.+$/;t.urlParse=o,t.urlGenerate=i,t.normalize=a,t.join=s,t.relative=u,t.toSetString=c,t.fromSetString=l,t.compareByOriginalPositions=p,t.compareByGeneratedPositions=h}.call(t,r,t,e),!(void 0!==n&&(e.exports=n))},function(e,t,r){var n;n=function(e,t,r){function n(e,t,r,o,i){var a=Math.floor((t-e)/2)+e,s=i(r,o[a],!0);return 0===s?a:s>0?t-a>1?n(a,t,r,o,i):a:a-e>1?n(e,a,r,o,i):0>e?-1:e}t.search=function(e,t,r){return 0===t.length?-1:n(-1,t.length,e,t,r)}}.call(t,r,t,e),!(void 0!==n&&(e.exports=n))},function(e,t,r){var n;n=function(e,t,n){function o(){this._array=[],this._set={}}var i=r(1);o.fromArray=function(e,t){for(var r=new o,n=0,i=e.length;i>n;n++)r.add(e[n],t);return r},o.prototype.add=function(e,t){var r=this.has(e),n=this._array.length;(!r||t)&&this._array.push(e),r||(this._set[i.toSetString(e)]=n)},o.prototype.has=function(e){return Object.prototype.hasOwnProperty.call(this._set,i.toSetString(e))},o.prototype.indexOf=function(e){if(this.has(e))return this._set[i.toSetString(e)];throw new Error('"'+e+'" is not in the set.')},o.prototype.at=function(e){if(e>=0&&ee?(-e<<1)+1:(e<<1)+0}function i(e){var t=1===(1&e),r=e>>1;return t?-r:r}var a=r(5),s=5,u=1<>>=s,n>0&&(t|=l),r+=a.encode(t);while(n>0);return r},t.decode=function(e,t){var r,n,o=0,u=e.length,f=0,p=0;do{if(o>=u)throw new Error("Expected more digits in base 64 VLQ value.");n=a.decode(e.charAt(o++)),r=!!(n&l),n&=c,f+=n<=200&&o.status<400)return t(o.responseText);n(new Error("Unable to retrieve "+e))}},o.send()}function o(e,t,r){for(var n,o,i,a=/function\s+([^(]*?)\s*\(([^)]*)\)/,s=/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/,u=/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/,c=e.split("\n"),l="",f=Math.min(t,20),p=0;f>p;++p)if(n=c[t-p-1],i=n.indexOf("//"),i>=0&&(n=n.substr(0,i)),n){if(l=n+l,o=s.exec(l),o&&o[1])return o[1];if(o=a.exec(l),o&&o[1])return o[1];if(o=u.exec(l),o&&o[1])return o[1]}return void 0}function i(){if("function"!=typeof Object.defineProperty||"function"!=typeof Object.create)throw new Error("Unable to consume source maps in older browsers")}function a(e){if("object"!=typeof e)throw new TypeError("Given StackFrame is not an object");if("string"!=typeof e.fileName)throw new TypeError("Given file name is not a String");if("number"!=typeof e.lineNumber||e.lineNumber%1!==0||e.lineNumber<1)throw new TypeError("Given line number must be a positive integer");if("number"!=typeof e.columnNumber||e.columnNumber%1!==0||e.columnNumber<0)throw new TypeError("Given column number must be a non-negative integer");return!0}function s(e){var t=/\/\/[#@] ?sourceMappingURL=([^\s'"]+)$/.exec(e);if(t&&t[1])return t[1];throw new Error("sourceMappingURL not found")}function u(r,n,o,i){var a=new e.SourceMapConsumer(r).originalPositionFor({line:o,column:i});return new t(a.name,n,a.source,a.line,a.column)}return function c(e){return this instanceof c?(e=e||{},this.sourceCache=e.sourceCache||{},this.ajax=n,this._atob=function(e){if(window&&window.atob)return window.atob(e);if("undefined"!=typeof Buffer)return new Buffer(e,"base64").toString("utf-8");throw new Error("No base64 decoder available")},this._get=function(t){return new Promise(function(r,n){var o="data:"===t.substr(0,5);if(this.sourceCache[t])r(this.sourceCache[t]);else if(e.offline&&!o)n(new Error("Cannot make network requests in offline mode"));else if(o){var i="application/json;base64";if(t.substr(5,i.length)!==i)n(new Error("The encoding of the inline sourcemap is not supported"));else{var a="data:".length+i.length+",".length,s=t.substr(a),u=this._atob(s);this.sourceCache[t]=u,r(u)}}else this.ajax(t,function(e){this.sourceCache[t]=e,r(e)}.bind(this),n)}.bind(this))},this.pinpoint=function(e){return new Promise(function(t,r){this.getMappedLocation(e).then(function(e){function r(){t(e)}this.findFunctionName(e).then(t,r)["catch"](r)}.bind(this),r)}.bind(this))},this.findFunctionName=function(e){return new Promise(function(r,n){a(e),this._get(e.fileName).then(function(n){var i=o(n,e.lineNumber,e.columnNumber);r(new t(i,e.args,e.fileName,e.lineNumber,e.columnNumber))},n)}.bind(this))},void(this.getMappedLocation=function(e){return new Promise(function(t,r){i(),a(e);var n=e.fileName;this._get(n).then(function(o){var i=s(o);"/"!==i[0]&&(i=n.substring(0,n.lastIndexOf("/")+1)+i),this._get(i).then(function(r){var n=e.lineNumber,o=e.columnNumber;t(u(r,e.args,n,o))},r)["catch"](r)}.bind(this),r)["catch"](r)}.bind(this))})):new c(e)}}),function(e,t){"use strict";"function"==typeof define&&define.amd?define("stack-generator",["stackframe"],t):"object"==typeof exports?module.exports=t(require("stackframe")):e.StackGenerator=t(e.StackFrame)}(this,function(e){return{backtrace:function(t){var r=[],n=10;"object"==typeof t&&"number"==typeof t.maxStackSize&&(n=t.maxStackSize);for(var o=arguments.callee;o&&r.length-1&&e.message.split("\n").length>e.stacktrace.split("\n").length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(t){for(var r=/Line (\d+).*script (?:in )?(\S+)/i,n=t.message.split("\n"),o=[],i=2,a=n.length;a>i;i+=2){var s=r.exec(n[i]);s&&o.push(new e(void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera10:function(t){for(var r=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,n=t.stacktrace.split("\n"),o=[],i=0,a=n.length;a>i;i+=2){var s=r.exec(n[i]);s&&o.push(new e(s[3]||void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera11:function(r){return r.stack.split("\n").filter(function(e){return!!e.match(t)&&!e.match(/^Error created at/)},this).map(function(t){var r,n=t.split("@"),o=this.extractLocation(n.pop()),i=n.shift()||"",a=i.replace(//,"$2").replace(/\([^\)]*\)/g,"")||void 0;i.match(/\(([^\)]*)\)/)&&(r=i.replace(/^[^\(]+\(([^\)]*)\)$/,"$1"));var s=void 0===r||"[arguments not available]"===r?void 0:r.split(",");return new e(a,s,o[0],o[1],o[2],t)},this)}}}),function(e,t){"use strict";"function"==typeof define&&define.amd?define("stacktrace",["error-stack-parser","stack-generator","stacktrace-gps"],t):"object"==typeof exports?module.exports=t(require("error-stack-parser"),require("stack-generator"),require("stacktrace-gps")):e.StackTrace=t(e.ErrorStackParser,e.StackGenerator,e.StackTraceGPS)}(this,function(e,t,r){function n(e,t){var r={};return[e,t].forEach(function(e){for(var t in e)e.hasOwnProperty(t)&&(r[t]=e[t]);return r}),r}function o(e){return e.stack||e["opera#sourceloc"]}var i={filter:function(e){return-1===(e.functionName||"").indexOf("StackTrace$$")&&-1===(e.functionName||"").indexOf("ErrorStackParser$$")&&-1===(e.functionName||"").indexOf("StackTraceGPS$$")&&-1===(e.functionName||"").indexOf("StackGenerator$$")}};return{get:function(e){try{throw new Error}catch(t){return o(t)?this.fromError(t,e):this.generateArtificially(e)}},fromError:function(t,o){return o=n(i,o),new Promise(function(n){var i=e.parse(t);"function"==typeof o.filter&&(i=i.filter(o.filter)),n(Promise.all(i.map(function(e){return new Promise(function(t){function n(r){t(e)}new r(o).pinpoint(e).then(t,n)["catch"](n)})})))}.bind(this))},generateArtificially:function(e){e=n(i,e);var r=t.backtrace(e);return"function"==typeof e.filter&&(r=r.filter(e.filter)),Promise.resolve(r)},instrument:function(e,t,r,n){if("function"!=typeof e)throw new Error("Cannot instrument non-function object");if("function"==typeof e.__stacktraceOriginalFn)return e;var i=function(){try{this.get().then(t,r)["catch"](r),e.apply(n||this,arguments)}catch(i){throw o(i)&&this.fromError(i).then(t,r)["catch"](r),i}}.bind(this);return i.__stacktraceOriginalFn=e,i},deinstrument:function(e){if("function"!=typeof e)throw new Error("Cannot de-instrument non-function object");return"function"==typeof e.__stacktraceOriginalFn?e.__stacktraceOriginalFn:e},report:function(e,t){return new Promise(function(r,n){var o=new XMLHttpRequest;o.onerror=n,o.onreadystatechange=function(){4===o.readyState&&(o.status>=200&&o.status<400?r(o.responseText):n(new Error("POST to "+t+" failed with status: "+o.status)))},o.open("post",t),o.setRequestHeader("Content-Type","application/json"),o.send(JSON.stringify({stack:e}))})}}}); +//# sourceMappingURL=vendor/stacktrace-js/dist/stacktrace-with-polyfills.min.js.map /** * @license * Fuse - Lightweight fuzzy-search @@ -30601,7 +30604,7 @@ function calculateAmounts(invoice) { total += roundToTwo(invoice.custom_value2); } - invoice.total_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance)); + invoice.total_amount = roundToTwo(roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance))); invoice.discount_amount = discount; invoice.tax_amount1 = taxAmount1; invoice.tax_amount2 = taxAmount2; diff --git a/public/js/script.js b/public/js/script.js index b806bd538ac5..8b53e500ef64 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -710,7 +710,7 @@ function calculateAmounts(invoice) { total += roundToTwo(invoice.custom_value2); } - invoice.total_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance)); + invoice.total_amount = roundToTwo(roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance))); invoice.discount_amount = discount; invoice.tax_amount1 = taxAmount1; invoice.tax_amount2 = taxAmount2; From f9c36fd761ca3ccaab704a3c7ea4a58555e5a91e Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 21 Apr 2016 13:02:19 +0300 Subject: [PATCH 127/258] Fix for saving invoice client with id/public_id --- app/Console/Commands/PruneData.php | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 app/Console/Commands/PruneData.php diff --git a/app/Console/Commands/PruneData.php b/app/Console/Commands/PruneData.php new file mode 100644 index 000000000000..89703764243e --- /dev/null +++ b/app/Console/Commands/PruneData.php @@ -0,0 +1,57 @@ +info(date('Y-m-d').' Running PruneData...'); + + // delete accounts who never registered, didn't create any invoices, + // hansn't logged in within the past 6 months and isn't linked to another account + $sql = 'select a.id + from (select id, last_login from accounts) a + left join users u on u.account_id = a.id and u.public_id = 0 + left join invoices i on i.account_id = a.id + left join user_accounts ua1 on ua1.user_id1 = u.id + left join user_accounts ua2 on ua2.user_id2 = u.id + left join user_accounts ua3 on ua3.user_id3 = u.id + left join user_accounts ua4 on ua4.user_id4 = u.id + left join user_accounts ua5 on ua5.user_id5 = u.id + where u.registered = 0 + and a.last_login < DATE_SUB(now(), INTERVAL 6 MONTH) + and (ua1.id is null and ua2.id is null and ua3.id is null and ua4.id is null and ua5.id is null) + group by a.id + having count(i.id) = 0'; + + $results = DB::select($sql); + + foreach ($results as $result) { + $this->info("Deleting {$result->id}"); + DB::table('accounts') + ->where('id', '=', $result->id) + ->delete(); + } + + $this->info('Done'); + } + + protected function getArguments() + { + return array( + //array('example', InputArgument::REQUIRED, 'An example argument.'), + ); + } + + protected function getOptions() + { + return array( + //array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null), + ); + } +} From ad68d64dca7f36a11434dec90edf24d080c4efe1 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 21 Apr 2016 22:01:17 +0300 Subject: [PATCH 128/258] Changes for white label license --- app/Console/Commands/SendRenewalInvoices.php | 8 +++++-- app/Http/Middleware/StartupCheck.php | 2 ++ app/Http/routes.php | 1 + .../2016_04_16_103943_enterprise_plan.php | 23 ++++++++++++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/Console/Commands/SendRenewalInvoices.php b/app/Console/Commands/SendRenewalInvoices.php index 64db2fad4fb1..87a840aed2c0 100644 --- a/app/Console/Commands/SendRenewalInvoices.php +++ b/app/Console/Commands/SendRenewalInvoices.php @@ -62,8 +62,12 @@ class SendRenewalInvoices extends Command $invoice->due_date = date('Y-m-d', strtotime('+ 10 days')); $invoice->save(); - $this->mailer->sendInvoice($invoice); - $this->info("Sent invoice to {$client->getDisplayName()}"); + if ($term == PLAN_TERM_YEARLY) { + $this->mailer->sendInvoice($invoice); + $this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}"); + } else { + $this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}"); + } } $this->info('Done'); diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index be96ade97efe..63197550d9c0 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -142,7 +142,9 @@ class StartupCheck } elseif ($productId == PRODUCT_WHITE_LABEL) { if ($data == 'valid') { $company = Auth::user()->account->company; + $company->plan_term = PLAN_TERM_YEARLY; $company->plan_paid = date_create()->format('Y-m-d'); + $company->plan_expires = date_create()->modify('+1 year')->format('Y-m-d'); $company->plan = PLAN_WHITE_LABEL; $company->save(); diff --git a/app/Http/routes.php b/app/Http/routes.php index 48f06e5e7b3d..e5915b394a2b 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -554,6 +554,7 @@ if (!defined('CONTACT_EMAIL')) { define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com'); + define('NINJA_DATE', '2000-01-01'); define('NINJA_VERSION', '2.5.1.3'); define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index 8ca5632ad358..8a3a63717367 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -127,13 +127,24 @@ class EnterprisePlan extends Migration $company->plan_started = $primaryAccount->pro_plan_paid; $company->plan_paid = $primaryAccount->pro_plan_paid; + $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); + $expires->modify('+1 year'); + $expires = $expires->format('Y-m-d'); + + // check for self host white label licenses if (!Utils::isNinjaProd()) { - $company->plan = 'white_label'; - $company->plan_term = null; - } elseif ($company->plan_paid != '2000-01-01'/* NINJA_DATE*/) { - $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); - $expires->modify('+1 year'); - $company->plan_expires = $expires->format('Y-m-d'); + if ($company->plan_paid) { + $company->plan = 'white_label'; + // old ones were unlimited, new ones are yearly + if ($company->plan_paid == NINJA_DATE) { + $company->plan_term = null; + } else { + $company->plan_term = PLAN_TERM_YEARLY; + $company->plan_expires = $expires; + } + } + } elseif ($company->plan_paid != NINJA_DATE) { + $company->plan_expires = $expires; } } From fdf1b25b16abc542626d3ee8963121a25cf4429a Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Sat, 23 Apr 2016 11:52:36 -0400 Subject: [PATCH 129/258] Begin adding authorization policies --- app/Http/Controllers/BaseController.php | 30 ++----------- app/Http/Controllers/InvoiceController.php | 4 +- app/Models/EntityModel.php | 52 ---------------------- app/Models/User.php | 4 ++ app/Policies/EntityPolicy.php | 25 +++++++++++ app/Policies/InvoicePolicy.php | 23 ++++++++++ app/Providers/AuthServiceProvider.php | 29 ++++++++++++ config/app.php | 1 + 8 files changed, 86 insertions(+), 82 deletions(-) create mode 100644 app/Policies/EntityPolicy.php create mode 100644 app/Policies/InvoicePolicy.php create mode 100644 app/Providers/AuthServiceProvider.php diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 5124097636a9..c62c496e8c60 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -22,39 +22,15 @@ class BaseController extends Controller } } - protected function checkViewPermission($object, &$response = null){ - if(!$object->canView()){ - $response = response('Unauthorized.', 401); - return false; - } - return true; - } - - protected function checkEditPermission($object, &$response = null){ - if(!$object->canEdit()){ - $response = response('Unauthorized.', 401); - return false; - } - return true; - } - - protected function checkCreatePermission(&$response = null){ - if(!call_user_func(array($this->model, 'canCreate'))){ - $response = response('Unauthorized.', 401); - return false; - } - return true; - } - - protected function checkUpdatePermission($input, &$response = null){ + protected function authorizeUpdate($input){ $creating = empty($input['public_id']) || $input['public_id'] == '-1'; if($creating){ - return $this->checkCreatePermission($response); + $this->authorize('create', $this->model); } else{ $object = call_user_func(array($this->model, 'scope'), $input['public_id'])->firstOrFail(); - return $this->checkEditPermission($object, $response); + $this->authorize('edit', $object); } } } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 774ed7a7a70c..bbc8ab87db09 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -96,9 +96,7 @@ class InvoiceController extends BaseController ->withTrashed() ->firstOrFail(); - if(!$this->checkEditPermission($invoice, $response)){ - return $response; - } + $this->authorize('edit', $invoice) $entityType = $invoice->getEntityType(); diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index 53bb1d0d1a48..8d0da39d3fab 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -118,56 +118,4 @@ class EntityModel extends Eloquent $name = $parts[count($parts)-1]; return strtolower($name) . '_id'; } - - public static function canCreate() { - return Auth::user()->hasPermission('create_all'); - } - - public function canEdit() { - return static::canEditItem($this); - } - - public static function canEditItem($item) { - return Auth::user()->hasPermission('edit_all') || (isset($item->user_id) && Auth::user()->id == $item->user_id); - } - - public static function canEditItemById($item_id) { - if(Auth::user()->hasPermission('edit_all')) { - return true; - } - - return static::whereId($item_id)->first()->user_id == Auth::user()->id; - } - - public static function canEditItemByOwner($user_id) { - if(Auth::user()->hasPermission('edit_all')) { - return true; - } - - return Auth::user()->id == $user_id; - } - - public function canView() { - return static::canViewItem($this); - } - - public static function canViewItem($item) { - return Auth::user()->hasPermission('view_all') || (isset($item->user_id) && Auth::user()->id == $item->user_id); - } - - public static function canViewItemById($item_id) { - if(Auth::user()->hasPermission('view_all')) { - return true; - } - - return static::whereId($item_id)->first()->user_id == Auth::user()->id; - } - - public static function canViewItemByOwner($user_id) { - if(Auth::user()->hasPermission('view_all')) { - return true; - } - - return Auth::user()->id == $user_id; - } } diff --git a/app/Models/User.php b/app/Models/User.php index 32a4960a8482..a7ef6fcf5bd7 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -326,6 +326,10 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return false; } + + public function owns($entity) { + return !empty($entity->user_id) && $entity->user_id == $this->id; + } } User::updating(function ($user) { diff --git a/app/Policies/EntityPolicy.php b/app/Policies/EntityPolicy.php new file mode 100644 index 000000000000..79f60e8ff504 --- /dev/null +++ b/app/Policies/EntityPolicy.php @@ -0,0 +1,25 @@ +hasPermission('create_all'); + } + + public static function edit($user, $item) { + $user->hasPermission('edit_all') || $user->owns($item); + } + + public static function view($user, $item) { + $user->hasPermission('view_all') || $user->owns($item); + } +} \ No newline at end of file diff --git a/app/Policies/InvoicePolicy.php b/app/Policies/InvoicePolicy.php new file mode 100644 index 000000000000..46a1adcc3d84 --- /dev/null +++ b/app/Policies/InvoicePolicy.php @@ -0,0 +1,23 @@ + InvoicePolicy::class, + ]; + + /** + * Register any application authentication / authorization services. + * + * @param \Illuminate\Contracts\Auth\Access\Gate $gate + * @return void + */ + public function boot(GateContract $gate) + { + $this->registerPolicies($gate); + } +} \ No newline at end of file diff --git a/config/app.php b/config/app.php index c86362a9afea..027c3d5ece28 100644 --- a/config/app.php +++ b/config/app.php @@ -157,6 +157,7 @@ return [ /* * Application Service Providers... */ + 'App\Providers\AuthServiceProvider', 'App\Providers\AppServiceProvider', //'App\Providers\BusServiceProvider', 'App\Providers\ConfigServiceProvider', From 07f50d188f801181880af3be72be77ba208d3216 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 00:08:16 +0300 Subject: [PATCH 130/258] Fix for partial invoice label on PDF --- public/built.js | 25 +++++++++---------------- public/js/pdf.pdfmake.js | 25 +++++++++---------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/public/built.js b/public/built.js index 0522b23e67c3..faffe4a2c348 100644 --- a/public/built.js +++ b/public/built.js @@ -31601,25 +31601,18 @@ NINJA.invoiceDetails = function(invoice) { ]) } - var isPartial = NINJA.parseFloat(invoice.partial); - - if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.amount, invoice)} - ]); - } else if (isPartial) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.total_amount, invoice)} - ]); - } - data.push([ - {text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, - {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']} ]) + if (NINJA.parseFloat(invoice.partial)) { + data.push([ + {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + ]) + } + return NINJA.prepareDataPairs(data, 'invoiceDetails'); } diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 1e5d1e93b3f8..db8e16f79442 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -597,25 +597,18 @@ NINJA.invoiceDetails = function(invoice) { ]) } - var isPartial = NINJA.parseFloat(invoice.partial); - - if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.amount, invoice)} - ]); - } else if (isPartial) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.total_amount, invoice)} - ]); - } - data.push([ - {text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, - {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']} ]) + if (NINJA.parseFloat(invoice.partial)) { + data.push([ + {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + ]) + } + return NINJA.prepareDataPairs(data, 'invoiceDetails'); } From 604c132a3a0405657a4d3eeee252bdb7c9f67ebf Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 16:14:35 +0300 Subject: [PATCH 131/258] Temporary workaround to support not setting the CVC/CVV --- app/Services/PaymentService.php | 8 ++++++-- resources/views/payments/payment.blade.php | 11 ++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 61af6cd2336c..858a175f8954 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -98,9 +98,13 @@ class PaymentService extends BaseService 'number' => isset($input['card_number']) ? $input['card_number'] : null, 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null, 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null, - 'cvv' => isset($input['cvv']) ? $input['cvv'] : '', ]; - + + // allow space until there's a setting to disable + if (isset($input['cvv']) && $input['cvv'] != ' ') { + $data['cvv'] = $input['cvv']; + } + if (isset($input['country_id'])) { $country = Country::find($input['country_id']); diff --git a/resources/views/payments/payment.blade.php b/resources/views/payments/payment.blade.php index 48ff913c4ea3..274361938bac 100644 --- a/resources/views/payments/payment.blade.php +++ b/resources/views/payments/payment.blade.php @@ -20,10 +20,14 @@ address_zip: $('#postal_code').val(), address_country: $("#country_id option:selected").text(), number: $('#card_number').val(), - cvc: $('#cvv').val(), exp_month: $('#expiration_month').val(), exp_year: $('#expiration_year').val() }; + + // allow space until there's a setting to disable + if ($('#cvv').val() != ' ') { + data.cvc = $('#cvv').val(); + } // Validate the card details if (!Stripe.card.validateCardNumber(data.number)) { @@ -34,11 +38,12 @@ $('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn(); return false; } - if (!Stripe.card.validateCVC(data.cvc)) { + + if (data.hasOwnProperty('cvc') && !Stripe.card.validateCVC(data.cvc)) { $('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn(); return false; } - + // Disable the submit button to prevent repeated clicks $form.find('button').prop('disabled', true); $('#js-error-message').hide(); From 71b1a9c209d6219e4d0bdaf6995f68c33c248356 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 16:34:48 +0300 Subject: [PATCH 132/258] Improved filtering the payment list --- app/Ninja/Repositories/PaymentRepository.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index a027cb62aedb..85dfab9964f8 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -64,7 +64,14 @@ class PaymentRepository extends BaseRepository if ($filter) { $query->where(function ($query) use ($filter) { - $query->where('clients.name', 'like', '%'.$filter.'%'); + $query->where('clients.name', 'like', '%'.$filter.'%') + ->orWhere('invoices.invoice_number', 'like', '%'.$filter.'%') + ->orWhere('payments.transaction_reference', 'like', '%'.$filter.'%') + ->orWhere('gateways.name', 'like', '%'.$filter.'%') + ->orWhere('payment_types.name', 'like', '%'.$filter.'%') + ->orWhere('contacts.first_name', 'like', '%'.$filter.'%') + ->orWhere('contacts.last_name', 'like', '%'.$filter.'%') + ->orWhere('contacts.email', 'like', '%'.$filter.'%'); }); } From cfb4dc43515777c0a4bea9004951bae354c3eeb9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 20:41:30 +0300 Subject: [PATCH 133/258] Hide zapier button for resellers --- resources/views/accounts/api_tokens.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/accounts/api_tokens.blade.php b/resources/views/accounts/api_tokens.blade.php index 9c328e6b2643..49029bf8d2b3 100644 --- a/resources/views/accounts/api_tokens.blade.php +++ b/resources/views/accounts/api_tokens.blade.php @@ -6,7 +6,7 @@
    {!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/api-documentation/')->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!} - @if (Utils::isNinja()) + @if (Utils::isNinja() && !Utils::isReseller()) {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} @endif @if (Utils::hasFeature(FEATURE_API)) From fc0d4d3edd3e186b2db232b263f4a1f76579e847 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Mon, 25 Apr 2016 21:53:39 -0400 Subject: [PATCH 134/258] Finish migrating to Laravel ACL --- app/Http/Controllers/BaseController.php | 15 ++++++-- app/Http/Controllers/ClientController.php | 33 ++++++----------- app/Http/Controllers/CreditController.php | 10 ++--- app/Http/Controllers/DocumentController.php | 18 +++------ app/Http/Controllers/ExpenseController.php | 18 +++------ app/Http/Controllers/InvoiceController.php | 16 +++----- app/Http/Controllers/PaymentController.php | 18 +++------ app/Http/Controllers/QuoteController.php | 6 +-- app/Http/Controllers/TaskController.php | 14 ++----- app/Http/Controllers/VendorController.php | 22 +++-------- app/Models/Document.php | 14 ------- app/Models/Product.php | 4 -- app/Models/TaxRate.php | 4 -- app/Models/User.php | 6 ++- app/Ninja/Repositories/ExpenseRepository.php | 5 ++- app/Ninja/Repositories/InvoiceRepository.php | 13 ++++--- app/Policies/ClientPolicy.php | 5 +++ app/Policies/CreditPolicy.php | 5 +++ app/Policies/DocumentPolicy.php | 20 ++++++++++ app/Policies/EntityPolicy.php | 18 ++++++--- app/Policies/ExpensePolicy.php | 5 +++ app/Policies/GenericEntityPolicy.php | 39 ++++++++++++++++++++ app/Policies/InvoicePolicy.php | 20 +--------- app/Policies/PaymentPolicy.php | 5 +++ app/Policies/ProductPolicy.php | 9 +++++ app/Policies/TaskPolicy.php | 5 +++ app/Policies/TaxRatePolicy.php | 9 +++++ app/Policies/VendorPolicy.php | 5 +++ app/Providers/AppServiceProvider.php | 15 ++++---- app/Providers/AuthServiceProvider.php | 13 ++++++- app/Services/BaseService.php | 2 +- app/Services/ClientService.php | 20 +++++----- app/Services/CreditService.php | 4 +- app/Services/ExpenseService.php | 12 +++--- app/Services/InvoiceService.php | 24 ++++++------ app/Services/PaymentService.php | 6 +-- app/Services/RecurringInvoiceService.php | 2 +- app/Services/TaskService.php | 10 ++--- app/Services/VendorService.php | 6 +-- config/app.php | 1 + resources/views/clients/show.blade.php | 12 +++--- resources/views/dashboard.blade.php | 12 +++--- resources/views/invoices/edit.blade.php | 8 ++-- resources/views/list.blade.php | 4 +- 44 files changed, 275 insertions(+), 237 deletions(-) create mode 100644 app/Policies/ClientPolicy.php create mode 100644 app/Policies/CreditPolicy.php create mode 100644 app/Policies/DocumentPolicy.php create mode 100644 app/Policies/ExpensePolicy.php create mode 100644 app/Policies/GenericEntityPolicy.php create mode 100644 app/Policies/PaymentPolicy.php create mode 100644 app/Policies/ProductPolicy.php create mode 100644 app/Policies/TaskPolicy.php create mode 100644 app/Policies/TaxRatePolicy.php create mode 100644 app/Policies/VendorPolicy.php diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index c62c496e8c60..a9060e47fdec 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -2,13 +2,14 @@ use App\Http\Middleware\PermissionsRequired; use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Auth; class BaseController extends Controller { - use DispatchesJobs; + use DispatchesJobs, AuthorizesRequests; - protected $model = 'App\Models\EntityModel'; + protected $entity; /** * Setup the layout used by the controller. @@ -22,14 +23,20 @@ class BaseController extends Controller } } + protected function authorizeCreate() { + $this->authorize('create', $this->entity); + } + protected function authorizeUpdate($input){ $creating = empty($input['public_id']) || $input['public_id'] == '-1'; if($creating){ - $this->authorize('create', $this->model); + $this->authorize('create', $this->entity); } else{ - $object = call_user_func(array($this->model, 'scope'), $input['public_id'])->firstOrFail(); + $className = ucwords($this->entity, '_'); + + $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); $this->authorize('edit', $object); } } diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 2992eb9a9550..49988ab78b79 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -35,7 +35,7 @@ class ClientController extends BaseController { protected $clientService; protected $clientRepo; - protected $model = 'App\Models\Client'; + protected $entity = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { @@ -83,9 +83,7 @@ class ClientController extends BaseController { $data = $request->input(); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $client = $this->clientService->save($data); @@ -104,17 +102,16 @@ class ClientController extends BaseController { $client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail(); - if(!$this->checkViewPermission($client, $response)){ - return $response; - } + $this->authorize('view', $client); + $user = Auth::user(); Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT); $actionLinks = []; - if(Task::canCreate()){ + if($user->can('create', ENTITY_TASK)){ $actionLinks[] = ['label' => trans('texts.new_task'), 'url' => URL::to('/tasks/create/'.$client->public_id)]; } - if (Utils::hasFeature(FEATURE_QUOTES) && Invoice::canCreate()) { + if (Utils::hasFeature(FEATURE_QUOTES) && $user->can('create', ENTITY_INVOICE)) { $actionLinks[] = ['label' => trans('texts.new_quote'), 'url' => URL::to('/quotes/create/'.$client->public_id)]; } @@ -122,15 +119,15 @@ class ClientController extends BaseController $actionLinks[] = \DropdownButton::DIVIDER; } - if(Payment::canCreate()){ + if($user->can('create', ENTITY_PAYMENT)){ $actionLinks[] = ['label' => trans('texts.enter_payment'), 'url' => URL::to('/payments/create/'.$client->public_id)]; } - if(Credit::canCreate()){ + if($user->can('create', ENTITY_CREDIT)){ $actionLinks[] = ['label' => trans('texts.enter_credit'), 'url' => URL::to('/credits/create/'.$client->public_id)]; } - if(Expense::canCreate()){ + if($user->can('create', ENTITY_EXPENSE)){ $actionLinks[] = ['label' => trans('texts.enter_expense'), 'url' => URL::to('/expenses/create/0/'.$client->public_id)]; } @@ -156,9 +153,7 @@ class ClientController extends BaseController */ public function create() { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); if (Client::scope()->withTrashed()->count() > Auth::user()->getMaxNumClients()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumClients()." clients"]); @@ -186,9 +181,7 @@ class ClientController extends BaseController { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); - if(!$this->checkEditPermission($client, $response)){ - return $response; - } + $this->authorize('edit', $client); $data = [ 'client' => $client, @@ -234,9 +227,7 @@ class ClientController extends BaseController { $data = $request->input(); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $client = $this->clientService->save($data); diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 26085c3d6b32..23577f951389 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -17,7 +17,7 @@ class CreditController extends BaseController { protected $creditRepo; protected $creditService; - protected $model = 'App\Models\Credit'; + protected $entity = ENTITY_CREDIT; public function __construct(CreditRepository $creditRepo, CreditService $creditService) { @@ -57,9 +57,7 @@ class CreditController extends BaseController public function create($clientPublicId = 0) { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); $data = array( 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, @@ -78,9 +76,7 @@ class CreditController extends BaseController { $credit = Credit::scope($publicId)->firstOrFail(); - if(!$this->checkEditPermission($credit, $response)){ - return $response; - } + $this->authorize('edit', $credit); $credit->credit_date = Utils::fromSqlDate($credit->credit_date); diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 15f3ced57fd2..c0e25e773d11 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -15,7 +15,7 @@ use App\Ninja\Repositories\DocumentRepository; class DocumentController extends BaseController { protected $documentRepo; - protected $model = 'App\Models\Document'; + protected $entity = ENTITY_DOCUMENT; public function __construct(DocumentRepository $documentRepo) { @@ -29,9 +29,7 @@ class DocumentController extends BaseController $document = Document::scope($publicId) ->firstOrFail(); - if(!$this->checkViewPermission($document, $response)){ - return $response; - } + $this->authorize('view', $document); return static::getDownloadResponse($document); } @@ -67,9 +65,7 @@ class DocumentController extends BaseController $document = Document::scope($publicId) ->firstOrFail(); - if(!$this->checkViewPermission($document, $response)){ - return $response; - } + $this->authorize('view', $document); if(empty($document->preview)){ return Response::view('error', array('error'=>'Preview does not exist!'), 404); @@ -95,9 +91,7 @@ class DocumentController extends BaseController $name = substr($name, 0, -3); } - if(!$this->checkViewPermission($document, $response)){ - return $response; - } + $this->authorize('view', $document); if(!$document->isPDFEmbeddable()){ return Response::view('error', array('error'=>'Image does not exist!'), 404); @@ -118,9 +112,7 @@ class DocumentController extends BaseController return; } - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); $result = $this->documentRepo->upload(Input::all()['file'], $doc_array); diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index b8e82cddda48..e53122e46dcb 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -25,7 +25,7 @@ class ExpenseController extends BaseController // Expenses protected $expenseRepo; protected $expenseService; - protected $model = 'App\Models\Expense'; + protected $entity = ENTITY_EXPENSE; public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService) { @@ -71,9 +71,7 @@ class ExpenseController extends BaseController public function create($vendorPublicId = null, $clientPublicId = null) { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); if($vendorPublicId != 0) { $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); @@ -101,9 +99,7 @@ class ExpenseController extends BaseController { $expense = Expense::scope($publicId)->with('documents')->firstOrFail(); - if(!$this->checkEditPermission($expense, $response)){ - return $response; - } + $this->authorize('edit', $expense); $expense->expense_date = Utils::fromSqlDate($expense->expense_date); @@ -160,9 +156,7 @@ class ExpenseController extends BaseController $data = $request->input(); $data['documents'] = $request->file('documents'); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $expense = $this->expenseService->save($data, true); @@ -181,9 +175,7 @@ class ExpenseController extends BaseController $data = $request->input(); $data['documents'] = $request->file('documents'); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $expense = $this->expenseService->save($data); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index bbc8ab87db09..79ce2aee93b1 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -37,7 +37,7 @@ class InvoiceController extends BaseController protected $documentRepo; protected $invoiceService; protected $recurringInvoiceService; - protected $model = 'App\Models\Invoice'; + protected $entity = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService) { @@ -96,7 +96,7 @@ class InvoiceController extends BaseController ->withTrashed() ->firstOrFail(); - $this->authorize('edit', $invoice) + $this->authorize('edit', $invoice); $entityType = $invoice->getEntityType(); @@ -231,9 +231,7 @@ class InvoiceController extends BaseController public function create($clientPublicId = 0, $isRecurring = false) { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); $account = Auth::user()->account; $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; @@ -402,9 +400,7 @@ class InvoiceController extends BaseController $data = $request->input(); $data['documents'] = $request->file('documents'); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $action = Input::get('action'); $entityType = Input::get('entityType'); @@ -443,9 +439,7 @@ class InvoiceController extends BaseController $data = $request->input(); $data['documents'] = $request->file('documents'); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $action = Input::get('action'); $entityType = Input::get('entityType'); diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 1f53b4a0bc54..4654a3e69856 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -30,7 +30,7 @@ use App\Http\Requests\UpdatePaymentRequest; class PaymentController extends BaseController { - protected $model = 'App\Models\Payment'; + protected $entity = ENTITY_PAYMENT; public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService) { @@ -69,9 +69,7 @@ class PaymentController extends BaseController public function create($clientPublicId = 0, $invoicePublicId = 0) { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); $invoices = Invoice::scope() ->where('is_recurring', '=', false) @@ -100,9 +98,7 @@ class PaymentController extends BaseController { $payment = Payment::scope($publicId)->firstOrFail(); - if(!$this->checkEditPermission($payment, $response)){ - return $response; - } + $this->authorize('edit', $payment); $payment->payment_date = Utils::fromSqlDate($payment->payment_date); @@ -594,9 +590,7 @@ class PaymentController extends BaseController { $input = $request->input(); - if(!$this->checkUpdatePermission($input, $response)){ - return $response; - } + $this->authorizeUpdate($data); $input['invoice_id'] = Invoice::getPrivateId($input['invoice']); $input['client_id'] = Client::getPrivateId($input['client']); @@ -616,9 +610,7 @@ class PaymentController extends BaseController { $input = $request->input(); - if(!$this->checkUpdatePermission($input, $response)){ - return $response; - } + $this->authorizeUpdate($data); $payment = $this->paymentRepo->save($input); diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 4aaa504bfd1b..e2228ab2a0c0 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -33,7 +33,7 @@ class QuoteController extends BaseController protected $invoiceRepo; protected $clientRepo; protected $invoiceService; - protected $model = 'App\Models\Invoice'; + protected $entity = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { @@ -80,9 +80,7 @@ class QuoteController extends BaseController public function create($clientPublicId = 0) { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); if (!Utils::hasFeature(FEATURE_QUOTES)) { return Redirect::to('/invoices/create'); diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 59f49da2514f..7ff19436ead2 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -22,7 +22,7 @@ class TaskController extends BaseController { protected $taskRepo; protected $taskService; - protected $model = 'App\Models\Task'; + protected $entity = ENTITY_TASK; public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService) { @@ -85,9 +85,7 @@ class TaskController extends BaseController */ public function create($clientPublicId = 0) { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); $this->checkTimezone(); $data = [ @@ -117,9 +115,7 @@ class TaskController extends BaseController $task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail(); - if(!$this->checkEditPermission($task, $response)){ - return $response; - } + $this->authorize('edit', $task); $actions = []; if ($task->invoice) { @@ -184,9 +180,7 @@ class TaskController extends BaseController { $action = Input::get('action'); - if(!$this->checkUpdatePermission(array('public_id'=>$publicId)/* Hacky, but works */, $response)){ - return $response; - } + $this->authorizeUpdate(array('public_id'=>$publicId)/* Hacky, but works */); if (in_array($action, ['archive', 'delete', 'restore'])) { return self::bulk(); diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index b337c1df29e7..00f5bbe83e4d 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -30,7 +30,7 @@ class VendorController extends BaseController { protected $vendorService; protected $vendorRepo; - protected $model = 'App\Models\Vendor'; + protected $entity = ENTITY_VENDOR; public function __construct(VendorRepository $vendorRepo, VendorService $vendorService) { @@ -79,9 +79,7 @@ class VendorController extends BaseController { $data = $request->input(); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $vendor = $this->vendorService->save($data); @@ -100,9 +98,7 @@ class VendorController extends BaseController { $vendor = Vendor::withTrashed()->scope($publicId)->with('vendorcontacts', 'size', 'industry')->firstOrFail(); - if(!$this->checkViewPermission($vendor, $response)){ - return $response; - } + $this->authorize('view', $vendor); Utils::trackViewed($vendor->getDisplayName(), 'vendor'); @@ -131,9 +127,7 @@ class VendorController extends BaseController */ public function create() { - if(!$this->checkCreatePermission($response)){ - return $response; - } + $this->authorizeCreate(); if (Vendor::scope()->count() > Auth::user()->getMaxNumVendors()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumVendors()." vendors"]); @@ -161,9 +155,7 @@ class VendorController extends BaseController { $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); - if(!$this->checkEditPermission($vendor, $response)){ - return $response; - } + $this->authorize('edit', $vendor) $data = [ 'vendor' => $vendor, @@ -203,9 +195,7 @@ class VendorController extends BaseController { $data = $request->input(); - if(!$this->checkUpdatePermission($data, $response)){ - return $response; - } + $this->authorizeUpdate($data); $vendor = $this->vendorService->save($data); diff --git a/app/Models/Document.php b/app/Models/Document.php index 96051d9db1b2..0d7fc049aac8 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -222,20 +222,6 @@ class Document extends EntityModel return $document; } - - public static function canCreate(){ - return true; - } - - public static function canViewItem($document){ - if(Auth::user()->hasPermission('view_all'))return true; - if($document->expense){ - if($document->expense->invoice)return $document->expense->invoice->canView(); - return $document->expense->canView(); - } - if($document->invoice)return $document->invoice->canView(); - return Auth::user()->id == $item->user_id; - } } Document::deleted(function ($document) { diff --git a/app/Models/Product.php b/app/Models/Product.php index 4098c67063d2..8de7c7ac5b2c 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -22,8 +22,4 @@ class Product extends EntityModel { return $this->belongsTo('App\Models\TaxRate'); } - - public function canEdit() { - return Auth::user()->hasPermission('admin'); - } } diff --git a/app/Models/TaxRate.php b/app/Models/TaxRate.php index cf0a576a8f0d..72ad266b07d8 100644 --- a/app/Models/TaxRate.php +++ b/app/Models/TaxRate.php @@ -17,8 +17,4 @@ class TaxRate extends EntityModel { return ENTITY_TAX_RATE; } - - public function canEdit() { - return Auth::user()->hasPermission('admin'); - } } diff --git a/app/Models/User.php b/app/Models/User.php index a7ef6fcf5bd7..71069d25821c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -7,20 +7,22 @@ use App\Libraries\Utils; use App\Events\UserSettingsChanged; use App\Events\UserSignedUp; use Illuminate\Auth\Authenticatable; +use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Database\Eloquent\Model; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; +use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; use Illuminate\Database\Eloquent\SoftDeletes; -class User extends Model implements AuthenticatableContract, CanResetPasswordContract { +class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract { public static $all_permissions = array( 'create_all' => 0b0001, 'view_all' => 0b0010, 'edit_all' => 0b0100, ); - use Authenticatable, CanResetPassword; + use Authenticatable, Authorizable, CanResetPassword; /** * The database table used by the model. diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index aae7729c474d..3492740d6a94 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -2,6 +2,7 @@ use DB; use Utils; +use Auth; use App\Models\Expense; use App\Models\Vendor; use App\Models\Document; @@ -159,14 +160,14 @@ class ExpenseRepository extends BaseRepository $document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || $document->canEdit()){ + if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ $document->invoice_id = null; $document->expense_id = $expense->id; $document->save(); } } - if(!empty($input['documents']) && Document::canCreate()){ + if(!empty($input['documents']) && Auth::user()->can('create', ENTITY_DOCUMENT)){ // Fallback upload $doc_errors = array(); foreach($input['documents'] as $upload){ diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 6b41db461a31..b84536e88c9d 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -3,6 +3,7 @@ use DB; use Utils; use Session; +use Auth; use App\Models\Invoice; use App\Models\InvoiceItem; use App\Models\Invitation; @@ -418,7 +419,7 @@ class InvoiceRepository extends BaseRepository $document_ids = !empty($data['document_ids'])?array_map('intval', $data['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || $document->canEdit()){ + if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ if($document->invoice_id && $document->invoice_id != $invoice->id){ // From a clone @@ -432,7 +433,7 @@ class InvoiceRepository extends BaseRepository } } - if(!empty($data['documents']) && Document::canCreate()){ + if(!empty($data['documents']) && Auth::user()->can('create', ENTITY_DOCUMENT)){ // Fallback upload $doc_errors = array(); foreach($data['documents'] as $upload){ @@ -471,7 +472,7 @@ class InvoiceRepository extends BaseRepository $task = false; if (isset($item['task_public_id']) && $item['task_public_id']) { $task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || $task->canEdit()){ + if(!$checkSubPermissions || Auth::user()->can('edit', $task)){ $task->invoice_id = $invoice->id; $task->client_id = $invoice->client_id; $task->save(); @@ -481,7 +482,7 @@ class InvoiceRepository extends BaseRepository $expense = false; if (isset($item['expense_public_id']) && $item['expense_public_id']) { $expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || $expense->canEdit()){ + if(!$checkSubPermissions || Auth::user()->can('edit', $expense)){ $expense->invoice_id = $invoice->id; $expense->client_id = $invoice->client_id; $expense->save(); @@ -492,7 +493,7 @@ class InvoiceRepository extends BaseRepository if (\Auth::user()->account->update_products && ! strtotime($productKey)) { $product = Product::findProductByKey($productKey); if (!$product) { - if(!$checkSubPermissions || Product::canCreate()){ + if(!$checkSubPermissions || Auth::user()->can('create', ENTITY_PRODUCT)){ $product = Product::createNew(); $product->product_key = trim($item['product_key']); } @@ -500,7 +501,7 @@ class InvoiceRepository extends BaseRepository $product = null; } } - if($product && (!$checkSubPermissions || $product->canEdit())){ + if($product && (!$checkSubPermissions || Auth::user()->can('edit', $product))){ $product->notes = ($task || $expense) ? '' : $item['notes']; $product->cost = $expense ? 0 : $item['cost']; $product->save(); diff --git a/app/Policies/ClientPolicy.php b/app/Policies/ClientPolicy.php new file mode 100644 index 000000000000..4610c139fbfa --- /dev/null +++ b/app/Policies/ClientPolicy.php @@ -0,0 +1,5 @@ +hasPermission('view_all'))return true; + if($document->expense){ + if($document->expense->invoice)return $user->can('view', $document->expense->invoice); + return $user->can('view', $document->expense); + } + if($document->invoice)return $user->can('view', $document->invoice); + + return $user->owns($item); + } +} \ No newline at end of file diff --git a/app/Policies/EntityPolicy.php b/app/Policies/EntityPolicy.php index 79f60e8ff504..4c5e8ded9d07 100644 --- a/app/Policies/EntityPolicy.php +++ b/app/Policies/EntityPolicy.php @@ -7,19 +7,27 @@ use App\Models\EntityModel; use Illuminate\Auth\Access\HandlesAuthorization; -class InvoicePolicy extends EntityPolicy +class EntityPolicy { use HandlesAuthorization; - public static function canCreate() { - return Auth::user()->hasPermission('create_all'); + public static function create($user) { + return $user->hasPermission('create_all'); } public static function edit($user, $item) { - $user->hasPermission('edit_all') || $user->owns($item); + return $user->hasPermission('edit_all') || $user->owns($item); } public static function view($user, $item) { - $user->hasPermission('view_all') || $user->owns($item); + return $user->hasPermission('view_all') || $user->owns($item); + } + + public static function viewByOwner($user, $ownerUserId) { + return $user->hasPermission('view_all') || $user->id == $ownerUserId; + } + + public static function editByOwner($user, $ownerUserId) { + return $user->hasPermission('edit_all') || $user->id == $ownerUserId; } } \ No newline at end of file diff --git a/app/Policies/ExpensePolicy.php b/app/Policies/ExpensePolicy.php new file mode 100644 index 000000000000..4fdac4d627aa --- /dev/null +++ b/app/Policies/ExpensePolicy.php @@ -0,0 +1,5 @@ +hasPermission('admin'); + } +} \ No newline at end of file diff --git a/app/Policies/TaskPolicy.php b/app/Policies/TaskPolicy.php new file mode 100644 index 000000000000..b1fbe2902974 --- /dev/null +++ b/app/Policies/TaskPolicy.php @@ -0,0 +1,5 @@ +hasPermission('admin'); + } +} \ No newline at end of file diff --git a/app/Policies/VendorPolicy.php b/app/Policies/VendorPolicy.php new file mode 100644 index 000000000000..681cdcb50240 --- /dev/null +++ b/app/Policies/VendorPolicy.php @@ -0,0 +1,5 @@ + '.trans("texts.$types").''; $items = []; - if(Auth::user()->hasPermission('create_all')){ - $items[] = '
  • '.trans("texts.new_$type").'
  • '; - } + if($user->can('create', $type))$items[] = '
  • '.trans("texts.new_$type").'
  • '; if ($type == ENTITY_INVOICE) { if(!empty($items))$items[] = '
  • '; $items[] = '
  • '.trans("texts.recurring_invoices").'
  • '; - if(Invoice::canCreate())$items[] = '
  • '.trans("texts.new_recurring_invoice").'
  • '; - if (Auth::user()->hasFeature(FEATURE_QUOTES)) { + if($user->can('create', ENTITY_INVOICE))$items[] = '
  • '.trans("texts.new_recurring_invoice").'
  • '; + if ($user->hasFeature(FEATURE_QUOTES)) { $items[] = '
  • '; $items[] = '
  • '.trans("texts.quotes").'
  • '; - if(Invoice::canCreate())$items[] = '
  • '.trans("texts.new_quote").'
  • '; + if($user->can('create', ENTITY_INVOICE))$items[] = '
  • '.trans("texts.new_quote").'
  • '; } } else if ($type == ENTITY_CLIENT) { if(!empty($items))$items[] = '
  • '; $items[] = '
  • '.trans("texts.credits").'
  • '; - if(Credit::canCreate())$items[] = '
  • '.trans("texts.new_credit").'
  • '; + if($user->can('create', ENTITY_CREDIT))$items[] = '
  • '.trans("texts.new_credit").'
  • '; } else if ($type == ENTITY_EXPENSE) { if(!empty($items))$items[] = '
  • '; $items[] = '
  • '.trans("texts.vendors").'
  • '; - if(Vendor::canCreate())$items[] = '
  • '.trans("texts.new_vendor").'
  • '; + if($user->can('create', ENTITY_VENDOR))$items[] = '
  • '.trans("texts.new_vendor").'
  • '; } if(!empty($items)){ diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index a686b06ac9ba..884c2587503d 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -13,7 +13,14 @@ class AuthServiceProvider extends ServiceProvider * @var array */ protected $policies = [ - Invoice::class => InvoicePolicy::class, + \App\Models\Client::class => \App\Policies\ClientPolicy::class, + \App\Models\Credit::class => \App\Policies\CreditPolicy::class, + \App\Models\Document::class => \App\Policies\DocumentPolicy::class, + \App\Models\Expense::class => \App\Policies\ExpensePolicy::class, + \App\Models\Invoice::class => \App\Policies\InvoicePolicy::class, + \App\Models\Payment::class => \App\Policies\PaymentPolicy::class, + \App\Models\Task::class => \App\Policies\TaskPolicy::class, + \App\Models\Vendor::class => \App\Policies\VendorPolicy::class, ]; /** @@ -24,6 +31,10 @@ class AuthServiceProvider extends ServiceProvider */ public function boot(GateContract $gate) { + foreach (get_class_methods(new \App\Policies\GenericEntityPolicy) as $method) { + $gate->define($method, "App\Policies\GenericEntityPolicy@{$method}"); + } + $this->registerPolicies($gate); } } \ No newline at end of file diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php index b68b06482a04..90a7359910c2 100644 --- a/app/Services/BaseService.php +++ b/app/Services/BaseService.php @@ -21,7 +21,7 @@ class BaseService $entities = $this->getRepo()->findByPublicIdsWithTrashed($ids); foreach ($entities as $entity) { - if($entity->canEdit()){ + if(Auth::user()->can('edit', $entity)){ $this->getRepo()->$action($entity); } } diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index f7a7dac57edb..6f7383b0fced 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -101,13 +101,14 @@ class ClientService extends BaseService return URL::to("clients/{$model->public_id}/edit"); }, function ($model) { - return Client::canEditItem($model); + return Auth::user()->can('editByOwner', [ENTITY_CLIENT, $model->user_id]); } ], [ '--divider--', function(){return false;}, function ($model) { - return Client::canEditItem($model) && (Task::canCreate() || Invoice::canCreate()); + $user = Auth::user(); + return $user->can('editByOwner', [ENTITY_CLIENT, $model->user_id]) && ($user->can('create', ENTITY_TASK) || $user->can('create', ENTITY_INVOICE)); } ], [ @@ -116,7 +117,7 @@ class ClientService extends BaseService return URL::to("tasks/create/{$model->public_id}"); }, function ($model) { - return Task::canCreate(); + return Auth::user()->can('create', ENTITY_TASK); } ], [ @@ -125,7 +126,7 @@ class ClientService extends BaseService return URL::to("invoices/create/{$model->public_id}"); }, function ($model) { - return Invoice::canCreate(); + return Auth::user()->can('create', ENTITY_INVOICE); } ], [ @@ -134,13 +135,14 @@ class ClientService extends BaseService return URL::to("quotes/create/{$model->public_id}"); }, function ($model) { - return Auth::user()->hasFeature(FEATURE_QUOTES) && Invoice::canCreate(); + return Auth::user()->hasFeature(FEATURE_QUOTES) && Auth::user()->can('create', ENTITY_INVOICE); } ], [ '--divider--', function(){return false;}, function ($model) { - return (Task::canCreate() || Invoice::canCreate()) && (Payment::canCreate() || Credit::canCreate() || Expense::canCreate()); + $user = Auth::user(); + return ($user->can('create', ENTITY_TASK) || $user->can('create', ENTITY_INVOICE)) && ($user->can('create', ENTITY_PAYMENT) || $user->can('create', ENTITY_CREDIT) || $user->can('create', ENTITY_EXPENSE)); } ], [ @@ -149,7 +151,7 @@ class ClientService extends BaseService return URL::to("payments/create/{$model->public_id}"); }, function ($model) { - return Payment::canCreate(); + return Auth::user()->can('create', ENTITY_PAYMENT); } ], [ @@ -158,7 +160,7 @@ class ClientService extends BaseService return URL::to("credits/create/{$model->public_id}"); }, function ($model) { - return Credit::canCreate(); + return Auth::user()->can('create', ENTITY_CREDIT); } ], [ @@ -167,7 +169,7 @@ class ClientService extends BaseService return URL::to("expenses/create/0/{$model->public_id}"); }, function ($model) { - return Expense::canCreate(); + return Auth::user()->can('create', ENTITY_EXPENSE); } ] ]; diff --git a/app/Services/CreditService.php b/app/Services/CreditService.php index 2e9220ad0544..54ef659f05f9 100644 --- a/app/Services/CreditService.php +++ b/app/Services/CreditService.php @@ -47,7 +47,7 @@ class CreditService extends BaseService [ 'client_name', function ($model) { - if(!Client::canViewItemByOwner($model->client_user_id)){ + if(!Auth::user()->can('viewByOwner', [ENTITY_CLIENT, $model->client_user_id])){ return Utils::getClientDisplayName($model); } @@ -91,7 +91,7 @@ class CreditService extends BaseService return URL::to("payments/create/{$model->client_public_id}") . '?paymentTypeId=1'; }, function ($model) { - return Payment::canCreate(); + return Auth::user()->can('create', ENTITY_PAYMENT); } ] ]; diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index b574aa9983b8..afec1e4f9950 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -70,7 +70,7 @@ class ExpenseService extends BaseService function ($model) { if ($model->vendor_public_id) { - if(!Vendor::canViewItemByOwner($model->vendor_user_id)){ + if(!Auth::user()->can('viewByOwner', [ENTITY_VENDOR, $model->vendor_user_id])){ return $model->vendor_name; } @@ -85,7 +85,7 @@ class ExpenseService extends BaseService function ($model) { if ($model->client_public_id) { - if(!Client::canViewItemByOwner($model->client_user_id)){ + if(!Auth::user()->can('viewByOwner', [ENTITY_CLIENT, $model->client_user_id])){ return Utils::getClientDisplayName($model); } @@ -98,7 +98,7 @@ class ExpenseService extends BaseService [ 'expense_date', function ($model) { - if(!Expense::canEditItemByOwner($model->user_id)){ + if(!Auth::user()->can('editByOwner', [ENTITY_EXPENSE, $model->user_id])){ return Utils::fromSqlDate($model->expense_date); } @@ -172,7 +172,7 @@ class ExpenseService extends BaseService return URL::to("expenses/{$model->public_id}/edit") ; }, function ($model) { - return Expense::canEditItem($model); + return Auth::user()->can('editByOwner', [ENTITY_EXPENSE, $model->user_id]); } ], [ @@ -181,7 +181,7 @@ class ExpenseService extends BaseService return URL::to("/invoices/{$model->invoice_public_id}/edit"); }, function ($model) { - return $model->invoice_public_id && Invoice::canEditItemByOwner($model->invoice_user_id); + return $model->invoice_public_id && Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->invoice_user_id]); } ], [ @@ -190,7 +190,7 @@ class ExpenseService extends BaseService return "javascript:invoiceEntity({$model->public_id})"; }, function ($model) { - return ! $model->invoice_id && (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Invoice::canCreate(); + return ! $model->invoice_id && (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Auth::user()->can('create', ENTITY_INVOICE); } ], ]; diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index 9222e794dc17..66442ac0a31b 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -37,9 +37,9 @@ class InvoiceService extends BaseService if( ! $canSaveClient){ $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); if (empty($clientPublicId) || $clientPublicId == '-1') { - $canSaveClient = Client::canCreate(); + $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); } else { - $canSaveClient = Client::scope($clientPublicId)->first()->canEdit(); + $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); } } @@ -137,7 +137,7 @@ class InvoiceService extends BaseService [ 'invoice_number', function ($model) use ($entityType) { - if(!Invoice::canEditItem($model)){ + if(!Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id])){ return $model->invoice_number; } @@ -147,7 +147,7 @@ class InvoiceService extends BaseService [ 'client_name', function ($model) { - if(!Client::canViewItemByOwner($model->client_user_id)){ + if(!Auth::user()->can('viewByOwner', [ENTITY_CLIENT, $model->client_user_id])){ return Utils::getClientDisplayName($model); } return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml(); @@ -202,7 +202,7 @@ class InvoiceService extends BaseService return URL::to("{$entityType}s/{$model->public_id}/edit"); }, function ($model) { - return Invoice::canEditItem($model); + return Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]); } ], [ @@ -211,7 +211,7 @@ class InvoiceService extends BaseService return URL::to("{$entityType}s/{$model->public_id}/clone"); }, function ($model) { - return Invoice::canCreate(); + return Auth::user()->can('create', ENTITY_INVOICE); } ], [ @@ -223,7 +223,7 @@ class InvoiceService extends BaseService [ '--divider--', function(){return false;}, function ($model) { - return Invoice::canEditItem($model) || Payment::canCreate(); + return Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]) || Auth::user()->can('create', ENTITY_PAYMENT); } ], [ @@ -232,7 +232,7 @@ class InvoiceService extends BaseService return "javascript:markEntity({$model->public_id})"; }, function ($model) { - return $model->invoice_status_id < INVOICE_STATUS_SENT && Invoice::canEditItem($model); + return $model->invoice_status_id < INVOICE_STATUS_SENT && Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]); } ], [ @@ -241,7 +241,7 @@ class InvoiceService extends BaseService return URL::to("payments/create/{$model->client_public_id}/{$model->public_id}"); }, function ($model) use ($entityType) { - return $entityType == ENTITY_INVOICE && $model->balance > 0 && Payment::canCreate(); + return $entityType == ENTITY_INVOICE && $model->balance > 0 && Auth::user()->can('create', ENTITY_PAYMENT); } ], [ @@ -250,7 +250,7 @@ class InvoiceService extends BaseService return URL::to("quotes/{$model->quote_id}/edit"); }, function ($model) use ($entityType) { - return $entityType == ENTITY_INVOICE && $model->quote_id && Invoice::canEditItem($model); + return $entityType == ENTITY_INVOICE && $model->quote_id && Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]); } ], [ @@ -259,7 +259,7 @@ class InvoiceService extends BaseService return URL::to("invoices/{$model->quote_invoice_id}/edit"); }, function ($model) use ($entityType) { - return $entityType == ENTITY_QUOTE && $model->quote_invoice_id && Invoice::canEditItem($model); + return $entityType == ENTITY_QUOTE && $model->quote_invoice_id && Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]); } ], [ @@ -268,7 +268,7 @@ class InvoiceService extends BaseService return "javascript:convertEntity({$model->public_id})"; }, function ($model) use ($entityType) { - return $entityType == ENTITY_QUOTE && ! $model->quote_invoice_id && Invoice::canEditItem($model); + return $entityType == ENTITY_QUOTE && ! $model->quote_invoice_id && Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]); } ] ]; diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 61af6cd2336c..2321aeb9e8aa 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -334,7 +334,7 @@ class PaymentService extends BaseService [ 'invoice_number', function ($model) { - if(!Invoice::canEditItemByOwner($model->invoice_user_id)){ + if(!Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->invoice_user_id])){ return $model->invoice_number; } @@ -344,7 +344,7 @@ class PaymentService extends BaseService [ 'client_name', function ($model) { - if(!Client::canViewItemByOwner($model->client_user_id)){ + if(!Auth::user()->can('viewByOwner', [ENTITY_CLIENT, $model->client_user_id])){ return Utils::getClientDisplayName($model); } @@ -388,7 +388,7 @@ class PaymentService extends BaseService return URL::to("payments/{$model->public_id}/edit"); }, function ($model) { - return Payment::canEditItem($model); + return Auth::user()->can('editByOwner', [ENTITY_PAYMENT, $model->user_id]); } ] ]; diff --git a/app/Services/RecurringInvoiceService.php b/app/Services/RecurringInvoiceService.php index 2786ccfc6869..b003455abd6a 100644 --- a/app/Services/RecurringInvoiceService.php +++ b/app/Services/RecurringInvoiceService.php @@ -74,7 +74,7 @@ class RecurringInvoiceService extends BaseService return URL::to("invoices/{$model->public_id}/edit"); }, function ($model) { - return Invoice::canEditItem($model); + return Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->user_id]); } ] ]; diff --git a/app/Services/TaskService.php b/app/Services/TaskService.php index 70e7e22c7c88..e07793b2f85c 100644 --- a/app/Services/TaskService.php +++ b/app/Services/TaskService.php @@ -49,7 +49,7 @@ class TaskService extends BaseService [ 'client_name', function ($model) { - if(!Client::canViewItemByOwner($model->client_user_id)){ + if(!Auth::user()->can('viewByOwner', [ENTITY_CLIENT, $model->client_user_id])){ return Utils::getClientDisplayName($model); } @@ -93,7 +93,7 @@ class TaskService extends BaseService return URL::to('tasks/'.$model->public_id.'/edit'); }, function ($model) { - return (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Task::canEditItem($model); + return (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Auth::user()->can('editByOwner', [ENTITY_TASK, $model->user_id]); } ], [ @@ -102,7 +102,7 @@ class TaskService extends BaseService return URL::to("/invoices/{$model->invoice_public_id}/edit"); }, function ($model) { - return $model->invoice_number && Invoice::canEditItemByOwner($model->invoice_user_id); + return $model->invoice_number && Auth::user()->can('editByOwner', [ENTITY_INVOICE, $model->invoice_user_id]); } ], [ @@ -111,7 +111,7 @@ class TaskService extends BaseService return "javascript:stopTask({$model->public_id})"; }, function ($model) { - return $model->is_running && Task::canEditItem($model); + return $model->is_running && Auth::user()->can('editByOwner', [ENTITY_TASK, $model->user_id]); } ], [ @@ -120,7 +120,7 @@ class TaskService extends BaseService return "javascript:invoiceEntity({$model->public_id})"; }, function ($model) { - return ! $model->invoice_number && (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Invoice::canCreate(); + return ! $model->invoice_number && (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Auth::user()->can('create', ENTITY_INVOICE); } ] ]; diff --git a/app/Services/VendorService.php b/app/Services/VendorService.php index 1118f23c5356..6022507b452e 100644 --- a/app/Services/VendorService.php +++ b/app/Services/VendorService.php @@ -91,13 +91,13 @@ class VendorService extends BaseService return URL::to("vendors/{$model->public_id}/edit"); }, function ($model) { - return Vendor::canEditItem($model); + return Auth::user()->can('editByOwner', [ENTITY_VENDOR, $model->user_id]); } ], [ '--divider--', function(){return false;}, function ($model) { - return Vendor::canEditItem($model) && Expense::canCreate(); + return Auth::user()->can('editByOwner', [ENTITY_VENDOR, $model->user_id]) && Auth::user()->can('create', ENTITY_EXPENSE); } ], @@ -107,7 +107,7 @@ class VendorService extends BaseService return URL::to("expenses/create/{$model->public_id}"); }, function ($model) { - return Expense::canCreate(); + return Auth::user()->can('create', ENTITY_EXPENSE); } ] ]; diff --git a/config/app.php b/config/app.php index 027c3d5ece28..4a75f65f9e5f 100644 --- a/config/app.php +++ b/config/app.php @@ -195,6 +195,7 @@ return [ 'Eloquent' => 'Illuminate\Database\Eloquent\Model', 'Event' => 'Illuminate\Support\Facades\Event', 'File' => 'Illuminate\Support\Facades\File', + 'Gate' => 'Illuminate\Support\Facades\Gate', 'Hash' => 'Illuminate\Support\Facades\Hash', 'Input' => 'Illuminate\Support\Facades\Input', 'Lang' => 'Illuminate\Support\Facades\Lang', diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php index 0e434b3d07d4..3b1a0c09d52b 100644 --- a/resources/views/clients/show.blade.php +++ b/resources/views/clients/show.blade.php @@ -43,11 +43,11 @@ @endif @if ($client->trashed()) - @if ($client->canEdit()) + @can('edit', $client) {!! Button::primary(trans('texts.restore_client'))->withAttributes(['onclick' => 'onRestoreClick()']) !!} - @endif + @endcan @else - @if ($client->canEdit()) + @can('edit', $client) {!! DropdownButton::normal(trans('texts.edit_client')) ->withAttributes(['class'=>'normalDropDown']) ->withContents([ @@ -55,12 +55,12 @@ ['label' => trans('texts.delete_client'), 'url' => "javascript:onDeleteClick()"], ] )->split() !!} - @endif - @if (\App\Models\Invoice::canCreate()) + @endcan + @can('create', ENTITY_INVOICE) {!! DropdownButton::primary(trans('texts.new_invoice')) ->withAttributes(['class'=>'primaryDropDown']) ->withContents($actionLinks)->split() !!} - @endif + @endcan @endif {!! Former::close() !!} diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index d65b8290a597..bc1d2bb6e862 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -116,11 +116,11 @@ @foreach ($payments as $payment)
    - @if (\App\Models\Client::canViewItemByOwner($payment->client_user_id)) + @can('viewByOwner', [ENTITY_CLIENT, $payment->client_user_id]) @else - @endif + @endcan @@ -153,11 +153,11 @@ @if (!$invoice->is_quote) - @if (\App\Models\Client::canViewItemByOwner($invoice->client_user_id)) + @can('viewByOwner', [ENTITY_CLIENT, $invoice->client_user_id]) @else - @endif + @endcan @@ -188,11 +188,11 @@ @if (!$invoice->is_quote) - @if (\App\Models\Client::canViewItemByOwner($invoice->client_user_id)) + @can('viewByOwner', [ENTITY_CLIENT, $invoice->client_user_id]) @else - @endif + @endcan diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 823e9765f09d..0889f16ee11e 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -75,12 +75,12 @@

    - @if($invoice->client->canView()) - @if ($invoice->client->canEdit()) + @can('view', $invoice->client) + @can('edit', $invoice->client) {{ trans('texts.edit_client') }} | - @endif + @endcan {!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!} - @endif + @endcan
    diff --git a/resources/views/list.blade.php b/resources/views/list.blade.php index 8e8ba97116d7..2a267f0dae98 100644 --- a/resources/views/list.blade.php +++ b/resources/views/list.blade.php @@ -9,14 +9,14 @@ {!! Former::text('public_id') !!}
    - @if (\App\Models\Invoice::canCreate()) + @can('create', 'invoice') @if ($entityType == ENTITY_TASK) {!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!} @endif @if ($entityType == ENTITY_EXPENSE) {!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!} @endif - @endif + @endcan {!! DropdownButton::normal(trans('texts.archive'))->withContents([ ['label' => trans('texts.archive_'.$entityType), 'url' => 'javascript:submitForm("archive")'], From f28e0cd76b0cd3cd7210da558105b12ee235010d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 26 Apr 2016 12:41:34 +0300 Subject: [PATCH 135/258] Fix for JS rounding problem --- resources/views/invoices/knockout.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php index 8fe4bcbcce80..cd4ad5f73d2b 100644 --- a/resources/views/invoices/knockout.blade.php +++ b/resources/views/invoices/knockout.blade.php @@ -460,7 +460,7 @@ function InvoiceModel(data) { }); self.totals.rawPaidToDate = ko.computed(function() { - return accounting.toFixed(self.amount(),2) - accounting.toFixed(self.balance(),2); + return roundToTwo(accounting.toFixed(self.amount(),2) - accounting.toFixed(self.balance(),2)); }); self.totals.paidToDate = ko.computed(function() { From 996b6d4ce6b375fed464b890f58b5a0e506e4238 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 26 Apr 2016 21:11:41 +0300 Subject: [PATCH 136/258] Fixed typo in text files --- resources/lang/da/texts.php | 2 +- resources/lang/de/texts.php | 2 +- resources/lang/en/texts.php | 2 +- resources/lang/es/texts.php | 2 +- resources/lang/es_ES/texts.php | 2 +- resources/lang/fr/texts.php | 2 +- resources/lang/fr_CA/texts.php | 2 +- resources/lang/it/texts.php | 2 +- resources/lang/ja/texts.php | 2 +- resources/lang/lt/texts.php | 2 +- resources/lang/nb_NO/texts.php | 2 +- resources/lang/nl/texts.php | 2 +- resources/lang/pt_BR/texts.php | 2 +- resources/lang/sv/texts.php | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/resources/lang/da/texts.php b/resources/lang/da/texts.php index ffc74871cde0..2b9e21c2415a 100644 --- a/resources/lang/da/texts.php +++ b/resources/lang/da/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Faktura historik', 'quote_history' => 'Tilbuds historik', 'current_version' => 'Nuværende version', - 'select_versiony' => 'Vælg version', + 'select_version' => 'Vælg version', 'view_history' => 'Vis historik', 'edit_payment' => 'Redigér betaling', diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 08ffaf7b8a0b..c41dae9d08e2 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Rechnungshistorie', 'quote_history' => 'Angebotshistorie', 'current_version' => 'Aktuelle Version', - 'select_versiony' => 'Version auswählen', + 'select_version' => 'Version auswählen', 'view_history' => 'Historie anzeigen', 'edit_payment' => 'Zahlung bearbeiten', diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 37478c5d73e3..f101c79564e7 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -423,7 +423,7 @@ $LANG = array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', 'updated_payment' => 'Successfully updated payment', diff --git a/resources/lang/es/texts.php b/resources/lang/es/texts.php index 75bd1a75dae6..f5f68bc527f8 100644 --- a/resources/lang/es/texts.php +++ b/resources/lang/es/texts.php @@ -466,7 +466,7 @@ return array( 'invoice_history' => 'Facturar Historial', 'quote_history' => 'Cotizar Historial', 'current_version' => 'Versión actual', - 'select_versiony' => 'Seleccionar versión', + 'select_version' => 'Seleccionar versión', 'view_history' => 'Ver Historial', 'edit_payment' => 'Editar Pago', diff --git a/resources/lang/es_ES/texts.php b/resources/lang/es_ES/texts.php index 25054072dac5..55c38f712352 100644 --- a/resources/lang/es_ES/texts.php +++ b/resources/lang/es_ES/texts.php @@ -486,7 +486,7 @@ return array( 'invoice_history' => 'Historial de Facturas', 'quote_history' => 'Historial de Presupuestos', 'current_version' => 'Versión Actual', - 'select_versiony' => 'Seleccione la Versión', + 'select_version' => 'Seleccione la Versión', 'view_history' => 'Ver Historial', 'edit_payment' => 'Editar Pago', diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index 6cd75a9f0efa..215235a32413 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -486,7 +486,7 @@ return array( 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des devis', 'current_version' => 'Version courante', - 'select_versiony' => 'Choix de la verison', + 'select_version' => 'Choix de la verison', 'view_history' => 'Consulter l\'historique', 'edit_payment' => 'Editer le paiement', diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index c62227e45050..e6e4c79f0fe2 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -487,7 +487,7 @@ return array( 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des soumissions', 'current_version' => 'Version courante', - 'select_versiony' => 'Choix de la verison', + 'select_version' => 'Choix de la verison', 'view_history' => 'Consulter l\'historique', 'edit_payment' => 'Éditer le paiement', diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 154b9f3e5d21..17d77d5d4881 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', diff --git a/resources/lang/ja/texts.php b/resources/lang/ja/texts.php index 7e673ac11d33..1bfa46e4749a 100644 --- a/resources/lang/ja/texts.php +++ b/resources/lang/ja/texts.php @@ -424,7 +424,7 @@ $LANG = array( 'invoice_history' => '請求履歴', 'quote_history' => '見積履歴', 'current_version' => '現在のバージョン', - 'select_versiony' => 'バージョンを選択', + 'select_version' => 'バージョンを選択', 'view_history' => '履歴を閲覧', 'edit_payment' => '支払いを編集', 'updated_payment' => '支払いを更新しました', diff --git a/resources/lang/lt/texts.php b/resources/lang/lt/texts.php index 252a7f03bd9c..dc6c18154c6c 100644 --- a/resources/lang/lt/texts.php +++ b/resources/lang/lt/texts.php @@ -497,7 +497,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', diff --git a/resources/lang/nb_NO/texts.php b/resources/lang/nb_NO/texts.php index df06d7a32547..d9b2158ac3a9 100644 --- a/resources/lang/nb_NO/texts.php +++ b/resources/lang/nb_NO/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Faktura Historikk', 'quote_history' => 'Tilbuds Historikk', 'current_version' => 'Nåværende versjon', - 'select_versiony' => 'Velg versjon', + 'select_version' => 'Velg versjon', 'view_history' => 'Vis Historikk', 'edit_payment' => 'Rediger Betaling', diff --git a/resources/lang/nl/texts.php b/resources/lang/nl/texts.php index 26c639245bb9..d3b0b045dfe3 100644 --- a/resources/lang/nl/texts.php +++ b/resources/lang/nl/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Factuurgeschiedenis', 'quote_history' => 'Offertegeschiedenis', 'current_version' => 'Huidige versie', - 'select_versiony' => 'Selecteer versie', + 'select_version' => 'Selecteer versie', 'view_history' => 'Bekijk geschiedenis', 'edit_payment' => 'Bewerk betaling', diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index 3b180e93d6ad..4db4e84b9fee 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -487,7 +487,7 @@ return array( 'invoice_history' => 'Histórico de Faturas', 'quote_history' => 'Histórico de Orçamentos', 'current_version' => 'Versão Atual', - 'select_versiony' => 'Selecionar versão', + 'select_version' => 'Selecionar versão', 'view_history' => 'Visualizar Histórico', 'edit_payment' => 'Editar Pagamento', diff --git a/resources/lang/sv/texts.php b/resources/lang/sv/texts.php index 050076bd6e3e..745313fd14fd 100644 --- a/resources/lang/sv/texts.php +++ b/resources/lang/sv/texts.php @@ -492,7 +492,7 @@ return array( 'invoice_history' => 'Fakturahistorik', 'quote_history' => 'Offerthistorik', 'current_version' => 'Nuvarande version', - 'select_versiony' => 'Välj version', + 'select_version' => 'Välj version', 'view_history' => 'Visa historik', 'edit_payment' => 'Ändra betalning', From 91efade7f778c451e22bbee429b4f18b1b8f0534 Mon Sep 17 00:00:00 2001 From: Joshua Dwire Date: Tue, 26 Apr 2016 20:06:19 -0400 Subject: [PATCH 137/258] Don't require new ucwords signature --- app/Http/Controllers/BaseController.php | 3 ++- app/Policies/GenericEntityPolicy.php | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index a9060e47fdec..66abc53e2459 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -4,6 +4,7 @@ use App\Http\Middleware\PermissionsRequired; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Auth; +use Utils; class BaseController extends Controller { @@ -34,7 +35,7 @@ class BaseController extends Controller $this->authorize('create', $this->entity); } else{ - $className = ucwords($this->entity, '_'); + $className = Utils::getEntityName($this->entity); $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); $this->authorize('edit', $object); diff --git a/app/Policies/GenericEntityPolicy.php b/app/Policies/GenericEntityPolicy.php index dab0aab1bc93..ad0e76a6baa4 100644 --- a/app/Policies/GenericEntityPolicy.php +++ b/app/Policies/GenericEntityPolicy.php @@ -3,6 +3,7 @@ namespace App\Policies; use App\Models\User; +use Utils; use Illuminate\Auth\Access\HandlesAuthorization; @@ -11,7 +12,7 @@ class GenericEntityPolicy use HandlesAuthorization; public static function editByOwner($user, $itemType, $ownerUserId) { - $itemType = ucwords($itemType, '_'); + $itemType = Utils::getEntityName($itemType); if (method_exists("App\\Policies\\{$itemType}Policy", 'editByOwner')) { return call_user_func(array("App\\Policies\\{$itemType}Policy", 'editByOwner'), $user, $ownerUserId); } @@ -20,7 +21,7 @@ class GenericEntityPolicy } public static function viewByOwner($user, $itemType, $ownerUserId) { - $itemType = ucwords($itemType, '_'); + $itemType = Utils::getEntityName($itemType); if (method_exists("App\\Policies\\{$itemType}Policy", 'viewByOwner')) { return call_user_func(array("App\\Policies\\{$itemType}Policy", 'viewByOwner'), $user, $ownerUserId); } @@ -29,7 +30,7 @@ class GenericEntityPolicy } public static function create($user, $itemType) { - $itemType = ucwords($itemType, '_'); + $itemType = Utils::getEntityName($itemType); if (method_exists("App\\Policies\\{$itemType}Policy", 'create')) { return call_user_func(array("App\\Policies\\{$itemType}Policy", 'create'), $user); } From 6ba27c24757985a36ac14ae615320d027307cc7f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 11:49:54 +0300 Subject: [PATCH 138/258] Fixed problem sorting data tables by client --- app/Ninja/Repositories/CreditRepository.php | 2 +- app/Ninja/Repositories/ExpenseRepository.php | 2 +- app/Ninja/Repositories/InvoiceRepository.php | 6 +++--- app/Ninja/Repositories/PaymentRepository.php | 4 ++-- app/Ninja/Repositories/TaskRepository.php | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/Ninja/Repositories/CreditRepository.php b/app/Ninja/Repositories/CreditRepository.php index 9803b4628af8..4381ae25771c 100644 --- a/app/Ninja/Repositories/CreditRepository.php +++ b/app/Ninja/Repositories/CreditRepository.php @@ -27,7 +27,7 @@ class CreditRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'credits.public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'credits.amount', diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 3492740d6a94..c3106d669878 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -96,7 +96,7 @@ class ExpenseRepository extends BaseRepository 'vendors.name as vendor_name', 'vendors.public_id as vendor_public_id', 'vendors.user_id as vendor_user_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'contacts.first_name', diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index b84536e88c9d..d5970af8125e 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -58,7 +58,7 @@ class InvoiceRepository extends BaseRepository 'clients.user_id as client_user_id', 'invoice_number', 'invoice_status_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'invoices.balance', @@ -115,7 +115,7 @@ class InvoiceRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'clients.public_id as client_public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'frequencies.name as frequency', @@ -169,7 +169,7 @@ class InvoiceRepository extends BaseRepository 'invoices.balance as balance', 'invoices.due_date', 'clients.public_id as client_public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'invoices.start_date', diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index 85dfab9964f8..9b7d9787b457 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -34,7 +34,7 @@ class PaymentRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'payments.transaction_reference', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'payments.amount', @@ -101,7 +101,7 @@ class PaymentRepository extends BaseRepository 'invitations.invitation_key', 'payments.public_id', 'payments.transaction_reference', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'payments.amount', 'payments.payment_date', diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index a7655a8abd9e..d78b53c02d56 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -25,7 +25,7 @@ class TaskRepository ->where('clients.deleted_at', '=', null) ->select( 'tasks.public_id', - 'clients.name as client_name', + \DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'contacts.first_name', From 6f5a68e1f525729a380a9913dbe6070a8a75074b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 16:36:44 +0300 Subject: [PATCH 139/258] Fixed bug in vendor controller --- app/Http/Controllers/QuoteApiController.php | 75 --------------------- app/Http/Controllers/VendorController.php | 2 +- 2 files changed, 1 insertion(+), 76 deletions(-) delete mode 100644 app/Http/Controllers/QuoteApiController.php diff --git a/app/Http/Controllers/QuoteApiController.php b/app/Http/Controllers/QuoteApiController.php deleted file mode 100644 index 8111872b10d9..000000000000 --- a/app/Http/Controllers/QuoteApiController.php +++ /dev/null @@ -1,75 +0,0 @@ -invoiceRepo = $invoiceRepo; - } - - /** - * @SWG\Get( - * path="/quotes", - * tags={"quote"}, - * summary="List of quotes", - * @SWG\Response( - * response=200, - * description="A list with quotes", - * @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Invoice")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ - public function index() - { - $paginator = Invoice::scope(); - $invoices = Invoice::scope() - ->with('client', 'invitations', 'user', 'invoice_items') - ->where('invoices.is_quote', '=', true); - - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $invoices->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $invoices = $invoices->orderBy('created_at', 'desc')->paginate(); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $paginator = $paginator->paginate(); - - $data = $this->createCollection($invoices, $transformer, 'quotes', $paginator); - - return $this->response($data); - } - - /* - public function store() - { - $data = Input::all(); - $invoice = $this->invoiceRepo->save(false, $data, false); - - $response = json_encode($invoice, JSON_PRETTY_PRINT); - $headers = Utils::getApiHeaders(); - return Response::make($response, 200, $headers); - } - */ -} diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 00f5bbe83e4d..d53d6041fb18 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -155,7 +155,7 @@ class VendorController extends BaseController { $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); - $this->authorize('edit', $vendor) + $this->authorize('edit', $vendor); $data = [ 'vendor' => $vendor, From f77657f1c1c31959882b904afe4b48df1121adbf Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 17:10:27 +0300 Subject: [PATCH 140/258] Update log filenames in travis file --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e8b983e795d..083bbe4f2583 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,7 +89,8 @@ after_script: - mysql -u root -e 'select * from invoice_items;' ninja - mysql -u root -e 'select * from payments;' ninja - mysql -u root -e 'select * from credits;' ninja - - cat storage/logs/laravel.log + - cat storage/logs/laravel-error.log + - cat storage/logs/laravel-info.log notifications: email: From 4c4859917599fcd28ec2925c1cb5c7cb5cb4a9cb Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 17:22:50 +0300 Subject: [PATCH 141/258] Fix for permissions in payment controller --- app/Http/Controllers/PaymentController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 4654a3e69856..223b43bc41c1 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -590,7 +590,7 @@ class PaymentController extends BaseController { $input = $request->input(); - $this->authorizeUpdate($data); + $this->authorizeUpdate($input); $input['invoice_id'] = Invoice::getPrivateId($input['invoice']); $input['client_id'] = Client::getPrivateId($input['client']); From b930593d5fdc260da232c8483a19090ad57cf4ad Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 17:24:09 +0300 Subject: [PATCH 142/258] Fix for product policy --- app/Policies/ProductPolicy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Policies/ProductPolicy.php b/app/Policies/ProductPolicy.php index d9dd62c34e9b..6bd9c56d0c6c 100644 --- a/app/Policies/ProductPolicy.php +++ b/app/Policies/ProductPolicy.php @@ -2,7 +2,7 @@ namespace App\Policies; -class VendorPolicy extends EntityPolicy { +class ProductPolicy extends EntityPolicy { public static function edit($user, $item) { return $user->hasPermission('admin'); } From df93ffaaf9963b9693d759e8a61e092248c0409c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 18:09:04 +0300 Subject: [PATCH 143/258] Fix for permissions in payment controller --- app/Http/Controllers/PaymentController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 223b43bc41c1..0a0fe1485676 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -610,7 +610,7 @@ class PaymentController extends BaseController { $input = $request->input(); - $this->authorizeUpdate($data); + $this->authorizeUpdate($input); $payment = $this->paymentRepo->save($input); From 4f926ec8b84a1f048a0a92d3e891deb577057946 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 19:13:29 +0300 Subject: [PATCH 144/258] Fix for permissions in base service --- app/Services/BaseService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php index 90a7359910c2..6bbbc729b246 100644 --- a/app/Services/BaseService.php +++ b/app/Services/BaseService.php @@ -1,5 +1,6 @@ Date: Wed, 27 Apr 2016 19:25:24 +0300 Subject: [PATCH 145/258] Fix for recurring invoice list --- app/Ninja/Repositories/InvoiceRepository.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index d5970af8125e..a62d3aba8d25 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -125,7 +125,8 @@ class InvoiceRepository extends BaseRepository 'contacts.last_name', 'contacts.email', 'invoices.deleted_at', - 'invoices.is_deleted' + 'invoices.is_deleted', + 'invoices.user_id' ); if ($clientPublicId) { From ce2392563d34216e402f014a611b3e540b8a6404 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 23:56:14 +0300 Subject: [PATCH 146/258] Removed checkSubPermissions flag --- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceController.php | 4 ++-- app/Ninja/Repositories/ExpenseRepository.php | 4 ++-- app/Ninja/Repositories/InvoiceRepository.php | 13 ++++++------ app/Services/ExpenseService.php | 4 ++-- app/Services/InvoiceService.php | 21 +++++++++----------- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e53122e46dcb..e65a485fa2f0 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -158,7 +158,7 @@ class ExpenseController extends BaseController $this->authorizeUpdate($data); - $expense = $this->expenseService->save($data, true); + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.updated_expense')); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 79ce2aee93b1..7aa9ca2d4d14 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -405,7 +405,7 @@ class InvoiceController extends BaseController $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data, true); + $invoice = $this->invoiceService->save($data); $entityType = $invoice->getEntityType(); $message = trans("texts.created_{$entityType}"); @@ -444,7 +444,7 @@ class InvoiceController extends BaseController $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data, true); + $invoice = $this->invoiceService->save($data); $entityType = $invoice->getEntityType(); $message = trans("texts.updated_{$entityType}"); Session::flash('message', $message); diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index c3106d669878..62264f32d2b1 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -122,7 +122,7 @@ class ExpenseRepository extends BaseRepository return $query; } - public function save($input, $checkSubPermissions=false) + public function save($input) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; @@ -160,7 +160,7 @@ class ExpenseRepository extends BaseRepository $document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ + if($document && Auth::user()->can('edit', $document)){ $document->invoice_id = null; $document->expense_id = $expense->id; $document->save(); diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index a62d3aba8d25..15616e9cd75f 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -198,7 +198,7 @@ class InvoiceRepository extends BaseRepository ->make(); } - public function save($data, $checkSubPermissions = false) + public function save($data) { $account = \Auth::user()->account; $publicId = isset($data['public_id']) ? $data['public_id'] : false; @@ -420,7 +420,7 @@ class InvoiceRepository extends BaseRepository $document_ids = !empty($data['document_ids'])?array_map('intval', $data['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ + if($document && Auth::user()->can('edit', $document)){ if($document->invoice_id && $document->invoice_id != $invoice->id){ // From a clone @@ -473,7 +473,7 @@ class InvoiceRepository extends BaseRepository $task = false; if (isset($item['task_public_id']) && $item['task_public_id']) { $task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || Auth::user()->can('edit', $task)){ + if(Auth::user()->can('edit', $task)){ $task->invoice_id = $invoice->id; $task->client_id = $invoice->client_id; $task->save(); @@ -483,7 +483,7 @@ class InvoiceRepository extends BaseRepository $expense = false; if (isset($item['expense_public_id']) && $item['expense_public_id']) { $expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || Auth::user()->can('edit', $expense)){ + if(Auth::user()->can('edit', $expense)){ $expense->invoice_id = $invoice->id; $expense->client_id = $invoice->client_id; $expense->save(); @@ -494,7 +494,7 @@ class InvoiceRepository extends BaseRepository if (\Auth::user()->account->update_products && ! strtotime($productKey)) { $product = Product::findProductByKey($productKey); if (!$product) { - if(!$checkSubPermissions || Auth::user()->can('create', ENTITY_PRODUCT)){ + if (Auth::user()->can('create', ENTITY_PRODUCT)) { $product = Product::createNew(); $product->product_key = trim($item['product_key']); } @@ -502,7 +502,7 @@ class InvoiceRepository extends BaseRepository $product = null; } } - if($product && (!$checkSubPermissions || Auth::user()->can('edit', $product))){ + if ($product && (Auth::user()->can('edit', $product))) { $product->notes = ($task || $expense) ? '' : $item['notes']; $product->cost = $expense ? 0 : $item['cost']; $product->save(); @@ -516,7 +516,6 @@ class InvoiceRepository extends BaseRepository $invoiceItem->notes = trim($invoice->is_recurring ? $item['notes'] : Utils::processVariables($item['notes'])); $invoiceItem->cost = Utils::parseFloat($item['cost']); $invoiceItem->qty = Utils::parseFloat($item['qty']); - //$invoiceItem->tax_rate = 0; if (isset($item['custom_value1'])) { $invoiceItem->custom_value1 = $item['custom_value1']; diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index afec1e4f9950..0b28a7c4d6b1 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -28,7 +28,7 @@ class ExpenseService extends BaseService return $this->expenseRepo; } - public function save($data, $checkSubPermissions=false) + public function save($data) { if (isset($data['client_id']) && $data['client_id']) { $data['client_id'] = Client::getPrivateId($data['client_id']); @@ -38,7 +38,7 @@ class ExpenseService extends BaseService $data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']); } - return $this->expenseRepo->save($data, $checkSubPermissions); + return $this->expenseRepo->save($data); } public function getDatatable($search) diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index 66442ac0a31b..ecf0ad2fab0b 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -30,26 +30,23 @@ class InvoiceService extends BaseService return $this->invoiceRepo; } - public function save($data, $checkSubPermissions = false) + public function save($data) { if (isset($data['client'])) { - $canSaveClient = !$checkSubPermissions; - if( ! $canSaveClient){ - $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); - if (empty($clientPublicId) || $clientPublicId == '-1') { - $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); - } else { - $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); - } - } - + $canSaveClient = false; + $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); + if (empty($clientPublicId) || $clientPublicId == '-1') { + $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); + } else { + $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); + } if ($canSaveClient) { $client = $this->clientRepo->save($data['client']); $data['client_id'] = $client->id; } } - $invoice = $this->invoiceRepo->save($data, $checkSubPermissions); + $invoice = $this->invoiceRepo->save($data); $client = $invoice->client; $client->load('contacts'); From 882cfb79fd3936d3f7a2fcb394beeea258b681ce Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 13:53:29 +0300 Subject: [PATCH 147/258] Refactored controller->entity to entityType --- app/Http/Controllers/BaseController.php | 8 ++++---- app/Http/Controllers/ClientController.php | 2 +- app/Http/Controllers/CreditController.php | 2 +- app/Http/Controllers/DocumentController.php | 2 +- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceController.php | 2 +- app/Http/Controllers/PaymentController.php | 2 +- app/Http/Controllers/QuoteController.php | 2 +- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/VendorController.php | 4 ++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 66abc53e2459..867bd9e77a9f 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -10,7 +10,7 @@ class BaseController extends Controller { use DispatchesJobs, AuthorizesRequests; - protected $entity; + protected $entityType; /** * Setup the layout used by the controller. @@ -25,17 +25,17 @@ class BaseController extends Controller } protected function authorizeCreate() { - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } protected function authorizeUpdate($input){ $creating = empty($input['public_id']) || $input['public_id'] == '-1'; if($creating){ - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } else{ - $className = Utils::getEntityName($this->entity); + $className = Utils::getEntityName($this->entityType); $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); $this->authorize('edit', $object); diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 49988ab78b79..3ef8966d30b7 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -35,7 +35,7 @@ class ClientController extends BaseController { protected $clientService; protected $clientRepo; - protected $entity = ENTITY_CLIENT; + protected $entityType = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 23577f951389..1e3586af1922 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -17,7 +17,7 @@ class CreditController extends BaseController { protected $creditRepo; protected $creditService; - protected $entity = ENTITY_CREDIT; + protected $entityType = ENTITY_CREDIT; public function __construct(CreditRepository $creditRepo, CreditService $creditService) { diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index c0e25e773d11..0720e1a5babf 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -15,7 +15,7 @@ use App\Ninja\Repositories\DocumentRepository; class DocumentController extends BaseController { protected $documentRepo; - protected $entity = ENTITY_DOCUMENT; + protected $entityType = ENTITY_DOCUMENT; public function __construct(DocumentRepository $documentRepo) { diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e65a485fa2f0..88a493d3ea21 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -25,7 +25,7 @@ class ExpenseController extends BaseController // Expenses protected $expenseRepo; protected $expenseService; - protected $entity = ENTITY_EXPENSE; + protected $entityType = ENTITY_EXPENSE; public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService) { diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 7aa9ca2d4d14..c3627a01c301 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -37,7 +37,7 @@ class InvoiceController extends BaseController protected $documentRepo; protected $invoiceService; protected $recurringInvoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService) { diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 0a0fe1485676..bd5fb85199cf 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -30,7 +30,7 @@ use App\Http\Requests\UpdatePaymentRequest; class PaymentController extends BaseController { - protected $entity = ENTITY_PAYMENT; + protected $entityType = ENTITY_PAYMENT; public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService) { diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index e2228ab2a0c0..3a0062296098 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -33,7 +33,7 @@ class QuoteController extends BaseController protected $invoiceRepo; protected $clientRepo; protected $invoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 7ff19436ead2..a1118269ac51 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -22,7 +22,7 @@ class TaskController extends BaseController { protected $taskRepo; protected $taskService; - protected $entity = ENTITY_TASK; + protected $entityType = ENTITY_TASK; public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService) { diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index d53d6041fb18..9340b926d4eb 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -25,12 +25,12 @@ use App\Services\VendorService; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; -// vendor + class VendorController extends BaseController { protected $vendorService; protected $vendorRepo; - protected $entity = ENTITY_VENDOR; + protected $entityType = ENTITY_VENDOR; public function __construct(VendorRepository $vendorRepo, VendorService $vendorService) { From 38b6c94fe7cd7f49e6b49206ca4a7c3cb653a39f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:10:53 +0300 Subject: [PATCH 148/258] Fix for quote api controller in routes files --- app/Http/routes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index e5915b394a2b..bd94056d8791 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -253,8 +253,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() Route::get('accounts', 'AccountApiController@show'); Route::put('accounts', 'AccountApiController@update'); Route::resource('clients', 'ClientApiController'); - Route::get('quotes', 'QuoteApiController@index'); - Route::resource('quotes', 'QuoteApiController'); + //Route::get('quotes', 'QuoteApiController@index'); + //Route::resource('quotes', 'QuoteApiController'); Route::get('invoices', 'InvoiceApiController@index'); Route::resource('invoices', 'InvoiceApiController'); Route::get('payments', 'PaymentApiController@index'); From 9979eba5d9cd00628926e9e11a4e08a4c0e0e053 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:16:33 +0300 Subject: [PATCH 149/258] Working on custom route model binding --- app/Http/Controllers/BaseController.php | 15 ++++++++--- app/Http/Controllers/ClientController.php | 20 +++++--------- app/Http/Controllers/CreditController.php | 2 +- app/Http/Controllers/DocumentController.php | 2 +- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceController.php | 2 +- app/Http/Controllers/PaymentController.php | 2 +- app/Http/Controllers/QuoteController.php | 2 +- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/VendorController.php | 4 +-- app/Http/Requests/BaseRequest.php | 30 +++++++++++++++++++++ app/Http/Requests/ClientRequest.php | 21 +++++++++++++++ app/Http/Requests/CreateClientRequest.php | 9 +++---- app/Http/Requests/CreateCreditRequest.php | 9 +++---- app/Http/Requests/UpdateClientRequest.php | 9 +++---- app/Libraries/Utils.php | 5 ++++ app/Ninja/Repositories/ClientRepository.php | 6 +++-- app/Services/ClientService.php | 4 +-- 18 files changed, 98 insertions(+), 48 deletions(-) create mode 100644 app/Http/Requests/BaseRequest.php create mode 100644 app/Http/Requests/ClientRequest.php diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 66abc53e2459..4a33946c7631 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -3,6 +3,7 @@ use App\Http\Middleware\PermissionsRequired; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Input; use Auth; use Utils; @@ -10,7 +11,7 @@ class BaseController extends Controller { use DispatchesJobs, AuthorizesRequests; - protected $entity; + protected $entityType; /** * Setup the layout used by the controller. @@ -25,17 +26,23 @@ class BaseController extends Controller } protected function authorizeCreate() { - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } + /* + protected function authorizeUpdate($entity) { + $this->authorize('edit', $entity); + } + */ + protected function authorizeUpdate($input){ $creating = empty($input['public_id']) || $input['public_id'] == '-1'; if($creating){ - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } else{ - $className = Utils::getEntityName($this->entity); + $className = Utils::getEntityName($this->entityType); $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); $this->authorize('edit', $object); diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 49988ab78b79..c66c775947c8 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -28,6 +28,7 @@ use App\Models\Task; use App\Ninja\Repositories\ClientRepository; use App\Services\ClientService; +use App\Http\Requests\ClientRequest; use App\Http\Requests\CreateClientRequest; use App\Http\Requests\UpdateClientRequest; @@ -35,7 +36,7 @@ class ClientController extends BaseController { protected $clientService; protected $clientRepo; - protected $entity = ENTITY_CLIENT; + protected $entityType = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { @@ -81,11 +82,7 @@ class ClientController extends BaseController */ public function store(CreateClientRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $client = $this->clientService->save($data); + $client = $this->clientService->save($request->input()); Session::flash('message', trans('texts.created_client')); @@ -100,6 +97,7 @@ class ClientController extends BaseController */ public function show($publicId) { + //$client = $request->entity()->load('conacts'); $client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail(); $this->authorize('view', $client); @@ -177,9 +175,9 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(ClientRequest $request) { - $client = Client::scope($publicId)->with('contacts')->firstOrFail(); + $client = $request->entity(); $this->authorize('edit', $client); @@ -225,11 +223,7 @@ class ClientController extends BaseController */ public function update(UpdateClientRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $client = $this->clientService->save($data); + $client = $this->clientService->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_client')); diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 23577f951389..1e3586af1922 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -17,7 +17,7 @@ class CreditController extends BaseController { protected $creditRepo; protected $creditService; - protected $entity = ENTITY_CREDIT; + protected $entityType = ENTITY_CREDIT; public function __construct(CreditRepository $creditRepo, CreditService $creditService) { diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index c0e25e773d11..0720e1a5babf 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -15,7 +15,7 @@ use App\Ninja\Repositories\DocumentRepository; class DocumentController extends BaseController { protected $documentRepo; - protected $entity = ENTITY_DOCUMENT; + protected $entityType = ENTITY_DOCUMENT; public function __construct(DocumentRepository $documentRepo) { diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e65a485fa2f0..88a493d3ea21 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -25,7 +25,7 @@ class ExpenseController extends BaseController // Expenses protected $expenseRepo; protected $expenseService; - protected $entity = ENTITY_EXPENSE; + protected $entityType = ENTITY_EXPENSE; public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService) { diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 7aa9ca2d4d14..c3627a01c301 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -37,7 +37,7 @@ class InvoiceController extends BaseController protected $documentRepo; protected $invoiceService; protected $recurringInvoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService) { diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 0a0fe1485676..bd5fb85199cf 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -30,7 +30,7 @@ use App\Http\Requests\UpdatePaymentRequest; class PaymentController extends BaseController { - protected $entity = ENTITY_PAYMENT; + protected $entityType = ENTITY_PAYMENT; public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService) { diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index e2228ab2a0c0..3a0062296098 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -33,7 +33,7 @@ class QuoteController extends BaseController protected $invoiceRepo; protected $clientRepo; protected $invoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 7ff19436ead2..a1118269ac51 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -22,7 +22,7 @@ class TaskController extends BaseController { protected $taskRepo; protected $taskService; - protected $entity = ENTITY_TASK; + protected $entityType = ENTITY_TASK; public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService) { diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index d53d6041fb18..9340b926d4eb 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -25,12 +25,12 @@ use App\Services\VendorService; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; -// vendor + class VendorController extends BaseController { protected $vendorService; protected $vendorRepo; - protected $entity = ENTITY_VENDOR; + protected $entityType = ENTITY_VENDOR; public function __construct(VendorRepository $vendorRepo, VendorService $vendorService) { diff --git a/app/Http/Requests/BaseRequest.php b/app/Http/Requests/BaseRequest.php new file mode 100644 index 000000000000..f7be7a8de3cb --- /dev/null +++ b/app/Http/Requests/BaseRequest.php @@ -0,0 +1,30 @@ +entity) { + return $this->entity; + } + + //dd($this->clients); + $publicId = Input::get('public_id') ?: Input::get('id'); + + if ( ! $publicId) { + return null; + } + + $class = Utils::getEntityClass($this->entityType); + $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + + return $this->entity; + } +} diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php new file mode 100644 index 000000000000..2b491c46c7e4 --- /dev/null +++ b/app/Http/Requests/ClientRequest.php @@ -0,0 +1,21 @@ +user()->can('create', ENTITY_CLIENT); } /** diff --git a/app/Http/Requests/CreateCreditRequest.php b/app/Http/Requests/CreateCreditRequest.php index f2dc44d31aa0..46d40972c8dc 100644 --- a/app/Http/Requests/CreateCreditRequest.php +++ b/app/Http/Requests/CreateCreditRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_CREDIT); } /** diff --git a/app/Http/Requests/UpdateClientRequest.php b/app/Http/Requests/UpdateClientRequest.php index b73e019c4964..c1cdebe1891e 100644 --- a/app/Http/Requests/UpdateClientRequest.php +++ b/app/Http/Requests/UpdateClientRequest.php @@ -1,9 +1,6 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index e4ad99c6ce45..86107acfcd59 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -669,6 +669,11 @@ class Utils return $year + $offset; } + public static function getEntityClass($entityType) + { + return 'App\\Models\\' . static::getEntityName($entityType); + } + public static function getEntityName($entityType) { return ucwords(str_replace('_', ' ', $entityType)); diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index d3f943a07001..77ebcb7fd736 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -66,11 +66,13 @@ class ClientRepository extends BaseRepository return $query; } - public function save($data) + public function save($data, $client = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if (!$publicId || $publicId == '-1') { + if ($client) { + // do nothing + } if (!$publicId || $publicId == '-1') { $client = Client::createNew(); } else { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index 6f7383b0fced..96357991c519 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -30,13 +30,13 @@ class ClientService extends BaseService return $this->clientRepo; } - public function save($data) + public function save($data, $client = null) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } - return $this->clientRepo->save($data); + return $this->clientRepo->save($data, $client); } public function getDatatable($search) From 1a47598aeaa8cb46cecdac011c221d2ae8306b44 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:20:34 +0300 Subject: [PATCH 150/258] Working on custom route model binding --- app/Http/Controllers/ClientController.php | 2 +- app/Http/Requests/BaseRequest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index c66c775947c8..37786b401b53 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -184,7 +184,7 @@ class ClientController extends BaseController $data = [ 'client' => $client, 'method' => 'PUT', - 'url' => 'clients/'.$publicId, + 'url' => 'clients/'.$client->public_id, 'title' => trans('texts.edit_client'), ]; diff --git a/app/Http/Requests/BaseRequest.php b/app/Http/Requests/BaseRequest.php index f7be7a8de3cb..eb97a949a8f3 100644 --- a/app/Http/Requests/BaseRequest.php +++ b/app/Http/Requests/BaseRequest.php @@ -15,8 +15,8 @@ class BaseRequest extends Request { return $this->entity; } - //dd($this->clients); - $publicId = Input::get('public_id') ?: Input::get('id'); + $paramName = $this->entityType . 's'; + $publicId = $this->$paramName ?: (Input::get('public_id') ?: Input::get('id')); if ( ! $publicId) { return null; From 4b764ea72e86b983c5b0ead0cce62bdd0e363390 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:23:09 +0300 Subject: [PATCH 151/258] Working on custom route model binding --- app/Http/Requests/ClientRequest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index 2b491c46c7e4..e6470c6bd621 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -4,6 +4,11 @@ class ClientRequest extends BaseRequest { protected $entityType = ENTITY_CLIENT; + public function entity() + { + return parent::entity()->load('contacts'); + } + public function authorize() { return true; From 3ab4c0964d6dd3b51d8fa52b3bb0679667a15434 Mon Sep 17 00:00:00 2001 From: Philipp Hug Date: Thu, 28 Apr 2016 16:08:24 +0200 Subject: [PATCH 152/258] whitelabeling: allow more settings from environment. --- app/Http/routes.php | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index bd94056d8791..702cd1fcb98f 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -552,25 +552,25 @@ if (!defined('CONTACT_EMAIL')) { define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h'); define('NINJA_GATEWAY_ID', GATEWAY_STRIPE); define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); - define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); - define('NINJA_APP_URL', 'https://app.invoiceninja.com'); + 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.5.1.3'); + define('NINJA_VERSION', '2.5.1.3' . env('NINJA_VERSION_SUFFIX')); - define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); - define('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'); - define('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/'); + 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')); + define('SOCIAL_LINK_GITHUB', env('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/')); - define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); - define('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja'); - define('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'); - define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); - define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'); - define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/'); - define('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php'); - define('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/'); - define('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup'); - define('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all'); + define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com')); + define('RELEASES_URL', env('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja')); + define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja')); + define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/')); + define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html')); + define('PHANTOMJS_CLOUD', env('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/')); + define('PHP_DATE_FORMATS', env('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php')); + define('REFERRAL_PROGRAM_URL', env('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/')); + define('EMAIL_MARKUP_URL', env('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup')); + define('OFX_HOME_URL', env('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all')); define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); @@ -584,13 +584,13 @@ if (!defined('CONTACT_EMAIL')) { define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74'); define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); - define('PRO_PLAN_PRICE', 50); - define('PLAN_PRICE_PRO_MONTHLY', 5); - define('PLAN_PRICE_PRO_YEARLY', 50); - define('PLAN_PRICE_ENTERPRISE_MONTHLY', 10); - define('PLAN_PRICE_ENTERPRISE_YEARLY', 100); - define('WHITE_LABEL_PRICE', 20); - define('INVOICE_DESIGNS_PRICE', 10); + define('PRO_PLAN_PRICE', env('PRO_PLAN_PRICE', 50)); + define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 5)); + define('PLAN_PRICE_PRO_YEARLY', env('PLAN_PRICE_PRO_YEARLY', 50)); + define('PLAN_PRICE_ENTERPRISE_MONTHLY', env('PLAN_PRICE_ENTERPRISE_MONTHLY', 10)); + define('PLAN_PRICE_ENTERPRISE_YEARLY', env('PLAN_PRICE_ENTERPRISE_YEARLY', 100)); + define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20)); + define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10)); define('USER_TYPE_SELF_HOST', 'SELF_HOST'); define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST'); @@ -600,8 +600,8 @@ if (!defined('CONTACT_EMAIL')) { define('TEST_PASSWORD', 'password'); define('API_SECRET', 'API_SECRET'); - define('IOS_PRODUCTION_PUSH','ninjaIOS'); - define('IOS_DEV_PUSH','devNinjaIOS'); + define('IOS_PRODUCTION_PUSH', env('IOS_PRODUCTION_PUSH', 'ninjaIOS')); + define('IOS_DEV_PUSH', env('IOS_DEV_PUSH', 'devNinjaIOS')); define('TOKEN_BILLING_DISABLED', 1); define('TOKEN_BILLING_OPT_IN', 2); @@ -770,4 +770,4 @@ if (Utils::isNinjaDev()) //ini_set('memory_limit','1024M'); //Auth::loginUsingId(1); } -*/ \ No newline at end of file +*/ From c9ac804e5fb93432925454797a3067401e352404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Fri, 29 Apr 2016 21:54:38 +0200 Subject: [PATCH 153/258] Some more German translations I left out some strings when I was not completely sure about. --- resources/lang/de/texts.php | 102 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index c41dae9d08e2..9b8c0e2311e7 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -1118,8 +1118,8 @@ return array( 'first_page' => 'Erste Seite', 'all_pages' => 'Alle Seiten', 'last_page' => 'Letzte Seite', - 'all_pages_header' => 'Show header on', - 'all_pages_footer' => 'Show footer on', + 'all_pages_header' => 'Zeige Header auf', + 'all_pages_footer' => 'Zeige Footer auf', 'invoice_currency' => 'Rechnungs-Währung', 'enable_https' => 'Wir empfehlen dringend HTTPS zu verwenden, um Kreditkarten online zu akzeptieren.', 'quote_issued_to' => 'Quote issued to', @@ -1133,72 +1133,72 @@ return array( 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', 'navigation' => 'Navigation', - 'list_invoices' => 'List Invoices', - 'list_clients' => 'List Clients', - 'list_quotes' => 'List Quotes', - 'list_tasks' => 'List Tasks', - 'list_expenses' => 'List Expenses', - 'list_recurring_invoices' => 'List Recurring Invoices', - 'list_payments' => 'List Payments', + 'list_invoices' => 'Rechnungen Auflisten', + 'list_clients' => 'Klienten Auflisten', + 'list_quotes' => 'Angebote Auflisten', + 'list_tasks' => 'Aufgaben Auflisten', + 'list_expenses' => 'Ausgaben Auflisten', + 'list_recurring_invoices' => 'Wiederkehrende Rechnungen Auflisten', + 'list_payments' => 'Zahlungen Aufllisten', 'list_credits' => 'List Credits', - 'tax_name' => 'Tax Name', - 'report_settings' => 'Report Settings', + 'tax_name' => 'Steuer-Name', + 'report_settings' => 'Bericht-Einstellungen', 'search_hotkey' => 'shortcut is /', - 'new_user' => 'New User', - 'new_product' => 'New Product', - 'new_tax_rate' => 'New Tax Rate', + 'new_user' => 'Neuer Benutzer', + 'new_product' => 'Neues Produkt', + 'new_tax_rate' => 'Neue Steuersatz', 'invoiced_amount' => 'Invoiced Amount', 'invoice_item_fields' => 'Invoice Item Fields', - 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', - 'recurring_invoice_number' => 'Recurring Invoice Number', + 'custom_invoice_item_fields_help' => 'Feld bei Erstellung eines Rechnungs-Elements hinzufügen, und dessen Bezeichnung und Wert im PDF anzeigen.', + 'recurring_invoice_number' => 'Wiederkehrende Rechnungsnummer', 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.', + 'enable_client_portal_help' => 'Dashboard-Seite im Klienten-Portal anzeigen/verbergen.', // Client Passwords - 'enable_portal_password'=>'Password protect invoices', - 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', - 'send_portal_password'=>'Generate password automatically', - 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + 'enable_portal_password'=>'Sichere Rechnungen mit einem Passwort ab', + 'enable_portal_password_help'=>'Erlaubt, ein Passwort für jeden Kontakt zu setzen. Wenn ein Passwort gesetzt ist, wird dieses vom Kontakt benötigt, um Rechnungen anzusehen.', + 'send_portal_password'=>'Passwort automatisch generieren', + 'send_portal_password_help'=>'Wenn kein Passwort gesetzt ist, wird eines erstellt und mit der ersten Rechnung mit versendet.', - 'expired' => 'Expired', - 'invalid_card_number' => 'The credit card number is not valid.', - 'invalid_expiry' => 'The expiration date is not valid.', - 'invalid_cvv' => 'The CVV is not valid.', - 'cost' => 'Cost', - 'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.', + 'expired' => 'Abgelaufen', + 'invalid_card_number' => 'Die Kreditkartennummer ist nicht gültig.', + 'invalid_expiry' => 'Das Ablaufdatum ist nicht gültig.', + 'invalid_cvv' => 'Das CVV ist nicht gültig.', + 'cost' => 'Kosten', + 'create_invoice_for_sample' => 'Info: Erstelle deine erste Rechnung, um hier eine Voransicht zu sehen.', // User Permissions - 'owner' => 'Owner', + 'owner' => 'Eigentümer', 'administrator' => 'Administrator', - 'administrator_help' => 'Allow user to manage users, change settings and modify all records', - 'user_create_all' => 'Create clients, invoices, etc.', - 'user_view_all' => 'View all clients, invoices, etc.', - 'user_edit_all' => 'Edit all clients, invoices, etc.', - 'gateway_help_20' => ':link to sign up for Sage Pay.', - 'gateway_help_21' => ':link to sign up for Sage Pay.', - 'partial_due' => 'Partial Due', - 'restore_vendor' => 'Restore Vendor', - 'restored_vendor' => 'Successfully restored vendor', - 'restored_expense' => 'Successfully restored expense', - 'permissions' => 'Permissions', - 'create_all_help' => 'Allow user to create and modify records', - 'view_all_help' => 'Allow user to view records they didn\'t create', - 'edit_all_help' => 'Allow user to modify records they didn\'t create', - 'view_payment' => 'View Payment', + 'administrator_help' => 'Erlaube Benutzern, andere Benutzer zu administrieren, Einstellungen und alle Einträge zu verändern', + 'user_create_all' => 'Klienten, Rechnungen, etc. erstellen', + 'user_view_all' => 'Klienten, Rechnungen, etc. ansehen', + 'user_edit_all' => 'Klienten, Rechnungen, etc. bearbeiten', + 'gateway_help_20' => ':link um sich bei Sage Pay zu registrieren.', + 'gateway_help_21' => ':link um sich bei Sage Pay zu registrieren.', + 'partial_due' => 'Offener Teilbetrag', + 'restore_vendor' => 'Anbieter Wiederherstellen', + 'restored_vendor' => 'Anbieter erfolgreich wiederhergestellt', + 'restored_expense' => 'Ausgabe erfolgreich wiederhergestellt', + 'permissions' => 'Berechtigungen', + 'create_all_help' => 'Erlaube Benutzer, Einträge zu erstellen und zu verändern', + 'view_all_help' => 'Erlaube Benutzer, Einträge zu sehen, die er nicht selbst erstellt hat', + 'edit_all_help' => 'Erlaube Benutzer, Einträge zu verändern, die er nicht selbst erstellt hat', + 'view_payment' => 'Zahlung Ansehen', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', + 'january' => 'Jänner', + 'february' => 'Februar', + 'march' => 'März', 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', + 'may' => 'Mai', + 'june' => 'Juni', + 'july' => 'Juli', 'august' => 'August', 'september' => 'September', - 'october' => 'October', + 'october' => 'Oktober', 'november' => 'November', - 'december' => 'December', + 'december' => 'Dezember', ); From ac57dcdcc31273d748b4b4bdb07b4c98cd6dc82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Fri, 29 Apr 2016 21:57:18 +0200 Subject: [PATCH 154/258] Changed "Klient" to "Kunde" to be consistent. --- resources/lang/de/texts.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 9b8c0e2311e7..2520f229465a 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -1134,7 +1134,7 @@ return array( 'navigation' => 'Navigation', 'list_invoices' => 'Rechnungen Auflisten', - 'list_clients' => 'Klienten Auflisten', + 'list_clients' => 'Kunden Auflisten', 'list_quotes' => 'Angebote Auflisten', 'list_tasks' => 'Aufgaben Auflisten', 'list_expenses' => 'Ausgaben Auflisten', @@ -1154,7 +1154,7 @@ return array( 'recurring_invoice_number' => 'Wiederkehrende Rechnungsnummer', 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Dashboard-Seite im Klienten-Portal anzeigen/verbergen.', + 'enable_client_portal_help' => 'Dashboard-Seite im Kunden-Portal anzeigen/verbergen.', // Client Passwords 'enable_portal_password'=>'Sichere Rechnungen mit einem Passwort ab', @@ -1173,9 +1173,9 @@ return array( 'owner' => 'Eigentümer', 'administrator' => 'Administrator', 'administrator_help' => 'Erlaube Benutzern, andere Benutzer zu administrieren, Einstellungen und alle Einträge zu verändern', - 'user_create_all' => 'Klienten, Rechnungen, etc. erstellen', - 'user_view_all' => 'Klienten, Rechnungen, etc. ansehen', - 'user_edit_all' => 'Klienten, Rechnungen, etc. bearbeiten', + 'user_create_all' => 'Kunden, Rechnungen, etc. erstellen', + 'user_view_all' => 'Kunden, Rechnungen, etc. ansehen', + 'user_edit_all' => 'Kunden, Rechnungen, etc. bearbeiten', 'gateway_help_20' => ':link um sich bei Sage Pay zu registrieren.', 'gateway_help_21' => ':link um sich bei Sage Pay zu registrieren.', 'partial_due' => 'Offener Teilbetrag', From a24ae57b4eeeb4b61290c64afefe871c5db58cd0 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 30 Apr 2016 21:41:05 +0300 Subject: [PATCH 155/258] Fix for travis tests --- tests/acceptance/InvoiceCest.php | 2 +- tests/acceptance/TaxRatesCest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/InvoiceCest.php b/tests/acceptance/InvoiceCest.php index 2d8b818d161d..07ac60e24215 100644 --- a/tests/acceptance/InvoiceCest.php +++ b/tests/acceptance/InvoiceCest.php @@ -64,7 +64,7 @@ class InvoiceCest $this->fillItems($I); $I->executeJS("submitAction('email')"); - $I->wait(1); + $I->wait(2); $I->see($clientEmail); $invoiceNumber = $I->grabAttributeFrom('#invoice_number', 'value'); diff --git a/tests/acceptance/TaxRatesCest.php b/tests/acceptance/TaxRatesCest.php index 75f7de7a7508..ebe68f7b5880 100644 --- a/tests/acceptance/TaxRatesCest.php +++ b/tests/acceptance/TaxRatesCest.php @@ -78,7 +78,7 @@ class TaxRatesCest // check total is right before saving $I->see("\${$total}"); $I->click('Save'); - $I->wait(2); + $I->wait(3); $I->see($clientEmail); // check total is right after saving From a9e10985a189e22f220864b83f9a71b89b09f59b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 30 Apr 2016 21:52:53 +0300 Subject: [PATCH 156/258] Fix for tests --- tests/acceptance/CheckBalanceCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/CheckBalanceCest.php b/tests/acceptance/CheckBalanceCest.php index 33378baab0ba..68cae45c3154 100644 --- a/tests/acceptance/CheckBalanceCest.php +++ b/tests/acceptance/CheckBalanceCest.php @@ -46,7 +46,7 @@ class CheckBalanceCest $I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey); $I->click('table.invoice-table tbody tr:nth-child(1) .tt-selectable'); $I->click('Save'); - $I->wait(1); + $I->wait(2); $I->see($clientEmail); $invoiceId = $I->grabFromCurrentUrl('~invoices/(\d+)~'); $I->amOnPage("/clients/{$clientId}"); From 50de2d757edef8657da85f91f4f05e4489d15d14 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 30 Apr 2016 21:55:15 +0300 Subject: [PATCH 157/258] Working on permissions --- app/Http/Controllers/ClientController.php | 7 ++----- app/Http/Requests/ClientRequest.php | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 37786b401b53..ee400287b4f1 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -95,12 +95,9 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function show($publicId) + public function show(ClientRequest $request) { - //$client = $request->entity()->load('conacts'); - $client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail(); - - $this->authorize('view', $client); + $client = $request->entity(); $user = Auth::user(); Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT); diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index e6470c6bd621..9d8e51556c9e 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -11,7 +11,7 @@ class ClientRequest extends BaseRequest { public function authorize() { - return true; + return $this->user()->can('view', $this->entity()); } /** From 1000ff40887079550c1f29804942a0c0fb0e6a2b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 08:55:59 +0300 Subject: [PATCH 158/258] Fix for currency formatting on invoice --- app/Models/Invoice.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index c0da6b8faff6..deb65b75ab30 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -436,6 +436,7 @@ class Invoice extends EntityModel implements BalanceAffecting 'contacts', 'country', 'currency_id', + 'country_id', 'custom_value1', 'custom_value2', ]); From 2e2470e11ced5b02cb6ca32155840a03d1f183d4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 09:43:41 +0300 Subject: [PATCH 159/258] Send contact's email when creating Stripe token --- app/Services/PaymentService.php | 2 +- resources/views/payments/payment.blade.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 9b325bd4e447..23b5f4e8f167 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -63,7 +63,6 @@ class PaymentService extends BaseService if ($input) { $data = self::convertInputForOmnipay($input); - $data['email'] = $invitation->contact->email; Session::put($key, $data); } elseif (Session::get($key)) { $data = Session::get($key); @@ -95,6 +94,7 @@ class PaymentService extends BaseService $data = [ 'firstName' => $input['first_name'], 'lastName' => $input['last_name'], + 'email' => $input['email'], 'number' => isset($input['card_number']) ? $input['card_number'] : null, 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null, 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null, diff --git a/resources/views/payments/payment.blade.php b/resources/views/payments/payment.blade.php index 274361938bac..949723e7f17a 100644 --- a/resources/views/payments/payment.blade.php +++ b/resources/views/payments/payment.blade.php @@ -13,6 +13,7 @@ var data = { name: $('#first_name').val() + ' ' + $('#last_name').val(), + email: $('#email').val(), address_line1: $('#address1').val(), address_line2: $('#address2').val(), address_city: $('#city').val(), @@ -117,6 +118,7 @@ {{ Former::populate($client) }} {{ Former::populateField('first_name', $contact->first_name) }} {{ Former::populateField('last_name', $contact->last_name) }} + {{ Former::populateField('email', $contact->email) }} @if (!$client->country_id && $client->account->country_id) {{ Former::populateField('country_id', $client->account->country_id) }} @endif @@ -178,8 +180,7 @@ ->label('') !!} - @if (isset($paymentTitle)) -
    +
    {!! Former::text('email') ->placeholder(trans('texts.email')) @@ -187,7 +188,6 @@ ->label('') !!}
    - @endif

     
     

    From 1647effb20ffd34645394ef6346e795dfcb1dde2 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 11:43:10 +0300 Subject: [PATCH 160/258] Fix for client portal invoice table --- app/Ninja/Repositories/InvoiceRepository.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 15616e9cd75f..aebb177b5852 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -153,11 +153,14 @@ class InvoiceRepository extends BaseRepository ->join('accounts', 'accounts.id', '=', 'invitations.account_id') ->join('invoices', 'invoices.id', '=', 'invitations.invoice_id') ->join('clients', 'clients.id', '=', 'invoices.client_id') + ->join('contacts', 'contacts.client_id', '=', 'clients.id') ->where('invitations.contact_id', '=', $contactId) ->where('invitations.deleted_at', '=', null) ->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE) ->where('invoices.is_deleted', '=', false) ->where('clients.deleted_at', '=', null) + ->where('contacts.deleted_at', '=', null) + ->where('contacts.is_primary', '=', true) ->where('invoices.is_recurring', '=', false) // This needs to be a setting to also hide the activity on the dashboard page //->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT) From c144a1c604b72008644f9ca676645e0e1cec04e4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 11:43:30 +0300 Subject: [PATCH 161/258] Fix to make it easier to test features --- app/Models/Account.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Models/Account.php b/app/Models/Account.php index e2e7d2f8a002..c8ece3b4da95 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -796,6 +796,10 @@ class Account extends Eloquent public function hasFeature($feature) { + if (Utils::isNinjaDev()) { + return true; + } + $planDetails = $this->getPlanDetails(); $selfHost = !Utils::isNinjaProd(); From 05bd8d93188f3f887ff520ac1f0043e4c1cb4eb7 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 14:31:10 +0300 Subject: [PATCH 162/258] Moving permissions to form requests --- app/Commands/Command.php | 2 +- app/Console/Commands/GenerateResources.php | 2 +- app/Console/Commands/TestOFX.php | 2 +- app/Console/Kernel.php | 2 +- app/Http/Controllers/ClientController.php | 8 +--- app/Http/Controllers/CreditController.php | 16 +++---- app/Http/Controllers/ExpenseController.php | 44 +++++++------------ app/Http/Controllers/ImportController.php | 2 +- app/Http/Controllers/PaymentController.php | 29 +++++------- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/VendorController.php | 35 +++++---------- .../Middleware/DuplicateSubmissionCheck.php | 2 +- app/Http/Middleware/StartupCheck.php | 2 +- app/Http/Requests/ClientRequest.php | 28 +++++------- .../Requests/CreateBankAccountRequest.php | 2 +- app/Http/Requests/CreateCreditRequest.php | 2 +- app/Http/Requests/CreateExpenseRequest.php | 9 ++-- app/Http/Requests/CreateInvoiceRequest.php | 2 +- app/Http/Requests/CreatePaymentRequest.php | 8 ++-- .../Requests/CreatePaymentTermRequest.php | 2 +- app/Http/Requests/CreateTaxRateRequest.php | 2 +- app/Http/Requests/CreateVendorRequest.php | 9 ++-- .../{BaseRequest.php => EntityRequest.php} | 18 +++++++- app/Http/Requests/RegisterRequest.php | 2 +- .../Requests/SaveInvoiceWithClientRequest.php | 2 +- app/Http/Requests/UpdateAccountRequest.php | 2 +- app/Http/Requests/UpdateExpenseRequest.php | 10 ++--- app/Http/Requests/UpdateInvoiceRequest.php | 2 +- app/Http/Requests/UpdatePaymentRequest.php | 9 ++-- .../Requests/UpdatePaymentTermRequest.php | 2 +- app/Http/Requests/UpdateTaxRateRequest.php | 2 +- app/Http/Requests/UpdateUserRequest.php | 2 +- app/Http/Requests/UpdateVendorRequest.php | 9 ++-- app/Listeners/ActivityListener.php | 2 +- app/Listeners/CreditListener.php | 2 +- app/Listeners/ExpenseListener.php | 2 +- app/Listeners/HandleUserSignedUp.php | 2 +- app/Listeners/InvoiceListener.php | 2 +- app/Listeners/NotificationListener.php | 2 +- app/Listeners/QuoteListener.php | 2 +- app/Listeners/SubscriptionListener.php | 2 +- app/Listeners/TaskListener.php | 2 +- app/Models/InvoiceDesign.php | 2 +- app/Ninja/Repositories/DocumentRepository.php | 2 +- app/Ninja/Repositories/ExpenseRepository.php | 2 +- app/Ninja/Repositories/InvoiceRepository.php | 2 +- 46 files changed, 122 insertions(+), 176 deletions(-) rename app/Http/Requests/{BaseRequest.php => EntityRequest.php} (66%) diff --git a/app/Commands/Command.php b/app/Commands/Command.php index 5bc48501167e..d6a8d61150ae 100644 --- a/app/Commands/Command.php +++ b/app/Commands/Command.php @@ -1,4 +1,4 @@ -authorizeCreate(); - if (Client::scope()->withTrashed()->count() > Auth::user()->getMaxNumClients()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumClients()." clients"]); } @@ -175,9 +173,7 @@ class ClientController extends BaseController public function edit(ClientRequest $request) { $client = $request->entity(); - - $this->authorize('edit', $client); - + $data = [ 'client' => $client, 'method' => 'PUT', diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 1e3586af1922..c4250903fd32 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -12,6 +12,7 @@ use App\Models\Client; use App\Services\CreditService; use App\Ninja\Repositories\CreditRepository; use App\Http\Requests\CreateCreditRequest; +use App\Http\Requests\CreditRequest; class CreditController extends BaseController { @@ -55,23 +56,21 @@ class CreditController extends BaseController return $this->creditService->getDatatable($clientPublicId, Input::get('sSearch')); } - public function create($clientPublicId = 0) + public function create(CreditRequest $request) { - $this->authorizeCreate(); - $data = array( - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, - //'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'credit' => null, 'method' => 'POST', 'url' => 'credits', 'title' => trans('texts.new_credit'), - //'invoices' => Invoice::scope()->with('client', 'invoice_status')->orderBy('invoice_number')->get(), - 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + ); return View::make('credits.edit', $data); } + /* public function edit($publicId) { $credit = Credit::scope($publicId)->firstOrFail(); @@ -90,7 +89,8 @@ class CreditController extends BaseController return View::make('credit.edit', $data); } - + */ + public function store(CreateCreditRequest $request) { $credit = $this->creditRepo->save($request->input()); diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 88a493d3ea21..406613489c6b 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -17,6 +17,8 @@ use App\Models\Expense; use App\Models\Client; use App\Services\ExpenseService; use App\Ninja\Repositories\ExpenseRepository; + +use App\Http\Requests\ExpenseRequest; use App\Http\Requests\CreateExpenseRequest; use App\Http\Requests\UpdateExpenseRequest; @@ -69,17 +71,16 @@ class ExpenseController extends BaseController return $this->expenseService->getDatatableVendor($vendorPublicId); } - public function create($vendorPublicId = null, $clientPublicId = null) + public function create(ExpenseRequest $request) { - $this->authorizeCreate(); - - if($vendorPublicId != 0) { - $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); + if ($request->vendor_id != 0) { + $vendor = Vendor::scope($request->vendor_id)->with('vendorcontacts')->firstOrFail(); } else { $vendor = null; } + $data = array( - 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $vendorPublicId, + 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $request->vendor_id, 'expense' => null, 'method' => 'POST', 'url' => 'expenses', @@ -87,20 +88,18 @@ class ExpenseController extends BaseController 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), 'vendor' => $vendor, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), - 'clientPublicId' => $clientPublicId, - ); + 'clientPublicId' => $request->client_id, + ); $data = array_merge($data, self::getViewModel()); return View::make('expenses.edit', $data); } - public function edit($publicId) + public function edit(ExpenseRequest $request) { - $expense = Expense::scope($publicId)->with('documents')->firstOrFail(); - - $this->authorize('edit', $expense); - + $expense = $request->entity(); + $expense->expense_date = Utils::fromSqlDate($expense->expense_date); $actions = []; @@ -108,15 +107,6 @@ class ExpenseController extends BaseController $actions[] = ['url' => URL::to("invoices/{$expense->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")]; } else { $actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.invoice_expense")]; - - /* - // check for any open invoices - $invoices = $task->client_id ? $this->invoiceRepo->findOpenInvoices($task->client_id) : []; - - foreach ($invoices as $invoice) { - $actions[] = ['url' => 'javascript:submitAction("add_to_invoice", '.$invoice->public_id.')', 'label' => trans("texts.add_to_invoice", ["invoice" => $invoice->invoice_number])]; - } - */ } $actions[] = \DropdownButton::DIVIDER; @@ -131,7 +121,7 @@ class ExpenseController extends BaseController 'vendor' => null, 'expense' => $expense, 'method' => 'PUT', - 'url' => 'expenses/'.$publicId, + 'url' => 'expenses/'.$expense->public_id, 'title' => 'Edit Expense', 'actions' => $actions, 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), @@ -155,9 +145,7 @@ class ExpenseController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.updated_expense')); @@ -174,9 +162,7 @@ class ExpenseController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.created_expense')); diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 38618475064b..fe006332ca48 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -1,4 +1,4 @@ -paymentService->getDatatable($clientPublicId, Input::get('sSearch')); } - public function create($clientPublicId = 0, $invoicePublicId = 0) + public function create(PaymentRequest $request) { - $this->authorizeCreate(); - $invoices = Invoice::scope() ->where('is_recurring', '=', false) ->where('is_quote', '=', false) @@ -79,8 +78,8 @@ class PaymentController extends BaseController ->orderBy('invoice_number')->get(); $data = array( - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, - 'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), + 'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : ($request->invoice_id ?: 0), 'invoice' => null, 'invoices' => $invoices, 'payment' => null, @@ -94,12 +93,10 @@ class PaymentController extends BaseController return View::make('payments.edit', $data); } - public function edit($publicId) + public function edit(PaymentRequest $request) { - $payment = Payment::scope($publicId)->firstOrFail(); - - $this->authorize('edit', $payment); - + $payment = $request->entity(); + $payment->payment_date = Utils::fromSqlDate($payment->payment_date); $data = array( @@ -109,7 +106,7 @@ class PaymentController extends BaseController ->with('client', 'invoice_status')->orderBy('invoice_number')->get(), 'payment' => $payment, 'method' => 'PUT', - 'url' => 'payments/'.$publicId, + 'url' => 'payments/'.$payment->public_id, 'title' => trans('texts.edit_payment'), 'paymentTypes' => Cache::get('paymentTypes'), 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); @@ -589,9 +586,7 @@ class PaymentController extends BaseController public function store(CreatePaymentRequest $request) { $input = $request->input(); - - $this->authorizeUpdate($input); - + $input['invoice_id'] = Invoice::getPrivateId($input['invoice']); $input['client_id'] = Client::getPrivateId($input['client']); $payment = $this->paymentRepo->save($input); @@ -608,11 +603,7 @@ class PaymentController extends BaseController public function update(UpdatePaymentRequest $request) { - $input = $request->input(); - - $this->authorizeUpdate($input); - - $payment = $this->paymentRepo->save($input); + $payment = $this->paymentRepo->save($request->input()); Session::flash('message', trans('texts.updated_payment')); diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index a1118269ac51..565cb40955a9 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -90,7 +90,7 @@ class TaskController extends BaseController $data = [ 'task' => null, - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($clientPublicId ?: 0), 'method' => 'POST', 'url' => 'tasks', 'title' => trans('texts.new_task'), diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 9340b926d4eb..10c7f7f03e89 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -23,6 +23,7 @@ use App\Models\Country; use App\Ninja\Repositories\VendorRepository; use App\Services\VendorService; +use App\Http\Requests\VendorRequest; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; @@ -38,8 +39,6 @@ class VendorController extends BaseController $this->vendorRepo = $vendorRepo; $this->vendorService = $vendorService; - - } /** @@ -77,11 +76,7 @@ class VendorController extends BaseController */ public function store(CreateVendorRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $vendor = $this->vendorService->save($data); + $vendor = $this->vendorService->save($request->input()); Session::flash('message', trans('texts.created_vendor')); @@ -94,12 +89,10 @@ class VendorController extends BaseController * @param int $id * @return Response */ - public function show($publicId) + public function show(VendorRequest $request) { - $vendor = Vendor::withTrashed()->scope($publicId)->with('vendorcontacts', 'size', 'industry')->firstOrFail(); - - $this->authorize('view', $vendor); - + $vendor = $request->entity(); + Utils::trackViewed($vendor->getDisplayName(), 'vendor'); $actionLinks = [ @@ -125,10 +118,8 @@ class VendorController extends BaseController * * @return Response */ - public function create() + public function create(VendorRequest $request) { - $this->authorizeCreate(); - if (Vendor::scope()->count() > Auth::user()->getMaxNumVendors()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumVendors()." vendors"]); } @@ -151,16 +142,14 @@ class VendorController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(VendorRequest $request) { - $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); - - $this->authorize('edit', $vendor); + $vendor = $request->entity(); $data = [ 'vendor' => $vendor, 'method' => 'PUT', - 'url' => 'vendors/'.$publicId, + 'url' => 'vendors/'.$vendor->public_id, 'title' => trans('texts.edit_vendor'), ]; @@ -193,11 +182,7 @@ class VendorController extends BaseController */ public function update(UpdateVendorRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $vendor = $this->vendorService->save($data); + $vendor = $this->vendorService->save($request->input()); Session::flash('message', trans('texts.updated_vendor')); diff --git a/app/Http/Middleware/DuplicateSubmissionCheck.php b/app/Http/Middleware/DuplicateSubmissionCheck.php index 407ffab60071..6f3374a47ebf 100644 --- a/app/Http/Middleware/DuplicateSubmissionCheck.php +++ b/app/Http/Middleware/DuplicateSubmissionCheck.php @@ -1,4 +1,4 @@ -load('contacts'); + $client = parent::entity(); + + // eager load the contacts + if ($client && ! count($client->contacts)) { + $client->load('contacts'); + } + + return $client; } - - public function authorize() - { - return $this->user()->can('view', $this->entity()); - } - - /** - * Get the validation rules that apply to the request. - * - * @return array - */ - public function rules() - { - return []; - } -} +} \ No newline at end of file diff --git a/app/Http/Requests/CreateBankAccountRequest.php b/app/Http/Requests/CreateBankAccountRequest.php index 6c2fea62ec47..eac988349c8d 100644 --- a/app/Http/Requests/CreateBankAccountRequest.php +++ b/app/Http/Requests/CreateBankAccountRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_EXPENSE); } /** diff --git a/app/Http/Requests/CreateInvoiceRequest.php b/app/Http/Requests/CreateInvoiceRequest.php index 4a11ea56044a..8531a4074879 100644 --- a/app/Http/Requests/CreateInvoiceRequest.php +++ b/app/Http/Requests/CreateInvoiceRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_PAYMENT); } /** diff --git a/app/Http/Requests/CreatePaymentTermRequest.php b/app/Http/Requests/CreatePaymentTermRequest.php index d8581793160e..23bf3151d096 100644 --- a/app/Http/Requests/CreatePaymentTermRequest.php +++ b/app/Http/Requests/CreatePaymentTermRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_VENDOR); } /** diff --git a/app/Http/Requests/BaseRequest.php b/app/Http/Requests/EntityRequest.php similarity index 66% rename from app/Http/Requests/BaseRequest.php rename to app/Http/Requests/EntityRequest.php index eb97a949a8f3..53013004a4f9 100644 --- a/app/Http/Requests/BaseRequest.php +++ b/app/Http/Requests/EntityRequest.php @@ -4,7 +4,7 @@ use App\Http\Requests\Request; use Input; use Utils; -class BaseRequest extends Request { +class EntityRequest extends Request { protected $entityType; private $entity; @@ -14,7 +14,7 @@ class BaseRequest extends Request { if ($this->entity) { return $this->entity; } - + $paramName = $this->entityType . 's'; $publicId = $this->$paramName ?: (Input::get('public_id') ?: Input::get('id')); @@ -27,4 +27,18 @@ class BaseRequest extends Request { return $this->entity; } + + public function authorize() + { + if ($this->entity()) { + return $this->user()->can('view', $this->entity()); + } else { + return $this->user()->can('create', $this->entityType); + } + } + + public function rules() + { + return []; + } } diff --git a/app/Http/Requests/RegisterRequest.php b/app/Http/Requests/RegisterRequest.php index 91a27556924c..5c117fc23a09 100644 --- a/app/Http/Requests/RegisterRequest.php +++ b/app/Http/Requests/RegisterRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateInvoiceRequest.php b/app/Http/Requests/UpdateInvoiceRequest.php index 4b32bf4ccead..7827e2b1d1e1 100644 --- a/app/Http/Requests/UpdateInvoiceRequest.php +++ b/app/Http/Requests/UpdateInvoiceRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdatePaymentTermRequest.php b/app/Http/Requests/UpdatePaymentTermRequest.php index b3d4f536bc6e..ea9ff80e9772 100644 --- a/app/Http/Requests/UpdatePaymentTermRequest.php +++ b/app/Http/Requests/UpdatePaymentTermRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Listeners/ActivityListener.php b/app/Listeners/ActivityListener.php index 52c2e26f9027..7edd065f915d 100644 --- a/app/Listeners/ActivityListener.php +++ b/app/Listeners/ActivityListener.php @@ -1,4 +1,4 @@ - Date: Sun, 1 May 2016 15:04:55 +0300 Subject: [PATCH 163/258] Moving permissions to form requests --- app/Http/Controllers/TaskController.php | 39 ++++++++++------------- app/Http/Requests/CreateCreditRequest.php | 2 +- app/Http/Requests/CreateTaskRequest.php | 26 +++++++++++++++ app/Http/Requests/CreditRequest.php | 7 ++++ app/Http/Requests/ExpenseRequest.php | 18 +++++++++++ app/Http/Requests/PaymentRequest.php | 7 ++++ app/Http/Requests/TaskRequest.php | 7 ++++ app/Http/Requests/UpdateTaskRequest.php | 26 +++++++++++++++ app/Http/Requests/VendorRequest.php | 19 +++++++++++ app/Ninja/Repositories/TaskRepository.php | 14 -------- resources/views/tasks/edit.blade.php | 6 +++- 11 files changed, 132 insertions(+), 39 deletions(-) create mode 100644 app/Http/Requests/CreateTaskRequest.php create mode 100644 app/Http/Requests/CreditRequest.php create mode 100644 app/Http/Requests/ExpenseRequest.php create mode 100644 app/Http/Requests/PaymentRequest.php create mode 100644 app/Http/Requests/TaskRequest.php create mode 100644 app/Http/Requests/UpdateTaskRequest.php create mode 100644 app/Http/Requests/VendorRequest.php diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 565cb40955a9..e78462cd51ac 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -18,6 +18,10 @@ use App\Ninja\Repositories\TaskRepository; use App\Ninja\Repositories\InvoiceRepository; use App\Services\TaskService; +use App\Http\Requests\TaskRequest; +use App\Http\Requests\CreateTaskRequest; +use App\Http\Requests\UpdateTaskRequest; + class TaskController extends BaseController { protected $taskRepo; @@ -66,16 +70,16 @@ class TaskController extends BaseController * * @return Response */ - public function store() + public function store(CreateTaskRequest $request) { return $this->save(); } - public function show($publicId) + public function show(TaskRequest $request) { Session::reflash(); - return Redirect::to("tasks/{$publicId}/edit"); + return Redirect::to("tasks/{$request->task_id}/edit"); } /** @@ -83,14 +87,13 @@ class TaskController extends BaseController * * @return Response */ - public function create($clientPublicId = 0) + public function create(TaskRequest $request) { - $this->authorizeCreate(); $this->checkTimezone(); $data = [ 'task' => null, - 'clientPublicId' => Input::old('client') ? Input::old('client') : ($clientPublicId ?: 0), + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'method' => 'POST', 'url' => 'tasks', 'title' => trans('texts.new_task'), @@ -109,13 +112,11 @@ class TaskController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(TaskRequest $request) { $this->checkTimezone(); - $task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail(); - - $this->authorize('edit', $task); + $task = $request->entity(); $actions = []; if ($task->invoice) { @@ -143,7 +144,7 @@ class TaskController extends BaseController 'task' => $task, 'clientPublicId' => $task->client ? $task->client->public_id : 0, 'method' => 'PUT', - 'url' => 'tasks/'.$publicId, + 'url' => 'tasks/'.$task->public_id, 'title' => trans('texts.edit_task'), 'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(), 'actions' => $actions, @@ -163,9 +164,11 @@ class TaskController extends BaseController * @param int $id * @return Response */ - public function update($publicId) + public function update(UpdateTaskRequest $request) { - return $this->save($publicId); + $task = $request->entity(); + + return $this->save($task->public_id); } private static function getViewModel() @@ -180,20 +183,10 @@ class TaskController extends BaseController { $action = Input::get('action'); - $this->authorizeUpdate(array('public_id'=>$publicId)/* Hacky, but works */); - if (in_array($action, ['archive', 'delete', 'restore'])) { return self::bulk(); } - if ($validator = $this->taskRepo->getErrors(Input::all())) { - $url = $publicId ? 'tasks/'.$publicId.'/edit' : 'tasks/create'; - Session::flash('error', trans('texts.task_errors')); - return Redirect::to($url) - ->withErrors($validator) - ->withInput(); - } - $task = $this->taskRepo->save($publicId, Input::all()); Session::flash('message', trans($publicId ? 'texts.updated_task' : 'texts.created_task')); diff --git a/app/Http/Requests/CreateCreditRequest.php b/app/Http/Requests/CreateCreditRequest.php index 58d509c69b6d..b6f4fe3b37e3 100644 --- a/app/Http/Requests/CreateCreditRequest.php +++ b/app/Http/Requests/CreateCreditRequest.php @@ -1,6 +1,6 @@ user()->can('create', ENTITY_TASK); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'time_log' => 'time_log', + ]; + } +} diff --git a/app/Http/Requests/CreditRequest.php b/app/Http/Requests/CreditRequest.php new file mode 100644 index 000000000000..7968005555f4 --- /dev/null +++ b/app/Http/Requests/CreditRequest.php @@ -0,0 +1,7 @@ +documents)) { + $expense->load('documents'); + } + + return $expense; + } +} \ No newline at end of file diff --git a/app/Http/Requests/PaymentRequest.php b/app/Http/Requests/PaymentRequest.php new file mode 100644 index 000000000000..cb34349f5d20 --- /dev/null +++ b/app/Http/Requests/PaymentRequest.php @@ -0,0 +1,7 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'time_log' => 'time_log', + ]; + } +} diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php new file mode 100644 index 000000000000..600678686c0e --- /dev/null +++ b/app/Http/Requests/VendorRequest.php @@ -0,0 +1,19 @@ +vendorcontacts)) { + $vendor->load('vendorcontacts'); + } + + return $vendor; + } + +} \ No newline at end of file diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index d78b53c02d56..1bd0e38cb6b7 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -64,20 +64,6 @@ class TaskRepository return $query; } - public function getErrors($input) - { - $rules = [ - 'time_log' => 'time_log', - ]; - $validator = \Validator::make($input, $rules); - - if ($validator->fails()) { - return $validator; - } - - return false; - } - public function save($publicId, $data) { if ($publicId) { diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index fb3648206c6c..15c593b905b4 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -114,6 +114,10 @@
    + @if ($errors->first('time_log')) +
  • {{ trans('texts.task_errors') }}
  • + @endif + @@ -455,7 +459,7 @@ @endif @endif - @if (Session::has('error')) + @if ($errors->first('time_log')) loadTimeLog({!! json_encode(Input::old('time_log')) !!}); model.showTimeOverlaps(); showTimeDetails(); From 65c8ef40786eb65477b1e5568584acb6725921f5 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 15:35:51 +0300 Subject: [PATCH 164/258] Moving permissions to form requests --- resources/views/tasks/edit.blade.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index 15c593b905b4..0a42f343b6bf 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -19,6 +19,11 @@ + @if ($errors->first('time_log')) +
  • {{ trans('texts.task_errors') }}
  • + @endif + + {!! Former::open($url)->addClass('col-md-10 col-md-offset-1 warn-on-exit task-form')->method($method)->rules(array()) !!} @if ($task) {!! Former::populate($task) !!} @@ -114,10 +119,6 @@ - @if ($errors->first('time_log')) -
  • {{ trans('texts.task_errors') }}
  • - @endif - From 933f2968d65388f382d7435e1bd7bff59909f218 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 22:30:39 +0300 Subject: [PATCH 165/258] Working on form requests --- app/Http/Controllers/DocumentController.php | 31 +++++-------- app/Http/Controllers/InvoiceApiController.php | 8 ++-- app/Http/Controllers/InvoiceController.php | 45 +++++++++---------- app/Http/Controllers/QuoteController.php | 5 +-- app/Http/Controllers/TaskController.php | 4 +- app/Http/Requests/CreateDocumentRequest.php | 26 +++++++++++ app/Http/Requests/CreateInvoiceAPIRequest.php | 32 +++++++++++++ app/Http/Requests/CreateInvoiceRequest.php | 20 ++++----- app/Http/Requests/DocumentRequest.php | 7 +++ app/Http/Requests/EntityRequest.php | 7 ++- app/Http/Requests/InvoiceRequest.php | 20 +++++++++ .../Requests/SaveInvoiceWithClientRequest.php | 45 ------------------- app/Http/Requests/UpdateInvoiceAPIRequest.php | 36 +++++++++++++++ app/Http/Requests/UpdateInvoiceRequest.php | 27 +++++------ app/Http/routes.php | 20 ++++----- app/Models/Document.php | 10 ++--- app/Ninja/Repositories/DocumentRepository.php | 2 +- 17 files changed, 205 insertions(+), 140 deletions(-) create mode 100644 app/Http/Requests/CreateDocumentRequest.php create mode 100644 app/Http/Requests/CreateInvoiceAPIRequest.php create mode 100644 app/Http/Requests/DocumentRequest.php create mode 100644 app/Http/Requests/InvoiceRequest.php delete mode 100644 app/Http/Requests/SaveInvoiceWithClientRequest.php create mode 100644 app/Http/Requests/UpdateInvoiceAPIRequest.php diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 0720e1a5babf..d597ba004474 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -12,6 +12,9 @@ use Response; use App\Models\Document; use App\Ninja\Repositories\DocumentRepository; +use App\Http\Requests\DocumentRequest; +use App\Http\Requests\CreateDocumentRequest; + class DocumentController extends BaseController { protected $documentRepo; @@ -24,14 +27,9 @@ class DocumentController extends BaseController $this->documentRepo = $documentRepo; } - public function get($publicId) + public function get(DocumentRequest $request) { - $document = Document::scope($publicId) - ->firstOrFail(); - - $this->authorize('view', $document); - - return static::getDownloadResponse($document); + return static::getDownloadResponse($request->entity()); } public static function getDownloadResponse($document){ @@ -60,12 +58,9 @@ class DocumentController extends BaseController return $response; } - public function getPreview($publicId) + public function getPreview(DocumentRequest $request) { - $document = Document::scope($publicId) - ->firstOrFail(); - - $this->authorize('view', $document); + $document = $request->entity(); if(empty($document->preview)){ return Response::view('error', array('error'=>'Preview does not exist!'), 404); @@ -83,16 +78,14 @@ class DocumentController extends BaseController return $response; } - public function getVFSJS($publicId, $name){ - $document = Document::scope($publicId) - ->firstOrFail(); + public function getVFSJS(DocumentRequest $request, $publicId, $name) + { + $document = $request->entity(); if(substr($name, -3)=='.js'){ $name = substr($name, 0, -3); } - $this->authorize('view', $document); - if(!$document->isPDFEmbeddable()){ return Response::view('error', array('error'=>'Image does not exist!'), 404); } @@ -106,14 +99,12 @@ class DocumentController extends BaseController return $response; } - public function postUpload() + public function postUpload(CreateDocumentRequest $request) { if (!Utils::hasFeature(FEATURE_DOCUMENTS)) { return; } - $this->authorizeCreate(); - $result = $this->documentRepo->upload(Input::all()['file'], $doc_array); if(is_string($result)){ diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 53d2a2548e39..fab8aa3bfd3f 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -18,8 +18,8 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\InvoiceTransformer; -use App\Http\Requests\CreateInvoiceRequest; -use App\Http\Requests\UpdateInvoiceRequest; +use App\Http\Requests\CreateInvoiceAPIRequest; +use App\Http\Requests\UpdateInvoiceAPIRequest; use App\Services\InvoiceService; class InvoiceApiController extends BaseAPIController @@ -139,7 +139,7 @@ class InvoiceApiController extends BaseAPIController * ) * ) */ - public function store(CreateInvoiceRequest $request) + public function store(CreateInvoiceAPIRequest $request) { $data = Input::all(); $error = null; @@ -351,7 +351,7 @@ class InvoiceApiController extends BaseAPIController * ) * ) */ - public function update(UpdateInvoiceRequest $request, $publicId) + public function update(UpdateInvoiceAPIRequest $request, $publicId) { if ($request->action == ACTION_ARCHIVE) { $invoice = Invoice::scope($publicId)->firstOrFail(); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index c3627a01c301..4dc60f8c7421 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -27,7 +27,10 @@ use App\Ninja\Repositories\ClientRepository; use App\Ninja\Repositories\DocumentRepository; use App\Services\InvoiceService; use App\Services\RecurringInvoiceService; -use App\Http\Requests\SaveInvoiceWithClientRequest; + +use App\Http\Requests\InvoiceRequest; +use App\Http\Requests\CreateInvoiceRequest; +use App\Http\Requests\UpdateInvoiceRequest; class InvoiceController extends BaseController { @@ -88,18 +91,13 @@ class InvoiceController extends BaseController return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_INVOICE, $search); } - public function edit($publicId, $clone = false) + public function edit(InvoiceRequest $request, $publicId, $clone = false) { $account = Auth::user()->account; - $invoice = Invoice::scope($publicId) - ->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments') - ->withTrashed() - ->firstOrFail(); - - $this->authorize('edit', $invoice); + $invoice = $request->entity()->load('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments'); $entityType = $invoice->getEntityType(); - + $contactIds = DB::table('invitations') ->join('contacts', 'contacts.id', '=', 'invitations.contact_id') ->where('invitations.invoice_id', '=', $invoice->id) @@ -120,7 +118,7 @@ class InvoiceController extends BaseController } else { Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType()); $method = 'PUT'; - $url = "{$entityType}s/{$publicId}"; + $url = "{$entityType}s/{$invoice->public_id}"; $clients->whereId($invoice->client_id); } @@ -229,28 +227,27 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function create($clientPublicId = 0, $isRecurring = false) + public function create(InvoiceRequest $request, $clientPublicId = 0, $isRecurring = false) { - $this->authorizeCreate(); - $account = Auth::user()->account; + $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; $clientId = null; - if ($clientPublicId) { - $clientId = Client::getPrivateId($clientPublicId); + if ($request->client_id) { + $clientId = Client::getPrivateId($request->client_id); } $invoice = $account->createInvoice($entityType, $clientId); $invoice->public_id = 0; - if(Session::get('expenses')){ + if (Session::get('expenses')) { $invoice->expenses = Expense::scope(Session::get('expenses'))->with('documents')->get(); } $clients = Client::scope()->with('contacts', 'country')->orderBy('name'); - if(!Auth::user()->hasPermission('view_all')){ + if (!Auth::user()->hasPermission('view_all')) { $clients = $clients->where('clients.user_id', '=', Auth::user()->id); } @@ -267,9 +264,9 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function createRecurring($clientPublicId = 0) + public function createRecurring(ClientRequest $request, $clientPublicId = 0) { - return self::create($clientPublicId, true); + return self::create($request, $clientPublicId, true); } private static function getViewModel($invoice) @@ -395,7 +392,7 @@ class InvoiceController extends BaseController * * @return Response */ - public function store(SaveInvoiceWithClientRequest $request) + public function store(CreateInvoiceRequest $request) { $data = $request->input(); $data['documents'] = $request->file('documents'); @@ -434,7 +431,7 @@ class InvoiceController extends BaseController * @param int $id * @return Response */ - public function update(SaveInvoiceWithClientRequest $request) + public function update(UpdateInvoiceRequest $request) { $data = $request->input(); $data['documents'] = $request->file('documents'); @@ -521,7 +518,7 @@ class InvoiceController extends BaseController { Session::reflash(); - return Redirect::to("invoices/{$publicId}/edit"); + return Redirect::to("invoices/$publicId/edit"); } /** @@ -558,9 +555,9 @@ class InvoiceController extends BaseController return Redirect::to('invoices/'.$clone->public_id); } - public function cloneInvoice($publicId) + public function cloneInvoice(InvoiceRequest $request, $publicId) { - return self::edit($publicId, true); + return self::edit($request, $publicId, true); } public function invoiceHistory($publicId) diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 3a0062296098..a8ea0beaa476 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -26,6 +26,7 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\ClientRepository; use App\Events\QuoteInvitationWasApproved; use App\Services\InvoiceService; +use App\Http\Requests\InvoiceRequest; class QuoteController extends BaseController { @@ -78,10 +79,8 @@ class QuoteController extends BaseController return $this->invoiceService->getDatatable($accountId, $clientPublicId, ENTITY_QUOTE, $search); } - public function create($clientPublicId = 0) + public function create(InvoiceRequest $request, $clientPublicId = 0) { - $this->authorizeCreate(); - if (!Utils::hasFeature(FEATURE_QUOTES)) { return Redirect::to('/invoices/create'); } diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index e78462cd51ac..229a4751116e 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -75,11 +75,11 @@ class TaskController extends BaseController return $this->save(); } - public function show(TaskRequest $request) + public function show($publicId) { Session::reflash(); - return Redirect::to("tasks/{$request->task_id}/edit"); + return Redirect::to("tasks/{$publicId}/edit"); } /** diff --git a/app/Http/Requests/CreateDocumentRequest.php b/app/Http/Requests/CreateDocumentRequest.php new file mode 100644 index 000000000000..33330a90895e --- /dev/null +++ b/app/Http/Requests/CreateDocumentRequest.php @@ -0,0 +1,26 @@ +user()->can('create', ENTITY_DOCUMENT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + + ]; + } +} diff --git a/app/Http/Requests/CreateInvoiceAPIRequest.php b/app/Http/Requests/CreateInvoiceAPIRequest.php new file mode 100644 index 000000000000..141d8788abc3 --- /dev/null +++ b/app/Http/Requests/CreateInvoiceAPIRequest.php @@ -0,0 +1,32 @@ +user()->can('create', ENTITY_INVOICE); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $rules = [ + 'email' => 'required_without:client_id', + 'client_id' => 'required_without:email', + 'invoice_items' => 'valid_invoice_items', + 'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id, + 'discount' => 'positive', + ]; + + return $rules; + } +} diff --git a/app/Http/Requests/CreateInvoiceRequest.php b/app/Http/Requests/CreateInvoiceRequest.php index 8531a4074879..a3f556d408e9 100644 --- a/app/Http/Requests/CreateInvoiceRequest.php +++ b/app/Http/Requests/CreateInvoiceRequest.php @@ -1,11 +1,6 @@ user()->can('create', ENTITY_INVOICE); } /** @@ -25,13 +20,18 @@ class CreateInvoiceRequest extends Request public function rules() { $rules = [ - 'email' => 'required_without:client_id', - 'client_id' => 'required_without:email', + 'client.contacts' => 'valid_contacts', 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,'.Auth::user()->account_id, + 'invoice_number' => 'required|unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id, 'discount' => 'positive', ]; + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + return $rules; } } diff --git a/app/Http/Requests/DocumentRequest.php b/app/Http/Requests/DocumentRequest.php new file mode 100644 index 000000000000..a9d4396bc569 --- /dev/null +++ b/app/Http/Requests/DocumentRequest.php @@ -0,0 +1,7 @@ +entityType); - $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + + if (method_exists($class, 'withTrashed')) { + $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + } else { + $this->entity = $class::scope($publicId)->firstOrFail(); + } return $this->entity; } diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php new file mode 100644 index 000000000000..8e3591d88de7 --- /dev/null +++ b/app/Http/Requests/InvoiceRequest.php @@ -0,0 +1,20 @@ +documents)) { + $expense->load('documents'); + } + + return $expense; + } + */ +} \ No newline at end of file diff --git a/app/Http/Requests/SaveInvoiceWithClientRequest.php b/app/Http/Requests/SaveInvoiceWithClientRequest.php deleted file mode 100644 index a75aee39c8c3..000000000000 --- a/app/Http/Requests/SaveInvoiceWithClientRequest.php +++ /dev/null @@ -1,45 +0,0 @@ - 'valid_contacts', - 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, - 'discount' => 'positive', - ]; - - /* There's a problem parsing the dates - if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { - $rules['end_date'] = 'after' . Request::get('start_date'); - } - */ - - return $rules; - } -} diff --git a/app/Http/Requests/UpdateInvoiceAPIRequest.php b/app/Http/Requests/UpdateInvoiceAPIRequest.php new file mode 100644 index 000000000000..6fa3c4e92e45 --- /dev/null +++ b/app/Http/Requests/UpdateInvoiceAPIRequest.php @@ -0,0 +1,36 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + if ($this->action == ACTION_ARCHIVE) { + return []; + } + + $invoiceId = $this->entity()->id; + + $rules = [ + 'invoice_items' => 'valid_invoice_items', + 'invoice_number' => 'unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id, + 'discount' => 'positive', + ]; + + return $rules; + } +} diff --git a/app/Http/Requests/UpdateInvoiceRequest.php b/app/Http/Requests/UpdateInvoiceRequest.php index 7827e2b1d1e1..96d112b157c2 100644 --- a/app/Http/Requests/UpdateInvoiceRequest.php +++ b/app/Http/Requests/UpdateInvoiceRequest.php @@ -1,11 +1,6 @@ user()->can('edit', $this->entity()); } /** @@ -24,19 +19,21 @@ class UpdateInvoiceRequest extends Request */ public function rules() { - if ($this->action == ACTION_ARCHIVE) { - return []; - } - - $publicId = $this->route('invoices'); - $invoiceId = Invoice::getPrivateId($publicId); - + $invoiceId = $this->entity()->id; + $rules = [ + 'client.contacts' => 'valid_contacts', 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, + 'invoice_number' => 'required|unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id, 'discount' => 'positive', ]; + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + return $rules; } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 702cd1fcb98f..768c9c3d3d4c 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -48,8 +48,8 @@ Route::group(['middleware' => 'auth:client'], function() { Route::get('client/documents', 'PublicClientController@documentIndex'); Route::get('client/payments', 'PublicClientController@paymentIndex'); Route::get('client/dashboard', 'PublicClientController@dashboard'); - Route::get('client/document/js/{public_id}/{filename}', 'PublicClientController@getDocumentVFSJS'); - Route::get('client/document/{invitation_key}/{public_id}/{filename?}', 'PublicClientController@getDocument'); + Route::get('client/documents/js/{documents}/{filename}', 'PublicClientController@getDocumentVFSJS'); + Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'PublicClientController@getDocument'); Route::get('client/documents/{invitation_key}/{filename?}', 'PublicClientController@getInvoiceDocumentsZip'); Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable')); @@ -134,20 +134,20 @@ Route::group(['middleware' => 'auth:user'], function() { Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring'); Route::get('recurring_invoices', 'RecurringInvoiceController@index'); - Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice'); + Route::get('invoices/{invoices}/clone', 'InvoiceController@cloneInvoice'); Route::post('invoices/bulk', 'InvoiceController@bulk'); Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); - Route::get('document/{public_id}/{filename?}', 'DocumentController@get'); - Route::get('document/js/{public_id}/{filename}', 'DocumentController@getVFSJS'); - Route::get('document/preview/{public_id}/{filename?}', 'DocumentController@getPreview'); + Route::get('documents/{documents}/{filename?}', 'DocumentController@get'); + Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS'); + Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview'); Route::post('document', 'DocumentController@postUpload'); Route::get('quotes/create/{client_id?}', 'QuoteController@create'); - Route::get('quotes/{public_id}/clone', 'InvoiceController@cloneInvoice'); - Route::get('quotes/{public_id}/edit', 'InvoiceController@edit'); - Route::put('quotes/{public_id}', 'InvoiceController@update'); - Route::get('quotes/{public_id}', 'InvoiceController@edit'); + Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice'); + Route::get('quotes/{invoices}/edit', 'InvoiceController@edit'); + Route::put('quotes/{invoices}', 'InvoiceController@update'); + Route::get('quotes/{invoices}', 'InvoiceController@edit'); Route::post('quotes', 'InvoiceController@store'); Route::get('quotes', 'QuoteController@index'); Route::get('api/quotes/{client_id?}', array('as'=>'api.quotes', 'uses'=>'QuoteController@getDatatable')); diff --git a/app/Models/Document.php b/app/Models/Document.php index 0d7fc049aac8..6d9c24857143 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -173,11 +173,11 @@ class Document extends EntityModel } public function getUrl(){ - return url('document/'.$this->public_id.'/'.$this->name); + return url('documents/'.$this->public_id.'/'.$this->name); } public function getClientUrl($invitation){ - return url('client/document/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); + return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); } public function isPDFEmbeddable(){ @@ -186,16 +186,16 @@ class Document extends EntityModel public function getVFSJSUrl(){ if(!$this->isPDFEmbeddable())return null; - return url('document/js/'.$this->public_id.'/'.$this->name.'.js'); + return url('documents/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getClientVFSJSUrl(){ if(!$this->isPDFEmbeddable())return null; - return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js'); + return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getPreviewUrl(){ - return $this->preview?url('document/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; + return $this->preview?url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; } public function toArray() diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index f33837051e97..094b4848ebe0 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -211,7 +211,7 @@ class DocumentRepository extends BaseRepository }) ->addColumn('name', function ($model) { return link_to( - '/client/document/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name, + '/client/documents/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name, $model->name, ['target'=>'_blank'] )->toHtml(); From 7f168f87b930ed07b5408cd2aa1e650d4ca37005 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 22:33:01 +0300 Subject: [PATCH 166/258] Working on form requests --- app/Http/Controllers/InvoiceController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 4dc60f8c7421..c57966138125 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -264,7 +264,7 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function createRecurring(ClientRequest $request, $clientPublicId = 0) + public function createRecurring(InvoiceRequest $request, $clientPublicId = 0) { return self::create($request, $clientPublicId, true); } From e7296cf5137be361ef100e43a341a7bd67e3d0d9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 22:36:12 +0300 Subject: [PATCH 167/258] Working on form requests --- app/Http/Controllers/InvoiceController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index c57966138125..f25fb90da4cd 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -447,9 +447,9 @@ class InvoiceController extends BaseController Session::flash('message', $message); if ($action == 'clone') { - return $this->cloneInvoice($invoice->public_id); + return $this->cloneInvoice($request, $invoice->public_id); } elseif ($action == 'convert') { - return $this->convertQuote($invoice->public_id); + return $this->convertQuote($request, $invoice->public_id); } elseif ($action == 'email') { return $this->emailInvoice($invoice, Input::get('pdfupload')); } @@ -546,7 +546,7 @@ class InvoiceController extends BaseController } } - public function convertQuote($publicId) + public function convertQuote(InvoiceRequest $request, $publicId) { $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); $clone = $this->invoiceService->convertQuote($invoice); From 4db5a19885034dcec1096f9eb9540a609c47a548 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 23:55:13 +0300 Subject: [PATCH 168/258] Refactored API index methods --- app/Http/Controllers/BaseAPIController.php | 34 +++- app/Http/Controllers/ClientApiController.php | 18 +- app/Http/Controllers/ExpenseApiController.php | 15 +- app/Http/Controllers/InvoiceApiController.php | 37 +--- app/Http/Controllers/InvoiceController.php | 8 +- app/Http/Controllers/PaymentApiController.php | 191 ++++++++---------- app/Http/Controllers/ProductApiController.php | 19 +- app/Http/Controllers/TaskApiController.php | 25 +-- app/Http/Controllers/TaxRateApiController.php | 16 +- app/Http/Controllers/UserApiController.php | 17 +- app/Http/Controllers/VendorApiController.php | 13 +- app/Http/Requests/InvoiceRequest.php | 11 +- app/Models/EntityModel.php | 10 + 13 files changed, 186 insertions(+), 228 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 3a3a1e0bece9..12b859e1f357 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -2,6 +2,8 @@ use Session; use Utils; +use Auth; +use Input; use Response; use Request; use League\Fractal; @@ -9,8 +11,10 @@ use League\Fractal\Manager; use League\Fractal\Resource\Item; use League\Fractal\Resource\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use App\Models\EntityModel; use App\Ninja\Serializers\ArraySerializer; use League\Fractal\Serializer\JsonApiSerializer; +use Illuminate\Pagination\LengthAwarePaginator; /** * @SWG\Swagger( @@ -64,6 +68,23 @@ class BaseAPIController extends Controller } } + protected function returnList($query) + { + if ($clientPublicId = Input::get('client_id')) { + $filter = function($query) use ($clientPublicId) { + $query->where('public_id', '=', $clientPublicId); + }; + $query->whereHas('client', $filter); + } + + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $data = $this->createCollection($query, $transformer, $this->entityType); + + return $this->response($data); + } + protected function createItem($data, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { @@ -74,18 +95,19 @@ class BaseAPIController extends Controller return $this->manager->createData($resource)->toArray(); } - protected function createCollection($data, $transformer, $entityType, $paginator = false) + protected function createCollection($query, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { $entityType = null; } - $resource = new Collection($data, $transformer, $entityType); - - if ($paginator) { - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + if ($query instanceof LengthAwarePaginator) { + $resource = new Collection($query, $transformer, $entityType); + } else { + $resource = new Collection($query->get(), $transformer, $entityType); + $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate())); } - + return $this->manager->createData($resource)->toArray(); } diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index fd3b33df6aa5..51cd821f3f6b 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -18,6 +18,8 @@ class ClientApiController extends BaseAPIController protected $clientRepo; protected $clientService; + protected $entityType = ENTITY_CLIENT; + public function __construct(ClientRepository $clientRepo, ClientService $clientService) { parent::__construct(); @@ -53,26 +55,18 @@ class ClientApiController extends BaseAPIController { $clients = Client::scope() ->with($this->getIncluded()) - ->orderBy('created_at', 'desc')->withTrashed(); + ->orderBy('created_at', 'desc') + ->withTrashed(); // Filter by email if (Input::has('email')) { - $email = Input::get('email'); $clients = $clients->whereHas('contacts', function ($query) use ($email) { $query->where('email', $email); }); - } - - $clients = $clients->paginate(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Client::scope()->withTrashed()->paginate(); - - $data = $this->createCollection($clients, $transformer, ENTITY_CLIENT, $paginator); - - return $this->response($data); + + return $this->returnList($clients); } /** diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 88ff5497cefd..9b1a7e906244 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -1,5 +1,5 @@ withTrashed() ->orderBy('created_at','desc'); - $expenses = $expenses->paginate(); - - $transformer = new ExpenseTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Expense::scope()->withTrashed()->paginate(); - - $data = $this->createCollection($expenses, $transformer, ENTITY_EXPENSE, $paginator); - - return $this->response($data); - + return $this->returnList($expenses); } public function update() diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index fab8aa3bfd3f..ce5eaa1d72d6 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -26,6 +26,8 @@ class InvoiceApiController extends BaseAPIController { protected $invoiceRepo; + protected $entityType = ENTITY_INVOICE; + public function __construct(InvoiceService $invoiceService, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, PaymentRepository $paymentRepo, Mailer $mailer) { parent::__construct(); @@ -55,36 +57,12 @@ class InvoiceApiController extends BaseAPIController */ public function index() { - $paginator = Invoice::scope()->withTrashed(); - $invoices = Invoice::scope()->withTrashed() - ->with(array_merge(['invoice_items'], $this->getIncluded())); + $invoices = Invoice::scope() + ->withTrashed() + ->with(array_merge(['invoice_items'], $this->getIncluded())) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $invoices->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $invoices = $invoices->orderBy('created_at', 'desc')->paginate(); - - /* - // Add the first invitation link to the data - foreach ($invoices as $key => $invoice) { - foreach ($invoice->invitations as $subKey => $invitation) { - $invoices[$key]['link'] = $invitation->getLink(); - } - unset($invoice['invitations']); - } - */ - - $transformer = new InvoiceTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = $paginator->paginate(); - - $data = $this->createCollection($invoices, $transformer, 'invoices', $paginator); - - return $this->response($data); + return $this->returnList($invoices); } /** @@ -106,7 +84,6 @@ class InvoiceApiController extends BaseAPIController public function show($publicId) { - $invoice = Invoice::scope($publicId)->withTrashed()->first(); if(!$invoice) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index f25fb90da4cd..1a652ae8962c 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -546,13 +546,13 @@ class InvoiceController extends BaseController } } - public function convertQuote(InvoiceRequest $request, $publicId) + public function convertQuote(InvoiceRequest $request) { - $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); - $clone = $this->invoiceService->convertQuote($invoice); + $clone = $this->invoiceService->convertQuote($request->entity()); Session::flash('message', trans('texts.converted_to_invoice')); - return Redirect::to('invoices/'.$clone->public_id); + + return Redirect::to('invoices/' . $clone->public_id); } public function cloneInvoice(InvoiceRequest $request, $publicId) diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 7022f0c3e840..a7cab011fc36 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -17,13 +17,14 @@ class PaymentApiController extends BaseAPIController { protected $paymentRepo; + protected $entityType = ENTITY_PAYMENT; + public function __construct(PaymentRepository $paymentRepo, ContactMailer $contactMailer) { parent::__construct(); $this->paymentRepo = $paymentRepo; $this->contactMailer = $contactMailer; - } /** @@ -44,85 +45,71 @@ class PaymentApiController extends BaseAPIController */ public function index() { - $paginator = Payment::scope(); $payments = Payment::scope() - ->with('client.contacts', 'invitation', 'user', 'invoice')->withTrashed(); + ->withTrashed() + ->with(array_merge(['client.contacts', 'invitation', 'user', 'invoice'], $this->getIncluded())) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $payments->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $payments = $payments->orderBy('created_at', 'desc')->paginate(); - $paginator = $paginator->paginate(); - - $transformer = new PaymentTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createCollection($payments, $transformer, 'payments', $paginator); - - return $this->response($data); + return $this->returnList($payments); } + /** + * @SWG\Put( + * path="/payments/{payment_id", + * summary="Update a payment", + * tags={"payment"}, + * @SWG\Parameter( + * in="body", + * name="body", + * @SWG\Schema(ref="#/definitions/Payment") + * ), + * @SWG\Response( + * response=200, + * description="Update payment", + * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) + * ), + * @SWG\Response( + * response="default", + * description="an ""unexpected"" error" + * ) + * ) + */ - /** - * @SWG\Put( - * path="/payments/{payment_id", - * summary="Update a payment", - * tags={"payment"}, - * @SWG\Parameter( - * in="body", - * name="body", - * @SWG\Schema(ref="#/definitions/Payment") - * ), - * @SWG\Response( - * response=200, - * description="Update payment", - * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ + public function update(Request $request, $publicId) + { + $data = Input::all(); + $data['public_id'] = $publicId; + $error = false; - public function update(Request $request, $publicId) - { - $data = Input::all(); - $data['public_id'] = $publicId; - $error = false; - - if ($request->action == ACTION_ARCHIVE) { - $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); - $this->paymentRepo->archive($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - } - - $payment = $this->paymentRepo->save($data); - - if ($error) { - return $error; - } - - /* - $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->withTrashed()->first(); - */ + if ($request->action == ACTION_ARCHIVE) { + $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); + $this->paymentRepo->archive($payment); $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); $data = $this->createItem($payment, $transformer, 'invoice'); return $this->response($data); - } + $payment = $this->paymentRepo->save($data); + + if ($error) { + return $error; + } + + /* + $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { + $query->withTrashed(); + }])->withTrashed()->first(); + */ + + $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); + $data = $this->createItem($payment, $transformer, 'invoice'); + + return $this->response($data); + + } + /** * @SWG\Post( @@ -190,44 +177,44 @@ class PaymentApiController extends BaseAPIController } - /** - * @SWG\Delete( - * path="/payments/{payment_id}", - * summary="Delete a payment", - * tags={"payment"}, - * @SWG\Parameter( - * in="body", - * name="body", - * @SWG\Schema(ref="#/definitions/Payment") - * ), - * @SWG\Response( - * response=200, - * description="Delete payment", - * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ + /** + * @SWG\Delete( + * path="/payments/{payment_id}", + * summary="Delete a payment", + * tags={"payment"}, + * @SWG\Parameter( + * in="body", + * name="body", + * @SWG\Schema(ref="#/definitions/Payment") + * ), + * @SWG\Response( + * response=200, + * description="Delete payment", + * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) + * ), + * @SWG\Response( + * response="default", + * description="an ""unexpected"" error" + * ) + * ) + */ - public function destroy($publicId) - { + public function destroy($publicId) + { - $payment = Payment::scope($publicId)->withTrashed()->first(); - $invoiceId = $payment->invoice->public_id; + $payment = Payment::scope($publicId)->withTrashed()->first(); + $invoiceId = $payment->invoice->public_id; - $this->paymentRepo->delete($payment); + $this->paymentRepo->delete($payment); - /* - $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); + /* + $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { + $query->withTrashed(); + }])->first(); + */ + $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); + $data = $this->createItem($payment, $transformer, 'invoice'); - return $this->response($data); - } + return $this->response($data); + } } diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 87bf89403b8a..26736f5ef5c2 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -20,8 +20,9 @@ use App\Services\ProductService; class ProductApiController extends BaseAPIController { protected $productService; - - protected $productRepo; + protected $productRepo; + + protected $entityType = ENTITY_PRODUCT; public function __construct(ProductService $productService, ProductRepository $productRepo) { @@ -33,17 +34,11 @@ class ProductApiController extends BaseAPIController public function index() { + $products = Product::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); - $products = Product::scope()->withTrashed(); - $products = $products->paginate(); - - $paginator = Product::scope()->withTrashed()->paginate(); - - $transformer = new ProductTransformer(\Auth::user()->account, $this->serializer); - $data = $this->createCollection($products, $transformer, 'products', $paginator); - - return $this->response($data); - + return $this->returnList($products); } public function getDatatable() diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index 926ed2f98ad3..cb39e0a1f369 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -13,6 +13,8 @@ class TaskApiController extends BaseAPIController { protected $taskRepo; + protected $entityType = ENTITY_TASK; + public function __construct(TaskRepository $taskRepo) { parent::__construct(); @@ -38,25 +40,12 @@ class TaskApiController extends BaseAPIController */ public function index() { - $paginator = Task::scope(); - $tasks = Task::scope() - ->with($this->getIncluded()); + $payments = Task::scope() + ->withTrashed() + ->with($this->getIncluded()) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $tasks->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $tasks = $tasks->orderBy('created_at', 'desc')->paginate(); - $paginator = $paginator->paginate(); - $transformer = new TaskTransformer(\Auth::user()->account, Input::get('serializer')); - - $data = $this->createCollection($tasks, $transformer, 'tasks', $paginator); - - return $this->response($data); + return $this->returnList($payments); } /** diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 7cacfcf8311b..ddc0ab4aa968 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -14,6 +14,8 @@ class TaxRateApiController extends BaseAPIController protected $taxRateService; protected $taxRateRepo; + protected $entityType = ENTITY_TAX_RATE; + public function __construct(TaxRateService $taxRateService, TaxRateRepository $taxRateRepo) { parent::__construct(); @@ -24,15 +26,11 @@ class TaxRateApiController extends BaseAPIController public function index() { - $taxRates = TaxRate::scope()->withTrashed(); - $taxRates = $taxRates->paginate(); - - $paginator = TaxRate::scope()->withTrashed()->paginate(); - - $transformer = new TaxRateTransformer(Auth::user()->account, $this->serializer); - $data = $this->createCollection($taxRates, $transformer, 'tax_rates', $paginator); - - return $this->response($data); + $taxRates = TaxRate::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); + + return $this->returnList($taxRates); } public function store(CreateTaxRateRequest $request) diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index fcd48787b134..8fda74b33cbd 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -14,6 +14,8 @@ class UserApiController extends BaseAPIController protected $userService; protected $userRepo; + protected $entityType = ENTITY_USER; + public function __construct(UserService $userService, UserRepository $userRepo) { parent::__construct(); @@ -24,16 +26,11 @@ class UserApiController extends BaseAPIController public function index() { - $user = Auth::user(); - $users = User::whereAccountId($user->account_id)->withTrashed(); - $users = $users->paginate(); - - $paginator = User::whereAccountId($user->account_id)->withTrashed()->paginate(); - - $transformer = new UserTransformer(Auth::user()->account, $this->serializer); - $data = $this->createCollection($users, $transformer, 'users', $paginator); - - return $this->response($data); + $users = User::whereAccountId(Auth::user()->account_id) + ->withTrashed() + ->orderBy('created_at', 'desc'); + + return $this->returnList($users); } /* diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 4c32ee1eb3c5..b2487dfda2d3 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -14,6 +14,8 @@ class VendorApiController extends BaseAPIController { protected $vendorRepo; + protected $entityType = ENTITY_VENDOR; + public function __construct(VendorRepository $vendorRepo) { parent::__construct(); @@ -46,17 +48,12 @@ class VendorApiController extends BaseAPIController */ public function index() { - $vendors = Vendor::scope() + $vendors = Vendor::scope() ->with($this->getIncluded()) ->withTrashed() - ->orderBy('created_at', 'desc') - ->paginate(); + ->orderBy('created_at', 'desc'); - $transformer = new VendorTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Vendor::scope()->paginate(); - $data = $this->createCollection($vendors, $transformer, ENTITY_VENDOR, $paginator); - - return $this->response($data); + return $this->returnList($vendors); } /** diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php index 8e3591d88de7..bf24c38839a9 100644 --- a/app/Http/Requests/InvoiceRequest.php +++ b/app/Http/Requests/InvoiceRequest.php @@ -4,17 +4,16 @@ class InvoiceRequest extends EntityRequest { protected $entityType = ENTITY_INVOICE; - /* public function entity() { - $expense = parent::entity(); + $invoice = parent::entity(); // eager load the contacts - if ($expense && ! count($expense->documents)) { - $expense->load('documents'); + if ($invoice && ! count($invoice->invoice_items)) { + $invoice->load('invoice_items'); } - return $expense; + return $invoice; } - */ + } \ No newline at end of file diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index 8d0da39d3fab..c2c9110fe306 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -101,6 +101,16 @@ class EntityModel extends Eloquent return $this->getName(); } + public static function getClassName($entityType) + { + return 'App\\Models\\' . ucwords(Utils::toCamelCase($entityType)); + } + + public static function getTransformerName($entityType) + { + return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer'; + } + public function setNullValues() { foreach ($this->fillable as $field) { From 1b92f6648247235e359b0f3c4f57c23f55c03a99 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 09:33:48 +0300 Subject: [PATCH 169/258] Adding permissions to the API --- app/Http/Controllers/BaseAPIController.php | 16 +- app/Http/Controllers/ClientApiController.php | 8 +- .../_generated/AcceptanceTesterActions.php | 2 +- .../_generated/FunctionalTesterActions.php | 693 ++++++++++++++---- 4 files changed, 581 insertions(+), 138 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 12b859e1f357..4d03dea14b1d 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -70,6 +70,7 @@ class BaseAPIController extends Controller protected function returnList($query) { + //\DB::enableQueryLog(); if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { $query->where('public_id', '=', $clientPublicId); @@ -77,11 +78,20 @@ class BaseAPIController extends Controller $query->whereHas('client', $filter); } + if ( ! Utils::hasPermission('view_all')){ + if ($this->entityType == ENTITY_USER) { + $query->where('id', '=', Auth::user()->id); + } else { + $query->where('user_id', '=', Auth::user()->id); + } + } + $transformerClass = EntityModel::getTransformerName($this->entityType); $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); $data = $this->createCollection($query, $transformer, $this->entityType); + //return \DB::getQueryLog(); return $this->response($data); } @@ -101,11 +111,11 @@ class BaseAPIController extends Controller $entityType = null; } - if ($query instanceof LengthAwarePaginator) { - $resource = new Collection($query, $transformer, $entityType); - } else { + if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { $resource = new Collection($query->get(), $transformer, $entityType); $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate())); + } else { + $resource = new Collection($query, $transformer, $entityType); } return $this->manager->createData($resource)->toArray(); diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 51cd821f3f6b..4457973c7031 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -59,8 +59,7 @@ class ClientApiController extends BaseAPIController ->withTrashed(); // Filter by email - if (Input::has('email')) { - $email = Input::get('email'); + if ($email = Input::get('email')) { $clients = $clients->whereHas('contacts', function ($query) use ($email) { $query->where('email', $email); }); @@ -200,7 +199,6 @@ class ClientApiController extends BaseAPIController public function destroy($publicId) { - $client = Client::scope($publicId)->withTrashed()->first(); $this->clientRepo->delete($client); @@ -213,8 +211,6 @@ class ClientApiController extends BaseAPIController $data = $this->createItem($client, $transformer, ENTITY_CLIENT); return $this->response($data); - } - -} +} \ No newline at end of file diff --git a/tests/_support/_generated/AcceptanceTesterActions.php b/tests/_support/_generated/AcceptanceTesterActions.php index 82427d1e2a5e..d219111f887c 100644 --- a/tests/_support/_generated/AcceptanceTesterActions.php +++ b/tests/_support/_generated/AcceptanceTesterActions.php @@ -1,4 +1,4 @@ -amOnPage('/'); * // opens /register page * $I->amOnPage('/register'); - * ?> * ``` * * @param $page @@ -217,16 +216,31 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. + * Checks that the current page contains the given string (case insensitive). + * + * You can specify a specific HTML element (via CSS or XPath) as the second + * parameter to only search within that element. * * ``` php * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> + * $I->see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page + * $I->see('Sign Up', '//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->see('strong')` will return true for strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will *not* be true for strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -239,16 +253,31 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. + * Checks that the current page contains the given string (case insensitive). + * + * You can specify a specific HTML element (via CSS or XPath) as the second + * parameter to only search within that element. * * ``` php * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> + * $I->see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page + * $I->see('Sign Up', '//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->see('strong')` will return true for strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will *not* be true for strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -262,16 +291,29 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page doesn't contain the text specified. + * Checks that the current page doesn't contain the text specified (case insensitive). * Give a locator as the second parameter to match a specific region. * * ```php * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> + * $I->dontSee('Login'); // I can suppose user is already logged in + * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page + * $I->dontSee('Sign Up','//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->dontSee('strong')` will fail on strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will ignore strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -284,16 +326,29 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page doesn't contain the text specified. + * Checks that the current page doesn't contain the text specified (case insensitive). * Give a locator as the second parameter to match a specific region. * * ```php * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> + * $I->dontSee('Login'); // I can suppose user is already logged in + * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page + * $I->dontSee('Sign Up','//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->dontSee('strong')` will fail on strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will ignore strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -304,6 +359,80 @@ trait FunctionalTesterActions } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ``` php + * seeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Lib\InnerBrowser::seeInSource() + */ + public function canSeeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInSource', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ``` php + * seeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * @see \Codeception\Lib\InnerBrowser::seeInSource() + */ + public function seeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInSource', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ```php + * dontSeeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() + */ + public function cantSeeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInSource', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ```php + * dontSeeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() + */ + public function dontSeeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInSource', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -645,7 +774,6 @@ trait FunctionalTesterActions * * @param null $uri * - * @internal param $url * @return mixed * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() */ @@ -1074,15 +1202,28 @@ trait FunctionalTesterActions * [!] Method is generated. Documentation taken from corresponding module. * * Submits the given form on the page, optionally with the given form - * values. Give the form fields values as an array. + * values. Pass the form field's values as an array in the second + * parameter. * - * Skipped fields will be filled by their values from the page. + * Although this function can be used as a short-hand version of + * `fillField()`, `selectOption()`, `click()` etc. it has some important + * differences: + * + * * Only field *names* may be used, not CSS/XPath selectors nor field labels + * * If a field is sent to this function that does *not* exist on the page, + * it will silently be added to the HTTP request. This is helpful for testing + * some types of forms, but be aware that you will *not* get an exception + * like you would if you called `fillField()` or `selectOption()` with + * a missing field. + * + * Fields that are not provided will be filled by their values from the page, + * or from any previous calls to `fillField()`, `selectOption()` etc. * You don't need to click the 'Submit' button afterwards. * This command itself triggers the request to form's action. * - * You can optionally specify what button's value to include - * in the request with the last parameter as an alternative to - * explicitly setting its value in the second parameter, as + * You can optionally specify which button's value to include + * in the request with the last parameter (as an alternative to + * explicitly setting its value in the second parameter), as * button values are not otherwise included in the request. * * Examples: @@ -1156,7 +1297,8 @@ trait FunctionalTesterActions * ); * ``` * - * Pair this with seeInFormFields for quick testing magic. + * This function works well when paired with `seeInFormFields()` + * for quickly testing CRUD interfaces and form validation logic. * * ``` php * true, * // ... * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); + * $I->submitForm('#my-form', $form, 'submitButton'); * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> + * $I->seeInFormFields('#my-form', $form); * ``` * * Parameter values can be set to arrays for multiple input fields * of the same name, or multi-select combo boxes. For checkboxes, - * either the string value can be used, or boolean values which will + * you can use either the string value or boolean `true`/`false` which will * be replaced by the checkbox's value in the DOM. * * ``` php @@ -1183,7 +1324,7 @@ trait FunctionalTesterActions * 'field1' => 'value', * 'checkbox' => [ * 'value of first checkbox', - * 'value of second checkbox, + * 'value of second checkbox', * ], * 'otherCheckboxes' => [ * true, @@ -1195,27 +1336,29 @@ trait FunctionalTesterActions * 'second option value' * ] * ]); - * ?> * ``` * * Mixing string and boolean values for a checkbox's value is not supported * and may produce unexpected results. * - * Field names ending in "[]" must be passed without the trailing square + * Field names ending in `[]` must be passed without the trailing square * bracket characters, and must contain an array for its value. This allows * submitting multiple values with the same name, consider: * * ```php + * submitForm('#my-form', [ * 'field[]' => 'value', - * 'field[]' => 'another value', // 'field[]' is already a defined key + * 'field[]' => 'another value', // 'field[]' is already a defined key * ]); * ``` * * The solution is to pass an array value: * * ```php - * // this way both values are submitted + * submitForm('#my-form', [ * 'field' => [ * 'value', @@ -1454,7 +1597,7 @@ trait FunctionalTesterActions * * @param $cssOrXpath * @param $attribute - * @internal param $element + * * @return mixed * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() */ @@ -1466,7 +1609,28 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * + * Grabs either the text content, or attribute values, of nodes + * matched by $cssOrXpath and returns them as an array. + * + * ```html + * First + * Second + * Third + * ``` + * + * ```php + * grabMultiple('a'); + * + * // would return ['#first', '#second', '#third'] + * $aLinks = $I->grabMultiple('a', 'href'); + * ?> + * ``` + * + * @param $cssOrXpath + * @param $attribute + * @return string[] * @see \Codeception\Lib\InnerBrowser::grabMultiple() */ public function grabMultiple($cssOrXpath, $attribute = null) { @@ -1491,7 +1655,7 @@ trait FunctionalTesterActions * [!] Method is generated. Documentation taken from corresponding module. * * Sets a cookie with the given name and value. - * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument. + * You can set additional cookie params like `domain`, `path`, `expires`, `secure` in array passed as last argument. * * ``` php * + * ``` + * + * ``` php + * switchToIframe("another_frame"); + * ``` + * + * @param string $name + * @see \Codeception\Lib\InnerBrowser::switchToIframe() + */ + public function switchToIframe($name) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('switchToIframe', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Moves back in history. + * + * @param int $numberOfSteps (default value 1) + * @see \Codeception\Lib\InnerBrowser::moveBack() + */ + public function moveBack($numberOfSteps = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('moveBack', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -1996,6 +2197,40 @@ trait FunctionalTesterActions } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Enable Laravel exception handling. + * + * ``` php + * enableExceptionHandling(); + * ?> + * ``` + * @see \Codeception\Module\Laravel5::enableExceptionHandling() + */ + public function enableExceptionHandling() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('enableExceptionHandling', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Disable Laravel exception handling. + * + * ``` php + * disableExceptionHandling(); + * ?> + * ``` + * @see \Codeception\Module\Laravel5::disableExceptionHandling() + */ + public function disableExceptionHandling() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('disableExceptionHandling', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -2016,17 +2251,99 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Enable middleware for the next requests. + * Disable events for the next requests. * * ``` php * enableMiddleware(); + * $I->disableEvents(); * ?> * ``` - * @see \Codeception\Module\Laravel5::enableMiddleware() + * @see \Codeception\Module\Laravel5::disableEvents() */ - public function enableMiddleware() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('enableMiddleware', func_get_args())); + public function disableEvents() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('disableEvents', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events fired during the test. + * + * ``` php + * seeEventTriggered('App\MyEvent'); + * $I->seeEventTriggered(new App\Events\MyEvent()); + * $I->seeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::seeEventTriggered() + */ + public function canSeeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeEventTriggered', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events fired during the test. + * + * ``` php + * seeEventTriggered('App\MyEvent'); + * $I->seeEventTriggered(new App\Events\MyEvent()); + * $I->seeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * @see \Codeception\Module\Laravel5::seeEventTriggered() + */ + public function seeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeEventTriggered', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events did not fire during the test. + * + * ``` php + * dontSeeEventTriggered('App\MyEvent'); + * $I->dontSeeEventTriggered(new App\Events\MyEvent()); + * $I->dontSeeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::dontSeeEventTriggered() + */ + public function cantSeeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeEventTriggered', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events did not fire during the test. + * + * ``` php + * dontSeeEventTriggered('App\MyEvent'); + * $I->dontSeeEventTriggered(new App\Events\MyEvent()); + * $I->dontSeeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * @see \Codeception\Module\Laravel5::dontSeeEventTriggered() + */ + public function dontSeeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeEventTriggered', func_get_args())); } @@ -2050,6 +2367,41 @@ trait FunctionalTesterActions } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that current url matches route + * + * ``` php + * seeCurrentRouteIs('posts.index'); + * ?> + * ``` + * @param $routeName + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() + */ + public function canSeeCurrentRouteIs($routeName) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentRouteIs', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that current url matches route + * + * ``` php + * seeCurrentRouteIs('posts.index'); + * ?> + * ``` + * @param $routeName + * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() + */ + public function seeCurrentRouteIs($routeName) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentRouteIs', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -2070,43 +2422,6 @@ trait FunctionalTesterActions } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches route - * - * ``` php - * seeCurrentRouteIs('posts.index'); - * ?> - * ``` - * @param $route - * @param array $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() - */ - public function canSeeCurrentRouteIs($route, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentRouteIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches route - * - * ``` php - * seeCurrentRouteIs('posts.index'); - * ?> - * ``` - * @param $route - * @param array $params - * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() - */ - public function seeCurrentRouteIs($route, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentRouteIs', func_get_args())); - } - - /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -2119,11 +2434,10 @@ trait FunctionalTesterActions * ``` * * @param $action - * @param array $params * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeCurrentActionIs() */ - public function canSeeCurrentActionIs($action, $params = null) { + public function canSeeCurrentActionIs($action) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentActionIs', func_get_args())); } /** @@ -2138,10 +2452,9 @@ trait FunctionalTesterActions * ``` * * @param $action - * @param array $params * @see \Codeception\Module\Laravel5::seeCurrentActionIs() */ - public function seeCurrentActionIs($action, $params = null) { + public function seeCurrentActionIs($action) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentActionIs', func_get_args())); } @@ -2159,7 +2472,7 @@ trait FunctionalTesterActions * ``` * * @param string|array $key - * @param mixed $value + * @param mixed|null $value * @return void * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeInSession() @@ -2180,7 +2493,7 @@ trait FunctionalTesterActions * ``` * * @param string|array $key - * @param mixed $value + * @param mixed|null $value * @return void * @see \Codeception\Module\Laravel5::seeInSession() */ @@ -2270,16 +2583,53 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Assert that specific form error messages are set in the view. - * - * Useful for validation messages e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage + * Assert that there are no form errors bound to the View. * * ``` php * seeFormErrorMessages(array('username'=>'Invalid Username')); + * $I->dontSeeFormErrors(); + * ?> + * ``` + * + * @return bool + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::dontSeeFormErrors() + */ + public function cantSeeFormErrors() { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFormErrors', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Assert that there are no form errors bound to the View. + * + * ``` php + * dontSeeFormErrors(); + * ?> + * ``` + * + * @return bool + * @see \Codeception\Module\Laravel5::dontSeeFormErrors() + */ + public function dontSeeFormErrors() { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeFormErrors', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Assert that specific form error messages are set in the view. + * + * This method calls `seeFormErrorMessage` for each entry in the `$bindings` array. + * + * ``` php + * seeFormErrorMessages([ + * 'username' => 'Invalid Username', + * 'password' => null, + * ]); * ?> * ``` * @param array $bindings @@ -2294,14 +2644,14 @@ trait FunctionalTesterActions * * Assert that specific form error messages are set in the view. * - * Useful for validation messages e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage + * This method calls `seeFormErrorMessage` for each entry in the `$bindings` array. * * ``` php * seeFormErrorMessages(array('username'=>'Invalid Username')); + * $I->seeFormErrorMessages([ + * 'username' => 'Invalid Username', + * 'password' => null, + * ]); * ?> * ``` * @param array $bindings @@ -2315,48 +2665,50 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Assert that specific form error message is set in the view. + * Assert that a specific form error message is set in the view. * - * Useful for validation messages and generally messages array - * e.g. - * return `Redirect::to('register')->withErrors($validator);` + * If you want to assert that there is a form error message for a specific key + * but don't care about the actual error message you can omit `$expectedErrorMessage`. * - * Example of Usage + * If you do pass `$expectedErrorMessage`, this method checks if the actual error message for a key + * contains `$expectedErrorMessage`. * * ``` php * seeFormErrorMessage('username'); * $I->seeFormErrorMessage('username', 'Invalid Username'); * ?> * ``` * @param string $key - * @param string $errorMessage + * @param string|null $expectedErrorMessage * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeFormErrorMessage() */ - public function canSeeFormErrorMessage($key, $errorMessage) { + public function canSeeFormErrorMessage($key, $expectedErrorMessage = null) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFormErrorMessage', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * - * Assert that specific form error message is set in the view. + * Assert that a specific form error message is set in the view. * - * Useful for validation messages and generally messages array - * e.g. - * return `Redirect::to('register')->withErrors($validator);` + * If you want to assert that there is a form error message for a specific key + * but don't care about the actual error message you can omit `$expectedErrorMessage`. * - * Example of Usage + * If you do pass `$expectedErrorMessage`, this method checks if the actual error message for a key + * contains `$expectedErrorMessage`. * * ``` php * seeFormErrorMessage('username'); * $I->seeFormErrorMessage('username', 'Invalid Username'); * ?> * ``` * @param string $key - * @param string $errorMessage + * @param string|null $expectedErrorMessage * @see \Codeception\Module\Laravel5::seeFormErrorMessage() */ - public function seeFormErrorMessage($key, $errorMessage) { + public function seeFormErrorMessage($key, $expectedErrorMessage = null) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFormErrorMessage', func_get_args())); } @@ -2368,8 +2720,19 @@ trait FunctionalTesterActions * Takes either an object that implements the User interface or * an array of credentials. * + * ``` php + * amLoggedAs(['username' => 'jane@example.com', 'password' => 'password']); + * + * // provide User object + * $I->amLoggedAs( new User ); + * + * // can be verified with $I->seeAuthentication(); + * ?> + * ``` * @param \Illuminate\Contracts\Auth\User|array $user - * @param string $driver + * @param string|null $driver The authentication driver for Laravel <= 5.1.*, guard name for Laravel >= 5.2 * @return void * @see \Codeception\Module\Laravel5::amLoggedAs() */ @@ -2381,7 +2744,7 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Logs user out + * Logout user. * @see \Codeception\Module\Laravel5::logout() */ public function logout() { @@ -2392,20 +2755,24 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that user is authenticated + * Checks that a user is authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeAuthentication() */ - public function canSeeAuthentication() { + public function canSeeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeAuthentication', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that user is authenticated + * Checks that a user is authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * @see \Codeception\Module\Laravel5::seeAuthentication() */ - public function seeAuthentication() { + public function seeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeAuthentication', func_get_args())); } @@ -2413,20 +2780,24 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Check that user is not authenticated + * Check that user is not authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::dontSeeAuthentication() */ - public function cantSeeAuthentication() { + public function cantSeeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeAuthentication', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * - * Check that user is not authenticated + * Check that user is not authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * @see \Codeception\Module\Laravel5::dontSeeAuthentication() */ - public function dontSeeAuthentication() { + public function dontSeeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeAuthentication', func_get_args())); } @@ -2437,7 +2808,6 @@ trait FunctionalTesterActions * Return an instance of a class from the IoC Container. * (http://laravel.com/docs/ioc) * - * Example * ``` php * getScenario()->runStep(new \Codeception\Step\Action('grabRecord', func_get_args())); } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * + * @see \Codeception\Module\Laravel5::haveModel() + */ + public function haveModel($model, $attributes = null, $name = null, $times = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('haveModel', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Use Laravel's model factory to create a model. + * Can only be used with Laravel 5.1 and later. + * + * ``` php + * createModel('App\User'); + * $I->createModel('App\User', ['name' => 'John Doe']); + * $I->createModel('App\User', [], 'admin'); + * $I->createModel('App\User', [], 'admin', 3); + * ?> + * ``` + * + * @see http://laravel.com/docs/5.1/testing#model-factories + * @param string $model + * @param array $attributes + * @param string $name + * @param int $times + * @return mixed + * @see \Codeception\Module\Laravel5::createModel() + */ + public function createModel($model, $attributes = null, $name = null, $times = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('createModel', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Use Laravel's model factory to make a model. + * Can only be used with Laravel 5.1 and later. + * + * ``` php + * makeModel('App\User'); + * $I->makeModel('App\User', ['name' => 'John Doe']); + * $I->makeModel('App\User', [], 'admin'); + * $I->makeModel('App\User', [], 'admin', 3); + * ?> + * ``` + * + * @see http://laravel.com/docs/5.1/testing#model-factories + * @param string $model + * @param array $attributes + * @param string $name + * @param int $times + * @return mixed + * @see \Codeception\Module\Laravel5::makeModel() + */ + public function makeModel($model, $attributes = null, $name = null, $times = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('makeModel', func_get_args())); + } } From 1d6011caadf30710e4a4d5f57485b69a2faff94d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 11:38:01 +0300 Subject: [PATCH 170/258] Working on permissions in the API --- app/Http/Controllers/AccountApiController.php | 7 ++ app/Http/Controllers/BaseAPIController.php | 25 +++++- app/Http/Controllers/ClientApiController.php | 69 ++-------------- app/Http/Controllers/ExpenseApiController.php | 2 +- app/Http/Controllers/InvoiceApiController.php | 2 +- app/Http/Controllers/PaymentApiController.php | 78 +++---------------- app/Http/Controllers/ProductApiController.php | 2 +- app/Http/Controllers/TaskApiController.php | 2 +- app/Http/Controllers/TaxRateApiController.php | 2 +- app/Http/Controllers/UserApiController.php | 2 +- app/Http/Controllers/VendorApiController.php | 2 +- app/Http/Requests/CreatePaymentAPIRequest.php | 48 ++++++++++++ app/Http/routes.php | 2 +- app/Models/EntityModel.php | 2 +- 14 files changed, 107 insertions(+), 138 deletions(-) create mode 100644 app/Http/Requests/CreatePaymentAPIRequest.php diff --git a/app/Http/Controllers/AccountApiController.php b/app/Http/Controllers/AccountApiController.php index 02697ba039de..65175cdd5ba1 100644 --- a/app/Http/Controllers/AccountApiController.php +++ b/app/Http/Controllers/AccountApiController.php @@ -34,6 +34,13 @@ class AccountApiController extends BaseAPIController $this->accountRepo = $accountRepo; } + public function ping() + { + $headers = Utils::getApiHeaders(); + + return Response::make(RESULT_SUCCESS, 200, $headers); + } + public function register(RegisterRequest $request) { diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 4d03dea14b1d..7bfdfc931c90 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -68,7 +68,19 @@ class BaseAPIController extends Controller } } - protected function returnList($query) + protected function handleAction($request) + { + $entity = $request->entity(); + $action = $request->action; + + $repo = Utils::toCamelCase($this->entityType) . 'Repo'; + + $this->$repo->$action($entity); + + return $this->itemResponse($entity); + } + + protected function listResponse($query) { //\DB::enableQueryLog(); if ($clientPublicId = Input::get('client_id')) { @@ -95,6 +107,16 @@ class BaseAPIController extends Controller return $this->response($data); } + protected function itemResponse($item) + { + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $data = $this->createItem($item, $transformer, $this->entityType); + + return $this->response($data); + } + protected function createItem($data, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { @@ -155,7 +177,6 @@ class BaseAPIController extends Controller } - protected function getIncluded() { $data = ['user']; diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 4457973c7031..83689e274318 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -10,29 +10,19 @@ use App\Ninja\Repositories\ClientRepository; use App\Http\Requests\CreateClientRequest; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\ClientTransformer; -use App\Services\ClientService; use App\Http\Requests\UpdateClientRequest; class ClientApiController extends BaseAPIController { protected $clientRepo; - protected $clientService; protected $entityType = ENTITY_CLIENT; - public function __construct(ClientRepository $clientRepo, ClientService $clientService) + public function __construct(ClientRepository $clientRepo) { parent::__construct(); $this->clientRepo = $clientRepo; - $this->clientService = $clientService; - } - - public function ping() - { - $headers = Utils::getApiHeaders(); - - return Response::make('', 200, $headers); } /** @@ -65,7 +55,7 @@ class ClientApiController extends BaseAPIController }); } - return $this->returnList($clients); + return $this->listResponse($clients); } /** @@ -93,14 +83,7 @@ class ClientApiController extends BaseAPIController { $client = $this->clientRepo->save($request->input()); - $client = Client::scope($client->public_id) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->first(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } /** @@ -127,51 +110,15 @@ class ClientApiController extends BaseAPIController public function update(UpdateClientRequest $request, $publicId) { - if ($request->action == ACTION_ARCHIVE) { - - - $client = Client::scope($publicId)->withTrashed()->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Record not found'], 400); - - $this->clientRepo->archive($client); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } - else if ($request->action == ACTION_RESTORE){ - - $client = Client::scope($publicId)->withTrashed()->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Client not found.'], 400); - - $this->clientRepo->restore($client); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); - } - + $data = $request->input(); $data['public_id'] = $publicId; - $this->clientRepo->save($data); + $client = $this->clientRepo->save($data); - $client = Client::scope($publicId) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Client not found.'],400); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 9b1a7e906244..44722f2a18ce 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -32,7 +32,7 @@ class ExpenseApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at','desc'); - return $this->returnList($expenses); + return $this->listResponse($expenses); } public function update() diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index ce5eaa1d72d6..1ff6aa71faeb 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -62,7 +62,7 @@ class InvoiceApiController extends BaseAPIController ->with(array_merge(['invoice_items'], $this->getIncluded())) ->orderBy('created_at', 'desc'); - return $this->returnList($invoices); + return $this->listResponse($invoices); } /** diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index a7cab011fc36..e37e74971be3 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -12,6 +12,8 @@ use App\Ninja\Repositories\PaymentRepository; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\PaymentTransformer; use App\Ninja\Transformers\InvoiceTransformer; +use App\Http\Requests\UpdatePaymentRequest; +use App\Http\Requests\CreatePaymentAPIRequest; class PaymentApiController extends BaseAPIController { @@ -50,7 +52,7 @@ class PaymentApiController extends BaseAPIController ->with(array_merge(['client.contacts', 'invitation', 'user', 'invoice'], $this->getIncluded())) ->orderBy('created_at', 'desc'); - return $this->returnList($payments); + return $this->listResponse($payments); } /** @@ -75,39 +77,17 @@ class PaymentApiController extends BaseAPIController * ) */ - public function update(Request $request, $publicId) + public function update(UpdatePaymentRequest $request, $publicId) { - $data = Input::all(); - $data['public_id'] = $publicId; - $error = false; - - if ($request->action == ACTION_ARCHIVE) { - $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); - $this->paymentRepo->archive($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } + $data = $request->input(); + $data['public_id'] = $publicId; $payment = $this->paymentRepo->save($data); - if ($error) { - return $error; - } - - /* - $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->withTrashed()->first(); - */ - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($payment); } @@ -132,49 +112,15 @@ class PaymentApiController extends BaseAPIController * ) * ) */ - public function store() + public function store(CreatePaymentAPIRequest $request) { - $data = Input::all(); - $error = false; - - if (isset($data['invoice_id'])) { - $invoice = Invoice::scope($data['invoice_id'])->with('client')->first(); - - if ($invoice) { - $data['invoice_id'] = $invoice->id; - $data['client_id'] = $invoice->client->id; - } else { - $error = trans('validation.not_in', ['attribute' => 'invoice_id']); - } - } else { - $error = trans('validation.not_in', ['attribute' => 'invoice_id']); - } - - if (!isset($data['transaction_reference'])) { - $data['transaction_reference'] = ''; - } - - if ($error) { - return $error; - } - - $payment = $this->paymentRepo->save($data); + $payment = $this->paymentRepo->save($request->input()); if (Input::get('email_receipt')) { $this->contactMailer->sendPaymentConfirmation($payment); } - /* - $invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($payment); } /** diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 26736f5ef5c2..1d3e4577048c 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -38,7 +38,7 @@ class ProductApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($products); + return $this->listResponse($products); } public function getDatatable() diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index cb39e0a1f369..5e5e05ef80e9 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -45,7 +45,7 @@ class TaskApiController extends BaseAPIController ->with($this->getIncluded()) ->orderBy('created_at', 'desc'); - return $this->returnList($payments); + return $this->listResponse($payments); } /** diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index ddc0ab4aa968..1aa5417d9563 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -30,7 +30,7 @@ class TaxRateApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($taxRates); + return $this->listResponse($taxRates); } public function store(CreateTaxRateRequest $request) diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 8fda74b33cbd..6786e4046544 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -30,7 +30,7 @@ class UserApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($users); + return $this->listResponse($users); } /* diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index b2487dfda2d3..1dea751e790d 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -53,7 +53,7 @@ class VendorApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($vendors); + return $this->listResponse($vendors); } /** diff --git a/app/Http/Requests/CreatePaymentAPIRequest.php b/app/Http/Requests/CreatePaymentAPIRequest.php new file mode 100644 index 000000000000..08a520c16655 --- /dev/null +++ b/app/Http/Requests/CreatePaymentAPIRequest.php @@ -0,0 +1,48 @@ +user()->can('create', ENTITY_PAYMENT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + if ( ! $this->invoice_id || ! $this->amount) { + return [ + 'invoice_id' => 'required', + 'amount' => 'required', + ]; + } + + $invoice = Invoice::scope($this->invoice_id)->firstOrFail(); + + $this->merge([ + 'invoice_id' => $invoice->id, + 'client_id' => $invoice->client->id, + ]); + + $rules = array( + 'amount' => "required|less_than:{$invoice->balance}|positive", + ); + + if ($this->payment_type_id == PAYMENT_TYPE_CREDIT) { + $rules['payment_type_id'] = 'has_credit:' . $invoice->client->public_id . ',' . $this->amount; + } + + return $rules; + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 768c9c3d3d4c..386d02427e0b 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -246,7 +246,7 @@ Route::group([ // Route groups for API Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() { - Route::get('ping', 'ClientApiController@ping'); + Route::get('ping', 'AccountApiController@ping'); Route::post('login', 'AccountApiController@login'); Route::post('register', 'AccountApiController@register'); Route::get('static', 'AccountApiController@getStaticData'); diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index c2c9110fe306..95e85e6acef4 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -110,7 +110,7 @@ class EntityModel extends Eloquent { return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer'; } - + public function setNullValues() { foreach ($this->fillable as $field) { From e7f4368cbb3f8bb85142d346621be5c1b22f877c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 16:12:37 +0300 Subject: [PATCH 171/258] Working on permissions in the API --- app/Http/Controllers/ProductApiController.php | 78 ++++--------------- app/Http/Controllers/TaxRateApiController.php | 48 +++++------- app/Http/Controllers/UserApiController.php | 5 -- app/Http/Controllers/VendorApiController.php | 4 +- app/Http/Requests/CreateTaxRateRequest.php | 4 +- app/Http/Requests/UpdateTaxRateRequest.php | 4 +- app/Http/Requests/UpdateUserRequest.php | 2 +- app/Libraries/Utils.php | 2 +- app/Models/Product.php | 8 ++ app/Ninja/Repositories/ProductRepository.php | 18 +++++ app/Providers/AuthServiceProvider.php | 2 + 11 files changed, 70 insertions(+), 105 deletions(-) diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 1d3e4577048c..5bf1b239e4c0 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -1,34 +1,20 @@ productService = $productService; $this->productRepo = $productRepo; } @@ -41,58 +27,28 @@ class ProductApiController extends BaseAPIController return $this->listResponse($products); } - public function getDatatable() + public function store(CreateProductRequest $request) { - return $this->productService->getDatatable(Auth::user()->account_id); + $product = $this->productRepo->save($request->input()); + + return $this->itemResponse($product); } - public function store() + public function update(UpdateProductRequest $request, $publicId) { - return $this->save(); - } - - public function update(\Illuminate\Http\Request $request, $publicId) - { - - if ($request->action == ACTION_ARCHIVE) { - $product = Product::scope($publicId)->withTrashed()->firstOrFail(); - $this->productRepo->archive($product); - - $transformer = new ProductTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($product, $transformer, 'products'); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } - else - return $this->save($publicId); + + $data = $request->input(); + $data['public_id'] = $publicId; + $product = $this->productRepo->save($data); + + return $this->itemResponse($product); } public function destroy($publicId) { //stub } - - private function save($productPublicId = false) - { - if ($productPublicId) { - $product = Product::scope($productPublicId)->firstOrFail(); - } else { - $product = Product::createNew(); - } - - $product->product_key = trim(Input::get('product_key')); - $product->notes = trim(Input::get('notes')); - $product->cost = trim(Input::get('cost')); - //$product->default_tax_rate_id = Input::get('default_tax_rate_id'); - - $product->save(); - - $transformer = new ProductTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($product, $transformer, 'products'); - - return $this->response($data); - - } - - } diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 1aa5417d9563..1c4e9bf255bc 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -1,26 +1,20 @@ taxRateService = $taxRateService; $this->taxRateRepo = $taxRateRepo; } @@ -29,38 +23,32 @@ class TaxRateApiController extends BaseAPIController $taxRates = TaxRate::scope() ->withTrashed() ->orderBy('created_at', 'desc'); - + return $this->listResponse($taxRates); } public function store(CreateTaxRateRequest $request) { - return $this->save($request); + $taxRate = $this->taxRateRepo->save($request->input()); + + return $this->itemResponse($taxRate); } - public function update(UpdateTaxRateRequest $request, $taxRatePublicId) + public function update(UpdateTaxRateRequest $request, $publicId) { - $taxRate = TaxRate::scope($taxRatePublicId)->firstOrFail(); - - if ($request->action == ACTION_ARCHIVE) { - $this->taxRateRepo->archive($taxRate); - - $transformer = new TaxRateTransformer(Auth::user()->account, $request->serializer); - $data = $this->createItem($taxRate, $transformer, 'tax_rates'); - - return $this->response($data); - } else { - return $this->save($request, $taxRate); + if ($request->action) { + return $this->handleAction($request); } + + $data = $request->input(); + $data['public_id'] = $publicId; + $taxRate = $this->taxRateRepo->save($data); + + return $this->itemResponse($taxRate); } - private function save($request, $taxRate = false) + public function destroy($publicId) { - $taxRate = $this->taxRateRepo->save($request->input(), $taxRate); - - $transformer = new TaxRateTransformer(\Auth::user()->account, $request->serializer); - $data = $this->createItem($taxRate, $transformer, 'tax_rates'); - - return $this->response($data); + //stub } } diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 6786e4046544..2869c3512f5a 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -42,11 +42,6 @@ class UserApiController extends BaseAPIController public function update(UpdateUserRequest $request, $userPublicId) { - /* - // temporary fix for ids starting at 0 - $userPublicId -= 1; - $user = User::scope($userPublicId)->firstOrFail(); - */ $user = Auth::user(); if ($request->action == ACTION_ARCHIVE) { diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 1dea751e790d..d3cc27094760 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -85,8 +85,6 @@ class VendorApiController extends BaseAPIController ->with('country', 'vendorcontacts', 'industry', 'size', 'currency') ->first(); - $transformer = new VendorTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($vendor, $transformer, ENTITY_VENDOR); - return $this->response($data); + return $this->itemResponse($vendor); } } diff --git a/app/Http/Requests/CreateTaxRateRequest.php b/app/Http/Requests/CreateTaxRateRequest.php index 1596c814f127..d8fef50093b7 100644 --- a/app/Http/Requests/CreateTaxRateRequest.php +++ b/app/Http/Requests/CreateTaxRateRequest.php @@ -3,7 +3,7 @@ use App\Http\Requests\Request; use Illuminate\Validation\Factory; -class CreateTaxRateRequest extends Request +class CreateTaxRateRequest extends TaxRateRequest { // Expenses /** @@ -13,7 +13,7 @@ class CreateTaxRateRequest extends Request */ public function authorize() { - return true; + return $this->user()->can('create', ENTITY_TAX_RATE); } /** diff --git a/app/Http/Requests/UpdateTaxRateRequest.php b/app/Http/Requests/UpdateTaxRateRequest.php index bcfa298e06c5..a4bdc6301ca2 100644 --- a/app/Http/Requests/UpdateTaxRateRequest.php +++ b/app/Http/Requests/UpdateTaxRateRequest.php @@ -3,7 +3,7 @@ use App\Http\Requests\Request; use Illuminate\Validation\Factory; -class UpdateTaxRateRequest extends Request +class UpdateTaxRateRequest extends TaxRateRequest { // Expenses /** @@ -13,7 +13,7 @@ class UpdateTaxRateRequest extends Request */ public function authorize() { - return true; + return $this->user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateUserRequest.php b/app/Http/Requests/UpdateUserRequest.php index 91d7a73bc568..b3149d2b61ff 100644 --- a/app/Http/Requests/UpdateUserRequest.php +++ b/app/Http/Requests/UpdateUserRequest.php @@ -14,7 +14,7 @@ class UpdateUserRequest extends Request */ public function authorize() { - return true; + return $this->user()->can('edit', $this->entity()); } /** diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 86107acfcd59..141eacfc3058 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -676,7 +676,7 @@ class Utils public static function getEntityName($entityType) { - return ucwords(str_replace('_', ' ', $entityType)); + return ucwords(Utils::toCamelCase($entityType)); } public static function getClientDisplayName($model) diff --git a/app/Models/Product.php b/app/Models/Product.php index 8de7c7ac5b2c..0d3221f2a1ea 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -8,6 +8,14 @@ class Product extends EntityModel use SoftDeletes; protected $dates = ['deleted_at']; + protected $fillable = [ + 'product_key', + 'notes', + 'cost', + 'qty', + 'default_tax_rate_id', + ]; + public function getEntityType() { return ENTITY_PRODUCT; diff --git a/app/Ninja/Repositories/ProductRepository.php b/app/Ninja/Repositories/ProductRepository.php index 417b49f23640..7100b083a553 100644 --- a/app/Ninja/Repositories/ProductRepository.php +++ b/app/Ninja/Repositories/ProductRepository.php @@ -1,6 +1,7 @@ firstOrFail(); + } else { + $product = Product::createNew(); + } + + $product->fill($data); + $product->save(); + + return $product; + } + } \ No newline at end of file diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 884c2587503d..02aba3e1d6d3 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -21,6 +21,8 @@ class AuthServiceProvider extends ServiceProvider \App\Models\Payment::class => \App\Policies\PaymentPolicy::class, \App\Models\Task::class => \App\Policies\TaskPolicy::class, \App\Models\Vendor::class => \App\Policies\VendorPolicy::class, + \App\Models\Product::class => \App\Policies\ProductPolicy::class, + \App\Models\TaxRate::class => \App\Policies\TaxRatePolicy::class, ]; /** From 6acddfc3c7fc8d1f753e123e1a4e13072bdd482a Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 16:45:12 +0300 Subject: [PATCH 172/258] Working on permissions in the API --- app/Http/Controllers/AppController.php | 2 +- app/Http/Controllers/InvoiceApiController.php | 90 +++++-------------- app/Http/Controllers/PaymentApiController.php | 5 -- app/Http/Requests/CreateProductRequest.php | 26 ++++++ app/Http/Requests/ProductRequest.php | 6 ++ app/Http/Requests/TaxRateRequest.php | 7 ++ app/Http/Requests/UpdateProductRequest.php | 26 ++++++ 7 files changed, 90 insertions(+), 72 deletions(-) create mode 100644 app/Http/Requests/CreateProductRequest.php create mode 100644 app/Http/Requests/ProductRequest.php create mode 100644 app/Http/Requests/TaxRateRequest.php create mode 100644 app/Http/Requests/UpdateProductRequest.php diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 9dcf243fbe02..568cb4d45f5b 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -306,7 +306,7 @@ class AppController extends BaseController public function stats() { - if (Input::get('password') != env('RESELLER_PASSWORD')) { + if ( ! hash_equals(Input::get('password'), env('RESELLER_PASSWORD'))) { sleep(3); return ''; } diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 1ff6aa71faeb..e4cc870f210c 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -18,6 +18,7 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\InvoiceTransformer; +use App\Http\Requests\InvoiceRequest; use App\Http\Requests\CreateInvoiceAPIRequest; use App\Http\Requests\UpdateInvoiceAPIRequest; use App\Services\InvoiceService; @@ -82,17 +83,9 @@ class InvoiceApiController extends BaseAPIController * ) */ - public function show($publicId) + public function show(InvoiceRequest $request) { - $invoice = Invoice::scope($publicId)->withTrashed()->first(); - - if(!$invoice) - return $this->errorResponse(['message'=>'Invoice does not exist!'], 404); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($request->entity()); } /** @@ -187,11 +180,11 @@ class InvoiceApiController extends BaseAPIController } } - $invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->first(); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + $invoice = Invoice::scope($invoice->public_id) + ->with('client', 'invoice_items', 'invitations') + ->first(); + + return $this->itemResponse($invoice); } private function prepareData($data, $client) @@ -277,36 +270,21 @@ class InvoiceApiController extends BaseAPIController $item[$key] = $val; } } - + return $item; } - public function emailInvoice() + public function emailInvoice(InvoiceRequest $request) { - $data = Input::all(); - $error = null; + $invoice = $request->entity(); - $invoice = Invoice::scope($data['id'])->withTrashed()->first(); - - if(!$invoice) - return $this->errorResponse(['message'=>'Invoice does not exist.'], 400); - - - $this->mailer->sendInvoice($invoice, false, false); - - - if($error) { - return $this->errorResponse(['message'=>'There was an error sending the invoice'], 400); - } - else { - $response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT); - } + $this->mailer->sendInvoice($invoice); + $response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT); $headers = Utils::getApiHeaders(); - return Response::make($response, $error ? 400 : 200, $headers); + return Response::make($response, 200, $headers); } - /** * @SWG\Put( * path="/invoices", @@ -330,43 +308,23 @@ class InvoiceApiController extends BaseAPIController */ public function update(UpdateInvoiceAPIRequest $request, $publicId) { - if ($request->action == ACTION_ARCHIVE) { - $invoice = Invoice::scope($publicId)->firstOrFail(); - $this->invoiceRepo->archive($invoice); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - } - else if ($request->action == ACTION_CONVERT) { - $quote = Invoice::scope($publicId)->firstOrFail(); + if ($request->action == ACTION_CONVERT) { + $quote = $request->entity(); $invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - } - else if ($request->action == ACTION_RESTORE) { - $invoice = Invoice::scope($publicId)->withTrashed()->firstOrFail(); - $this->invoiceRepo->restore($invoice); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($invoice); + } elseif ($request->action) { + return $this->handleAction($request); } $data = $request->input(); $data['public_id'] = $publicId; $this->invoiceService->save($data); - $invoice = Invoice::scope($publicId)->with('client', 'invoice_items', 'invitations')->firstOrFail(); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + $invoice = Invoice::scope($publicId) + ->with('client', 'invoice_items', 'invitations') + ->firstOrFail(); + + return $this->itemResponse($invoice); } /** diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index e37e74971be3..c1d0fd8ae0b6 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -153,11 +153,6 @@ class PaymentApiController extends BaseAPIController $this->paymentRepo->delete($payment); - /* - $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); $data = $this->createItem($payment, $transformer, 'invoice'); diff --git a/app/Http/Requests/CreateProductRequest.php b/app/Http/Requests/CreateProductRequest.php new file mode 100644 index 000000000000..0fad2af14a23 --- /dev/null +++ b/app/Http/Requests/CreateProductRequest.php @@ -0,0 +1,26 @@ +user()->can('create', ENTITY_PRODUCT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'product_key' => 'required', + ]; + } +} diff --git a/app/Http/Requests/ProductRequest.php b/app/Http/Requests/ProductRequest.php new file mode 100644 index 000000000000..2420bdcfcaf0 --- /dev/null +++ b/app/Http/Requests/ProductRequest.php @@ -0,0 +1,6 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'product_key' => 'required', + ]; + } +} From 325cac1603a81e6d271603d8a8eeb9a65cb24b1d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 20:02:15 +0300 Subject: [PATCH 173/258] Added DocumentTransformer --- .../Transformers/DocumentTransformer.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/Ninja/Transformers/DocumentTransformer.php diff --git a/app/Ninja/Transformers/DocumentTransformer.php b/app/Ninja/Transformers/DocumentTransformer.php new file mode 100644 index 000000000000..61d18506eba1 --- /dev/null +++ b/app/Ninja/Transformers/DocumentTransformer.php @@ -0,0 +1,21 @@ + (int) $document->public_id, + 'name' => $document->name, + 'account_key' => $this->account->account_key, + 'type' => $document->type, + 'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null, + 'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null, + ]; + } +} \ No newline at end of file From e6bcdb36b38979f6d592c4ee92a786d46921e6db Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 20:42:13 +0300 Subject: [PATCH 174/258] Removed extra entity load in the repos save function --- app/Http/Controllers/BaseController.php | 24 ------------------- app/Http/Controllers/ClientApiController.php | 2 +- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceApiController.php | 2 +- app/Http/Controllers/InvoiceController.php | 10 +++----- app/Http/Controllers/PaymentApiController.php | 2 +- app/Http/Controllers/PaymentController.php | 2 +- app/Http/Controllers/ProductApiController.php | 2 +- app/Http/Controllers/TaxRateApiController.php | 2 +- app/Http/Controllers/TaxRateController.php | 4 +--- app/Http/Controllers/VendorController.php | 2 +- app/Ninja/Repositories/ClientRepository.php | 5 ++-- app/Ninja/Repositories/CreditRepository.php | 9 ++++--- app/Ninja/Repositories/ExpenseRepository.php | 7 ++++-- app/Ninja/Repositories/InvoiceRepository.php | 7 ++++-- app/Ninja/Repositories/PaymentRepository.php | 7 ++++-- app/Ninja/Repositories/ProductRepository.php | 7 ++++-- app/Ninja/Repositories/TaskRepository.php | 7 ++++-- app/Ninja/Repositories/TaxRateRepository.php | 15 ++++++------ app/Ninja/Repositories/VendorRepository.php | 7 ++++-- app/Services/ExpenseService.php | 4 ++-- app/Services/InvoiceService.php | 4 ++-- app/Services/VendorService.php | 4 ++-- 23 files changed, 65 insertions(+), 72 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 4a33946c7631..2ce7a633f179 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -24,28 +24,4 @@ class BaseController extends Controller $this->layout = View::make($this->layout); } } - - protected function authorizeCreate() { - $this->authorize('create', $this->entityType); - } - - /* - protected function authorizeUpdate($entity) { - $this->authorize('edit', $entity); - } - */ - - protected function authorizeUpdate($input){ - $creating = empty($input['public_id']) || $input['public_id'] == '-1'; - - if($creating){ - $this->authorize('create', $this->entityType); - } - else{ - $className = Utils::getEntityName($this->entityType); - - $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); - $this->authorize('edit', $object); - } - } } diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 83689e274318..ba6b56c952fa 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -116,7 +116,7 @@ class ClientApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $client = $this->clientRepo->save($data); + $client = $this->clientRepo->save($data, $request->entity()); return $this->itemResponse($client); } diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 406613489c6b..3da123c2d972 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -146,7 +146,7 @@ class ExpenseController extends BaseController $data = $request->input(); $data['documents'] = $request->file('documents'); - $expense = $this->expenseService->save($data); + $expense = $this->expenseService->save($data, $request->entity()); Session::flash('message', trans('texts.updated_expense')); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index e4cc870f210c..32d38cc9316d 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -318,7 +318,7 @@ class InvoiceApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $this->invoiceService->save($data); + $this->invoiceService->save($data, $request->entity()); $invoice = Invoice::scope($publicId) ->with('client', 'invoice_items', 'invitations') diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 1a652ae8962c..270cc10b96ac 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -396,9 +396,7 @@ class InvoiceController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $action = Input::get('action'); $entityType = Input::get('entityType'); @@ -435,13 +433,11 @@ class InvoiceController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data); + $invoice = $this->invoiceService->save($data, $request->entity()); $entityType = $invoice->getEntityType(); $message = trans("texts.updated_{$entityType}"); Session::flash('message', $message); diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index c1d0fd8ae0b6..5cea7f23641a 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -85,7 +85,7 @@ class PaymentApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $payment = $this->paymentRepo->save($data); + $payment = $this->paymentRepo->save($data, $request->entity()); return $this->itemResponse($payment); } diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index e3307f6f47e5..12537dbebaa1 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -603,7 +603,7 @@ class PaymentController extends BaseController public function update(UpdatePaymentRequest $request) { - $payment = $this->paymentRepo->save($request->input()); + $payment = $this->paymentRepo->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_payment')); diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 5bf1b239e4c0..6a8756eda4b6 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -42,7 +42,7 @@ class ProductApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $product = $this->productRepo->save($data); + $product = $this->productRepo->save($data, $request->entity()); return $this->itemResponse($product); } diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 1c4e9bf255bc..85756205d9a1 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -42,7 +42,7 @@ class TaxRateApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $taxRate = $this->taxRateRepo->save($data); + $taxRate = $this->taxRateRepo->save($data, $request->entity()); return $this->itemResponse($taxRate); } diff --git a/app/Http/Controllers/TaxRateController.php b/app/Http/Controllers/TaxRateController.php index 223f7491092e..cba4058756de 100644 --- a/app/Http/Controllers/TaxRateController.php +++ b/app/Http/Controllers/TaxRateController.php @@ -75,9 +75,7 @@ class TaxRateController extends BaseController public function update(UpdateTaxRateRequest $request, $publicId) { - $taxRate = TaxRate::scope($publicId)->firstOrFail(); - - $this->taxRateRepo->save($request->input(), $taxRate); + $this->taxRateRepo->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_tax_rate')); return Redirect::to('settings/' . ACCOUNT_TAX_RATES); diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 10c7f7f03e89..f1952e20dee3 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -182,7 +182,7 @@ class VendorController extends BaseController */ public function update(UpdateVendorRequest $request) { - $vendor = $this->vendorService->save($request->input()); + $vendor = $this->vendorService->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_vendor')); diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 77ebcb7fd736..8d8c3ed527d4 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -72,12 +72,13 @@ class ClientRepository extends BaseRepository if ($client) { // do nothing - } if (!$publicId || $publicId == '-1') { + } elseif (!$publicId || $publicId == '-1') { $client = Client::createNew(); } else { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); + \Log::warning('Entity not set in client repo save'); } - + // convert currency code to id if (isset($data['currency_code'])) { $currencyCode = strtolower($data['currency_code']); diff --git a/app/Ninja/Repositories/CreditRepository.php b/app/Ninja/Repositories/CreditRepository.php index 4381ae25771c..068707ace02b 100644 --- a/app/Ninja/Repositories/CreditRepository.php +++ b/app/Ninja/Repositories/CreditRepository.php @@ -59,12 +59,15 @@ class CreditRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $credit = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - - if ($publicId) { + + if ($credit) { + // do nothing + } elseif ($publicId) { $credit = Credit::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in credit repo save'); } else { $credit = Credit::createNew(); } diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 4c67c0e7c91c..442af74bd32b 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -122,12 +122,15 @@ class ExpenseRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $expense = null) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; - if ($publicId) { + if ($expense) { + // do nothing + } elseif ($publicId) { $expense = Expense::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in expense repo save'); } else { $expense = Expense::createNew(); } diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 2ae18a738bd8..bbf58ca16c71 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -201,14 +201,16 @@ class InvoiceRepository extends BaseRepository ->make(); } - public function save($data) + public function save($data, $invoice = null) { $account = \Auth::user()->account; $publicId = isset($data['public_id']) ? $data['public_id'] : false; $isNew = !$publicId || $publicId == '-1'; - if ($isNew) { + if ($invoice) { + // do nothing + } elseif ($isNew) { $entityType = ENTITY_INVOICE; if (isset($data['is_recurring']) && filter_var($data['is_recurring'], FILTER_VALIDATE_BOOLEAN)) { $entityType = ENTITY_RECURRING_INVOICE; @@ -224,6 +226,7 @@ class InvoiceRepository extends BaseRepository } } else { $invoice = Invoice::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in invoice repo save'); } $invoice->fill($data); diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index 9b7d9787b457..dd99fccdfdd4 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -123,12 +123,15 @@ class PaymentRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $payment = null) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; - if ($publicId) { + if ($payment) { + // do nothing + } elseif ($publicId) { $payment = Payment::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in payment repo save'); } else { $payment = Payment::createNew(); } diff --git a/app/Ninja/Repositories/ProductRepository.php b/app/Ninja/Repositories/ProductRepository.php index 7100b083a553..eb0e7383e9e5 100644 --- a/app/Ninja/Repositories/ProductRepository.php +++ b/app/Ninja/Repositories/ProductRepository.php @@ -31,12 +31,15 @@ class ProductRepository extends BaseRepository ); } - public function save($data) + public function save($data, $product = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if ($publicId) { + if ($product) { + // do nothing + } elseif ($publicId) { $product = Product::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in product repo save'); } else { $product = Product::createNew(); } diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index 1bd0e38cb6b7..3a5eca04a112 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -64,10 +64,13 @@ class TaskRepository return $query; } - public function save($publicId, $data) + public function save($publicId, $data, $task = null) { - if ($publicId) { + if ($task) { + // do nothing + } elseif ($publicId) { $task = Task::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in task repo save'); } else { $task = Task::createNew(); } diff --git a/app/Ninja/Repositories/TaxRateRepository.php b/app/Ninja/Repositories/TaxRateRepository.php index 2e325100a016..8f1ad7f6550f 100644 --- a/app/Ninja/Repositories/TaxRateRepository.php +++ b/app/Ninja/Repositories/TaxRateRepository.php @@ -20,14 +20,15 @@ class TaxRateRepository extends BaseRepository ->select('tax_rates.public_id', 'tax_rates.name', 'tax_rates.rate', 'tax_rates.deleted_at'); } - public function save($data, $taxRate = false) + public function save($data, $taxRate = null) { - if ( ! $taxRate) { - if (isset($data['public_id'])) { - $taxRate = TaxRate::scope($data['public_id'])->firstOrFail(); - } else { - $taxRate = TaxRate::createNew(); - } + if ($taxRate) { + // do nothing + } elseif (isset($data['public_id'])) { + $taxRate = TaxRate::scope($data['public_id'])->firstOrFail(); + \Log::warning('Entity not set in tax rate repo save'); + } else { + $taxRate = TaxRate::createNew(); } $taxRate->fill($data); diff --git a/app/Ninja/Repositories/VendorRepository.php b/app/Ninja/Repositories/VendorRepository.php index df885f62e12e..ef5648f47d81 100644 --- a/app/Ninja/Repositories/VendorRepository.php +++ b/app/Ninja/Repositories/VendorRepository.php @@ -62,14 +62,17 @@ class VendorRepository extends BaseRepository return $query; } - public function save($data) + public function save($data, $vendor = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if (!$publicId || $publicId == '-1') { + if ($vendor) { + // do nothing + } elseif (!$publicId || $publicId == '-1') { $vendor = Vendor::createNew(); } else { $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); + \Log::warning('Entity not set in vendor repo save'); } $vendor->fill($data); diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index 0b28a7c4d6b1..671648ea32a5 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -28,7 +28,7 @@ class ExpenseService extends BaseService return $this->expenseRepo; } - public function save($data) + public function save($data, $expense = null) { if (isset($data['client_id']) && $data['client_id']) { $data['client_id'] = Client::getPrivateId($data['client_id']); @@ -38,7 +38,7 @@ class ExpenseService extends BaseService $data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']); } - return $this->expenseRepo->save($data); + return $this->expenseRepo->save($data, $expense); } public function getDatatable($search) diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index ecf0ad2fab0b..edbee8caf84c 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -30,7 +30,7 @@ class InvoiceService extends BaseService return $this->invoiceRepo; } - public function save($data) + public function save($data, $invoice = null) { if (isset($data['client'])) { $canSaveClient = false; @@ -46,7 +46,7 @@ class InvoiceService extends BaseService } } - $invoice = $this->invoiceRepo->save($data); + $invoice = $this->invoiceRepo->save($data, $invoice); $client = $invoice->client; $client->load('contacts'); diff --git a/app/Services/VendorService.php b/app/Services/VendorService.php index 6022507b452e..41f5fd4664bb 100644 --- a/app/Services/VendorService.php +++ b/app/Services/VendorService.php @@ -26,13 +26,13 @@ class VendorService extends BaseService return $this->vendorRepo; } - public function save($data) + public function save($data, $vendor = null) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } - return $this->vendorRepo->save($data); + return $this->vendorRepo->save($data, $vendor); } public function getDatatable($search) From 29a03179a9e2be0cfd6fcf1fb009558a6b71022f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 22:34:30 +0300 Subject: [PATCH 175/258] Fix for travis tests --- tests/acceptance/ExpenseCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index 01cc3112d83f..c32d21d7a4d0 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -49,7 +49,7 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); $I->click('Save'); - $I->wait(1); + $I->wait(2); $I->see($clientEmail); $I->see($amount); } From f74b30cddecf4ed8c24f1311cccd5420c6539a35 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 22:51:22 +0300 Subject: [PATCH 176/258] Added automated tests for API --- .../_support/_generated/AcceptanceTesterActions.php | 2 +- tests/acceptance/APICest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/_support/_generated/AcceptanceTesterActions.php b/tests/_support/_generated/AcceptanceTesterActions.php index d219111f887c..82427d1e2a5e 100644 --- a/tests/_support/_generated/AcceptanceTesterActions.php +++ b/tests/_support/_generated/AcceptanceTesterActions.php @@ -1,4 +1,4 @@ -createEntity('tax_rate', $data); $this->listEntities('tax_rate'); + $data = new stdClass; + $data->product_key = $this->faker->word; + $data->notes = $this->faker->realText(100); + $this->createEntity('product', $data); + $this->listEntities('product'); + + $data = new stdClass; + $data->name = $this->faker->word; + $data->vendorcontacts = []; + $this->createEntity('vendor', $data); + $this->listEntities('vendor'); + $this->listEntities('account'); } @@ -79,6 +91,7 @@ class APICest $response = $this->sendRequest("{$entityType}s", $data); $entityId = $response->data->id; + PHPUnit_Framework_Assert::assertGreaterThan(0, $entityId); return $entityId; From 65d70c620ef88731a6de679b5ad4c88ade439786 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 23:06:36 +0300 Subject: [PATCH 177/258] Enabled setting per_page for API results --- app/Http/Controllers/BaseAPIController.php | 3 ++- app/Http/routes.php | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 7bfdfc931c90..489053d055f0 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -134,8 +134,9 @@ class BaseAPIController extends Controller } if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { + $limit = min(MAX_API_PAGE_SIZE, Input::get('per_page', DEFAULT_API_PAGE_SIZE)); $resource = new Collection($query->get(), $transformer, $entityType); - $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate())); + $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate($limit))); } else { $resource = new Collection($query, $transformer, $entityType); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 386d02427e0b..df1f1add4bb8 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -599,6 +599,8 @@ if (!defined('CONTACT_EMAIL')) { define('TEST_USERNAME', 'user@example.com'); define('TEST_PASSWORD', 'password'); define('API_SECRET', 'API_SECRET'); + define('DEFAULT_API_PAGE_SIZE', 15); + define('MAX_API_PAGE_SIZE', 100); define('IOS_PRODUCTION_PUSH', env('IOS_PRODUCTION_PUSH', 'ninjaIOS')); define('IOS_DEV_PUSH', env('IOS_DEV_PUSH', 'devNinjaIOS')); From 95fdda6e1b06f53c6ba89d9e02a28f7f5d32bffa Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 23:11:16 +0300 Subject: [PATCH 178/258] Fix for travis tests --- tests/acceptance/ExpenseCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index c32d21d7a4d0..72d7666aab9d 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -49,7 +49,7 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); $I->click('Save'); - $I->wait(2); + $I->wait(3); $I->see($clientEmail); $I->see($amount); } From 6cd4ff025ead38879899ca85027ca558015f4c62 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 09:46:24 +0300 Subject: [PATCH 179/258] API optimizations --- app/Http/Controllers/BaseAPIController.php | 16 +++++++++++++--- app/Http/Controllers/ClientApiController.php | 1 - app/Http/Controllers/InvoiceApiController.php | 2 +- app/Http/Controllers/PaymentApiController.php | 2 +- app/Http/Controllers/TaskApiController.php | 1 - app/Http/Controllers/VendorApiController.php | 1 - app/Ninja/Transformers/ClientTransformer.php | 2 +- app/Ninja/Transformers/ExpenseTransformer.php | 10 ++++++++-- app/Ninja/Transformers/InvoiceTransformer.php | 11 +++++++++-- app/Ninja/Transformers/PaymentTransformer.php | 9 +++++---- 10 files changed, 38 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 489053d055f0..07ed5bb42f03 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -3,6 +3,7 @@ use Session; use Utils; use Auth; +use Log; use Input; use Response; use Request; @@ -66,6 +67,10 @@ class BaseAPIController extends Controller } else { $this->manager->setSerializer(new ArraySerializer()); } + + if (Utils::isNinjaDev()) { + \DB::enableQueryLog(); + } } protected function handleAction($request) @@ -82,7 +87,8 @@ class BaseAPIController extends Controller protected function listResponse($query) { - //\DB::enableQueryLog(); + $query->with($this->getIncluded()); + if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { $query->where('public_id', '=', $clientPublicId); @@ -103,7 +109,6 @@ class BaseAPIController extends Controller $data = $this->createCollection($query, $transformer, $this->entityType); - //return \DB::getQueryLog(); return $this->response($data); } @@ -113,7 +118,7 @@ class BaseAPIController extends Controller $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); $data = $this->createItem($item, $transformer, $this->entityType); - + return $this->response($data); } @@ -146,6 +151,11 @@ class BaseAPIController extends Controller protected function response($response) { + if (Utils::isNinjaDev()) { + $count = count(\DB::getQueryLog()); + Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); + } + $index = Request::get('index') ?: 'data'; if ($index == 'none') { diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index ba6b56c952fa..557daa29fff6 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -44,7 +44,6 @@ class ClientApiController extends BaseAPIController public function index() { $clients = Client::scope() - ->with($this->getIncluded()) ->orderBy('created_at', 'desc') ->withTrashed(); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 32d38cc9316d..da8bc2b7a6cf 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -60,7 +60,7 @@ class InvoiceApiController extends BaseAPIController { $invoices = Invoice::scope() ->withTrashed() - ->with(array_merge(['invoice_items'], $this->getIncluded())) + ->with('invoice_items') ->orderBy('created_at', 'desc'); return $this->listResponse($invoices); diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 5cea7f23641a..b41100acfad9 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -49,7 +49,7 @@ class PaymentApiController extends BaseAPIController { $payments = Payment::scope() ->withTrashed() - ->with(array_merge(['client.contacts', 'invitation', 'user', 'invoice'], $this->getIncluded())) + ->with(['client.contacts', 'invitation', 'user', 'invoice']) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index 5e5e05ef80e9..7945008bcc40 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -42,7 +42,6 @@ class TaskApiController extends BaseAPIController { $payments = Task::scope() ->withTrashed() - ->with($this->getIncluded()) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index d3cc27094760..2995e5c5e5f2 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -49,7 +49,6 @@ class VendorApiController extends BaseAPIController public function index() { $vendors = Vendor::scope() - ->with($this->getIncluded()) ->withTrashed() ->orderBy('created_at', 'desc'); diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index f7d786a2ee25..e9a63e07d693 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -58,7 +58,7 @@ class ClientTransformer extends EntityTransformer public function includeInvoices(Client $client) { - $transformer = new InvoiceTransformer($this->account, $this->serializer); + $transformer = new InvoiceTransformer($this->account, $this->serializer, $client); return $this->includeCollection($client->invoices, $transformer, ENTITY_INVOICE); } diff --git a/app/Ninja/Transformers/ExpenseTransformer.php b/app/Ninja/Transformers/ExpenseTransformer.php index a6d358d15766..8f2e02c0a88f 100644 --- a/app/Ninja/Transformers/ExpenseTransformer.php +++ b/app/Ninja/Transformers/ExpenseTransformer.php @@ -6,9 +6,15 @@ use League\Fractal; class ExpenseTransformer extends EntityTransformer { + public function __construct($account = null, $serializer = null, $client = null) + { + parent::__construct($account, $serializer); + + $this->client = $client; + } + public function transform(Expense $expense) { - return [ 'id' => (int) $expense->public_id, 'private_notes' => $expense->private_notes, @@ -25,7 +31,7 @@ class ExpenseTransformer extends EntityTransformer 'exchange_rate' => (float) $expense->exchange_rate, 'invoice_currency_id' => (int) $expense->invoice_currency_id, 'is_deleted' => (bool) $expense->is_deleted, - 'client_id' => isset($expense->client->public_id) ? (int) $expense->client->public_id : null, + 'client_id' => $this->client ? $this->client->public_id : (isset($expense->client->public_id) ? (int) $expense->client->public_id : null), 'invoice_id' => isset($expense->invoice->public_id) ? (int) $expense->invoice->public_id : null, 'vendor_id' => isset($expense->vendor->public_id) ? (int) $expense->vendor->public_id : null, ]; diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 413f43ce1a10..51af3d029936 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -31,6 +31,13 @@ class InvoiceTransformer extends EntityTransformer 'expenses', ]; + public function __construct($account = null, $serializer = null, $client = null) + { + parent::__construct($account, $serializer); + + $this->client = $client; + } + public function includeInvoiceItems(Invoice $invoice) { $transformer = new InvoiceItemTransformer($this->account, $this->serializer); @@ -45,7 +52,7 @@ class InvoiceTransformer extends EntityTransformer public function includePayments(Invoice $invoice) { - $transformer = new PaymentTransformer($this->account, $this->serializer); + $transformer = new PaymentTransformer($this->account, $this->serializer, $invoice); return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT); } @@ -68,7 +75,7 @@ class InvoiceTransformer extends EntityTransformer 'id' => (int) $invoice->public_id, 'amount' => (float) $invoice->amount, 'balance' => (float) $invoice->balance, - 'client_id' => (int) $invoice->client->public_id, + 'client_id' => (int) ($this->client ? $this->client->public_id : $invoice->client->public_id), 'invoice_status_id' => (int) $invoice->invoice_status_id, 'updated_at' => $this->getTimestamp($invoice->updated_at), 'archived_at' => $this->getTimestamp($invoice->deleted_at), diff --git a/app/Ninja/Transformers/PaymentTransformer.php b/app/Ninja/Transformers/PaymentTransformer.php index a1de09cd6ff4..8ea2dcbae051 100644 --- a/app/Ninja/Transformers/PaymentTransformer.php +++ b/app/Ninja/Transformers/PaymentTransformer.php @@ -26,10 +26,11 @@ class PaymentTransformer extends EntityTransformer ]; - public function __construct(Account $account) + public function __construct($account = null, $serializer = null, $invoice = null) { - parent::__construct($account); - + parent::__construct($account, $serializer); + + $this->invoice = $invoice; } public function includeInvoice(Payment $payment) @@ -57,7 +58,7 @@ class PaymentTransformer extends EntityTransformer 'archived_at' => $this->getTimestamp($payment->deleted_at), 'is_deleted' => (bool) $payment->is_deleted, 'payment_type_id' => (int) $payment->payment_type_id, - 'invoice_id' => (int) $payment->invoice->public_id, + 'invoice_id' => (int) ($this->invoice ? $this->invoice->public_id : $payment->invoice->public_id), ]; } } \ No newline at end of file From 0645cdddb15b8487c50432c447722bbe942823f6 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 09:47:47 +0300 Subject: [PATCH 180/258] Fix for travis tests --- tests/acceptance/ExpenseCest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index 72d7666aab9d..6fb3db0ff192 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -48,8 +48,10 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); + $I->wait(2); + $I->click('Save'); - $I->wait(3); + $I->wait(2); $I->see($clientEmail); $I->see($amount); } From be420ad1fc7cfadc62c9bfa8c66fa769103d81d0 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 11:39:10 +0300 Subject: [PATCH 181/258] API optimizations --- app/Http/Controllers/BaseAPIController.php | 15 +++++++++------ app/Ninja/Transformers/EntityTransformer.php | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 07ed5bb42f03..a1983ed2b5f0 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -87,7 +87,12 @@ class BaseAPIController extends Controller protected function listResponse($query) { - $query->with($this->getIncluded()); + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $include = $transformer->getDefaultIncludes(); + $include = $this->getRequestIncludes($include); + $query->with($include); if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { @@ -104,9 +109,6 @@ class BaseAPIController extends Controller } } - $transformerClass = EntityModel::getTransformerName($this->entityType); - $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); - $data = $this->createCollection($query, $transformer, $this->entityType); return $this->response($data); @@ -154,6 +156,7 @@ class BaseAPIController extends Controller if (Utils::isNinjaDev()) { $count = count(\DB::getQueryLog()); Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); + //Log::info(print_r(\DB::getQueryLog(), true)); } $index = Request::get('index') ?: 'data'; @@ -188,9 +191,9 @@ class BaseAPIController extends Controller } - protected function getIncluded() + protected function getRequestIncludes($data) { - $data = ['user']; + $data[] = 'user'; $included = Request::get('include'); $included = explode(',', $included); diff --git a/app/Ninja/Transformers/EntityTransformer.php b/app/Ninja/Transformers/EntityTransformer.php index ea0dba263d97..af1304acd67f 100644 --- a/app/Ninja/Transformers/EntityTransformer.php +++ b/app/Ninja/Transformers/EntityTransformer.php @@ -37,4 +37,9 @@ class EntityTransformer extends TransformerAbstract { return $date ? $date->getTimestamp() : null; } + + public function getDefaultIncludes() + { + return $this->defaultIncludes; + } } From 871456d402bffb441b8a404a2003afb8d7394648 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 11:40:17 +0300 Subject: [PATCH 182/258] Fix for travis test --- tests/acceptance/ExpenseCest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index 6fb3db0ff192..e22bebac3b7f 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -48,10 +48,9 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); - $I->wait(2); - + $I->wait(3); $I->click('Save'); - $I->wait(2); + $I->wait(3); $I->see($clientEmail); $I->see($amount); } From bf4c4d0ce556f504f49ca81cc1fcb63070b9d8e1 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 11:53:00 +0300 Subject: [PATCH 183/258] Add user permissions to API delete --- app/Http/Controllers/BaseAPIController.php | 3 +++ app/Http/Controllers/ClientApiController.php | 17 +++++------------ app/Http/Controllers/InvoiceApiController.php | 13 ++++--------- app/Http/Controllers/PaymentApiController.php | 18 +++++++----------- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index a1983ed2b5f0..db99a3beaf1d 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -202,6 +202,9 @@ class BaseAPIController extends Controller if ($include == 'invoices') { $data[] = 'invoices.invoice_items'; $data[] = 'invoices.user'; + } elseif ($include == 'client') { + $data[] = 'client.contacts'; + $data[] = 'client.user'; } elseif ($include == 'clients') { $data[] = 'clients.contacts'; $data[] = 'clients.user'; diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 557daa29fff6..dd82e9116131 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -143,20 +143,13 @@ class ClientApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdateClientRequest $request) { - $client = Client::scope($publicId)->withTrashed()->first(); + $client = $request->entity(); + $this->clientRepo->delete($client); - $client = Client::scope($publicId) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->withTrashed() - ->first(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } - + } \ No newline at end of file diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index da8bc2b7a6cf..1361aad9e9a3 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -349,18 +349,13 @@ class InvoiceApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdateInvoiceAPIRequest $request) { - $data['public_id'] = $publicId; - $invoice = Invoice::scope($publicId)->firstOrFail(); - + $invoice = $request->entity(); + $this->invoiceRepo->delete($invoice); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($invoice); } } diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index b41100acfad9..0dd8de50803b 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -49,7 +49,7 @@ class PaymentApiController extends BaseAPIController { $payments = Payment::scope() ->withTrashed() - ->with(['client.contacts', 'invitation', 'user', 'invoice']) + ->with(['invoice']) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); @@ -145,17 +145,13 @@ class PaymentApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdatePaymentRequest $request) { + $payment = $request->entity(); + + $this->clientRepo->delete($payment); - $payment = Payment::scope($publicId)->withTrashed()->first(); - $invoiceId = $payment->invoice->public_id; - - $this->paymentRepo->delete($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($payment); } + } From 75ee124626bece7516de5a4bf952ae78713d7aaa Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 14:05:35 +0300 Subject: [PATCH 184/258] Fix for TD Bank w/OFX --- app/Libraries/OFX.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/Libraries/OFX.php b/app/Libraries/OFX.php index 5563e917acf7..721c9f529f85 100644 --- a/app/Libraries/OFX.php +++ b/app/Libraries/OFX.php @@ -2,6 +2,8 @@ // https://github.com/denvertimothy/OFX +use Utils; +use Log; use SimpleXMLElement; class OFX @@ -21,13 +23,19 @@ class OFX $c = curl_init(); curl_setopt($c, CURLOPT_URL, $this->bank->url); curl_setopt($c, CURLOPT_POST, 1); - curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx')); + // User-Agent: http://www.ofxhome.com/ofxforum/viewtopic.php?pid=108091#p108091 + curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx', 'User-Agent: httpclient')); curl_setopt($c, CURLOPT_POSTFIELDS, $this->request); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); + $this->response = curl_exec($c); - //print_r($this->response); - //\Log::info(print_r($this->response, true)); + + if (Utils::isNinjaDev()) { + Log::info(print_r($this->response, true)); + } + curl_close($c); + $tmp = explode('', $this->response); $this->responseHeader = $tmp[0]; $this->responseBody = ''.$tmp[1]; @@ -35,14 +43,15 @@ class OFX public function xml() { $xml = $this->responseBody; - self::closeTags($xml); + $xml = self::closeTags($xml); $x = new SimpleXMLElement($xml); return $x; } - public static function closeTags(&$x) + public static function closeTags($x) { - $x = preg_replace('/(<([^<\/]+)>)(?!.*?<\/\2>)([^<]+)/', '\1\3', $x); + $x = preg_replace('/\s+/', '', $x); + return preg_replace('/(<([^<\/]+)>)(?!.*?<\/\2>)([^<]+)/', '\1\3', $x); } } @@ -224,3 +233,4 @@ class Account } } } + From 39141d469f5f69ec9eb1923e6a7968fe2430c346 Mon Sep 17 00:00:00 2001 From: Bartlomiej Szala Date: Tue, 3 May 2016 12:47:26 +0000 Subject: [PATCH 185/258] Add polish translations --- ...16_05_02_071859_add_polish_translation.php | 29 + resources/lang/pl/pagination.php | 20 + resources/lang/pl/passwords.php | 22 + resources/lang/pl/reminders.php | 24 + resources/lang/pl/texts.php | 1184 +++++++++++++++++ resources/lang/pl/validation.php | 106 ++ 6 files changed, 1385 insertions(+) create mode 100644 database/migrations/2016_05_02_071859_add_polish_translation.php create mode 100644 resources/lang/pl/pagination.php create mode 100644 resources/lang/pl/passwords.php create mode 100644 resources/lang/pl/reminders.php create mode 100644 resources/lang/pl/texts.php create mode 100644 resources/lang/pl/validation.php diff --git a/database/migrations/2016_05_02_071859_add_polish_translation.php b/database/migrations/2016_05_02_071859_add_polish_translation.php new file mode 100644 index 000000000000..b78969cfbbde --- /dev/null +++ b/database/migrations/2016_05_02_071859_add_polish_translation.php @@ -0,0 +1,29 @@ +insert(['name' => 'Polish', 'locale' => self::LANGUAGE_LOCALE]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + $language = \App\Models\Language::whereLocale(self::LANGUAGE_LOCALE)->first(); + $language->delete(); + } +} diff --git a/resources/lang/pl/pagination.php b/resources/lang/pl/pagination.php new file mode 100644 index 000000000000..950c5e96cc41 --- /dev/null +++ b/resources/lang/pl/pagination.php @@ -0,0 +1,20 @@ + '« Poprzedni', + + 'next' => 'Następny »', + +); diff --git a/resources/lang/pl/passwords.php b/resources/lang/pl/passwords.php new file mode 100644 index 000000000000..28ccb957b349 --- /dev/null +++ b/resources/lang/pl/passwords.php @@ -0,0 +1,22 @@ + "Hasło musi mieć conajmniej sześć znaków i być takie samo jak potwierdzające.", + "user" => "Użytkownik o podanym adresie e-mail nie istnieje.", + "token" => "Wprowadzony token jest nieprawidłowy.", + "sent" => "Link do resetowania hasła został wysłany.", + "reset" => "Twoje hasło zostało zresetowane!", + +]; \ No newline at end of file diff --git a/resources/lang/pl/reminders.php b/resources/lang/pl/reminders.php new file mode 100644 index 000000000000..25824a7f285a --- /dev/null +++ b/resources/lang/pl/reminders.php @@ -0,0 +1,24 @@ + "Hasło musi mieć conajmniej sześć znaków i być takie samo jak potwierdzające.", + + "user" => "Użytkownik o podanym adresie e-mail nie istnieje.", + + "token" => "Wprowadzony token jest nieprawidłowy.", + + "sent" => "Przypomnienie hasła zostało wysłane!", + +); \ No newline at end of file diff --git a/resources/lang/pl/texts.php b/resources/lang/pl/texts.php new file mode 100644 index 000000000000..19b0253937db --- /dev/null +++ b/resources/lang/pl/texts.php @@ -0,0 +1,1184 @@ + 'Organizacja', + 'name' => 'Nazwa', + 'website' => 'Strona internetowa', + 'work_phone' => 'Telefon służbowy', + 'address' => 'Adres', + 'address1' => 'Ulica', + 'address2' => 'Nr', + 'city' => 'Miasto', + 'state' => 'Województwo', + 'postal_code' => 'Kod pocztowy', + 'country_id' => 'Kraj', + 'contacts' => 'Kontakty', + 'first_name' => 'Imię', + 'last_name' => 'Nazwisko', + 'phone' => 'Telefon', + 'email' => 'Email', + 'additional_info' => 'Dodatkowe informacje', + 'payment_terms' => 'Warunki płatnicze', + 'currency_id' => 'Waluta', + 'size_id' => 'Wielkość firmy', + 'industry_id' => 'Branża', + 'private_notes' => 'Prywatne notatki', + 'invoice' => 'Faktura', + 'client' => 'Klient', + 'invoice_date' => 'Data Faktury', + 'due_date' => 'Termin', + 'invoice_number' => 'Numer Faktury', + 'invoice_number_short' => 'Faktura #', + 'po_number' => 'PO Number', + 'po_number_short' => 'PO #', + 'frequency_id' => 'Jak często', + 'discount' => 'Przecena', + 'taxes' => 'Podatki', + 'tax' => 'Podatek', + 'item' => 'Pozycja', + 'description' => 'Opis', + 'unit_cost' => 'Cena jednostkowa', + 'quantity' => 'Ilość', + 'line_total' => 'Wartość całkowita', + 'subtotal' => 'Suma częściowa', + 'paid_to_date' => 'Wypłacono do tej pory', + 'balance_due' => 'Balance Due', + 'invoice_design_id' => 'Szablon', + 'terms' => 'Warunki', + 'your_invoice' => 'Twoja faktura', + 'remove_contact' => 'Usuń kontakt', + 'add_contact' => 'Dodaj kontakt', + 'create_new_client' => 'Dodaj nowego klienta', + 'edit_client_details' => 'Edytuj dane klienta', + 'enable' => 'Aktywuj', + 'learn_more' => 'Więcej informacji', + 'manage_rates' => 'Zarządzaj stawkami', + 'note_to_client' => 'Informacja dla klienta', + 'invoice_terms' => 'Warunki do faktury', + 'save_as_default_terms' => 'Zapisz jako domyślne warunki', + 'download_pdf' => 'Pobierz PDF', + 'pay_now' => 'Zapłać teraz', + 'save_invoice' => 'Zapisz Fakturę', + 'clone_invoice' => 'Skopiuj Fakturę', + 'archive_invoice' => 'Zarchiwizuj Fakturę', + 'delete_invoice' => 'Usuń Fakturę', + 'email_invoice' => 'Wyślij Fakturę', + 'enter_payment' => 'Wprowadź Płatność', + 'tax_rates' => 'Stawki podatkowe', + 'rate' => 'Stawka', + 'settings' => 'Ustawienia', + 'enable_invoice_tax' => 'Aktywuj możliwość ustawienia podatku do faktury', + 'enable_line_item_tax' => 'Aktywuj możliwość ustawienia podatku do pozycji na fakturze', + 'dashboard' => 'Pulpit', + 'clients' => 'Klienci', + 'invoices' => 'Faktury', + 'payments' => 'Płatności', + 'credits' => 'Kredyty', + 'history' => 'Historia', + 'search' => 'Szukaj', + 'sign_up' => 'Zapisz się', + 'guest' => 'Gość', + 'company_details' => 'Dane Firmy', + 'online_payments' => 'Płatności Online', + 'notifications' => 'Powiadomienia E-mail', + 'import_export' => 'Import | Export', + 'done' => 'Gotowe', + 'save' => 'Zapisz', + 'create' => 'Dodaj', + 'upload' => 'Prześlij', + 'import' => 'Import', + 'download' => 'Pobierz', + 'cancel' => 'Anuluj', + 'close' => 'Zamknij', + 'provide_email' => 'Wprowadź poprawny e-mail.', + 'powered_by' => 'Oparte na', + 'no_items' => 'Brak pozycji', + 'recurring_invoices' => 'Faktury okresowe', + 'recurring_help' => '

    Wysyła automatycznie te same faktury klientom tygodniowo, miesięcznie, kwartałowo lub rocznie.

    +

    Użyj :MONTH, :QUARTER lub :YEAR dla dynamicznych dat. Podstawowa arytmetyka działa również, np: :MONTH-1.

    +

    Przykłady dynamicznych zmiennych na fakturze:

    +
      +
    • "Gym membership for the month of :MONTH" => "Gym membership for the month of July"
    • +
    • ":YEAR+1 yearly subscription" => "2015 Yearly Subscription"
    • +
    • "Retainer payment for :QUARTER+1" => "Retainer payment for Q2"
    • +
    ', + 'in_total_revenue' => 'całkowity przychód', + 'billed_client' => 'Obciążony klient', + 'billed_clients' => 'Obciążeni klienci', + 'active_client' => 'Aktywny klient', + 'active_clients' => 'Aktywni klienci', + 'invoices_past_due' => 'Opóźnione faktury', + 'upcoming_invoices' => 'Nadchodzące Faktury', + 'average_invoice' => 'Średnia wartość Faktury', + 'archive' => 'Archiwum', + 'delete' => 'Usuń', + 'archive_client' => 'Zarchiwizuj klienta', + 'delete_client' => 'Usuń klienta', + 'archive_payment' => 'Zarchiwizuj płatność', + 'delete_payment' => 'Usuń płatność', + 'archive_credit' => 'Zarchiwizuj kredyt', + 'delete_credit' => 'Usuń kredyt', + 'show_archived_deleted' => 'Pokaż zarchiwizowane/usunięte', + 'filter' => 'Filtruj', + 'new_client' => 'Nowy klient', + 'new_invoice' => 'Nowa Faktura', + 'new_payment' => 'Nowa Płatność', + 'new_credit' => 'Nowy Kredyt', + 'contact' => 'Kontakt', + 'date_created' => 'Data utworzenia', + 'last_login' => 'Ostatnie logowanie', + 'balance' => 'Saldo', + 'action' => 'Akcja', + 'status' => 'Status', + 'invoice_total' => 'Faktura ogółem', + 'frequency' => 'Częstotliwość', + 'start_date' => 'Początkowa data', + 'end_date' => 'Końcowa data', + 'transaction_reference' => 'Referencja transakcji', + 'method' => 'Metoda', + 'payment_amount' => 'Kwota płatności', + 'payment_date' => 'Data płatności', + 'credit_amount' => 'Kwota kredytu', + 'credit_balance' => 'Saldo kredytowe', + 'credit_date' => 'Data kredytu', + 'empty_table' => 'Brak danych w tabeli', + 'select' => 'Wybierz', + 'edit_client' => 'Edytuj klienta', + 'edit_invoice' => 'Edytuj fakturę', + 'create_invoice' => 'Utwórz Fakturę', + 'enter_credit' => 'Wprowadź kredyt', + 'last_logged_in' => 'Ostatnie logowanie w', + 'details' => 'Szczegóły', + 'standing' => 'Standing', + 'credit' => 'Kredyt', + 'activity' => 'Czynność', + 'date' => 'Data', + 'message' => 'Wiadomość', + 'adjustment' => 'Dostosowanie', + 'are_you_sure' => 'Jesteś pewny?', + 'payment_type_id' => 'Typ płatności', + 'amount' => 'Kwota', + 'work_email' => 'Email', + 'language_id' => 'Język', + 'timezone_id' => 'Strefa czasowa', + 'date_format_id' => 'Format daty', + 'datetime_format_id' => 'Format Data/Godzina', + 'users' => 'Użytkownicy', + 'localization' => 'Lokalizacja', + 'remove_logo' => 'Usuń logo', + 'logo_help' => 'Obsługiwane: JPEG, GIF i PNG', + 'payment_gateway' => 'Bramka płatnicza', + 'gateway_id' => 'Bramka płatnicza', + 'email_notifications' => 'Email powiadomienia', + 'email_sent' => 'Powiadom mnie kiedy faktura jest wysłana', + 'email_viewed' => 'Powiadom mnie kiedy faktura jest otworzona', + 'email_paid' => 'Powiadom mnie kiedy faktura jest zapłacona', + 'site_updates' => 'Aktualizacje strony', + 'custom_messages' => 'Niestandardowe komunikaty', + 'default_email_footer' => 'Ustaw domyślny podpis email', + 'select_file' => 'Wybierz plik', + 'first_row_headers' => 'Użyj pierwszego wiersza jako nagłówek', + 'column' => 'Kolumna', + 'sample' => 'Przykład', + 'import_to' => 'Zaimportuj do', + 'client_will_create' => 'klient będzie utworzony', + 'clients_will_create' => 'klienci będą utworzeni', + 'email_settings' => 'Ustawienia e-mail', + 'client_view_styling' => 'Client View Styling', + 'pdf_email_attachment' => 'Attach PDFs', + 'custom_css' => 'Custom CSS', + 'import_clients' => 'Import Client Data', + 'csv_file' => 'CSV file', + 'export_clients' => 'Export Client Data', + 'created_client' => 'Successfully created client', + 'created_clients' => 'Successfully created :count client(s)', + 'updated_settings' => 'Successfully updated settings', + 'removed_logo' => 'Successfully removed logo', + 'sent_message' => 'Successfully sent message', + 'invoice_error' => 'Please make sure to select a client and correct any errors', + 'limit_clients' => 'Sorry, this will exceed the limit of :count clients', + 'payment_error' => 'There was an error processing your payment. Please try again later.', + 'registration_required' => 'Please sign up to email an invoice', + 'confirmation_required' => 'Please confirm your email address', + 'updated_client' => 'Successfully updated client', + 'created_client' => 'Successfully created client', + 'archived_client' => 'Successfully archived client', + 'archived_clients' => 'Successfully archived :count clients', + 'deleted_client' => 'Successfully deleted client', + 'deleted_clients' => 'Successfully deleted :count clients', + 'updated_invoice' => 'Successfully updated invoice', + 'created_invoice' => 'Successfully created invoice', + 'cloned_invoice' => 'Successfully cloned invoice', + 'emailed_invoice' => 'Successfully emailed invoice', + 'and_created_client' => 'and created client', + 'archived_invoice' => 'Successfully archived invoice', + 'archived_invoices' => 'Successfully archived :count invoices', + 'deleted_invoice' => 'Successfully deleted invoice', + 'deleted_invoices' => 'Successfully deleted :count invoices', + 'created_payment' => 'Successfully created payment', + 'created_payments' => 'Successfully created :count payment(s)', + 'archived_payment' => 'Successfully archived payment', + 'archived_payments' => 'Successfully archived :count payments', + 'deleted_payment' => 'Successfully deleted payment', + 'deleted_payments' => 'Successfully deleted :count payments', + 'applied_payment' => 'Successfully applied payment', + 'created_credit' => 'Successfully created credit', + 'archived_credit' => 'Successfully archived credit', + 'archived_credits' => 'Successfully archived :count credits', + 'deleted_credit' => 'Successfully deleted credit', + 'deleted_credits' => 'Successfully deleted :count credits', + 'imported_file' => 'Successfully imported file', + 'updated_vendor' => 'Successfully updated vendor', + 'created_vendor' => 'Successfully created vendor', + 'archived_vendor' => 'Successfully archived vendor', + 'archived_vendors' => 'Successfully archived :count vendors', + 'deleted_vendor' => 'Successfully deleted vendor', + 'deleted_vendors' => 'Successfully deleted :count vendors', + 'confirmation_subject' => 'Invoice Ninja Account Confirmation', + 'confirmation_header' => 'Account Confirmation', + 'confirmation_message' => 'Please access the link below to confirm your account.', + 'invoice_subject' => 'New invoice :invoice from :account', + 'invoice_message' => 'To view your invoice for :amount, click the link below.', + 'payment_subject' => 'Payment Received', + 'payment_message' => 'Thank you for your payment of :amount.', + 'email_salutation' => 'Drogi :name,', + 'email_signature' => 'Pozdrowienia,', + 'email_from' => 'Zespół The Invoice Ninja', + 'invoice_link_message' => 'To view the invoice click the link below:', + 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client', + 'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client', + 'notification_invoice_viewed_subject' => 'Invoice :invoice was viewed by :client', + 'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.', + 'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.', + 'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.', + 'reset_password' => 'You can reset your account password by clicking the following button:', + 'secure_payment' => 'Bezpieczna płatność', + 'card_number' => 'Numer karty', + 'expiration_month' => 'Expiration Month', + 'expiration_year' => 'Expiration Year', + 'cvv' => 'CVV', + 'logout' => 'Wyloguj się', + 'sign_up_to_save' => 'Sign up to save your work', + 'agree_to_terms' => 'I agree to the Invoice Ninja :terms', + 'terms_of_service' => 'Terms of Service', + 'email_taken' => 'The email address is already registered', + 'working' => 'Working', + 'success' => 'Success', + 'success_message' => 'You have successfully registered! Please visit the link in the account confirmation email to verify your email address.', + 'erase_data' => 'This will permanently erase your data.', + 'password' => 'Hasło', + 'pro_plan_product' => 'Pro Plan', + 'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!

     
    + Next Steps

    A payable invoice has been sent to the email + address associated with your account. To unlock all of the awesome + Pro features, please follow the instructions on the invoice to pay + for a year of Pro-level invoicing.

    + Can\'t find the invoice? Need further assistance? We\'re happy to help + -- email us at contact@invoiceninja.com', + 'unsaved_changes' => 'You have unsaved changes', + 'custom_fields' => 'Custom Fields', + 'company_fields' => 'Company Fields', + 'client_fields' => 'Client Fields', + 'field_label' => 'Field Label', + 'field_value' => 'Field Value', + 'edit' => 'Edytuj', + 'set_name' => 'Set your company name', + 'view_as_recipient' => 'View as recipient', + 'product_library' => 'Product Library', + 'product' => 'Produkt', + 'products' => 'Produkty', + 'fill_products' => 'Auto-fill products', + 'fill_products_help' => 'Selecting a product will automatically fill in the description and cost', + 'update_products' => 'Auto-update products', + 'update_products_help' => 'Updating an invoice will automatically update the product library', + 'create_product' => 'Add Product', + 'edit_product' => 'Edit Product', + 'archive_product' => 'Archive Product', + 'updated_product' => 'Successfully updated product', + 'created_product' => 'Successfully created product', + 'archived_product' => 'Successfully archived product', + 'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan', + 'advanced_settings' => 'Advanced Settings', + 'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan', + 'invoice_design' => 'Invoice Design', + 'specify_colors' => 'Specify colors', + 'specify_colors_label' => 'Select the colors used in the invoice', + 'chart_builder' => 'Chart Builder', + 'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!', + 'go_pro' => 'Go Pro', + 'quote' => 'Oferta', + 'quotes' => 'Oferty', + 'quote_number' => 'Numer oferty', + 'quote_number_short' => 'Oferta #', + 'quote_date' => 'Data oferty', + 'quote_total' => 'Suma oferty', + 'your_quote' => 'Twoja oferta', + 'total' => 'Suma', + 'clone' => 'Clone', + 'new_quote' => 'Nowa oferta', + 'create_quote' => 'Stwórz ofertę', + 'edit_quote' => 'Edytuj ofertę', + 'archive_quote' => 'Archiwizuj ofertę', + 'delete_quote' => 'Usuń ofertę', + 'save_quote' => 'Zapisz ofertę', + 'email_quote' => 'Wyślij ofertę', + 'clone_quote' => 'Clone Quote', + 'convert_to_invoice' => 'Konwertuj do faktury', + 'view_invoice' => 'Zobacz fakturę', + 'view_client' => 'Zobacz klienta', + 'view_quote' => 'Zobacz ofertę', + 'updated_quote' => 'Successfully updated quote', + 'created_quote' => 'Successfully created quote', + 'cloned_quote' => 'Successfully cloned quote', + 'emailed_quote' => 'Successfully emailed quote', + 'archived_quote' => 'Successfully archived quote', + 'archived_quotes' => 'Successfully archived :count quotes', + 'deleted_quote' => 'Successfully deleted quote', + 'deleted_quotes' => 'Successfully deleted :count quotes', + 'converted_to_invoice' => 'Successfully converted quote to invoice', + 'quote_subject' => 'New quote $quote from :account', + 'quote_message' => 'To view your quote for :amount, click the link below.', + 'quote_link_message' => 'To view your client quote click the link below:', + 'notification_quote_sent_subject' => 'Quote :invoice was sent to :client', + 'notification_quote_viewed_subject' => 'Quote :invoice was viewed by :client', + 'notification_quote_sent' => 'The following client :client was emailed Quote :invoice for :amount.', + 'notification_quote_viewed' => 'The following client :client viewed Quote :invoice for :amount.', + 'session_expired' => 'Your session has expired.', + 'invoice_fields' => 'Invoice Fields', + 'invoice_options' => 'Invoice Options', + 'hide_quantity' => 'Hide Quantity', + 'hide_quantity_help' => 'If your line items quantities are always 1, then you can declutter invoices by no longer displaying this field.', + 'hide_paid_to_date' => 'Hide Paid to Date', + 'hide_paid_to_date_help' => 'Only display the "Paid to Date" area on your invoices once a payment has been received.', + 'charge_taxes' => 'Charge taxes', + 'user_management' => 'User Management', + 'add_user' => 'Add User', + 'send_invite' => 'Send invitation', + 'sent_invite' => 'Successfully sent invitation', + 'updated_user' => 'Successfully updated user', + 'invitation_message' => 'You\'ve been invited by :invitor. ', + 'register_to_add_user' => 'Please sign up to add a user', + 'user_state' => 'State', + 'edit_user' => 'Edit User', + 'delete_user' => 'Delete User', + 'active' => 'Active', + 'pending' => 'Pending', + 'deleted_user' => 'Successfully deleted user', + 'confirm_email_invoice' => 'Are you sure you want to email this invoice?', + 'confirm_email_quote' => 'Are you sure you want to email this quote?', + 'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?', + 'cancel_account' => 'Usuń konto', + 'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.', + 'go_back' => 'Go Back', + 'data_visualizations' => 'Data Visualizations', + 'sample_data' => 'Sample data shown', + 'hide' => 'Hide', + 'new_version_available' => 'A new version of :releases_link is available. You\'re running v:user_version, the latest is v:latest_version', + 'invoice_settings' => 'Invoice Settings', + 'invoice_number_prefix' => 'Invoice Number Prefix', + 'invoice_number_counter' => 'Invoice Number Counter', + 'quote_number_prefix' => 'Quote Number Prefix', + 'quote_number_counter' => 'Quote Number Counter', + 'share_invoice_counter' => 'Share invoice counter', + 'invoice_issued_to' => 'Invoice issued to', + 'invalid_counter' => 'To prevent a possible conflict please set either an invoice or quote number prefix', + 'mark_sent' => 'Mark Sent', + 'gateway_help_1' => ':link to sign up for Authorize.net.', + 'gateway_help_2' => ':link to sign up for Authorize.net.', + 'gateway_help_17' => ':link to get your PayPal API signature.', + 'gateway_help_27' => ':link to sign up for TwoCheckout.', + 'more_designs' => 'More designs', + 'more_designs_title' => 'Additional Invoice Designs', + 'more_designs_cloud_header' => 'Go Pro for more invoice designs', + 'more_designs_cloud_text' => '', + 'more_designs_self_host_text' => '', + 'buy' => 'Buy', + 'bought_designs' => 'Successfully added additional invoice designs', + 'sent' => 'sent', + 'vat_number' => 'VAT Number', + 'timesheets' => 'Timesheets', + 'payment_title' => 'Enter Your Billing Address and Credit Card information', + 'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', + 'payment_footer1' => '*Billing address must match address associated with credit card.', + 'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', + 'id_number' => 'ID Number', + 'white_label_link' => 'White label', + 'white_label_header' => 'White Label', + 'bought_white_label' => 'Successfully enabled white label license', + 'white_labeled' => 'White labeled', + 'restore' => 'Przywróć', + 'restore_invoice' => 'Przywróć fakturę', + 'restore_quote' => 'Przywróć ofertę', + 'restore_client' => 'Przywróć klienta', + 'restore_credit' => 'Przywróć kredyt', + 'restore_payment' => 'Przywróć płatność', + 'restored_invoice' => 'Faktura została przywrócona', + 'restored_quote' => 'Oferta została przywrócona', + 'restored_client' => 'Klient został przywrócony', + 'restored_payment' => 'Płatność została przywrócona', + 'restored_credit' => 'Kredyt został przywrócony', + 'reason_for_canceling' => 'Help us improve our site by telling us why you\'re leaving.', + 'discount_percent' => 'Procent', + 'discount_amount' => 'Kwota', + 'invoice_history' => 'Historia faktury', + 'quote_history' => 'Historia oferty', + 'current_version' => 'Aktualna wersja', + 'select_version' => 'Wybierz wersję', + 'view_history' => 'Zobacz historię', + 'edit_payment' => 'Edytuj płatność', + 'updated_payment' => 'Successfully updated payment', + 'deleted' => 'Usunięte', + 'restore_user' => 'Przywróć użytkownika', + 'restored_user' => 'Użytkownik został przywrócony', + 'show_deleted_users' => 'Pokaż usuniętych użytkowników', + 'email_templates' => 'Szablony e-mail', + 'invoice_email' => 'Email faktury', + 'payment_email' => 'Email płatności', + 'quote_email' => 'Email oferty', + 'reset_all' => 'Resetuj wszystko', + 'approve' => 'Zatwierdź', + 'token_billing_type_id' => 'Token Billing', + 'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', + 'token_billing_1' => 'Wyłączone', + 'token_billing_2' => 'Opt-in - checkbox is shown but not selected', + 'token_billing_3' => 'Opt-out - checkbox is shown and selected', + 'token_billing_4' => 'Zawsze', + 'token_billing_checkbox' => 'Store credit card details', + 'view_in_stripe' => 'View in Stripe', + 'use_card_on_file' => 'Use card on file', + 'edit_payment_details' => 'Edit payment details', + 'token_billing' => 'Save card details', + 'token_billing_secure' => 'The data is stored securely by :stripe_link', + 'support' => 'Support', + 'contact_information' => 'Contact Information', + '256_encryption' => '256-Bit Encryption', + 'amount_due' => 'Amount due', + 'billing_address' => 'Adres billingowy (rozliczeniowy)', + 'billing_method' => 'Billing Method', + 'order_overview' => 'Order overview', + 'match_address' => '*Address must match address associated with credit card.', + 'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', + 'invoice_footer' => 'Stopka faktury', + 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'updated_token' => 'Successfully updated token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + 'add_gateway' => 'Add Gateway', + 'delete_gateway' => 'Delete Gateway', + 'edit_gateway' => 'Edit Gateway', + 'updated_gateway' => 'Successfully updated gateway', + 'created_gateway' => 'Successfully created gateway', + 'deleted_gateway' => 'Successfully deleted gateway', + 'pay_with_paypal' => 'PayPal', + 'pay_with_card' => 'Credit Card', + 'change_password' => 'Zmień hasło', + 'current_password' => 'Aktualne hasło', + 'new_password' => 'Nowe hasło', + 'confirm_password' => 'Potwierdź hasło', + 'password_error_incorrect' => 'The current password is incorrect.', + 'password_error_invalid' => 'The new password is invalid.', + 'updated_password' => 'Successfully updated password', + 'api_tokens' => 'API Tokens', + 'users_and_tokens' => 'Users & Tokens', + 'account_login' => 'Account Login', + 'recover_password' => 'Recover your password', + 'forgot_password' => 'Forgot your password?', + 'email_address' => 'Email address', + 'lets_go' => 'Let\'s go', + 'password_recovery' => 'Password Recovery', + 'send_email' => 'Wyślij email', + 'set_password' => 'Ustaw hasło', + 'converted' => 'Converted', + 'email_approved' => 'Email me when a quote is approved', + 'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', + 'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', + 'resend_confirmation' => 'Resend confirmation email', + 'confirmation_resent' => 'The confirmation email was resent', + 'gateway_help_42' => ':link to sign up for BitPay.
    Note: use a Legacy API Key, not an API token.', + 'payment_type_credit_card' => 'Karta kredytowa', + 'payment_type_paypal' => 'PayPal', + 'payment_type_bitcoin' => 'Bitcoin', + 'knowledge_base' => 'Baza wiedzy', + 'partial' => 'Partial', + 'partial_remaining' => ':partial of :balance', + 'more_fields' => 'More Fields', + 'less_fields' => 'Less Fields', + 'client_name' => 'Nazwa klienta', + 'pdf_settings' => 'Ustawienia PDF', + 'product_settings' => 'Ustawienia produktu', + 'auto_wrap' => 'Auto Line Wrap', + 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', + 'view_documentation' => 'View Documentation', + 'app_title' => 'Free Open-Source Online Invoicing', + 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', + 'rows' => 'wierszy', + 'www' => 'www', + 'logo' => 'Logo', + 'subdomain' => 'Subdomain', + 'provide_name_or_email' => 'Proszę podać imię i nazwisko lub adres e-mail', + 'charts_and_reports' => 'Raporty i wykresy', + 'chart' => 'Wykres', + 'report' => 'Raport', + 'group_by' => 'Grupuj według', + 'paid' => 'Zapłacone', + 'enable_report' => 'Raport', + 'enable_chart' => 'Wykres', + 'totals' => 'Suma', + 'run' => 'Run', + 'export' => 'Export', + 'documentation' => 'Documentation', + 'zapier' => 'Zapier', + 'recurring' => 'Okresowe', + 'last_invoice_sent' => 'Last invoice sent :date', + 'processed_updates' => 'Pomyślnie zakończona aktualizacja', + 'tasks' => 'Zadania', + 'new_task' => 'Nowe zadanie', + 'start_time' => 'Czas rozpoczęcia', + 'created_task' => 'Pomyślnie utworzono zadanie', + 'updated_task' => 'Pomyślnie zaktualizowano zadanie', + 'edit_task' => 'Edytuj zadanie', + 'archive_task' => 'Archiwizuj zadanie', + 'restore_task' => 'Przywróć zadanie', + 'delete_task' => 'Usuń zadanie', + 'stop_task' => 'Zatrzymaj zadanie', + 'time' => 'Czas', + 'start' => 'Rozpocznij', + 'stop' => 'Zatrzymaj', + 'now' => 'Teraz', + 'timer' => 'Czasomierz', + 'manual' => 'Manualnie', + 'date_and_time' => 'Data i czas', + 'second' => 'sekunda', + 'seconds' => 'sekund', + 'minute' => 'minuta', + 'minutes' => 'minut', + 'hour' => 'godzina', + 'hours' => 'godzin', + 'task_details' => 'Szczegóły zadania', + 'duration' => 'Czas trwania', + 'end_time' => 'Czas zakończenia', + 'end' => 'Koniec', + 'invoiced' => 'Invoiced', + 'logged' => 'Zalogowany', + 'running' => 'Running', + 'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', + 'task_error_running' => 'Please stop running tasks first', + 'task_error_invoiced' => 'Tasks have already been invoiced', + 'restored_task' => 'Successfully restored task', + 'archived_task' => 'Successfully archived task', + 'archived_tasks' => 'Successfully archived :count tasks', + 'deleted_task' => 'Successfully deleted task', + 'deleted_tasks' => 'Successfully deleted :count tasks', + 'create_task' => 'Create Task', + 'stopped_task' => 'Successfully stopped task', + 'invoice_task' => 'Invoice Task', + 'invoice_labels' => 'Invoice Labels', + 'prefix' => 'Prefix', + 'counter' => 'Counter', + 'payment_type_dwolla' => 'Dwolla', + 'gateway_help_43' => ':link to sign up for Dwolla', + 'partial_value' => 'Must be greater than zero and less than the total', + 'more_actions' => 'More Actions', + 'pro_plan_title' => 'NINJA PRO', + 'pro_plan_call_to_action' => 'Upgrade Now!', + 'pro_plan_feature1' => 'Create Unlimited Clients', + 'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs', + 'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"', + 'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"', + 'pro_plan_feature5' => 'Multi-user Access & Activity Tracking', + 'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices', + 'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', + 'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', + 'resume' => 'Resume', + 'break_duration' => 'Break', + 'edit_details' => 'Edit Details', + 'work' => 'Work', + 'timezone_unset' => 'Please :link to set your timezone', + 'click_here' => 'click here', + 'email_receipt' => 'Wyślij potwierdzenie zapłaty do klienta', + 'created_payment_emailed_client' => 'Successfully created payment and emailed client', + 'add_company' => 'Add Company', + 'untitled' => 'Untitled', + 'new_company' => 'New Company', + 'associated_accounts' => 'Successfully linked accounts', + 'unlinked_account' => 'Successfully unlinked accounts', + 'login' => 'Login', + 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'krotnie', + 'set_now' => 'Ustaw na teraz', + 'dark_mode' => 'Tryb ciemny', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + 'from' => 'From', + 'to' => 'To', + 'font_size' => 'Font Size', + 'primary_color' => 'Primary Color', + 'secondary_color' => 'Secondary Color', + 'customize_design' => 'Customize Design', + 'content' => 'Content', + 'styles' => 'Styles', + 'defaults' => 'Defaults', + 'margins' => 'Margins', + 'header' => 'Header', + 'footer' => 'Footer', + 'custom' => 'Custom', + 'invoice_to' => 'Invoice to', + 'invoice_no' => 'Invoice No.', + 'recent_payments' => 'Ostatnie płatności', + 'outstanding' => 'Zaległe', + 'manage_companies' => 'Manage Companies', + 'total_revenue' => 'Całkowity dochód', + 'current_user' => 'Aktualny użytkownik', + 'new_recurring_invoice' => 'Nowa faktura okresowa', + 'recurring_invoice' => 'Okresowa faktura', + 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date', + 'created_by_invoice' => 'Utworzona przez :invoice', + 'primary_user' => 'Główny Użytkownik', + 'help' => 'Pomoc', + 'customize_help' => '

    We use pdfmake to define the invoice designs declaratively. The pdfmake playground provide\'s a great way to see the library in action.

    +

    To access a child property using dot notation. For example to show the client name you could use $client.name.

    +

    If you need help figuring something out post a question to our support forum.

    ', + 'invoice_due_date' => 'Termin', + 'quote_due_date' => 'Valid Until', + 'valid_until' => 'Valid Until', + 'reset_terms' => 'Resetuj warunki', + 'reset_footer' => 'Resetuj stópkę', + 'invoices_sent' => ':count faktura wysłana|:count faktury wysłane', + 'status_draft' => 'Wersja robocza', + 'status_sent' => 'Wysłane', + 'status_viewed' => 'Przeglądnięte', + 'status_partial' => 'Częściowo', + 'status_paid' => 'Zapłacone', + 'show_line_item_tax' => 'Wyświetl podatki pozycji w tej samej linii', + 'iframe_url' => 'Website', + 'iframe_url_help1' => 'Copy the following code to a page on your site.', + 'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.', + 'auto_bill' => 'Automatyczny Rachunek', + 'military_time' => '24 godzinny czas', + 'last_sent' => 'Ostatnio wysłany', + 'reminder_emails' => 'Reminder Emails', + 'templates_and_reminders' => 'Szablony i przypomnienia', + 'subject' => 'Temat', + 'body' => 'Treść', + 'first_reminder' => 'Pierwsze przypomnienie', + 'second_reminder' => 'Drugie przypomnienie', + 'third_reminder' => 'Trzecie przypomnienie', + 'num_days_reminder' => 'Dni po terminie', + 'reminder_subject' => 'Reminder: Invoice :invoice from :account', + 'reset' => 'Reset', + 'invoice_not_found' => 'The requested invoice is not available', + 'referral_program' => 'Program referencyjny', + 'referral_code' => 'Referencyjny URL', + 'last_sent_on' => 'Ostatnio wysłany: :date', + 'page_expire' => 'This page will expire soon, :click_here to keep working', + 'upcoming_quotes' => 'Nadchodzące oferty', + 'expired_quotes' => 'Wygaśnięte oferty', + 'sign_up_using' => 'Zarejestruj się przy użyciu', + 'invalid_credentials' => 'These credentials do not match our records', + 'show_all_options' => 'Pokaż wszystkie opcje', + 'user_details' => 'Dane użytkownika', + 'oneclick_login' => 'One-Click Logowanie', + 'disable' => 'Wyłącz', + 'invoice_quote_number' => 'Numery faktur i ofert', + 'invoice_charges' => 'Opłaty faktury', + 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', + 'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', + 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.', + 'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice', + 'custom_invoice_link' => 'Custom Invoice Link', + 'total_invoiced' => 'Total Invoiced', + 'open_balance' => 'Open Balance', + 'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.', + 'basic_settings' => 'Ustawienia podstawowe', + 'pro' => 'Pro', + 'gateways' => 'Payment Gateways', + 'next_send_on' => 'Send Next: :date', + 'no_longer_running' => 'This invoice is not scheduled to run', + 'general_settings' => 'General Settings', + 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + 'enable_with_stripe' => 'Aktywuj | Wymaga Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Dodaj stawkę podatkową', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive Tax Rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Domyśłna stawka podatkowa', + 'tax_rate' => 'Stawka podatkowa', + 'recurring_hour' => 'Okresowa godzina', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + 'activity_30' => ':user created :vendor', + 'activity_31' => ':user created :vendor', + 'activity_32' => ':user created :vendor', + 'activity_33' => ':user created :vendor', + 'activity_34' => ':user created expense :expense', + 'activity_35' => ':user created :vendor', + 'activity_36' => ':user created :vendor', + 'activity_37' => ':user created :vendor', + 'payment' => 'Payment', + 'system' => 'System', + 'signature' => 'Podpis e-mail', + 'default_messages' => 'Default Messages', + 'quote_terms' => 'Warunki oferty', + 'default_quote_terms' => 'Domyślne warunki oferty', + 'default_invoice_terms' => 'Domyślne warunki faktury', + 'default_invoice_footer' => 'Domyślna stopka faktury', + 'quote_footer' => 'Quote Footer', + 'free' => 'Free', + 'quote_is_approved' => 'This quote is approved', + 'apply_credit' => 'Apply Credit', + 'system_settings' => 'System Settings', + 'archive_token' => 'Archive Token', + 'archived_token' => 'Successfully archived token', + 'archive_user' => 'Archive User', + 'archived_user' => 'Successfully archived user', + 'archive_account_gateway' => 'Archive Gateway', + 'archived_account_gateway' => 'Successfully archived gateway', + 'archive_recurring_invoice' => 'Archiwizuj okresową fakturę', + 'archived_recurring_invoice' => 'Okresowa faktura została zarchiwizowana', + 'delete_recurring_invoice' => 'Usuń okresową fakturę', + 'deleted_recurring_invoice' => 'Okresowa faktura została usunięta.', + 'restore_recurring_invoice' => 'Przywróć okresową fakturę', + 'restored_recurring_invoice' => 'Okresowa faktura została przywrócona', + 'archived' => 'Zarchiwizowany', + 'untitled_account' => 'Firma bez nazwy', + 'before' => 'Przed', + 'after' => 'Po', + 'reset_terms_help' => 'Reset to the default account terms', + 'reset_footer_help' => 'Reset to the default account footer', + 'export_data' => 'Exportuj dane', + 'user' => 'Użytkownik', + 'country' => 'Kraj', + 'include' => 'Include', + 'logo_too_large' => 'Your logo is :size, for better PDF performance we suggest uploading an image file less than 200KB', + 'import_freshbooks' => 'Import From FreshBooks', + 'import_data' => 'Import Data', + 'source' => 'Źródło', + 'csv' => 'CSV', + 'client_file' => 'Plik klienta', + 'invoice_file' => 'Plik faktury', + 'task_file' => 'Plik zadania', + 'no_mapper' => 'No valid mapping for file', + 'invalid_csv_header' => 'Invalid CSV Header', + 'client_portal' => 'Portal klienta', + 'admin' => 'Administrator', + 'disabled' => 'Wyłączony', + 'show_archived_users' => 'Pokaż zarchiwizowanych użytkowników', + 'notes' => 'Notatki', + 'invoice_will_create' => 'client will be created', + 'invoices_will_create' => 'invoices will be created', + 'failed_to_import' => 'The following records failed to import, they either already exist or are missing required fields.', + 'publishable_key' => 'Publishable Key', + 'secret_key' => 'Sekretny klucz', + 'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', + 'email_design' => 'Email Design', + 'due_by' => 'Płatny do :date', + 'enable_email_markup' => 'Enable Markup', + 'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', + 'template_help_title' => 'Templates Help', + 'template_help_1' => 'Available variables:', + 'email_design_id' => 'Email Style', + 'email_design_help' => 'Make your emails look more professional with HTML layouts', + 'plain' => 'Plain', + 'light' => 'Light', + 'dark' => 'Dark', + 'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', + 'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', + 'invoice_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the invoice number.', + 'quote_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the quote number.', + 'custom_client_fields_helps' => 'Add a field when creating a client and display the label and value on the PDF.', + 'custom_account_fields_helps' => 'Add a label and value to the company details section of the PDF.', + 'custom_invoice_fields_helps' => 'Add a field when creating an invoice and display the label and value on the PDF.', + 'custom_invoice_charges_helps' => 'Add a field when creating an invoice and include the charge in the invoice subtotals.', + 'token_expired' => 'Validation token was expired. Please try again.', + 'invoice_link' => 'Invoice Link', + 'button_confirmation_message' => 'Click to confirm your email address.', + 'confirm' => 'Confirm', + 'email_preferences' => 'Email Preferences', + 'created_invoices' => 'Successfully created :count invoice(s)', + 'next_invoice_number' => 'The next invoice number is :number.', + 'next_quote_number' => 'The next quote number is :number.', + 'days_before' => 'days before', + 'days_after' => 'days after', + 'field_due_date' => 'termin', + 'field_invoice_date' => 'invoice date', + 'schedule' => 'Schedule', + 'email_designs' => 'Email Designs', + 'assigned_when_sent' => 'Assigned when sent', + 'white_label_purchase_link' => 'Purchase a white label license', + 'expense' => 'Wydatek', + 'expenses' => 'Wydatki', + 'new_expense' => 'Nowy wydatek', + 'enter_expense' => 'Dodaj wydatek', + 'vendors' => 'Vendors', + 'new_vendor' => 'New Vendor', + 'payment_terms_net' => 'Net', + 'vendor' => 'Vendor', + 'edit_vendor' => 'Edit Vendor', + 'archive_vendor' => 'Archive Vendor', + 'delete_vendor' => 'Delete Vendor', + 'view_vendor' => 'View Vendor', + 'deleted_expense' => 'Successfully deleted expense', + 'archived_expense' => 'Successfully archived expense', + 'deleted_expenses' => 'Successfully deleted expenses', + 'archived_expenses' => 'Successfully archived expenses', + 'expense_amount' => 'Expense Amount', + 'expense_balance' => 'Expense Balance', + 'expense_date' => 'Expense Date', + 'expense_should_be_invoiced' => 'Should this expense be invoiced?', + 'public_notes' => 'Public Notes', + 'invoice_amount' => 'Invoice Amount', + 'exchange_rate' => 'Exchange Rate', + 'yes' => 'Yes', + 'no' => 'No', + 'should_be_invoiced' => 'Should be invoiced', + 'view_expense' => 'View expense # :expense', + 'edit_expense' => 'Edit Expense', + 'archive_expense' => 'Archive Expense', + 'delete_expense' => 'Delete Expense', + 'view_expense_num' => 'Expense # :expense', + 'updated_expense' => 'Successfully updated expense', + 'created_expense' => 'Successfully created expense', + 'enter_expense' => 'Dodaj wydatek', + 'view' => 'View', + 'restore_expense' => 'Przywróć wydatek', + 'invoice_expense' => 'Faktura na wydatek', + 'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients', + 'expense_error_invoiced' => 'Expense has already been invoiced', + 'convert_currency' => 'Konwersja waluty', + 'num_days' => 'Liczba dni', + 'create_payment_term' => 'Utwórz warunki płatności', + 'edit_payment_terms' => 'Edytuj warunki płatności', + 'edit_payment_term' => 'Edytuj warunki płatności', + 'archive_payment_term' => 'Zarchiwizuj warunki płatności', + 'recurring_due_dates' => 'Terminy faktur okresowych', + 'recurring_due_date_help' => '

    Automatycznie ustawia termin faktury.

    +

    Invoices on a monthly or yearly cycle set to be due on or before the day they are created will be due the next month. Invoices set to be due on the 29th or 30th in months that don\'t have that day will be due the last day of the month.

    +

    Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.

    +

    For example:

    +
      +
    • Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.
    • +
    • Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month. +
    • +
    • Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of next month. +
    • +
    • Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today. +
    • +
    ', + 'due' => 'Opłata', + 'next_due_on' => 'Następna opłata: :date', + 'use_client_terms' => 'Use client terms', + 'day_of_month' => ':ordinal day of month', + 'last_day_of_month' => 'Last day of month', + 'day_of_week_after' => ':ordinal :day after', + 'sunday' => 'Niedziela', + 'monday' => 'Poniedziałek', + 'tuesday' => 'Wtorek', + 'wednesday' => 'Środa', + 'thursday' => 'Czwartek', + 'friday' => 'Piątek', + 'saturday' => 'Sobota', + 'header_font_id' => 'Czcionka nagłówka', + 'body_font_id' => 'Czcionka', + 'color_font_help' => 'Notatka: Podstawowe kolory i czcionki są wykorzystywane na portalu klienta i w niestandardowych szablonach email-owych.', + 'live_preview' => 'Podgląd', + 'invalid_mail_config' => 'E-mail nie został wysłany. Sprawdź czy ustawienia mailowe są poprawne.', + 'invoice_message_button' => 'Aby wyświetlić fakturę za :amount, kliknij przycisk poniżej.', + 'quote_message_button' => 'Aby wyświetlić swoją ofertę na :amount, kliknij przycisk poniżej.', + 'payment_message_button' => 'Dziekuję za wpłatę :amount.', + 'payment_type_direct_debit' => 'Polecenie zapłaty', + 'bank_accounts' => 'Karty kredytowe i banki', + 'add_bank_account' => 'Dodaj konto bankowe', + 'setup_account' => 'Ustawienia konta', + 'import_expenses' => 'Koszty importu', + 'bank_id' => 'Bank', + 'integration_type' => 'Rodzaj integracji', + 'updated_bank_account' => 'Konto bankowe zostało zaktualizowane', + 'edit_bank_account' => 'Edytuj konto bankowe', + 'archive_bank_account' => 'Archiwizuj konto bankowe', + 'archived_bank_account' => 'Konto bankowe zostało zarchiwizowane.', + 'created_bank_account' => 'Konto bankowe zostało utworzone', + 'validate_bank_account' => 'Zatwierdź konto bankowe', + 'bank_password_help' => 'Notatka: Twoje hasło zostało wysłane bezpiecznie i nie jest przechowywane na naszych serwerach.', + 'bank_password_warning' => 'Ostrzeżenie: Twoje hasło może być wysłane w postaci zwykłego tekstu, rozwaź aktywację protokołu HTTPS.', + 'username' => 'Użytkownik', + 'account_number' => 'Numer konta', + 'account_name' => 'Nazwa konta', + 'bank_account_error' => 'Nie można pobrać danych konta, sprawdź uprawnienia.', + 'status_approved' => 'Zatwierdzono', + 'quote_settings' => 'Ustawienia oferty', + 'auto_convert_quote' => 'Automatycznie konwertuj ofertę', + 'auto_convert_quote_help' => 'Utwórz automatycznie fakturę z oferty zaakceptowanej przez klienta.', + 'validate' => 'Zatwierdź', + 'info' => 'Informacja', + 'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)', + 'iframe_url_help3' => 'Note: if you plan on accepting credit cards details we strongly recommend enabling HTTPS on your site.', + 'expense_error_multiple_currencies' => 'The expenses can\'t have different currencies.', + 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', + 'trello_roadmap' => 'Trello Roadmap', + 'header_footer' => 'Header/Footer', + 'first_page' => 'Pierwsza strona', + 'all_pages' => 'Wszystkie strony', + 'last_page' => 'Ostatnia strona', + 'all_pages_header' => 'Pokaż nagłówek na', + 'all_pages_footer' => 'Pokaż stopkę na', + 'invoice_currency' => 'Waluta faktury', + 'enable_https' => 'Zalecamy korzystanie z protokołu HTTPS, aby zaakceptować dane karty kredytowej online.', + 'quote_issued_to' => 'Oferta wydana do', + 'show_currency_code' => 'Kod waluty', + 'trial_message' => 'Twoje konto otrzyma bezpłatny dwutygodniowy okres próbny naszego pro planu.', + 'trial_footer' => 'Bezpłatny okres próbny ważny tylko :count dni, aby aktualizować kliknij: :link.', + 'trial_footer_last_day' => 'To ostatni dzień twojego bezpłatnego okresu próbnego, aby zaktualizować kliknij: :link.', + 'trial_call_to_action' => 'Rozpocznij darmowy okres próbny', + 'trial_success' => 'Darmowy okres próbny został włączony', + 'overdue' => 'Zaległy', + + + 'white_label_text' => 'Kup white label licencję na JEDEN ROK za $'.WHITE_LABEL_PRICE.' aby usunąć z portalu klienta logo Invoice Ninja i wesprzeć nasz projekt.', + 'user_email_footer' => 'Aby dostosować ustawienia powiadomień email, zobacz '.SITE_URL.'/settings/notifications', + 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, + 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', + 'more_designs_self_host_header' => 'Kup 6 szablonów faktur za jedyne $'.INVOICE_DESIGNS_PRICE, + 'old_browser' => 'Proszę użyć nowszej przeglądarki', + 'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', + 'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and 400+ US banks.', + 'security' => [ + 'too_many_attempts' => 'Zbyt wiele prób. Spróbuj ponownie za kilka minut.', + 'wrong_credentials' => 'Nieprawidłowy e-mail lub hasło.', + 'confirmation' => 'Twoje konto zostało potwierdzone!', + 'wrong_confirmation' => 'Błędny kod potwierdzający.', + 'password_forgot' => 'Informacje dotyczące resetowania hasła zostały wysłane na Twój adres e-mail.', + 'password_reset' => 'Twoje hasło zostało zmienione.', + 'wrong_password_reset' => 'Nieprawidłowe hasło. Spróbuj ponownie', + ], + 'pro_plan' => [ + 'remove_logo' => ':link to remove the Invoice Ninja logo by joining the Pro Plan', + 'remove_logo_link' => 'Kliknij tutaj', + ], + 'invitation_status' => [ + 'sent' => 'E-mail wysłany', + 'opened' => 'Email otwarty', + 'viewed' => 'Przeglądana faktura', + ], + 'email_errors' => [ + 'inactive_client' => 'E-maile nie mogą być wysyłane do klientów nieaktywnych', + 'inactive_contact' => 'E-mail nie może zostać wysłany do nieaktywnych kontaktów', + 'inactive_invoice' => 'E-mail nie może zostać wysłany do nieaktywnych faktur', + 'user_unregistered' => 'Proszę zarejestrować swoje konto, aby wysyłać e-maile', + 'user_unconfirmed' => 'Proszę potwierdzić swoje konto do wysyłania e-maili', + 'invalid_contact_email' => 'Nieprawidłowy e-mail kontaktowy', + ], + + 'navigation' => 'Nawigacja', + 'list_invoices' => 'Lista faktur', + 'list_clients' => 'Lista klientów', + 'list_quotes' => 'Lista ofert', + 'list_tasks' => 'Lista zadań', + 'list_expenses' => 'Lista wydatków', + 'list_recurring_invoices' => 'Lista faktur okresowych', + 'list_payments' => 'Lista wpłat', + 'list_credits' => 'Lista kredytów', + 'tax_name' => 'Nazwa podatku', + 'report_settings' => 'Ustawienia raportu', + 'search_hotkey' => 'skrót to /', + + 'new_user' => 'Nowy użytkownik', + 'new_product' => 'Nowy produkt', + 'new_tax_rate' => 'Nowa stawka podatkowa', + 'invoiced_amount' => 'Fakturowana kwota', + 'invoice_item_fields' => 'Invoice Item Fields', + 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', + 'recurring_invoice_number' => 'Numer faktury okresowej', + 'recurring_invoice_number_prefix_help' => 'Dodaj własny prefix do numeru faktury okresowej. Wartość domyślna to \'R\'.', + + // Client Passwords + 'enable_portal_password'=>'Hasło ochrony faktur', + 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', + 'send_portal_password'=>'Generate password automatically', + 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + + 'expired' => 'Wygasło', + 'invalid_card_number' => 'Numer karty kredytowej jest nieprawidłowy.', + 'invalid_expiry' => 'Data ważności jest nieprawidłowa.', + 'invalid_cvv' => 'Kod CVV jest nieprawidłowy.', + 'cost' => 'Koszt', + 'create_invoice_for_sample' => 'Notatka: aby zobaczyć podgląd, utwórz fakturę.', + + // User Permissions + 'owner' => 'Właściciel', + 'administrator' => 'Administrator', + 'administrator_help' => 'Allow user to manage users, change settings and modify all records', + 'user_create_all' => 'Create clients, invoices, etc.', + 'user_view_all' => 'View all clients, invoices, etc.', + 'user_edit_all' => 'Edit all clients, invoices, etc.', + 'gateway_help_20' => ':link to sign up for Sage Pay.', + 'gateway_help_21' => ':link to sign up for Sage Pay.', + 'partial_due' => 'Partial Due', + 'restore_vendor' => 'Restore Vendor', + 'restored_vendor' => 'Successfully restored vendor', + 'restored_expense' => 'Successfully restored expense', + 'permissions' => 'Permissions', + 'create_all_help' => 'Allow user to create and modify records', + 'view_all_help' => 'Allow user to view records they didn\'t create', + 'edit_all_help' => 'Allow user to modify records they didn\'t create', + 'view_payment' => 'Zobacz wpłatę', + + 'january' => 'Styczeń', + 'february' => 'Luty', + 'march' => 'Marzec', + 'april' => 'Kwiecień', + 'may' => 'Maj', + 'june' => 'Czerwiec', + 'july' => 'Lipiec', + 'august' => 'Sierpień', + 'september' => 'Wrzesień', + 'october' => 'Październik', + 'november' => 'Listopad', + 'december' => 'Grudzień', + + // Documents + 'documents_header' => 'Dokumenty:', + 'email_documents_header' => 'Dokumenty:', + 'email_documents_example_1' => 'Widgets Receipt.pdf', + 'email_documents_example_2' => 'Final Deliverable.zip', + 'invoice_documents' => 'Dokumenty', + 'expense_documents' => 'Załączone dokumenty', + 'invoice_embed_documents' => 'Embed Documents', + 'invoice_embed_documents_help' => 'Include attached images in the invoice.', + 'document_email_attachment' => 'Załącz dokumenty', + 'download_documents' => 'Ściągnij dokumenty (:size)', + 'documents_from_expenses' => 'From Expenses:', + 'dropzone' => array(// See http://www.dropzonejs.com/#config-dictDefaultMessage + 'DefaultMessage' => 'Upuść pliki lub kliknij, aby przesłać', + 'FallbackMessage' => 'Your browser does not support drag\'n\'drop file uploads.', + 'FallbackText' => 'Please use the fallback form below to upload your files like in the olden days.', + 'FileTooBig' => 'Plik jest zbyt duży ({{filesize}}MiB). Max rozmiar pliku: {{maxFilesize}}MiB.', + 'InvalidFileType' => 'Nie możesz przesłać plików tego typu.', + 'ResponseError' => 'Serwer zwraca {{statusCode}} kod.', + 'CancelUpload' => 'Anuluj przesyłanie', + 'CancelUploadConfirmation' => 'Czy na pewno chcesz anulować przesyłanie pliku?', + 'RemoveFile' => 'Usuń plik', + ), + 'documents' => 'Dokumenty', + 'document_date' => 'Data dokumentu', + 'document_size' => 'Rozmiar', + + 'enable_client_portal' => 'Portal Klienta', + 'enable_client_portal_help' => 'Pokaż/ukryj portal klienta.', + 'enable_client_portal_dashboard' => 'Pulpit', + 'enable_client_portal_dashboard_help' => 'Pokaż/ukryj pulpit w panelu klienta.', + + // Plans + 'account_management' => 'Zarządzanie kontem', + 'plan_status' => 'Plan Status', + + 'plan_upgrade' => 'Aktualizuj', + 'plan_change' => 'Zmień plan', + 'pending_change_to' => 'Zmienia się na', + 'plan_changes_to' => ':plan on :date', + 'plan_term_changes_to' => ':plan (:term) on :date', + 'cancel_plan_change' => 'Anuluj zmianę', + 'plan' => 'Plan', + 'expires' => 'Wygasa', + 'renews' => 'Odnawia', + 'plan_expired' => ':plan Plan Expired', + 'trial_expired' => ':plan Plan Trial Ended', + 'never' => 'Nigdy', + 'plan_free' => 'Darmowy', + 'plan_pro' => 'Pro', + 'plan_enterprise' => 'Enterprise', + 'plan_white_label' => 'Self Hosted (White labeled)', + 'plan_free_self_hosted' => 'Self Hosted (Free)', + 'plan_trial' => 'Trial', + 'plan_term' => 'Term', + 'plan_term_monthly' => 'Miesięcznie', + 'plan_term_yearly' => 'Rocznie', + 'plan_term_month' => 'Miesiąc', + 'plan_term_year' => 'Rok', + 'plan_price_monthly' => '$:price/miesiąc', + 'plan_price_yearly' => '$:price/rok', + 'updated_plan' => 'Ustawienia planu zaktualizowane', + 'plan_paid' => 'Termin rozpoczął', + 'plan_started' => 'Plan rozpoczął', + 'plan_expires' => 'Plan Wygasa', + + 'white_label_button' => 'Biała etykieta', + + 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', + 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', + 'enterprise_plan_product' => 'Plan Enterprise', + 'enterprise_plan_year_description' => 'One year enrollment in the Invoice Ninja Enterprise Plan.', + 'enterprise_plan_month_description' => 'One month enrollment in the Invoice Ninja Enterprise Plan.', + 'plan_credit_product' => 'Kredyt', + 'plan_credit_description' => 'Kredyt za niewykorzystany czas', + 'plan_pending_monthly' => 'Will switch to monthly on :date', + 'plan_refunded' => 'Zwrot został wystawiony.', + + 'live_preview' => 'Podgląd', + 'page_size' => 'Rozmiar strony', + 'live_preview_disabled' => 'Podgląd obrazu na żywo został wyłączony w celu wsparcia wybranej czcionki', + 'invoice_number_padding' => 'Padding', + +); + +return $LANG; + +?>. \ No newline at end of file diff --git a/resources/lang/pl/validation.php b/resources/lang/pl/validation.php new file mode 100644 index 000000000000..d3ffdb6a8155 --- /dev/null +++ b/resources/lang/pl/validation.php @@ -0,0 +1,106 @@ + ":attribute musi być zaakceptowany.", + "active_url" => ":attribute nie jest poprawnym URL-em.", + "after" => ":attribute musi być datą za :date.", + "alpha" => ":attribute może zawierać tylko litery.", + "alpha_dash" => ":attribute może zawierać tylko litery, liczby i myślniki.", + "alpha_num" => ":attribute może zawierać tylko litery i liczby.", + "array" => ":attribute musi być tablicą.", + "before" => ":attribute musi być datą przed :date.", + "between" => array( + "numeric" => ":attribute musi być pomiędzy :min - :max.", + "file" => ":attribute musi mieć rozmiar pomiędzy :min - :max kilobajtów.", + "string" => ":attribute musi mieć pomiędzy :min - :max znaków.", + "array" => ":attribute musi zawierać :min - :max pozycji.", + ), + "confirmed" => ":attribute potwierdzenie nie jest zgodne.", + "date" => ":attribute nie jest prawidłową datą.", + "date_format" => ":attribute nie jest zgodne z formatem :format.", + "different" => ":attribute i :other muszą być różne.", + "digits" => ":attribute musi mieć :digits cyfr.", + "digits_between" => ":attribute musi być w przedziale od :min do :max cyfr.", + "email" => ":attribute format jest nieprawidłowy.", + "exists" => "Zaznaczony :attribute jest niepoprawny.", + "image" => ":attribute musi być zdjęciem.", + "in" => "Zaznaczony :attribute jest niepoprawny.", + "integer" => ":attribute musi być liczbą całkowitą.", + "ip" => ":attribute musi być poprawnym adresem IP.", + "max" => array( + "numeric" => ":attribute nie może być większy niż :max.", + "file" => ":attribute nie może być większy niż :max kilobajtów.", + "string" => ":attribute nie może być dłuższy niż :max znaków.", + "array" => ":attribute nie może zawierać więcej niż :max pozycji.", + ), + "mimes" => ":attribute musi być plikiem o typie: :values.", + "min" => array( + "numeric" => ":attribute musi być przynajmniej :min.", + "file" => ":attribute musi mieć przynajmniej :min kilobajtów.", + "string" => ":attribute musi mieć przynajmniej :min znaków.", + "array" => ":attribute musi zawierać przynajmniej :min pozycji.", + ), + "not_in" => "Zaznaczony :attribute jest niepoprawny.", + "numeric" => ":attribute musi być cyfrą.", + "regex" => ":attribute format jest niepoprawny.", + "required" => ":attribute pole jest wymagane.", + "required_if" => ":attribute pole jest wymagane jeśli :other ma :value.", + "required_with" => ":attribute pole jest wymagane kiedy :values jest obecne.", + "required_without" => ":attribute pole jest wymagane kiedy :values nie występuje.", + "same" => ":attribute i :other muszą być takie same.", + "size" => array( + "numeric" => ":attribute musi mieć :size.", + "file" => ":attribute musi mieć :size kilobajtów.", + "string" => ":attribute musi mieć :size znaków.", + "array" => ":attribute musi zawierać :size pozycji.", + ), + "unique" => ":attribute już istnieje.", + "url" => ":attribute format jest nieprawidłowy.", + + "positive" => ":attribute musi być większe niż zero.", + "has_credit" => "Klient ma niewystarczająco kredytu.", + "notmasked" => "Wartości są maskowane", + "less_than" => ":attribute musi być mniejsze od :value", + "has_counter" => "Wartość musi zawierać {\$counter}", + "valid_contacts" => "Kontakt musi posiadać e-mail lub nazwę", + "valid_invoice_items" => "Faktura przekracza maksymalną kwotę", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); \ No newline at end of file From fb473eeb3b35af80d253af98ded4c82250049787 Mon Sep 17 00:00:00 2001 From: emiliolodigiani Date: Tue, 3 May 2016 15:09:13 +0200 Subject: [PATCH 186/258] Update italian translation --- resources/lang/it/texts.php | 140 ++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 17d77d5d4881..b4fb1dc2b00d 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -382,11 +382,11 @@ return array( 'converted_to_invoice' => 'Il preventivo è stato convertito a fattura con successo', 'quote_subject' => 'Nuovo preventivo da :account', - 'quote_message' => 'Per visualizzare il vostro preventivo per :amount, cliccare il collegamento sotto.', - 'quote_link_message' => 'Per visualizzare il preventivo del vostro cliante cliccate il collegamento sotto:', + 'quote_message' => 'Per visualizzare il vostro preventivo di :amount, cliccate il collegamento sotto.', + 'quote_link_message' => 'Per visualizzare il preventivo del vostro cliente cliccate il collegamento sotto:', 'notification_quote_sent_subject' => 'Il preventivo :invoice è stato inviato a :client', 'notification_quote_viewed_subject' => 'Il preventivo :invoice è stato visualizzato da :client', - 'notification_quote_sent' => 'Al seguente cliente :client è stata inviata la fattura :invoice per :amount.', + 'notification_quote_sent' => 'Al seguente cliente :client è stata inviato il preventivo :invoice per un importo di :amount.', 'notification_quote_viewed' => 'Il seguente cliente :client ha visualizzato il preventivo :invoice di :amount.', 'session_expired' => 'La vostra sessione è scaduta.', @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_version' => 'Select version', + 'select_versiony' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', @@ -728,12 +728,12 @@ return array( 'header' => 'Header', 'footer' => 'Footer', 'custom' => 'Custom', - 'invoice_to' => 'Invoice to', - 'invoice_no' => 'Invoice No.', - 'recent_payments' => 'Recent Payments', - 'outstanding' => 'Outstanding', - 'manage_companies' => 'Manage Companies', - 'total_revenue' => 'Total Revenue', + 'invoice_to' => 'Fattura a', + 'invoice_no' => 'Fattura N.', + 'recent_payments' => 'Pagamenti recenti', + 'outstanding' => 'Inevaso', + 'manage_companies' => 'Gestisci aziende', + 'total_revenue' => 'Ricavo totale', 'current_user' => 'Current User', 'new_recurring_invoice' => 'Nuova Fattura Ricorrente', @@ -784,7 +784,7 @@ return array( 'last_sent_on' => 'Last sent on :date', 'page_expire' => 'This page will expire soon, :click_here to keep working', - 'upcoming_quotes' => 'Upcoming Quotes', + 'upcoming_quotes' => 'Preventivi in scadenza', 'expired_quotes' => 'Preventivi Scaduti', 'sign_up_using' => 'Sign up using', @@ -842,19 +842,19 @@ return array( 'quote_counter' => 'Quote Counter', 'type' => 'Type', - 'activity_1' => ':user created client :client', - 'activity_2' => ':user archived client :client', + 'activity_1' => ':user ha creato il cliente :client', + 'activity_2' => ':user ha archiviato il cliente :client', 'activity_3' => ':user deleted client :client', 'activity_4' => ':user ha creato la fattura :invoice', 'activity_5' => ':user ha aggiornato la fattura :invoice', - 'activity_6' => ':user emailed invoice :invoice to :contact', - 'activity_7' => ':contact viewed invoice :invoice', - 'activity_8' => ':user archived invoice :invoice', - 'activity_9' => ':user deleted invoice :invoice', - 'activity_10' => ':contact entered payment :payment for :invoice', - 'activity_11' => ':user updated payment :payment', - 'activity_12' => ':user archived payment :payment', - 'activity_13' => ':user deleted payment :payment', + 'activity_6' => ':user ha inviato per email la fattura :invoice a :contact', + 'activity_7' => ':contact ha visto la fattura :invoice', + 'activity_8' => ':user ha archiviato la fattura :invoice', + 'activity_9' => ':user ha cancellato la fattura :invoice', + 'activity_10' => ':contact ha inserito il pagamento :payment per :invoice', + 'activity_11' => ':user ha aggiornato il pagamento :payment', + 'activity_12' => ':user ha archiviato il pagamento :payment', + 'activity_13' => ':user ha cancellato il pagamento :payment', 'activity_14' => ':user entered :credit credit', 'activity_15' => ':user updated :credit credit', 'activity_16' => ':user archived :credit credit', @@ -862,7 +862,7 @@ return array( 'activity_18' => ':user created quote :quote', 'activity_19' => ':user updated quote :quote', 'activity_20' => ':user emailed quote :quote to :contact', - 'activity_21' => ':contact viewed quote :quote', + 'activity_21' => ':contact ha visto il preventivo :quote', 'activity_22' => ':user archived quote :quote', 'activity_23' => ':user deleted quote :quote', 'activity_24' => ':user restored quote :quote', @@ -870,7 +870,7 @@ return array( 'activity_26' => ':user restored client :client', 'activity_27' => ':user restored payment :payment', 'activity_28' => ':user restored :credit credit', - 'activity_29' => ':contact approved quote :quote', + 'activity_29' => ':contact ha approvato la fattura :quote', 'payment' => 'Payment', 'system' => 'System', @@ -1004,33 +1004,33 @@ return array( 'archived_expenses' => 'Successfully archived expenses', // Expenses - 'expense_amount' => 'Expense Amount', - 'expense_balance' => 'Expense Balance', - 'expense_date' => 'Expense Date', - 'expense_should_be_invoiced' => 'Should this expense be invoiced?', - 'public_notes' => 'Public Notes', - 'invoice_amount' => 'Invoice Amount', - 'exchange_rate' => 'Exchange Rate', - 'yes' => 'Yes', + 'expense_amount' => 'Importo Spesa', + 'expense_balance' => 'Bilancio Spesa', + 'expense_date' => 'Data Spesa', + 'expense_should_be_invoiced' => 'Questa spesa deve essere fatturata?', + 'public_notes' => 'Note Pubbliche (Descrizione in fattura)', + 'invoice_amount' => 'Importo Fattura', + 'exchange_rate' => 'Tasso di Cambio', + 'yes' => 'Si', 'no' => 'No', - 'should_be_invoiced' => 'Should be invoiced', - 'view_expense' => 'View expense # :expense', - 'edit_expense' => 'Edit Expense', - 'archive_expense' => 'Archive Expense', - 'delete_expense' => 'Delete Expense', - 'view_expense_num' => 'Expense # :expense', - 'updated_expense' => 'Successfully updated expense', - 'created_expense' => 'Successfully created expense', - 'enter_expense' => 'Enter Expense', - 'view' => 'View', - 'restore_expense' => 'Restore Expense', - 'invoice_expense' => 'Invoice Expense', - 'expense_error_multiple_clients' =>'The expenses can\'t belong to different clients', - 'expense_error_invoiced' => 'Expense has already been invoiced', - 'convert_currency' => 'Convert currency', + 'should_be_invoiced' => 'Deve essere fatturata', + 'view_expense' => 'Vedi spesa # :expense', + 'edit_expense' => 'Modifica Spesa', + 'archive_expense' => 'Archivia Spesa', + 'delete_expense' => 'Cancella Spesa', + 'view_expense_num' => 'Spesa # :expense', + 'updated_expense' => 'Spesa aggiornata con successo', + 'created_expense' => 'Spesa creata con successo', + 'enter_expense' => 'Inserisci Spesa', + 'view' => 'Vedi', + 'restore_expense' => 'Ripristina Spesa', + 'invoice_expense' => 'Fattura Spesa', + 'expense_error_multiple_clients' =>'Le spese non possono appartenere a clienti differenti', + 'expense_error_invoiced' => 'La spesa è stata già fatturata', + 'convert_currency' => 'Converti valuta', // Payment terms - 'num_days' => 'Number of days', + 'num_days' => 'Numero di giorni', 'create_payment_term' => 'Create Payment Term', 'edit_payment_terms' => 'Edit Payment Term', 'edit_payment_term' => 'Edit Payment Term', @@ -1053,17 +1053,17 @@ return array( ', 'due' => 'Due', 'next_due_on' => 'Due Next: :date', - 'use_client_terms' => 'Use client terms', - 'day_of_month' => ':ordinal day of month', - 'last_day_of_month' => 'Last day of month', - 'day_of_week_after' => ':ordinal :day after', - 'sunday' => 'Sunday', - 'monday' => 'Monday', - 'tuesday' => 'Tuesday', - 'wednesday' => 'Wednesday', - 'thursday' => 'Thursday', - 'friday' => 'Friday', - 'saturday' => 'Saturday', + 'use_client_terms' => 'Usa i termini del cliente', + 'day_of_month' => ':ordinal giorno del mese', + 'last_day_of_month' => 'L\'ultimo giorno del mese', + 'day_of_week_after' => ':ordinal :day dopo', + 'sunday' => 'Domenica', + 'monday' => 'Lunedì', + 'tuesday' => 'Martedì', + 'wednesday' => 'Mercoledì', + 'thursday' => 'Giovedì', + 'friday' => 'Venerdì', + 'saturday' => 'Sabato', // Fonts 'header_font_id' => 'Header Font', @@ -1182,17 +1182,17 @@ return array( 'edit_all_help' => 'Allow user to modify records they didn\'t create', 'view_payment' => 'View Payment', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December', + 'january' => 'Gennaio', + 'february' => 'Febbraio', + 'march' => 'Marzo', + 'april' => 'Aprile', + 'may' => 'Maggio', + 'june' => 'Giugno', + 'july' => 'Luglio', + 'august' => 'Agosto', + 'september' => 'Settembre', + 'october' => 'Ottobre', + 'november' => 'Novembre', + 'december' => 'Dicembre', ); From 5295bf30fc56d3f31149bc3b40b0098b4cfbed98 Mon Sep 17 00:00:00 2001 From: emiliolodigiani Date: Tue, 3 May 2016 15:11:34 +0200 Subject: [PATCH 187/258] Revert typo fix --- resources/lang/it/texts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index b4fb1dc2b00d..526ce14013ba 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', From c0a5084282e039f7375d27bb5464b9734c1cb34d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 19:06:38 +0300 Subject: [PATCH 188/258] Added details to cybersource error --- app/Http/Controllers/PaymentController.php | 3 ++- app/Ninja/Transformers/AccountTransformer.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 12537dbebaa1..165f54ef9ed0 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -551,7 +551,8 @@ class PaymentController extends BaseController $payment = $this->paymentService->createPayment($invitation, $accountGateway, $token, $payerId); Session::flash('message', trans('texts.applied_payment')); } else { - Session::flash('error', Input::get('message')); + $message = Input::get('message') . ': ' . Input::get('invalid_fields'); + Session::flash('error', $message); } return Redirect::to($invitation->getLink()); } elseif (method_exists($gateway, 'completePurchase') diff --git a/app/Ninja/Transformers/AccountTransformer.php b/app/Ninja/Transformers/AccountTransformer.php index 6a1c32f30e09..f56635bad2b1 100644 --- a/app/Ninja/Transformers/AccountTransformer.php +++ b/app/Ninja/Transformers/AccountTransformer.php @@ -14,12 +14,12 @@ class AccountTransformer extends EntityTransformer 'users', 'products', 'taxRates', - 'payments' ]; protected $availableIncludes = [ 'clients', 'invoices', + 'payments', ]; public function includeUsers(Account $account) From 5218568c918900bc3010745a69c4af47df9824a5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Szala Date: Tue, 3 May 2016 18:40:21 +0200 Subject: [PATCH 189/258] Remove transaction migration --- ...16_05_02_071859_add_polish_translation.php | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 database/migrations/2016_05_02_071859_add_polish_translation.php diff --git a/database/migrations/2016_05_02_071859_add_polish_translation.php b/database/migrations/2016_05_02_071859_add_polish_translation.php deleted file mode 100644 index b78969cfbbde..000000000000 --- a/database/migrations/2016_05_02_071859_add_polish_translation.php +++ /dev/null @@ -1,29 +0,0 @@ -insert(['name' => 'Polish', 'locale' => self::LANGUAGE_LOCALE]); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - $language = \App\Models\Language::whereLocale(self::LANGUAGE_LOCALE)->first(); - $language->delete(); - } -} From 2d878b6480f674bc99f5d80bb91e9d79ff796347 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 23:02:29 +0300 Subject: [PATCH 190/258] API optimizations --- app/Http/Controllers/BaseAPIController.php | 9 +-------- app/Http/Controllers/PaymentApiController.php | 2 +- app/Models/InvoiceItem.php | 5 +++++ app/Models/Product.php | 5 +++++ app/Models/TaxRate.php | 5 +++++ app/Ninja/Transformers/ClientTransformer.php | 6 ++---- app/Ninja/Transformers/ContactTransformer.php | 5 ++--- app/Ninja/Transformers/CreditTransformer.php | 5 ++--- app/Ninja/Transformers/DocumentTransformer.php | 6 ++---- app/Ninja/Transformers/EntityTransformer.php | 15 +++++++++++++++ app/Ninja/Transformers/ExpenseTransformer.php | 5 ++--- app/Ninja/Transformers/InvoiceItemTransformer.php | 6 ++---- app/Ninja/Transformers/InvoiceTransformer.php | 6 ++---- app/Ninja/Transformers/PaymentTransformer.php | 6 ++---- app/Ninja/Transformers/ProductTransformer.php | 5 ++--- app/Ninja/Transformers/TaskTransformer.php | 6 ++---- app/Ninja/Transformers/TaxRateTransformer.php | 5 ++--- .../Transformers/VendorContactTransformer.php | 5 ++--- app/Ninja/Transformers/VendorTransformer.php | 6 ++---- 19 files changed, 58 insertions(+), 55 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index db99a3beaf1d..8e48f3bff878 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -193,26 +193,19 @@ class BaseAPIController extends Controller protected function getRequestIncludes($data) { - $data[] = 'user'; - $included = Request::get('include'); $included = explode(',', $included); foreach ($included as $include) { if ($include == 'invoices') { $data[] = 'invoices.invoice_items'; - $data[] = 'invoices.user'; } elseif ($include == 'client') { $data[] = 'client.contacts'; - $data[] = 'client.user'; } elseif ($include == 'clients') { $data[] = 'clients.contacts'; - $data[] = 'clients.user'; } elseif ($include == 'vendors') { $data[] = 'vendors.vendorcontacts'; - $data[] = 'vendors.user'; - } - elseif ($include) { + } elseif ($include) { $data[] = $include; } } diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 0dd8de50803b..3355b6bff8f4 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -49,7 +49,7 @@ class PaymentApiController extends BaseAPIController { $payments = Payment::scope() ->withTrashed() - ->with(['invoice']) + ->with(['invoice']) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); diff --git a/app/Models/InvoiceItem.php b/app/Models/InvoiceItem.php index d3981c931c62..e80482bafdb8 100644 --- a/app/Models/InvoiceItem.php +++ b/app/Models/InvoiceItem.php @@ -19,6 +19,11 @@ class InvoiceItem extends EntityModel return $this->belongsTo('App\Models\Invoice'); } + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + public function product() { return $this->belongsTo('App\Models\Product'); diff --git a/app/Models/Product.php b/app/Models/Product.php index 0d3221f2a1ea..05944c9fff94 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -26,6 +26,11 @@ class Product extends EntityModel return Product::scope()->where('product_key', '=', $key)->first(); } + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + public function default_tax_rate() { return $this->belongsTo('App\Models\TaxRate'); diff --git a/app/Models/TaxRate.php b/app/Models/TaxRate.php index 72ad266b07d8..384ccf933b36 100644 --- a/app/Models/TaxRate.php +++ b/app/Models/TaxRate.php @@ -17,4 +17,9 @@ class TaxRate extends EntityModel { return ENTITY_TAX_RATE; } + + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } } diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index e9a63e07d693..63f0baa0c871 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -77,13 +77,11 @@ class ClientTransformer extends EntityTransformer public function transform(Client $client) { - return [ + return array_merge($this->getDefaults($client), [ 'id' => (int) $client->public_id, 'name' => $client->name, 'balance' => (float) $client->balance, 'paid_to_date' => (float) $client->paid_to_date, - 'user_id' => (int) $client->user->public_id + 1, - 'account_key' => $this->account->account_key, 'updated_at' => $this->getTimestamp($client->updated_at), 'archived_at' => $this->getTimestamp($client->deleted_at), 'address1' => $client->address1, @@ -106,6 +104,6 @@ class ClientTransformer extends EntityTransformer 'currency_id' => (int) $client->currency_id, 'custom_value1' => $client->custom_value1, 'custom_value2' => $client->custom_value2, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/ContactTransformer.php b/app/Ninja/Transformers/ContactTransformer.php index e404b73660d4..68172e156c60 100644 --- a/app/Ninja/Transformers/ContactTransformer.php +++ b/app/Ninja/Transformers/ContactTransformer.php @@ -8,7 +8,7 @@ class ContactTransformer extends EntityTransformer { public function transform(Contact $contact) { - return [ + return array_merge($this->getDefaults($contact), [ 'id' => (int) $contact->public_id, 'first_name' => $contact->first_name, 'last_name' => $contact->last_name, @@ -18,8 +18,7 @@ class ContactTransformer extends EntityTransformer 'is_primary' => (bool) $contact->is_primary, 'phone' => $contact->phone, 'last_login' => $contact->last_login, - 'account_key' => $this->account->account_key, 'send_invoice' => (bool) $contact->send_invoice, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/CreditTransformer.php b/app/Ninja/Transformers/CreditTransformer.php index a33185d2ff39..39ce5ff1d8f3 100644 --- a/app/Ninja/Transformers/CreditTransformer.php +++ b/app/Ninja/Transformers/CreditTransformer.php @@ -8,17 +8,16 @@ class CreditTransformer extends EntityTransformer { public function transform(Credit $credit) { - return [ + return array_merge($this->getDefaults($credit), [ 'id' => (int) $credit->public_id, 'amount' => (float) $credit->amount, 'balance' => (float) $credit->balance, 'updated_at' => $this->getTimestamp($credit->updated_at), 'archived_at' => $this->getTimestamp($credit->deleted_at), 'is_deleted' => (bool) $credit->is_deleted, - 'account_key' => $this->account->account_key, 'credit_date' => $credit->credit_date, 'credit_number' => $credit->credit_number, 'private_notes' => $credit->private_notes, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/DocumentTransformer.php b/app/Ninja/Transformers/DocumentTransformer.php index 61d18506eba1..4cbfa0619193 100644 --- a/app/Ninja/Transformers/DocumentTransformer.php +++ b/app/Ninja/Transformers/DocumentTransformer.php @@ -8,14 +8,12 @@ class DocumentTransformer extends EntityTransformer { public function transform(Document $document) { - - return [ + return array_merge($this->getDefaults($document), [ 'id' => (int) $document->public_id, 'name' => $document->name, - 'account_key' => $this->account->account_key, 'type' => $document->type, 'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null, 'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/EntityTransformer.php b/app/Ninja/Transformers/EntityTransformer.php index af1304acd67f..d2d02bd0b6ee 100644 --- a/app/Ninja/Transformers/EntityTransformer.php +++ b/app/Ninja/Transformers/EntityTransformer.php @@ -1,5 +1,6 @@ defaultIncludes; } + + protected function getDefaults($entity) + { + $data = [ + 'account_key' => $this->account->account_key, + 'is_owner' => (bool) Auth::user()->owns($entity), + ]; + + if ($entity->relationLoaded('user')) { + $data['user_id'] = (int) $entity->user->public_id + 1; + } + + return $data; + } } diff --git a/app/Ninja/Transformers/ExpenseTransformer.php b/app/Ninja/Transformers/ExpenseTransformer.php index 8f2e02c0a88f..46c334cb3e55 100644 --- a/app/Ninja/Transformers/ExpenseTransformer.php +++ b/app/Ninja/Transformers/ExpenseTransformer.php @@ -15,7 +15,7 @@ class ExpenseTransformer extends EntityTransformer public function transform(Expense $expense) { - return [ + return array_merge($this->getDefaults($expense), [ 'id' => (int) $expense->public_id, 'private_notes' => $expense->private_notes, 'public_notes' => $expense->public_notes, @@ -25,7 +25,6 @@ class ExpenseTransformer extends EntityTransformer 'transaction_id' => $expense->transaction_id, 'bank_id' => $expense->bank_id, 'expense_currency_id' => (int) $expense->expense_currency_id, - 'account_key' => $this->account->account_key, 'amount' => (float) $expense->amount, 'expense_date' => $expense->expense_date, 'exchange_rate' => (float) $expense->exchange_rate, @@ -34,6 +33,6 @@ class ExpenseTransformer extends EntityTransformer 'client_id' => $this->client ? $this->client->public_id : (isset($expense->client->public_id) ? (int) $expense->client->public_id : null), 'invoice_id' => isset($expense->invoice->public_id) ? (int) $expense->invoice->public_id : null, 'vendor_id' => isset($expense->vendor->public_id) ? (int) $expense->vendor->public_id : null, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/InvoiceItemTransformer.php b/app/Ninja/Transformers/InvoiceItemTransformer.php index 895d8e8d2f4a..080234f35e1b 100644 --- a/app/Ninja/Transformers/InvoiceItemTransformer.php +++ b/app/Ninja/Transformers/InvoiceItemTransformer.php @@ -8,11 +8,9 @@ class InvoiceItemTransformer extends EntityTransformer { public function transform(InvoiceItem $item) { - return [ + return array_merge($this->getDefaults($item), [ 'id' => (int) $item->public_id, 'product_key' => $item->product_key, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $item->user_id, 'updated_at' => $this->getTimestamp($item->updated_at), 'archived_at' => $this->getTimestamp($item->deleted_at), 'product_key' => $item->product_key, @@ -23,6 +21,6 @@ class InvoiceItemTransformer extends EntityTransformer 'tax_rate1' => (float) $item->tax_rate1, 'tax_name2' => $item->tax_name2 ? $item->tax_name1 : '', 'tax_rate2' => (float) $item->tax_rate2, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 51af3d029936..0933422875c2 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -71,7 +71,7 @@ class InvoiceTransformer extends EntityTransformer public function transform(Invoice $invoice) { - return [ + return array_merge($this->getDefaults($invoice), [ 'id' => (int) $invoice->public_id, 'amount' => (float) $invoice->amount, 'balance' => (float) $invoice->balance, @@ -105,8 +105,6 @@ class InvoiceTransformer extends EntityTransformer 'partial' => (float) $invoice->partial, 'has_tasks' => (bool) $invoice->has_tasks, 'auto_bill' => (bool) $invoice->auto_bill, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $invoice->user->public_id + 1, 'custom_value1' => (float) $invoice->custom_value1, 'custom_value2' => (float) $invoice->custom_value2, 'custom_taxes1' => (bool) $invoice->custom_taxes1, @@ -115,6 +113,6 @@ class InvoiceTransformer extends EntityTransformer 'quote_invoice_id' => (int) $invoice->quote_invoice_id, 'custom_text_value1' => $invoice->custom_text_value1, 'custom_text_value2' => $invoice->custom_text_value2, - ]; + ]); } } diff --git a/app/Ninja/Transformers/PaymentTransformer.php b/app/Ninja/Transformers/PaymentTransformer.php index 8ea2dcbae051..c4e4328cb845 100644 --- a/app/Ninja/Transformers/PaymentTransformer.php +++ b/app/Ninja/Transformers/PaymentTransformer.php @@ -47,11 +47,9 @@ class PaymentTransformer extends EntityTransformer public function transform(Payment $payment) { - return [ + return array_merge($this->getDefaults($payment), [ 'id' => (int) $payment->public_id, 'amount' => (float) $payment->amount, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $payment->user->public_id + 1, 'transaction_reference' => $payment->transaction_reference, 'payment_date' => $payment->payment_date, 'updated_at' => $this->getTimestamp($payment->updated_at), @@ -59,6 +57,6 @@ class PaymentTransformer extends EntityTransformer 'is_deleted' => (bool) $payment->is_deleted, 'payment_type_id' => (int) $payment->payment_type_id, 'invoice_id' => (int) ($this->invoice ? $this->invoice->public_id : $payment->invoice->public_id), - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/ProductTransformer.php b/app/Ninja/Transformers/ProductTransformer.php index 34fbcf7f18ff..8331d7666154 100644 --- a/app/Ninja/Transformers/ProductTransformer.php +++ b/app/Ninja/Transformers/ProductTransformer.php @@ -7,16 +7,15 @@ class ProductTransformer extends EntityTransformer { public function transform(Product $product) { - return [ + return array_merge($this->getDefaults(), [ 'id' => (int) $product->public_id, 'product_key' => $product->product_key, 'notes' => $product->notes, 'cost' => $product->cost, 'qty' => $product->qty, - 'account_key' =>$this->account->account_key, 'default_tax_rate_id' =>$product->default_tax_rate_id, 'updated_at' =>$this->getTimestamp($product->updated_at), 'archived_at' => $this->getTimestamp($product->deleted_at), - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/TaskTransformer.php b/app/Ninja/Transformers/TaskTransformer.php index 908a8118aaea..7bfc474a72f2 100644 --- a/app/Ninja/Transformers/TaskTransformer.php +++ b/app/Ninja/Transformers/TaskTransformer.php @@ -39,12 +39,10 @@ class TaskTransformer extends EntityTransformer public function transform(Task $task) { - return [ + return array_merge($this->getDefaults($task), [ 'id' => (int) $task->public_id, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $task->user->public_id + 1, 'description' => $task->description, 'duration' => $task->getDuration() - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/TaxRateTransformer.php b/app/Ninja/Transformers/TaxRateTransformer.php index f7d307bf7bb4..a0c5aab539a0 100644 --- a/app/Ninja/Transformers/TaxRateTransformer.php +++ b/app/Ninja/Transformers/TaxRateTransformer.php @@ -21,13 +21,12 @@ class TaxRateTransformer extends EntityTransformer public function transform(TaxRate $taxRate) { - return [ + return array_merge($this->getDefaults($taxRate), [ 'id' => (int) $taxRate->public_id, 'name' => $taxRate->name, 'rate' => (float) $taxRate->rate, 'updated_at' => $this->getTimestamp($taxRate->updated_at), 'archived_at' => $this->getTimestamp($taxRate->deleted_at), - 'account_key' => $this->account->account_key, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/VendorContactTransformer.php b/app/Ninja/Transformers/VendorContactTransformer.php index 3b75aee53a28..f277964cec28 100644 --- a/app/Ninja/Transformers/VendorContactTransformer.php +++ b/app/Ninja/Transformers/VendorContactTransformer.php @@ -8,7 +8,7 @@ class VendorContactTransformer extends EntityTransformer { public function transform(VendorContact $contact) { - return [ + return array_merge($this->getDefaults($contact), [ 'id' => (int) $contact->public_id, 'first_name' => $contact->first_name, 'last_name' => $contact->last_name, @@ -17,7 +17,6 @@ class VendorContactTransformer extends EntityTransformer 'archived_at' => $this->getTimestamp($contact->deleted_at), 'is_primary' => (bool) $contact->is_primary, 'phone' => $contact->phone, - 'account_key' => $this->account->account_key, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index f0b8fd0415f0..f9c37ac44200 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -61,13 +61,11 @@ class VendorTransformer extends EntityTransformer public function transform(Vendor $vendor) { - return [ + return array_merge($this->getDefaults($vendor), [ 'id' => (int) $vendor->public_id, 'name' => $vendor->name, 'balance' => (float) $vendor->balance, 'paid_to_date' => (float) $vendor->paid_to_date, - 'user_id' => (int) $vendor->user->public_id + 1, - 'account_key' => $this->account->account_key, 'updated_at' => $this->getTimestamp($vendor->updated_at), 'archived_at' => $this->getTimestamp($vendor->deleted_at), 'address1' => $vendor->address1, @@ -84,6 +82,6 @@ class VendorTransformer extends EntityTransformer 'vat_number' => $vendor->vat_number, 'id_number' => $vendor->id_number, 'currency_id' => (int) $vendor->currency_id - ]; + ]); } } \ No newline at end of file From dfa6c518abb8529e8f55b38e5ef0f42e80b4a4a9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 23:15:41 +0300 Subject: [PATCH 191/258] Fix for travis test --- tests/acceptance/ExpenseCest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index e22bebac3b7f..770125574d9e 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -44,13 +44,14 @@ class ExpenseCest $I->selectDropdown($I, $vendorName, '.vendor-select .dropdown-toggle'); $I->selectDropdown($I, $clientEmail, '.client-select .dropdown-toggle'); $I->click('Save'); + $I->wait(2); $I->seeInDatabase('expenses', ['vendor_id' => $vendorId]); // invoice expense $I->executeJS('submitAction(\'invoice\')'); - $I->wait(3); + $I->wait(2); $I->click('Save'); - $I->wait(3); + $I->wait(2); $I->see($clientEmail); $I->see($amount); } From 2fcb1d66a5ec9230e197abde576517441a9deeca Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 09:53:43 +0300 Subject: [PATCH 192/258] Fix for travis tests --- .travis.yml | 3 ++- app/Exceptions/Handler.php | 2 +- app/Libraries/Utils.php | 5 +++++ app/Ninja/Transformers/ProductTransformer.php | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 083bbe4f2583..1f66f7aa29f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,8 @@ before_script: - php artisan key:generate --no-interaction - sed -i 's/APP_ENV=production/APP_ENV=development/g' .env - sed -i 's/APP_DEBUG=false/APP_DEBUG=true/g' .env - - sed -i 's/REQUIRE_HTTPS=false/NINJA_DEV=true/g' .env + - sed -i '$a NINJA_DEV=true' .env + - sed -i '$a TRAVIS=true' .env # create the database and user - mysql -u root -e "create database IF NOT EXISTS ninja;" - mysql -u root -e "GRANT ALL PRIVILEGES ON ninja.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;" diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 5333d90f9e3b..2a8b0d305277 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -39,7 +39,7 @@ class Handler extends ExceptionHandler { return false; } - if (Utils::isNinja()) { + if (Utils::isNinja() && ! Utils::isTravis()) { Utils::logError(Utils::getErrorString($e)); return false; } else { diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 141eacfc3058..7c1b11dfd84b 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -51,6 +51,11 @@ class Utils return php_sapi_name() == 'cli'; } + public static function isTravis() + { + return env('TRAVIS') == 'true'; + } + public static function isNinja() { return self::isNinjaProd() || self::isNinjaDev(); diff --git a/app/Ninja/Transformers/ProductTransformer.php b/app/Ninja/Transformers/ProductTransformer.php index 8331d7666154..309305815fb0 100644 --- a/app/Ninja/Transformers/ProductTransformer.php +++ b/app/Ninja/Transformers/ProductTransformer.php @@ -7,7 +7,7 @@ class ProductTransformer extends EntityTransformer { public function transform(Product $product) { - return array_merge($this->getDefaults(), [ + return array_merge($this->getDefaults($product), [ 'id' => (int) $product->public_id, 'product_key' => $product->product_key, 'notes' => $product->notes, From 2a6876a902fceeab4a7e13bb41d771bd1ecd9258 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 11:14:20 +0300 Subject: [PATCH 193/258] Fix for travis tests --- .../Requests/CreatePaymentTermRequest.php | 30 ------------------- .../Requests/UpdatePaymentTermRequest.php | 30 ------------------- 2 files changed, 60 deletions(-) delete mode 100644 app/Http/Requests/CreatePaymentTermRequest.php delete mode 100644 app/Http/Requests/UpdatePaymentTermRequest.php diff --git a/app/Http/Requests/CreatePaymentTermRequest.php b/app/Http/Requests/CreatePaymentTermRequest.php deleted file mode 100644 index 23bf3151d096..000000000000 --- a/app/Http/Requests/CreatePaymentTermRequest.php +++ /dev/null @@ -1,30 +0,0 @@ - 'required', - 'name' => 'required', - ]; - } -} diff --git a/app/Http/Requests/UpdatePaymentTermRequest.php b/app/Http/Requests/UpdatePaymentTermRequest.php deleted file mode 100644 index ea9ff80e9772..000000000000 --- a/app/Http/Requests/UpdatePaymentTermRequest.php +++ /dev/null @@ -1,30 +0,0 @@ - 'required|positive', - ]; - - } -} From 9ebb539cce7d81b6334bfbdd833d58f9380be006 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 14:53:27 +0300 Subject: [PATCH 194/258] API optimizations --- app/Console/Commands/CreateTestData.php | 172 ++++++++++++++++++ app/Console/Kernel.php | 1 + app/Http/Controllers/ExpenseApiController.php | 1 + app/Http/Controllers/InvoiceApiController.php | 2 +- app/Ninja/Transformers/ClientTransformer.php | 2 +- app/Ninja/Transformers/InvoiceTransformer.php | 2 +- app/Ninja/Transformers/VendorTransformer.php | 7 +- database/factories/ModelFactory.php | 48 +++++ 8 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 app/Console/Commands/CreateTestData.php create mode 100644 database/factories/ModelFactory.php diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php new file mode 100644 index 000000000000..4499e1cc09c4 --- /dev/null +++ b/app/Console/Commands/CreateTestData.php @@ -0,0 +1,172 @@ +faker = Factory::create(); + + $this->clientRepo = $clientRepo; + $this->invoiceRepo = $invoiceRepo; + $this->paymentRepo = $paymentRepo; + $this->vendorRepo = $vendorRepo; + $this->expenseRepo = $expenseRepo; + } + + public function fire() + { + if (Utils::isNinjaProd()) { + return false; + } + + $this->info(date('Y-m-d').' Running CreateTestData...'); + + Auth::loginUsingId(1); + $this->count = $this->argument('count'); + + $this->createClients(); + $this->createVendors(); + + $this->info('Done'); + } + + private function createClients() + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'name' => $this->faker->name, + 'address1' => $this->faker->streetAddress, + 'address2' => $this->faker->secondaryAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + 'contacts' => [[ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->safeEmail, + 'phone' => $this->faker->phoneNumber, + ]] + ]; + + $client = $this->clientRepo->save($data); + $this->info('Client: ' . $client->name); + + $this->createInvoices($client); + } + } + + private function createInvoices($client) + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'client_id' => $client->id, + 'invoice_items' => [[ + 'product_key' => $this->faker->word, + 'qty' => $this->faker->randomDigit + 1, + 'cost' => $this->faker->randomFloat(2, 1, 10), + 'notes' => $this->faker->text($this->faker->numberBetween(50, 300)) + ]] + ]; + + $invoice = $this->invoiceRepo->save($data); + $this->info('Invoice: ' . $invoice->invoice_number); + + $this->createPayment($client, $invoice); + } + } + + private function createPayment($client, $invoice) + { + $data = [ + 'invoice_id' => $invoice->id, + 'client_id' => $client->id, + 'amount' => $this->faker->randomFloat(2, 0, $invoice->amount) + ]; + + $payment = $this->paymentRepo->save($data); + + $this->info('Payment: ' . $payment->amount); + } + + private function createVendors() + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'name' => $this->faker->name, + 'address1' => $this->faker->streetAddress, + 'address2' => $this->faker->secondaryAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + 'vendorcontacts' => [[ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->safeEmail, + 'phone' => $this->faker->phoneNumber, + ]] + ]; + + $vendor = $this->vendorRepo->save($data); + $this->info('Vendor: ' . $vendor->name); + + $this->createExpense($vendor); + } + } + + private function createExpense($vendor) + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'vendor_id' => $vendor->id, + 'amount' => $this->faker->randomFloat(2, 1, 10), + 'expense_date' => null, + 'public_notes' => null, + ]; + + $expense = $this->expenseRepo->save($data); + $this->info('Expense: ' . $expense->amount); + } + } + + protected function getArguments() + { + return array( + //array('example', InputArgument::REQUIRED, 'An example argument.'), + ); + } + + protected function getOptions() + { + return array( + //array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null), + ); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 2bc2b1b20787..179e423388f1 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -17,6 +17,7 @@ class Kernel extends ConsoleKernel 'App\Console\Commands\ResetData', 'App\Console\Commands\CheckData', 'App\Console\Commands\PruneData', + 'App\Console\Commands\CreateTestData', 'App\Console\Commands\SendRenewalInvoices', 'App\Console\Commands\ChargeRenewalInvoices', 'App\Console\Commands\SendReminders', diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 44722f2a18ce..725067aa1f2c 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -30,6 +30,7 @@ class ExpenseApiController extends BaseAPIController { $expenses = Expense::scope() ->withTrashed() + ->with('client', 'invoice', 'vendor') ->orderBy('created_at','desc'); return $this->listResponse($expenses); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 1361aad9e9a3..667690395eb2 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -60,7 +60,7 @@ class InvoiceApiController extends BaseAPIController { $invoices = Invoice::scope() ->withTrashed() - ->with('invoice_items') + ->with('invoice_items', 'client') ->orderBy('created_at', 'desc'); return $this->listResponse($invoices); diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index 63f0baa0c871..ea282f74ceca 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -47,7 +47,7 @@ class ClientTransformer extends EntityTransformer protected $availableIncludes = [ 'invoices', 'credits', - 'expenses', + //'expenses', ]; public function includeContacts(Client $client) diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 0933422875c2..c866f1213540 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -28,7 +28,7 @@ class InvoiceTransformer extends EntityTransformer 'invitations', 'payments', 'client', - 'expenses', + //'expenses', ]; public function __construct($account = null, $serializer = null, $client = null) diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index f9c37ac44200..2af79422fde8 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -35,10 +35,13 @@ class VendorTransformer extends EntityTransformer * @SWG\Property(property="id_number", type="string", example="123456") */ - protected $availableIncludes = [ + protected $defaultIncludes = [ 'vendorContacts', + ]; + + protected $availableIncludes = [ 'invoices', - 'expenses', + //'expenses', ]; public function includeVendorContacts(Vendor $vendor) diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php new file mode 100644 index 000000000000..018da7b2357b --- /dev/null +++ b/database/factories/ModelFactory.php @@ -0,0 +1,48 @@ +define(Contact::class, function (Faker\Generator $faker) { + return [ + 'client_id' => function() { + return factory(Client::class)->create()->id; + }, + 'user_id' => 1, + 'account_id' => 1, + 'public_id' => Contact::count() + 1, + 'is_primary' => true, + 'send_invoice' => true, + 'first_name' => $faker->firstName, + 'last_name' => $faker->lastName, + 'email' => $faker->safeEmail, + 'phone' => $faker->phoneNumber, + ]; +}); + +$factory->define(Client::class, function (Faker\Generator $faker) { + return [ + 'user_id' => 1, + 'account_id' => 1, + 'public_id' => Client::count() + 1, + 'name' => $faker->name, + 'address1' => $faker->streetAddress, + 'address2' => $faker->secondaryAddress, + 'city' => $faker->city, + 'state' => $faker->state, + 'postal_code' => $faker->postcode, + 'country_id' => Country::all()->random()->id, + ]; +}); \ No newline at end of file From db9d75ceffb143e71f71c9444b65a928edcb9da9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 15:16:49 +0300 Subject: [PATCH 195/258] Changed vendorcontacts to vendor_contacts --- app/Console/Commands/CreateTestData.php | 2 +- app/Http/Controllers/BaseAPIController.php | 2 +- app/Http/Controllers/ExpenseController.php | 6 ++-- app/Http/Controllers/ExportController.php | 4 +-- app/Http/Controllers/VendorApiController.php | 2 +- app/Http/Requests/VendorRequest.php | 4 +-- app/Models/Vendor.php | 4 +-- app/Ninja/Repositories/VendorRepository.php | 6 ++-- app/Ninja/Transformers/VendorTransformer.php | 4 +-- resources/views/vendors/edit.blade.php | 34 ++++++++++---------- resources/views/vendors/show.blade.php | 2 +- tests/acceptance/APICest.php | 2 +- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php index 4499e1cc09c4..6a0643e46c5a 100644 --- a/app/Console/Commands/CreateTestData.php +++ b/app/Console/Commands/CreateTestData.php @@ -126,7 +126,7 @@ class CreateTestData extends Command 'city' => $this->faker->city, 'state' => $this->faker->state, 'postal_code' => $this->faker->postcode, - 'vendorcontacts' => [[ + 'vendor_contacts' => [[ 'first_name' => $this->faker->firstName, 'last_name' => $this->faker->lastName, 'email' => $this->faker->safeEmail, diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 8e48f3bff878..8ffc45ffcf51 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -204,7 +204,7 @@ class BaseAPIController extends Controller } elseif ($include == 'clients') { $data[] = 'clients.contacts'; } elseif ($include == 'vendors') { - $data[] = 'vendors.vendorcontacts'; + $data[] = 'vendors.vendor_contacts'; } elseif ($include) { $data[] = $include; } diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 3da123c2d972..d4184abf3fca 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -74,7 +74,7 @@ class ExpenseController extends BaseController public function create(ExpenseRequest $request) { if ($request->vendor_id != 0) { - $vendor = Vendor::scope($request->vendor_id)->with('vendorcontacts')->firstOrFail(); + $vendor = Vendor::scope($request->vendor_id)->with('vendor_contacts')->firstOrFail(); } else { $vendor = null; } @@ -85,7 +85,7 @@ class ExpenseController extends BaseController 'method' => 'POST', 'url' => 'expenses', 'title' => trans('texts.new_expense'), - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendors' => Vendor::scope()->with('vendor_contacts')->orderBy('name')->get(), 'vendor' => $vendor, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), 'clientPublicId' => $request->client_id, @@ -124,7 +124,7 @@ class ExpenseController extends BaseController 'url' => 'expenses/'.$expense->public_id, 'title' => 'Edit Expense', 'actions' => $actions, - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendors' => Vendor::scope()->with('vendor_contacts')->orderBy('name')->get(), 'vendorPublicId' => $expense->vendor ? $expense->vendor->public_id : null, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), 'clientPublicId' => $expense->client ? $expense->client->public_id : null, diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index db74adb89cee..ebaaa1f6c306 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -164,12 +164,12 @@ class ExportController extends BaseController if ($request->input(ENTITY_VENDOR)) { $data['clients'] = Vendor::scope() - ->with('user', 'vendorcontacts', 'country') + ->with('user', 'vendor_contacts', 'country') ->withArchived() ->get(); $data['vendor_contacts'] = VendorContact::scope() - ->with('user', 'vendor.contacts') + ->with('user', 'vendor.vendor_contacts') ->withTrashed() ->get(); diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 2995e5c5e5f2..e15207934cd8 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -81,7 +81,7 @@ class VendorApiController extends BaseAPIController $vendor = $this->vendorRepo->save($request->input()); $vendor = Vendor::scope($vendor->public_id) - ->with('country', 'vendorcontacts', 'industry', 'size', 'currency') + ->with('country', 'vendor_contacts', 'industry', 'size', 'currency') ->first(); return $this->itemResponse($vendor); diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php index 600678686c0e..a06e5ad808e6 100644 --- a/app/Http/Requests/VendorRequest.php +++ b/app/Http/Requests/VendorRequest.php @@ -9,8 +9,8 @@ class VendorRequest extends EntityRequest { $vendor = parent::entity(); // eager load the contacts - if ($vendor && ! count($vendor->vendorcontacts)) { - $vendor->load('vendorcontacts'); + if ($vendor && ! count($vendor->vendor_contacts)) { + $vendor->load('vendor_contacts'); } return $vendor; diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 573a581e4774..6fcd10f14092 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -95,7 +95,7 @@ class Vendor extends EntityModel return $this->hasMany('App\Models\Payment'); } - public function vendorContacts() + public function vendor_contacts() { return $this->hasMany('App\Models\VendorContact'); } @@ -143,7 +143,7 @@ class Vendor extends EntityModel $contact->fill($data); $contact->is_primary = $isPrimary; - return $this->vendorContacts()->save($contact); + return $this->vendor_contacts()->save($contact); } public function getRoute() diff --git a/app/Ninja/Repositories/VendorRepository.php b/app/Ninja/Repositories/VendorRepository.php index ef5648f47d81..82e1bfdd18f1 100644 --- a/app/Ninja/Repositories/VendorRepository.php +++ b/app/Ninja/Repositories/VendorRepository.php @@ -16,7 +16,7 @@ class VendorRepository extends BaseRepository public function all() { return Vendor::scope() - ->with('user', 'vendorcontacts', 'country') + ->with('user', 'vendor_contacts', 'country') ->withTrashed() ->where('is_deleted', '=', false) ->get(); @@ -71,7 +71,7 @@ class VendorRepository extends BaseRepository } elseif (!$publicId || $publicId == '-1') { $vendor = Vendor::createNew(); } else { - $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); + $vendor = Vendor::scope($publicId)->with('vendor_contacts')->firstOrFail(); \Log::warning('Entity not set in vendor repo save'); } @@ -79,7 +79,7 @@ class VendorRepository extends BaseRepository $vendor->save(); $first = true; - $vendorcontacts = isset($data['vendorcontact']) ? [$data['vendorcontact']] : $data['vendorcontacts']; + $vendorcontacts = isset($data['vendor_contact']) ? [$data['vendor_contact']] : $data['vendor_contacts']; foreach ($vendorcontacts as $vendorcontact) { $vendorcontact = $vendor->addVendorContact($vendorcontact, $first); diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index 2af79422fde8..df8dc5ea3461 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -36,7 +36,7 @@ class VendorTransformer extends EntityTransformer */ protected $defaultIncludes = [ - 'vendorContacts', + 'vendor_contacts', ]; protected $availableIncludes = [ @@ -47,7 +47,7 @@ class VendorTransformer extends EntityTransformer public function includeVendorContacts(Vendor $vendor) { $transformer = new VendorContactTransformer($this->account, $this->serializer); - return $this->includeCollection($vendor->vendorContacts, $transformer, ENTITY_CONTACT); + return $this->includeCollection($vendor->vendor_contacts, $transformer, ENTITY_CONTACT); } public function includeInvoices(Vendor $vendor) diff --git a/resources/views/vendors/edit.blade.php b/resources/views/vendors/edit.blade.php index 5a61287b2ba3..82a619281513 100644 --- a/resources/views/vendors/edit.blade.php +++ b/resources/views/vendors/edit.blade.php @@ -7,8 +7,8 @@ @section('content') -@if ($errors->first('vendorcontacts')) -
    {{ trans($errors->first('vendorcontacts')) }}
    +@if ($errors->first('vendor_contacts')) +
    {{ trans($errors->first('vendor_contacts')) }}
    @endif
    @@ -73,26 +73,26 @@
    -
    {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][public_id]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][public_id]'}") !!} {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][first_name]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][first_name]'}") !!} {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][last_name]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][last_name]'}") !!} {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][phone]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][phone]'}") !!}
    - + {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} - + {!! link_to('#', trans('texts.add_contact').' +', array('onclick'=>'return addContact()')) !!}
    @@ -186,10 +186,10 @@ function VendorModel(data) { var self = this; - self.vendorcontacts = ko.observableArray(); + self.vendor_contacts = ko.observableArray(); self.mapping = { - 'vendorcontacts': { + 'vendor_contacts': { create: function(options) { return new VendorContactModel(options.data); } @@ -199,12 +199,12 @@ if (data) { ko.mapping.fromJS(data, self.mapping, this); } else { - self.vendorcontacts.push(new VendorContactModel()); + self.vendor_contacts.push(new VendorContactModel()); } self.placeholderName = ko.computed(function() { - if (self.vendorcontacts().length == 0) return ''; - var contact = self.vendorcontacts()[0]; + if (self.vendor_contacts().length == 0) return ''; + var contact = self.vendor_contacts()[0]; if (contact.first_name() || contact.last_name()) { return contact.first_name() + ' ' + contact.last_name(); } else { @@ -226,12 +226,12 @@ ko.applyBindings(model); function addContact() { - model.vendorcontacts.push(new VendorContactModel()); + model.vendor_contacts.push(new VendorContactModel()); return false; } model.removeContact = function() { - model.vendorcontacts.remove(this); + model.vendor_contacts.remove(this); } diff --git a/resources/views/vendors/show.blade.php b/resources/views/vendors/show.blade.php index 62037179e741..37572ee4c5bc 100644 --- a/resources/views/vendors/show.blade.php +++ b/resources/views/vendors/show.blade.php @@ -109,7 +109,7 @@

    {{ trans('texts.contacts') }}

    - @foreach ($vendor->vendorcontacts as $contact) + @foreach ($vendor->vendor_contacts as $contact) @if ($contact->first_name || $contact->last_name) {{ $contact->first_name.' '.$contact->last_name }}
    @endif diff --git a/tests/acceptance/APICest.php b/tests/acceptance/APICest.php index ee36c6265e5c..41e922539677 100644 --- a/tests/acceptance/APICest.php +++ b/tests/acceptance/APICest.php @@ -78,7 +78,7 @@ class APICest $data = new stdClass; $data->name = $this->faker->word; - $data->vendorcontacts = []; + $data->vendor_contacts = []; $this->createEntity('vendor', $data); $this->listEntities('vendor'); From 3300619d28bfd29f0011f69baa8768c673e634d6 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 23:27:12 +0300 Subject: [PATCH 196/258] Added support for gzip to the .htaccess file --- .htaccess | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/.htaccess b/.htaccess index 27a6945d38f8..8b1f582ca455 100644 --- a/.htaccess +++ b/.htaccess @@ -6,3 +6,141 @@ # https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url # RewriteRule ^(.*)$ public/$1 [L] + +# https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess + + +# ###################################################################### +# # INTERNET EXPLORER # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Iframes cookies | +# ---------------------------------------------------------------------- + +# Allow cookies to be set from iframes in Internet Explorer. +# +# https://msdn.microsoft.com/en-us/library/ms537343.aspx +# http://www.w3.org/TR/2000/CR-P3P-20001215/ + + + Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" + + + +# ###################################################################### +# # MEDIA TYPES AND CHARACTER ENCODINGS # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Character encodings | +# ---------------------------------------------------------------------- + +# Serve all resources labeled as `text/html` or `text/plain` +# with the media type `charset` parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset + +AddDefaultCharset utf-8 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Serve the following file types with the media type `charset` +# parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset + + + AddCharset utf-8 .atom \ + .bbaw \ + .css \ + .geojson \ + .js \ + .json \ + .jsonld \ + .manifest \ + .rdf \ + .rss \ + .topojson \ + .vtt \ + .webapp \ + .webmanifest \ + .xloc \ + .xml + + + +# ###################################################################### +# # WEB PERFORMANCE # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Compression | +# ---------------------------------------------------------------------- + + + + # Force compression for mangled headers. + # https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html + + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Map certain file types to the specified encoding type in order to + # make Apache serve them with the appropriate `Content-Encoding` HTTP + # response header (this will NOT make Apache compress them!). + + # If the following file types wouldn't be served without the appropriate + # `Content-Enable` HTTP response header, client applications (e.g.: + # browsers) wouldn't know that they first need to uncompress the response, + # and thus, wouldn't be able to understand the content. + + # http://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding + + + AddEncoding gzip svgz + + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Compress all output labeled with one of the following media types. + + # IMPORTANT: For Apache versions below 2.3.7 you don't need to enable + # `mod_filter` and can remove the `` & `` + # lines as `AddOutputFilterByType` is still in the core directives. + + + AddOutputFilterByType DEFLATE "application/atom+xml" \ + "application/javascript" \ + "application/json" \ + "application/ld+json" \ + "application/manifest+json" \ + "application/rdf+xml" \ + "application/rss+xml" \ + "application/schema+json" \ + "application/vnd.geo+json" \ + "application/vnd.ms-fontobject" \ + "application/x-font-ttf" \ + "application/x-web-app-manifest+json" \ + "application/xhtml+xml" \ + "application/xml" \ + "font/opentype" \ + "image/svg+xml" \ + "image/x-icon" \ + "text/cache-manifest" \ + "text/css" \ + "text/html" \ + "text/javascript" \ + "text/plain" \ + "text/vtt" \ + "text/x-component" \ + "text/xml" + + + \ No newline at end of file From 32bf47aacff801a43aa188a1aba2e228e4ee2a9b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 23:49:20 +0300 Subject: [PATCH 197/258] Added support for filtering API lists by updated_at --- app/Http/Controllers/BaseAPIController.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 8ffc45ffcf51..a172c2e1c5db 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -90,10 +90,21 @@ class BaseAPIController extends Controller $transformerClass = EntityModel::getTransformerName($this->entityType); $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); - $include = $transformer->getDefaultIncludes(); - $include = $this->getRequestIncludes($include); - $query->with($include); - + $includes = $transformer->getDefaultIncludes(); + $includes = $this->getRequestIncludes($includes); + + if ($updatedAt = Input::get('updated_at')) { + $query->where('updated_at', '>=', $updatedAt); + } + + foreach ($includes as $include) { + $query->with([$include => function($query) use ($updatedAt) { + if ($updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + } + }]); + } + if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { $query->where('public_id', '=', $clientPublicId); @@ -156,7 +167,7 @@ class BaseAPIController extends Controller if (Utils::isNinjaDev()) { $count = count(\DB::getQueryLog()); Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); - //Log::info(print_r(\DB::getQueryLog(), true)); + //Log::info(json_encode(\DB::getQueryLog())); } $index = Request::get('index') ?: 'data'; From 5d00d186012c3e4494d89eef7d637fe6b6e64d12 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 00:26:39 +0300 Subject: [PATCH 198/258] Added support for filtering API lists by updated_at --- app/Http/Controllers/BaseAPIController.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index a172c2e1c5db..0718e95b91e4 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -93,17 +93,19 @@ class BaseAPIController extends Controller $includes = $transformer->getDefaultIncludes(); $includes = $this->getRequestIncludes($includes); - if ($updatedAt = Input::get('updated_at')) { - $query->where('updated_at', '>=', $updatedAt); - } + $query->with($includes); - foreach ($includes as $include) { - $query->with([$include => function($query) use ($updatedAt) { - if ($updatedAt) { - $query->where('updated_at', '>=', $updatedAt); + if ($updatedAt = Input::get('updated_at')) { + $updatedAt = date('Y-m-d H:i:s', $updatedAt); + $query->where(function($query) use ($includes, $updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + foreach ($includes as $include) { + $query->orWhereHas($include, function($query) use ($updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + }); } - }]); - } + }); + } if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { @@ -167,7 +169,7 @@ class BaseAPIController extends Controller if (Utils::isNinjaDev()) { $count = count(\DB::getQueryLog()); Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); - //Log::info(json_encode(\DB::getQueryLog())); + Log::info(json_encode(\DB::getQueryLog())); } $index = Request::get('index') ?: 'data'; From 3b508f656b5f0f2d26be0588790e71172b2f0958 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 09:35:39 +0300 Subject: [PATCH 199/258] Fix for travis test --- tests/acceptance/TaxRatesCest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/TaxRatesCest.php b/tests/acceptance/TaxRatesCest.php index ebe68f7b5880..e4db10b15909 100644 --- a/tests/acceptance/TaxRatesCest.php +++ b/tests/acceptance/TaxRatesCest.php @@ -84,7 +84,8 @@ class TaxRatesCest // check total is right after saving $I->see("\${$total}"); $I->amOnPage('/invoices'); - + $I->wait(2); + // check total is right in list view $I->see("\${$total}"); } From ef2bc3d811be4140198b91baacdc0440f83ecaa4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 09:52:59 +0300 Subject: [PATCH 200/258] White label changes --- resources/lang/en/texts.php | 2 +- resources/views/accounts/management.blade.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f101c79564e7..25eaa81de8e2 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -992,7 +992,7 @@ $LANG = array( 'overdue' => 'Overdue', - 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', + 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and emails.', 'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/settings/notifications', 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index cf9917deb005..cfb7b6eb28ee 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -94,9 +94,9 @@
    @endif @if (Utils::isNinjaProd()) - {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} - @else - {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} + {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} + @elseif (!$account->hasFeature(FEATURE_WHITE_LABEL)) + {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} @endif @endif
    From 7744c6c41ccbdadf1b40758576cdb53db5ad4f36 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 09:58:14 +0300 Subject: [PATCH 201/258] White label changes --- resources/lang/en/texts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 25eaa81de8e2..f101c79564e7 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -992,7 +992,7 @@ $LANG = array( 'overdue' => 'Overdue', - 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and emails.', + 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', 'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/settings/notifications', 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', From 2a5de605dcddd4c944d8aee124647e7151307dca Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 10:15:51 +0300 Subject: [PATCH 202/258] Added relationLoaded checks to optimize queries --- app/Http/Requests/ClientRequest.php | 2 +- app/Http/Requests/ExpenseRequest.php | 6 +++--- app/Http/Requests/InvoiceRequest.php | 4 ++-- app/Http/Requests/VendorRequest.php | 2 +- app/Models/Account.php | 6 ++++-- app/Models/Client.php | 10 +++++++--- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index adbe2d4c2ae6..ec28cdb77d1d 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -9,7 +9,7 @@ class ClientRequest extends EntityRequest { $client = parent::entity(); // eager load the contacts - if ($client && ! count($client->contacts)) { + if ($client && ! $client->relationLoaded('contacts')) { $client->load('contacts'); } diff --git a/app/Http/Requests/ExpenseRequest.php b/app/Http/Requests/ExpenseRequest.php index d5e2c793c371..ae2e83b6d12f 100644 --- a/app/Http/Requests/ExpenseRequest.php +++ b/app/Http/Requests/ExpenseRequest.php @@ -8,11 +8,11 @@ class ExpenseRequest extends EntityRequest { { $expense = parent::entity(); - // eager load the contacts - if ($expense && ! count($expense->documents)) { + // eager load the documents + if ($expense && ! $expense->relationLoaded('documents')) { $expense->load('documents'); } - + return $expense; } } \ No newline at end of file diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php index bf24c38839a9..5e2d93139003 100644 --- a/app/Http/Requests/InvoiceRequest.php +++ b/app/Http/Requests/InvoiceRequest.php @@ -8,8 +8,8 @@ class InvoiceRequest extends EntityRequest { { $invoice = parent::entity(); - // eager load the contacts - if ($invoice && ! count($invoice->invoice_items)) { + // eager load the invoice items + if ($invoice && ! $invoice->relationLoaded('invoice_items')) { $invoice->load('invoice_items'); } diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php index a06e5ad808e6..8f96e7c55025 100644 --- a/app/Http/Requests/VendorRequest.php +++ b/app/Http/Requests/VendorRequest.php @@ -9,7 +9,7 @@ class VendorRequest extends EntityRequest { $vendor = parent::entity(); // eager load the contacts - if ($vendor && ! count($vendor->vendor_contacts)) { + if ($vendor && ! $vendor->relationLoaded('vendor_contacts')) { $vendor->load('vendor_contacts'); } diff --git a/app/Models/Account.php b/app/Models/Account.php index c8ece3b4da95..5814ec76423a 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -212,7 +212,9 @@ class Account extends Eloquent public function isGatewayConfigured($gatewayId = 0) { - $this->load('account_gateways'); + if ( ! $this->relationLoaded('account_gateways')) { + $this->load('account_gateways'); + } if ($gatewayId) { return $this->getGatewayConfig($gatewayId) != false; @@ -241,7 +243,7 @@ class Account extends Eloquent return $this->name; } - $this->load('users'); + //$this->load('users'); $user = $this->users()->first(); return $user->getDisplayName(); diff --git a/app/Models/Client.php b/app/Models/Client.php index 85cb543a6b03..4b26f40df0a7 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -256,13 +256,17 @@ class Client extends EntityModel public function getGatewayToken() { - $this->account->load('account_gateways'); + $account = $this->account; + + if ( ! $account->relationLoaded('account_gateways')) { + $account->load('account_gateways'); + } - if (!count($this->account->account_gateways)) { + if (!count($account->account_gateways)) { return false; } - $accountGateway = $this->account->getGatewayConfig(GATEWAY_STRIPE); + $accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE); if (!$accountGateway) { return false; From a00a86a57898b52b4679c2b15b82c9e448c8f2fd Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 10:23:09 +0300 Subject: [PATCH 203/258] Fix for travis --- tests/acceptance/OnlinePaymentCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/OnlinePaymentCest.php b/tests/acceptance/OnlinePaymentCest.php index 44bb04ea6498..7e85f2cc6b5a 100644 --- a/tests/acceptance/OnlinePaymentCest.php +++ b/tests/acceptance/OnlinePaymentCest.php @@ -96,7 +96,7 @@ class OnlinePaymentCest $I->click('table.invoice-table tbody tr:nth-child(1) .tt-selectable'); $I->checkOption('#auto_bill'); $I->executeJS('preparePdfData(\'email\')'); - $I->wait(2); + $I->wait(3); $I->see("$0.00"); } From 9f8865d0f22d55c59a1d6606e6148ab543f4ad33 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 10:28:23 +0300 Subject: [PATCH 204/258] Added pages to navigation search --- app/Ninja/Repositories/AccountRepository.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index e8fe2d041843..e650b28a419e 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -167,6 +167,7 @@ class AccountRepository ENTITY_QUOTE, ENTITY_TASK, ENTITY_EXPENSE, + ENTITY_VENDOR, ENTITY_RECURRING_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT @@ -192,6 +193,10 @@ class AccountRepository $settings = array_merge(Account::$basicSettings, Account::$advancedSettings); + if ( ! Utils::isNinjaProd()) { + $settings[] = ACCOUNT_SYSTEM_SETTINGS; + } + foreach ($settings as $setting) { $features[] = [ $setting, From 501958cc1ec013cad8e11183d38880f8b577016c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 11:55:09 +0300 Subject: [PATCH 205/258] Added language seeder --- app/Http/Controllers/AppController.php | 13 +------- database/seeds/DatabaseSeeder.php | 1 + database/seeds/LanguageSeeder.php | 41 ++++++++++++++++++++++++++ database/seeds/UpdateSeeder.php | 1 + 4 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 database/seeds/LanguageSeeder.php diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 568cb4d45f5b..a602ae7ecb85 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -266,18 +266,7 @@ class AppController extends BaseController Cache::flush(); Session::flush(); Artisan::call('migrate', array('--force' => true)); - foreach ([ - 'PaymentLibraries', - 'Fonts', - 'Banks', - 'InvoiceStatus', - 'Currencies', - 'DateFormats', - 'InvoiceDesigns', - 'PaymentTerms', - ] as $seeder) { - Artisan::call('db:seed', array('--force' => true, '--class' => "{$seeder}Seeder")); - } + Artisan::call('db:seed', array('--force' => true, '--class' => "UpdateSeeder")); Event::fire(new UserSettingsChanged()); Session::flash('message', trans('texts.processed_updates')); } catch (Exception $e) { diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 8791f30e71fb..d7f38d3e1e53 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -23,5 +23,6 @@ class DatabaseSeeder extends Seeder $this->call('DateFormatsSeeder'); $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); + $this->call('LanguageSeeder'); } } diff --git a/database/seeds/LanguageSeeder.php b/database/seeds/LanguageSeeder.php new file mode 100644 index 000000000000..c82b2630771c --- /dev/null +++ b/database/seeds/LanguageSeeder.php @@ -0,0 +1,41 @@ + 'English', 'locale' => 'en'], + ['name' => 'Italian', 'locale' => 'it'], + ['name' => 'German', 'locale' => 'de'], + ['name' => 'French', 'locale' => 'fr'], + ['name' => 'Brazilian Portuguese', 'locale' => 'pt_BR'], + ['name' => 'Dutch', 'locale' => 'nl'], + ['name' => 'Spanish', 'locale' => 'es'], + ['name' => 'Norwegian', 'locale' => 'nb_NO'], + ['name' => 'Danish', 'locale' => 'da'], + ['name' => 'Japanese', 'locale' => 'ja'], + ['name' => 'Swedish', 'locale' => 'sv'], + ['name' => 'Spanish - Spain', 'locale' => 'es_ES'], + ['name' => 'French - Canada', 'locale' => 'fr_CA'], + ['name' => 'Lithuanian', 'locale' => 'lt'], + ['name' => 'Polish', 'locale' => 'pl'], + ]; + + foreach ($languages as $language) { + $record = Language::whereLocale($language['locale'])->first(); + if ($record) { + $record->name = $language['name']; + $record->save(); + } else { + Language::create($language); + } + } + + Eloquent::reguard(); + } +} diff --git a/database/seeds/UpdateSeeder.php b/database/seeds/UpdateSeeder.php index e4d43f78e512..d445811de506 100644 --- a/database/seeds/UpdateSeeder.php +++ b/database/seeds/UpdateSeeder.php @@ -19,5 +19,6 @@ class UpdateSeeder extends Seeder $this->call('DateFormatsSeeder'); $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); + $this->call('LanguageSeeder'); } } From c79d9fe24cf3fc776a69ceb1479111bf1d4e00fc Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 17:46:22 +0300 Subject: [PATCH 206/258] Added preview for email templates --- app/Http/Controllers/AccountController.php | 25 +++++ app/Http/routes.php | 1 + app/Models/Account.php | 5 + app/Ninja/Mailers/ContactMailer.php | 103 ++---------------- app/Services/TemplateService.php | 80 ++++++++++++++ resources/lang/en/texts.php | 1 + resources/views/accounts/template.blade.php | 7 +- .../templates_and_reminders.blade.php | 36 ++++++ resources/views/emails/design2_html.blade.php | 22 ++-- ..._html.blade.php => design3_html.blade.php} | 22 ++-- ..._text.blade.php => design3_text.blade.php} | 0 .../emails/partials/account_logo.blade.php | 2 +- 12 files changed, 188 insertions(+), 116 deletions(-) create mode 100644 app/Services/TemplateService.php rename resources/views/emails/{design1_html.blade.php => design3_html.blade.php} (72%) rename resources/views/emails/{design1_text.blade.php => design3_text.blade.php} (100%) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 3f8917b65427..a55188338073 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -1301,4 +1301,29 @@ class AccountController extends BaseController return Redirect::to("/settings/$section/", 301); } + + public function previewEmail(\App\Services\TemplateService $templateService) + { + $template = Input::get('template'); + $invoice = Invoice::scope()->first(); + $account = Auth::user()->account; + + // replace the variables with sample data + $data = [ + 'account' => $account, + 'invoice' => $invoice, + 'invitation' => $invoice->invitations->first(), + 'client' => $invoice->client, + 'amount' => $invoice->amount + ]; + + // create the email view + $view = 'emails.' . $account->getTemplateView() . '_html'; + $data = array_merge($data, [ + 'body' => $templateService->processVariables($template, $data), + 'entityType' => ENTITY_INVOICE, + ]); + + return Response::view($view, $data); + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index df1f1add4bb8..c1162db275c1 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -208,6 +208,7 @@ Route::group([ Route::resource('tax_rates', 'TaxRateController'); Route::post('tax_rates/bulk', 'TaxRateController@bulk'); + Route::get('settings/email_preview', 'AccountController@previewEmail'); Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy'); Route::get('settings/data_visualizations', 'ReportController@d3'); Route::get('settings/charts_and_reports', 'ReportController@showReports'); diff --git a/app/Models/Account.php b/app/Models/Account.php index 5814ec76423a..92e2a623ff7b 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -1169,6 +1169,11 @@ class Account extends Eloquent return str_replace('/>', ' />', $template); } + public function getTemplateView($view = '') + { + return $this->getEmailDesignId() == EMAIL_DESIGN_PLAIN ? $view : 'design' . $this->getEmailDesignId(); + } + public function getEmailFooter() { if ($this->email_footer) { diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 4b176628ebf0..fbebd0c5fdfd 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -1,17 +1,13 @@ templateService = $templateService; + } + public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); @@ -144,7 +145,7 @@ class ContactMailer extends Mailer } $data = [ - 'body' => $this->processVariables($body, $variables), + 'body' => $this->templateService->processVariables($body, $variables), 'link' => $invitation->getLink(), 'entityType' => $invoice->getEntityType(), 'invoiceId' => $invoice->id, @@ -160,14 +161,9 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($subject, $variables); + $subject = $this->templateService->processVariables($subject, $variables); $fromEmail = $user->email; - - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = ENTITY_INVOICE; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView(ENTITY_INVOICE); $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data); @@ -230,7 +226,7 @@ class ContactMailer extends Mailer ]; $data = [ - 'body' => $this->processVariables($emailTemplate, $variables), + 'body' => $this->templateService->processVariables($emailTemplate, $variables), 'link' => $invitation->getLink(), 'invoice' => $invoice, 'client' => $client, @@ -244,14 +240,10 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($emailSubject, $variables); + $subject = $this->templateService->processVariables($emailSubject, $variables); $data['invoice_id'] = $payment->invoice->id; - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = 'payment_confirmation'; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView('payment_confirmation'); if ($user->email && $contact->email) { $this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data); @@ -281,75 +273,4 @@ class ContactMailer extends Mailer $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } - - private function processVariables($template, $data) - { - $account = $data['account']; - $client = $data['client']; - $invitation = $data['invitation']; - $invoice = $invitation->invoice; - $passwordHTML = isset($data['password'])?'

    '.trans('texts.password').': '.$data['password'].'

    ':false; - $documentsHTML = ''; - - if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){ - $documentsHTML .= trans('texts.email_documents_header').'

    '; - } - - $variables = [ - '$footer' => $account->getEmailFooter(), - '$client' => $client->getDisplayName(), - '$account' => $account->getDisplayName(), - '$dueDate' => $account->formatDate($invoice->due_date), - '$invoiceDate' => $account->formatDate($invoice->invoice_date), - '$contact' => $invitation->contact->getDisplayName(), - '$firstName' => $invitation->contact->first_name, - '$amount' => $account->formatMoney($data['amount'], $client), - '$invoice' => $invoice->invoice_number, - '$quote' => $invoice->invoice_number, - '$link' => $invitation->getLink(), - '$password' => $passwordHTML, - '$viewLink' => $invitation->getLink().'$password', - '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', - '$paymentLink' => $invitation->getLink('payment').'$password', - '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', - '$customClient1' => $account->custom_client_label1, - '$customClient2' => $account->custom_client_label2, - '$customInvoice1' => $account->custom_invoice_text_label1, - '$customInvoice2' => $account->custom_invoice_text_label2, - '$documents' => $documentsHTML, - ]; - - // Add variables for available payment types - foreach (Gateway::$paymentTypes as $type) { - $camelType = Gateway::getPaymentTypeName($type); - $type = Utils::toSnakeCase($camelType); - $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; - $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); - } - - $includesPasswordPlaceholder = strpos($template, '$password') !== false; - - $str = str_replace(array_keys($variables), array_values($variables), $template); - - if(!$includesPasswordPlaceholder && $passwordHTML){ - $pos = strrpos($str, '$password'); - if($pos !== false) - { - $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); - } - } - $str = str_replace('$password', '', $str); - $str = autolink($str, 100); - - return $str; - } } diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php new file mode 100644 index 000000000000..5a41c705352d --- /dev/null +++ b/app/Services/TemplateService.php @@ -0,0 +1,80 @@ +invoice; + $passwordHTML = isset($data['password'])?'

    '.trans('texts.password').': '.$data['password'].'

    ':false; + $documentsHTML = ''; + + if ($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()) { + $documentsHTML .= trans('texts.email_documents_header').'

    '; + } + + $variables = [ + '$footer' => $account->getEmailFooter(), + '$client' => $client->getDisplayName(), + '$account' => $account->getDisplayName(), + '$dueDate' => $account->formatDate($invoice->due_date), + '$invoiceDate' => $account->formatDate($invoice->invoice_date), + '$contact' => $invitation->contact->getDisplayName(), + '$firstName' => $invitation->contact->first_name, + '$amount' => $account->formatMoney($data['amount'], $client), + '$invoice' => $invoice->invoice_number, + '$quote' => $invoice->invoice_number, + '$link' => $invitation->getLink(), + '$password' => $passwordHTML, + '$viewLink' => $invitation->getLink().'$password', + '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', + '$paymentLink' => $invitation->getLink('payment').'$password', + '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', + '$customClient1' => $account->custom_client_label1, + '$customClient2' => $account->custom_client_label2, + '$customInvoice1' => $account->custom_invoice_text_label1, + '$customInvoice2' => $account->custom_invoice_text_label2, + '$documents' => $documentsHTML, + ]; + + // Add variables for available payment types + foreach (Gateway::$paymentTypes as $type) { + $camelType = Gateway::getPaymentTypeName($type); + $type = Utils::toSnakeCase($camelType); + $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; + $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); + } + + $includesPasswordPlaceholder = strpos($template, '$password') !== false; + + $str = str_replace(array_keys($variables), array_values($variables), $template); + + if (!$includesPasswordPlaceholder && $passwordHTML) { + $pos = strrpos($str, '$password'); + if ($pos !== false) + { + $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); + } + } + $str = str_replace('$password', '', $str); + $str = autolink($str, 100); + + return $str; + } +} \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f101c79564e7..6d6f3229b059 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1176,6 +1176,7 @@ $LANG = array( 'page_size' => 'Page Size', 'live_preview_disabled' => 'Live preview has been disabled to support selected font', 'invoice_number_padding' => 'Padding', + 'preview' => 'Preview', ); diff --git a/resources/views/accounts/template.blade.php b/resources/views/accounts/template.blade.php index 0d4294af5b6f..68279299f607 100644 --- a/resources/views/accounts/template.blade.php +++ b/resources/views/accounts/template.blade.php @@ -62,11 +62,14 @@
    +

     

    -
    -

     

    +

    @include('partials/quill_toolbar', ['name' => $field])
    +
    + {!! Button::primary(trans('texts.preview'))->withAttributes(['onclick' => 'serverPreview("'.$field.'")'])->small() !!} +
    diff --git a/resources/views/accounts/templates_and_reminders.blade.php b/resources/views/accounts/templates_and_reminders.blade.php index 067433a21763..d8032f352a92 100644 --- a/resources/views/accounts/templates_and_reminders.blade.php +++ b/resources/views/accounts/templates_and_reminders.blade.php @@ -80,6 +80,26 @@
    + + +
    {!! \App\Models\Invoice::calcLink($payment) !!}{!! link_to('/clients/'.$payment->client_public_id, trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email)) !!}{{ trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email) }}{{ Utils::fromSqlDate($payment->payment_date) }} {{ Utils::formatMoney($payment->amount, $payment->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}
    {!! \App\Models\Invoice::calcLink($invoice) !!}{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}{{ trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email) }}{{ Utils::fromSqlDate($invoice->due_date) }} {{ Utils::formatMoney($invoice->balance, $invoice->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}
    {!! \App\Models\Invoice::calcLink($invoice) !!}{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}{{ trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email) }}{{ Utils::fromSqlDate($invoice->due_date) }} {{ Utils::formatMoney($invoice->balance, $invoice->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}
    - +
    - - -
    -

    +

    +

    @if ($invoice->due_date) {{ strtoupper(trans('texts.due_by', ['date' => $account->formatDate($invoice->due_date)])) }}
    @endif - + {{ trans("texts.{$entityType}") }} {{ $invoice->invoice_number }}

    -

    - - {{ strtoupper(trans('texts.' . $invoice->present()->balanceDueLabel)) }}: +

    +

    + + {{ trans('texts.' . $invoice->present()->balanceDueLabel) }}:
    - + {{ $account->formatMoney($invoice->getRequestedAmount(), $client) }}

    diff --git a/resources/views/emails/design1_html.blade.php b/resources/views/emails/design3_html.blade.php similarity index 72% rename from resources/views/emails/design1_html.blade.php rename to resources/views/emails/design3_html.blade.php index 62b621545479..550b5fed472a 100644 --- a/resources/views/emails/design1_html.blade.php +++ b/resources/views/emails/design3_html.blade.php @@ -12,30 +12,30 @@
    - +
    - - -
    -

    +

    +

    @if ($invoice->due_date) {{ strtoupper(trans('texts.due_by', ['date' => $account->formatDate($invoice->due_date)])) }}
    @endif - + {{ trans("texts.{$entityType}") }} {{ $invoice->invoice_number }}

    -

    - - {{ trans('texts.' . $invoice->present()->balanceDueLabel) }}: +

    +

    + + {{ strtoupper(trans('texts.' . $invoice->present()->balanceDueLabel)) }}:
    - + {{ $account->formatMoney($invoice->getRequestedAmount(), $client) }}

    diff --git a/resources/views/emails/design1_text.blade.php b/resources/views/emails/design3_text.blade.php similarity index 100% rename from resources/views/emails/design1_text.blade.php rename to resources/views/emails/design3_text.blade.php diff --git a/resources/views/emails/partials/account_logo.blade.php b/resources/views/emails/partials/account_logo.blade.php index d79002d6b938..a3a8ca167e07 100644 --- a/resources/views/emails/partials/account_logo.blade.php +++ b/resources/views/emails/partials/account_logo.blade.php @@ -3,7 +3,7 @@ @endif - + @if ($account->website) From bce4395f82cc9801a9afae99ea008198d64a4017 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 17:51:52 +0300 Subject: [PATCH 207/258] Added preview for email templates --- app/Http/Controllers/AccountController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index a55188338073..29d56e2db7f2 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -1318,7 +1318,7 @@ class AccountController extends BaseController ]; // create the email view - $view = 'emails.' . $account->getTemplateView() . '_html'; + $view = 'emails.' . $account->getTemplateView(ENTITY_INVOICE) . '_html'; $data = array_merge($data, [ 'body' => $templateService->processVariables($template, $data), 'entityType' => ENTITY_INVOICE, From 697106004942a794d4c86e2f9328584efc8aaa0b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 19:25:26 +0300 Subject: [PATCH 208/258] Added preview for email templates --- app/Http/Controllers/AccountController.php | 10 +++++++++- app/Models/Invoice.php | 6 ++++++ .../views/accounts/templates_and_reminders.blade.php | 2 +- resources/views/emails/design2_html.blade.php | 2 +- resources/views/emails/design3_html.blade.php | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 29d56e2db7f2..db2a65e6eca1 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -1305,7 +1305,15 @@ class AccountController extends BaseController public function previewEmail(\App\Services\TemplateService $templateService) { $template = Input::get('template'); - $invoice = Invoice::scope()->first(); + $invoice = Invoice::scope() + ->invoices() + ->withTrashed() + ->first(); + + if ( ! $invoice) { + return trans('texts.create_invoice_for_sample'); + } + $account = Auth::user()->account; // replace the variables with sample data diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index deb65b75ab30..cd08e33a855d 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -228,6 +228,12 @@ class Invoice extends EntityModel implements BalanceAffecting return $this->hasMany('App\Models\Expense','invoice_id','id')->withTrashed(); } + public function scopeInvoices($query) + { + return $query->where('is_quote', '=', false) + ->where('is_recurring', '=', false); + } + public function markInvitationsSent($notify = false) { foreach ($this->invitations as $invitation) { diff --git a/resources/views/accounts/templates_and_reminders.blade.php b/resources/views/accounts/templates_and_reminders.blade.php index d8032f352a92..c5eebd78d658 100644 --- a/resources/views/accounts/templates_and_reminders.blade.php +++ b/resources/views/accounts/templates_and_reminders.blade.php @@ -81,7 +81,7 @@
    -
    -