Commit a1579f89 by David Muzi

Refactor partial address handling to prevent a partial address to be applied on…

Refactor partial address handling to prevent a partial address to be applied on a completed checkout

fixes for integration tests

fix partialAddress unit test

Allow for partial address flag to be set to false on update

Prevent partial address from being sent post Apple Pay authentication
parent cdce7fc7
......@@ -364,13 +364,13 @@
{
BUYAddress *newAddress = [self buyAddressWithTestRecordFullDetails:NO];
XCTAssertNotNil(newAddress);
XCTAssertEqualObjects(BUYPartialAddressPlaceholder, newAddress.address1);
XCTAssertEqualObjects(nil, newAddress.address1);
XCTAssertEqualObjects(@"Ottawa", newAddress.city);
XCTAssertEqualObjects(@"Ontario", newAddress.province);
XCTAssertEqualObjects(@"K1N5T5", newAddress.zip);
XCTAssertNil(newAddress.country);
XCTAssertEqualObjects(@"CA", newAddress.countryCode);
XCTAssertEqualObjects(BUYPartialAddressPlaceholder, newAddress.phone);
XCTAssertEqualObjects(nil, newAddress.phone);
}
- (void)testAddressFromContact
......
......@@ -109,7 +109,11 @@
checkout = [[BUYCheckout alloc] initWithCart:cart];
BUYAddress *partialAddress = [[BUYAddress alloc] init];
partialAddress.address1 = BUYPartialAddressPlaceholder;
partialAddress.address1 = nil;
if ([partialAddress isPartialAddress]) {
checkout.partialAddresses = YES;
}
checkout.shippingAddress = partialAddress;
task = [self.client createCheckout:checkout completion:nil];
......
......@@ -966,6 +966,10 @@
[self createCart];
_checkout = [[BUYCheckout alloc] initWithCart:_cart];
_checkout.shippingAddress = [self partialShippingAddress];
if ([_checkout.shippingAddress isPartialAddress]) {
_checkout.partialAddresses = YES;
}
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
......@@ -994,6 +998,7 @@
//Update with full addresses
_checkout.shippingAddress = [self shippingAddress];
_checkout.billingAddress = [self billingAddress];
[self updateCheckout];
//We use a credit card here because we're not generating apple pay tokens in the tests
......@@ -1211,14 +1216,14 @@
- (BUYAddress *)partialShippingAddress
{
BUYAddress *address = [[BUYAddress alloc] init];
address.address1 = BUYPartialAddressPlaceholder;
address.address1 = nil;
address.city = @"Ottawa";
address.firstName = BUYPartialAddressPlaceholder;
address.lastName = BUYPartialAddressPlaceholder;
address.firstName = nil;
address.lastName = nil;
address.countryCode = @"CA";
address.provinceCode = @"ON";
address.zip = @"K1N5T5";
address.phone = BUYPartialAddressPlaceholder;
address.phone = nil;
return address;
}
......
......@@ -56,7 +56,6 @@
901930ED1BC5B9BC00D1134E /* BUYProductViewHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 90516C861B4C6F7000E35E45 /* BUYProductViewHeader.m */; };
901930EE1BC5B9BC00D1134E /* BUYNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 90516CC21B4F1ED700E35E45 /* BUYNavigationController.m */; };
901930EF1BC5B9BC00D1134E /* BUYVariantSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9496B11B4D96D800B38949 /* BUYVariantSelectionViewController.m */; };
901930F01BC5B9BC00D1134E /* BUYCheckout+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = BE6C5E711B1CF776003AD9D0 /* BUYCheckout+Additions.m */; };
901930F11BC5B9BC00D1134E /* NSString+Trim.m in Sources */ = {isa = PBXBuildFile; fileRef = F76CFF1C19CB7BE30079C703 /* NSString+Trim.m */; };
901930F21BC5B9BC00D1134E /* BUYOrder.m in Sources */ = {isa = PBXBuildFile; fileRef = 90E83BC31B9F550E00C95A1B /* BUYOrder.m */; };
901930F31BC5B9BC00D1134E /* BUYAddress+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = BE33B4FA1B177EC80067982B /* BUYAddress+Additions.m */; };
......@@ -118,7 +117,6 @@
9019312D1BC5B9BC00D1134E /* BUYTheme.h in Headers */ = {isa = PBXBuildFile; fileRef = 90516CB61B4F0DD500E35E45 /* BUYTheme.h */; settings = {ATTRIBUTES = (Public, ); }; };
9019312E1BC5B9BC00D1134E /* BUYProductDescriptionCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 90516C951B4D76D900E35E45 /* BUYProductDescriptionCell.h */; };
9019312F1BC5B9BC00D1134E /* BUYLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = F7FDA16C19C939FF00AF4E93 /* BUYLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
901931301BC5B9BC00D1134E /* BUYCheckout+Additions.h in Headers */ = {isa = PBXBuildFile; fileRef = BE6C5E701B1CF776003AD9D0 /* BUYCheckout+Additions.h */; };
901931311BC5B9BC00D1134E /* BUYProductViewHeaderOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 907874971B7276BA0023775B /* BUYProductViewHeaderOverlay.h */; };
901931321BC5B9BC00D1134E /* NSString+Trim.h in Headers */ = {isa = PBXBuildFile; fileRef = F76CFF1B19CB7BE30079C703 /* NSString+Trim.h */; };
901931331BC5B9BC00D1134E /* BUYAddress+Additions.h in Headers */ = {isa = PBXBuildFile; fileRef = BE33B4F91B177EC80067982B /* BUYAddress+Additions.h */; };
......@@ -281,6 +279,7 @@
BE9A64791B503D420033E558 /* BUYAddress+Additions.h in Headers */ = {isa = PBXBuildFile; fileRef = BE33B4F91B177EC80067982B /* BUYAddress+Additions.h */; };
BE9A647A1B503D450033E558 /* BUYCheckout+Additions.h in Headers */ = {isa = PBXBuildFile; fileRef = BE6C5E701B1CF776003AD9D0 /* BUYCheckout+Additions.h */; };
BE9A647B1B503D470033E558 /* BUYCheckout+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = BE6C5E711B1CF776003AD9D0 /* BUYCheckout+Additions.m */; };
BE9A647D1B503D840033E558 /* Buy.h in Headers */ = {isa = PBXBuildFile; fileRef = 904FB6011AE03D4500EA1758 /* Buy.h */; settings = {ATTRIBUTES = (Public, ); }; };
BE9A647E1B503D930033E558 /* BUYStoreViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1C4DF31AE98FBB00E21624 /* BUYStoreViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
BE9A647F1B503D960033E558 /* BUYStoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1C4DF41AE98FBB00E21624 /* BUYStoreViewController.m */; };
BE9A64801B503D990033E558 /* BUYViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1C4DF51AE98FBB00E21624 /* BUYViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -482,6 +481,12 @@
BE6C5E711B1CF776003AD9D0 /* BUYCheckout+Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "BUYCheckout+Additions.m"; sourceTree = "<group>"; };
BE6D05971BD6BA6700772EBB /* NSDictionary+Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Additions.h"; sourceTree = "<group>"; };
BE6D05981BD6BA6700772EBB /* NSDictionary+Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Additions.m"; sourceTree = "<group>"; };
BE6D059E1BD6C0A300772EBB /* BUYProductDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BUYProductDataSource.h; path = Data/BUYProductDataSource.h; sourceTree = "<group>"; };
BE6D059F1BD6C0A300772EBB /* BUYProductDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BUYProductDataSource.m; path = Data/BUYProductDataSource.m; sourceTree = "<group>"; };
BE6D05B11BD6C3B900772EBB /* BUYConfigurableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BUYConfigurableCell.h; path = Views/BUYConfigurableCell.h; sourceTree = "<group>"; };
BE741ADB1BC70C6B0062E9CD /* BUYProductCollectionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BUYProductCollectionCell.h; path = Views/BUYProductCollectionCell.h; sourceTree = "<group>"; };
BE741AE11BC7193D0062E9CD /* BUYProductDataSourceOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BUYProductDataSourceOperation.h; path = Data/BUYProductDataSourceOperation.h; sourceTree = "<group>"; };
BE741AF01BC747160062E9CD /* GetShopOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetShopOperation.h; sourceTree = "<group>"; };
BE9496681B45CCF400B38949 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; };
BE9496AE1B4D96D800B38949 /* BUYOptionSelectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = BUYOptionSelectionViewController.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
BE9496AF1B4D96D800B38949 /* BUYOptionSelectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYOptionSelectionViewController.m; sourceTree = "<group>"; };
......@@ -813,8 +818,6 @@
F70CE40E1A8BF1D90055BEB8 /* BUYApplePayAdditions.m */,
BE33B4ED1B15FF4D0067982B /* BUYApplePayHelpers.h */,
BE33B4EE1B15FF4D0067982B /* BUYApplePayHelpers.m */,
BE6C5E701B1CF776003AD9D0 /* BUYCheckout+Additions.h */,
BE6C5E711B1CF776003AD9D0 /* BUYCheckout+Additions.m */,
909944471B71B76800C40A33 /* UIFont+BUYAdditions.h */,
909944481B71B76800C40A33 /* UIFont+BUYAdditions.m */,
900E7C811B5DA32F006F3C81 /* BUYImageKit.h */,
......@@ -970,7 +973,6 @@
BEB74A2B1B554C150005A300 /* BUYTheme.h in Headers */,
BEB74A6B1B55641B0005A300 /* BUYProductDescriptionCell.h in Headers */,
BE9A645B1B503CDC0033E558 /* BUYLineItem.h in Headers */,
BE9A647A1B503D450033E558 /* BUYCheckout+Additions.h in Headers */,
907874991B7276BA0023775B /* BUYProductViewHeaderOverlay.h in Headers */,
BE9A64721B503D290033E558 /* NSString+Trim.h in Headers */,
BE9A64791B503D420033E558 /* BUYAddress+Additions.h in Headers */,
......@@ -1211,7 +1213,6 @@
901930ED1BC5B9BC00D1134E /* BUYProductViewHeader.m in Sources */,
901930EE1BC5B9BC00D1134E /* BUYNavigationController.m in Sources */,
901930EF1BC5B9BC00D1134E /* BUYVariantSelectionViewController.m in Sources */,
901930F01BC5B9BC00D1134E /* BUYCheckout+Additions.m in Sources */,
901930F11BC5B9BC00D1134E /* NSString+Trim.m in Sources */,
901930F21BC5B9BC00D1134E /* BUYOrder.m in Sources */,
901930F31BC5B9BC00D1134E /* BUYAddress+Additions.m in Sources */,
......@@ -1306,7 +1307,6 @@
BEB74A741B5564380005A300 /* BUYProductViewHeader.m in Sources */,
BEB74A681B55640F0005A300 /* BUYNavigationController.m in Sources */,
BEB74A7E1B5564890005A300 /* BUYVariantSelectionViewController.m in Sources */,
BE9A647B1B503D470033E558 /* BUYCheckout+Additions.m in Sources */,
BE9A64731B503D2C0033E558 /* NSString+Trim.m in Sources */,
90E83BC51B9F550E00C95A1B /* BUYOrder.m in Sources */,
BE9A64781B503D3F0033E558 /* BUYAddress+Additions.m in Sources */,
......
......@@ -33,7 +33,6 @@
#import "BUYProduct.h"
#import "BUYShippingRate.h"
#import "BUYShop.h"
#import "BUYCheckout+Additions.h"
#import "BUYCheckout_Private.h"
#import "NSDecimalNumber+BUYAdditions.h"
#import "BUYError.h"
......@@ -252,7 +251,7 @@ NSString * const BUYVersionString = @"1.2.1";
// Inject channel and marketing attributions
[self configureCheckout:checkout];
NSDictionary *json = [checkout jsonDictionaryForUpdatingCheckout];
NSDictionary *json = [checkout jsonDictionaryForCheckout];
return [self postCheckout:json completion:block];
}
......@@ -261,7 +260,7 @@ NSString * const BUYVersionString = @"1.2.1";
BUYCheckout *checkout = [[BUYCheckout alloc] initWithCartToken:cartToken];
[self configureCheckout:checkout];
NSDictionary *json = [checkout jsonDictionaryForUpdatingCheckout];
NSDictionary *json = [checkout jsonDictionaryForCheckout];
return [self postCheckout:json completion:block];
}
......@@ -343,7 +342,7 @@ NSString * const BUYVersionString = @"1.2.1";
- (NSURLSessionDataTask *)updateCheckout:(BUYCheckout *)checkout completion:(BUYDataCheckoutBlock)block
{
NSDictionary *json = [checkout jsonDictionaryForUpdatingCheckout];
NSDictionary *json = [checkout jsonDictionaryForCheckout];
NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil];
NSURLSessionDataTask *task = nil;
......
......@@ -249,6 +249,11 @@
@property (nonatomic, strong, readonly) BUYOrder *order;
/**
* Flag used to inform server that the shipping address is partially filled, suitable to retrieve shipping rates
*/
@property (nonatomic, assign) BOOL partialAddresses;
/**
* It is recommended to instantiate a checkout with a cart, or cart token
*
* @return Checkout
......
......@@ -27,8 +27,6 @@
@import Foundation;
#import "BUYAddress.h"
extern NSString * const BUYPartialAddressPlaceholder;
@interface BUYAddress (Additions)
/**
......
......@@ -26,16 +26,14 @@
#import "BUYAddress+Additions.h"
NSString * const BUYPartialAddressPlaceholder = @"---";
@implementation BUYAddress (Additions)
- (BOOL)isPartialAddress
{
if ([self.address1 isEqualToString:BUYPartialAddressPlaceholder] ||
[self.firstName isEqualToString:BUYPartialAddressPlaceholder] ||
[self.lastName isEqualToString:BUYPartialAddressPlaceholder]) {
if (self.address1.length == 0 ||
self.firstName.length == 0 ||
self.lastName.length == 0) {
return YES;
}
......
......@@ -143,14 +143,7 @@
//Grab the simple information
address.firstName = (__bridge_transfer NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
if ([address.firstName length] == 0) {
address.firstName = BUYPartialAddressPlaceholder;
}
address.lastName = (__bridge_transfer NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);
if ([[address lastName] length] == 0) {
address.lastName = BUYPartialAddressPlaceholder;
}
//Grab the address information
ABMultiValueRef addressMultiValue = ABRecordCopyValue(record, kABPersonAddressProperty);
......@@ -160,10 +153,6 @@
//NOTE: We do not receive an address1 line right now via this partial address, as Apple deemds it unimportant to calculate the shipping rates. We get the actual address later on in a later step.
address.address1 = (__bridge NSString *)CFDictionaryGetValue(firstAddress, kABPersonAddressStreetKey);
if (address.address1 == nil) {
address.address1 = BUYPartialAddressPlaceholder;
}
address.city = (__bridge NSString *)CFDictionaryGetValue(firstAddress, kABPersonAddressCityKey);
address.province = (__bridge NSString *)CFDictionaryGetValue(firstAddress, kABPersonAddressStateKey);
address.zip = (__bridge NSString *)CFDictionaryGetValue(firstAddress, kABPersonAddressZIPKey);
......@@ -185,9 +174,7 @@
if (allPhoneNumbers && CFArrayGetCount(allPhoneNumbers) > 0) {
address.phone = (__bridge NSString *)CFArrayGetValueAtIndex(allPhoneNumbers, 0);
}
if ([address.phone length] == 0) {
address.phone = BUYPartialAddressPlaceholder;
}
CFSafeRelease(phoneMultiValue);
CFSafeRelease(allPhoneNumbers);
......@@ -198,15 +185,15 @@
{
BUYAddress *address = [[BUYAddress alloc] init];
address.firstName = [contact.name.givenName length] ? contact.name.givenName : BUYPartialAddressPlaceholder;
address.lastName = [contact.name.familyName length] ? contact.name.familyName : BUYPartialAddressPlaceholder;
address.firstName = contact.name.givenName;
address.lastName = contact.name.familyName;
if (contact.postalAddress) {
// break up the address:
NSArray *addressComponents = [contact.postalAddress.street componentsSeparatedByString:@"\n"];
address.address1 = [addressComponents[0] length] ? addressComponents[0] : BUYPartialAddressPlaceholder;
address.address2 = ([addressComponents count] > 1 && addressComponents[1]) ? addressComponents[1] : nil;
address.city = [contact.postalAddress.city length] ? contact.postalAddress.city : BUYPartialAddressPlaceholder;
address.address1 = addressComponents[0];
address.address2 = (addressComponents.count > 1) ? addressComponents[1] : nil;
address.city = contact.postalAddress.city;
address.province = contact.postalAddress.state;
address.zip = contact.postalAddress.postalCode;
// The Checkout API accepts country OR ISO country code.
......@@ -219,7 +206,7 @@
}
}
address.phone = contact.phoneNumber.stringValue ?: BUYPartialAddressPlaceholder;
address.phone = contact.phoneNumber.stringValue;
return address;
}
......
......@@ -90,6 +90,7 @@ const NSTimeInterval PollDelay = 0.5;
// Update the checkout with the rest of the information. Apple has now provided us with a FULL billing address and a FULL shipping address.
// We now update the checkout with our new found data so that you can ship the products to the right address, and we collect whatever else we need.
self.checkout.partialAddresses = NO;
self.checkout.shippingAddress = self.checkout.requiresShipping ? [BUYAddress buy_addressFromRecord:[payment shippingAddress]] : nil;
self.checkout.billingAddress = [BUYAddress buy_addressFromRecord:[payment billingAddress]];
self.checkout.email = [BUYAddress buy_emailFromRecord:[payment billingAddress]];
......@@ -158,7 +159,9 @@ const NSTimeInterval PollDelay = 0.5;
#pragma mark -
- (void)updateCheckoutWithAddressCompletion:(void (^)(PKPaymentAuthorizationStatus, NSArray *shippingMethods, NSArray *summaryItems))completion
{
{
self.checkout.partialAddresses = [self.checkout.shippingAddress isPartialAddress];
if ([self.checkout.shippingAddress isValidAddressForShippingRates]) {
[self.client updateCheckout:self.checkout completion:^(BUYCheckout *checkout, NSError *error) {
......
//
// BUYCheckout+Additions.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "BUYCheckout.h"
@interface BUYCheckout (Additions)
/**
* Used for Apple Pay, specifically, to append true for
* the payload to set whether the address(es) are partial.
*
* @return A jsonDictionaryForCheckout JSON with optional "checkout.partial_addresses" key.
*/
- (NSDictionary *)jsonDictionaryForUpdatingCheckout;
@end
//
// BUYCheckout+Additions.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "BUYCheckout+Additions.h"
#import "BUYAddress+Additions.h"
@implementation BUYCheckout (Additions)
- (NSDictionary *)jsonDictionaryForUpdatingCheckout
{
NSMutableDictionary *json = [[self jsonDictionaryForCheckout] mutableCopy];
if ([self.shippingAddress isPartialAddress]) {
json[@"checkout"][@"partial_addresses"] = @YES;
}
return [json copy];
}
@end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment