Commit 115ebd8b authored by harrisonsean's avatar harrisonsean Committed by Commit Bot

[iOS][Safety Check] Add further password support, ok and issues

Add support for safety check safe state and issues.

Issues support includes deleting/managing in settings and on site.

Add support for password check error popovers.

Tests coming in later cl

Bug: 1078782
Change-Id: I6c2b388fce4d397a1c1e01d161b4e0b35c91e9b3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2339322Reviewed-by: default avatarJavier Ernesto Flores Robles <javierrobles@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Commit-Queue: Sean Harrison <harrisonsean@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797251}
parent 945fdda1
...@@ -331,6 +331,9 @@ locale. The strings in this file are specific to iOS. ...@@ -331,6 +331,9 @@ locale. The strings in this file are specific to iOS.
<message name="IDS_IOS_PASSWORD_CHECK_ERROR" desc="Description of the password check cell, explaining that Password Check failed. [iOS only]" meaning="Password Check failed due to some error."> <message name="IDS_IOS_PASSWORD_CHECK_ERROR" desc="Description of the password check cell, explaining that Password Check failed. [iOS only]" meaning="Password Check failed due to some error.">
Chromium can't check your passwords Chromium can't check your passwords
</message> </message>
<message name="IDS_IOS_PASSWORD_CHECK_ERROR_NO_PASSWORDS" desc="Description text for when password check cannot be run because the user has no saved passwords">
No saved passwords. Chromium can check your passwords when you save them.
</message>
<message name="IDS_IOS_PASSWORD_CHECK_ERROR_OFFLINE" desc="Text inside popover which is shown when the user wants to see detailed information about the error. [iOS only]" meaning="The user has no internet connection and can't check passwords."> <message name="IDS_IOS_PASSWORD_CHECK_ERROR_OFFLINE" desc="Text inside popover which is shown when the user wants to see detailed information about the error. [iOS only]" meaning="The user has no internet connection and can't check passwords.">
Chromium couldn't check your passwords. Try checking your internet connection. Chromium couldn't check your passwords. Try checking your internet connection.
</message> </message>
......
79dd83f5a48633878501b3b1cc8eebd4123017b6
\ No newline at end of file
...@@ -331,6 +331,9 @@ locale. The strings in this file are specific to iOS. ...@@ -331,6 +331,9 @@ locale. The strings in this file are specific to iOS.
<message name="IDS_IOS_PASSWORD_CHECK_ERROR" desc="Description of the password check cell, explaining that Password Check failed. [iOS only]" meaning="Password Check failed due to some error."> <message name="IDS_IOS_PASSWORD_CHECK_ERROR" desc="Description of the password check cell, explaining that Password Check failed. [iOS only]" meaning="Password Check failed due to some error.">
Chrome can't check your passwords Chrome can't check your passwords
</message> </message>
<message name="IDS_IOS_PASSWORD_CHECK_ERROR_NO_PASSWORDS" desc="Description text for when password check cannot be run because the user has no saved passwords">
No saved passwords. Chrome can check your passwords when you save them.
</message>
<message name="IDS_IOS_PASSWORD_CHECK_ERROR_OFFLINE" desc="Text inside popover which is shown when the user wants to see detailed information about the error. [iOS only]" meaning="The user has no internet connection and can't check passwords."> <message name="IDS_IOS_PASSWORD_CHECK_ERROR_OFFLINE" desc="Text inside popover which is shown when the user wants to see detailed information about the error. [iOS only]" meaning="The user has no internet connection and can't check passwords.">
Chrome couldn't check your passwords. Try checking your internet connection. Chrome couldn't check your passwords. Try checking your internet connection.
</message> </message>
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#import "ios/chrome/browser/main/browser.h" #import "ios/chrome/browser/main/browser.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h" #import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator.h"
#import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator_delegate.h" #import "ios/chrome/browser/ui/settings/password/password_details/password_details_coordinator_delegate.h"
#import "ios/chrome/browser/ui/settings/password/password_issue_with_form.h" #import "ios/chrome/browser/ui/settings/password/password_issue_with_form.h"
...@@ -52,6 +54,8 @@ ...@@ -52,6 +54,8 @@
if (self) { if (self) {
_baseNavigationController = navigationController; _baseNavigationController = navigationController;
_manager = manager; _manager = manager;
_dispatcher = HandlerForProtocol(self.browser->GetCommandDispatcher(),
ApplicationCommands);
} }
return self; return self;
} }
......
...@@ -1317,7 +1317,6 @@ std::vector<std::unique_ptr<autofill::PasswordForm>> CopyOf( ...@@ -1317,7 +1317,6 @@ std::vector<std::unique_ptr<autofill::PasswordForm>> CopyOf(
browser:_browser browser:_browser
passwordCheckManager:_passwordCheck.get()]; passwordCheckManager:_passwordCheck.get()];
_passwordIssuesCoordinator.delegate = self; _passwordIssuesCoordinator.delegate = self;
_passwordIssuesCoordinator.dispatcher = self.dispatcher;
_passwordIssuesCoordinator.reauthModule = _reauthenticationModule; _passwordIssuesCoordinator.reauthModule = _reauthenticationModule;
[_passwordIssuesCoordinator start]; [_passwordIssuesCoordinator start];
} }
......
...@@ -16,6 +16,8 @@ source_set("safety_check_ui") { ...@@ -16,6 +16,8 @@ source_set("safety_check_ui") {
"//ios/chrome/app/strings", "//ios/chrome/app/strings",
"//ios/chrome/browser/ui:feature_flags", "//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/settings:settings_root", "//ios/chrome/browser/ui/settings:settings_root",
"//ios/chrome/browser/ui/settings/cells",
"//ios/chrome/browser/ui/settings/cells:public",
"//ios/chrome/browser/ui/settings/utils", "//ios/chrome/browser/ui/settings/utils",
"//ios/chrome/browser/ui/table_view", "//ios/chrome/browser/ui/table_view",
"//ui/base", "//ui/base",
...@@ -43,16 +45,22 @@ source_set("safety_check") { ...@@ -43,16 +45,22 @@ source_set("safety_check") {
"//ios/chrome/browser/content_settings", "//ios/chrome/browser/content_settings",
"//ios/chrome/browser/main:public", "//ios/chrome/browser/main:public",
"//ios/chrome/browser/passwords", "//ios/chrome/browser/passwords",
"//ios/chrome/browser/signin",
"//ios/chrome/browser/sync",
"//ios/chrome/browser/ui:feature_flags", "//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/commands", "//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators", "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/settings:settings_root", "//ios/chrome/browser/ui/settings:settings_root",
"//ios/chrome/browser/ui/settings/cells", "//ios/chrome/browser/ui/settings/cells",
"//ios/chrome/browser/ui/settings/cells:public", "//ios/chrome/browser/ui/settings/cells:public",
"//ios/chrome/browser/ui/settings/password",
"//ios/chrome/browser/ui/settings/utils", "//ios/chrome/browser/ui/settings/utils",
"//ios/chrome/browser/ui/table_view", "//ios/chrome/browser/ui/table_view",
"//ios/chrome/browser/ui/table_view/cells:cells_constants", "//ios/chrome/browser/ui/table_view/cells:cells_constants",
"//ios/chrome/browser/ui/util", "//ios/chrome/browser/ui/util",
"//ios/chrome/common",
"//ios/chrome/common/ui/colors",
"//ios/chrome/common/ui/elements:popover_label_view_controller",
"//ui/base", "//ui/base",
] ]
frameworks = [ "UIKit.framework" ] frameworks = [ "UIKit.framework" ]
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h" #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
@protocol ApplicationCommands;
@class SafetyCheckCoordinator; @class SafetyCheckCoordinator;
// Delegate that allows to dereference the SafetyCheckCoordinator. // Delegate that allows to dereference the SafetyCheckCoordinator.
...@@ -20,6 +21,7 @@ ...@@ -20,6 +21,7 @@
// The coordinator for the Safety Check screen. // The coordinator for the Safety Check screen.
@interface SafetyCheckCoordinator : ChromeCoordinator @interface SafetyCheckCoordinator : ChromeCoordinator
// Delegate to pass user interactions to the mediator.
@property(nonatomic, weak) id<SafetyCheckCoordinatorDelegate> delegate; @property(nonatomic, weak) id<SafetyCheckCoordinatorDelegate> delegate;
- (instancetype)initWithBaseViewController:(UIViewController*)viewController - (instancetype)initWithBaseViewController:(UIViewController*)viewController
......
...@@ -5,20 +5,32 @@ ...@@ -5,20 +5,32 @@
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h" #import "ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/memory/scoped_refptr.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/main/browser.h" #include "ios/chrome/browser/main/browser.h"
#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager.h" #include "ios/chrome/browser/passwords/ios_chrome_password_check_manager.h"
#include "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h" #include "ios/chrome/browser/passwords/ios_chrome_password_check_manager_factory.h"
#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
#import "ios/chrome/browser/sync/sync_setup_service.h"
#import "ios/chrome/browser/sync/sync_setup_service_factory.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/browser_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h" #import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h" #import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.h" #import "ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
@interface SafetyCheckCoordinator () < @interface SafetyCheckCoordinator () <
PasswordIssuesCoordinatorDelegate,
SafetyCheckNavigationCommands, SafetyCheckNavigationCommands,
SafetyCheckTableViewControllerPresentationDelegate> SafetyCheckTableViewControllerPresentationDelegate>
...@@ -28,6 +40,13 @@ ...@@ -28,6 +40,13 @@
// The container view controller. // The container view controller.
@property(nonatomic, strong) SafetyCheckTableViewController* viewController; @property(nonatomic, strong) SafetyCheckTableViewController* viewController;
// Coordinator for passwords issues screen.
@property(nonatomic, strong)
PasswordIssuesCoordinator* passwordIssuesCoordinator;
// Dispatcher which can handle changing passwords on sites.
@property(nonatomic, strong) id<ApplicationCommands> handler;
@end @end
@implementation SafetyCheckCoordinator @implementation SafetyCheckCoordinator
...@@ -41,6 +60,8 @@ ...@@ -41,6 +60,8 @@
browser:browser]; browser:browser];
if (self) { if (self) {
_baseNavigationController = navigationController; _baseNavigationController = navigationController;
_handler = HandlerForProtocol(self.browser->GetCommandDispatcher(),
ApplicationCommands);
} }
return self; return self;
} }
...@@ -53,12 +74,18 @@ ...@@ -53,12 +74,18 @@
initWithStyle:UITableViewStylePlain]; initWithStyle:UITableViewStylePlain];
self.viewController = viewController; self.viewController = viewController;
scoped_refptr<IOSChromePasswordCheckManager> passwordCheckManager =
IOSChromePasswordCheckManagerFactory::GetForBrowserState(
self.browser->GetBrowserState());
self.mediator = [[SafetyCheckMediator alloc] self.mediator = [[SafetyCheckMediator alloc]
initWithUserPrefService:self.browser->GetBrowserState()->GetPrefs() initWithUserPrefService:self.browser->GetBrowserState()->GetPrefs()
passwordCheckManager:IOSChromePasswordCheckManagerFactory:: passwordCheckManager:passwordCheckManager
GetForBrowserState( authService:AuthenticationServiceFactory::GetForBrowserState(
self.browser->GetBrowserState())]; self.browser->GetBrowserState())
syncService:SyncSetupServiceFactory::GetForBrowserState(
self.browser->GetBrowserState())];
self.mediator.consumer = self.viewController; self.mediator.consumer = self.viewController;
self.mediator.handler = self;
self.viewController.serviceDelegate = self.mediator; self.viewController.serviceDelegate = self.mediator;
self.viewController.presentationDelegate = self; self.viewController.presentationDelegate = self;
...@@ -75,4 +102,57 @@ ...@@ -75,4 +102,57 @@
[self.delegate safetyCheckCoordinatorDidRemove:self]; [self.delegate safetyCheckCoordinatorDidRemove:self];
} }
#pragma mark - SafetyCheckNavigationCommands
- (void)showPasswordIssuesPage {
IOSChromePasswordCheckManager* passwordCheckManager =
IOSChromePasswordCheckManagerFactory::GetForBrowserState(
self.browser->GetBrowserState())
.get();
self.passwordIssuesCoordinator = [[PasswordIssuesCoordinator alloc]
initWithBaseNavigationController:self.baseNavigationController
browser:self.browser
passwordCheckManager:passwordCheckManager];
self.passwordIssuesCoordinator.delegate = self;
self.passwordIssuesCoordinator.reauthModule = nil;
[self.passwordIssuesCoordinator start];
}
- (void)showErrorInfoFrom:(UIButton*)buttonView
withText:(NSAttributedString*)text {
PopoverLabelViewController* errorInfoPopover =
[[PopoverLabelViewController alloc] initWithPrimaryAttributedString:text
secondaryAttributedString:nil];
errorInfoPopover.popoverPresentationController.sourceView = buttonView;
errorInfoPopover.popoverPresentationController.sourceRect = buttonView.bounds;
errorInfoPopover.popoverPresentationController.permittedArrowDirections =
UIPopoverArrowDirectionAny;
[self.viewController presentViewController:errorInfoPopover
animated:YES
completion:nil];
}
- (void)showUpdateOnAppStorePage {
// TODO(crbug.com/1078782): Add navigation to App Store Chrome page.
}
- (void)showSafeBrowsingPreferencePage {
// TODO(crbug.com/1078782): Add navigation to Safe Browsing preference page.
}
#pragma mark - PasswordIssuesCoordinatorDelegate
- (void)passwordIssuesCoordinatorDidRemove:
(PasswordIssuesCoordinator*)coordinator {
DCHECK_EQ(self.passwordIssuesCoordinator, coordinator);
[self.passwordIssuesCoordinator stop];
self.passwordIssuesCoordinator.delegate = nil;
self.passwordIssuesCoordinator = nil;
}
- (BOOL)willHandlePasswordDeletion:(const autofill::PasswordForm&)password {
return NO;
}
@end @end
...@@ -11,9 +11,12 @@ ...@@ -11,9 +11,12 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
class AuthenticationService;
class IOSChromePasswordCheckManager; class IOSChromePasswordCheckManager;
class PrefService; class PrefService;
@protocol SafetyCheckConsumer; @protocol SafetyCheckConsumer;
@protocol SafetyCheckNavigationCommands;
class SyncSetupService;
@class SafetyCheckTableViewController; @class SafetyCheckTableViewController;
...@@ -27,13 +30,19 @@ class PrefService; ...@@ -27,13 +30,19 @@ class PrefService;
- (instancetype)initWithUserPrefService:(PrefService*)userPrefService - (instancetype)initWithUserPrefService:(PrefService*)userPrefService
passwordCheckManager: passwordCheckManager:
(scoped_refptr<IOSChromePasswordCheckManager>) (scoped_refptr<IOSChromePasswordCheckManager>)
passwordCheckManager NS_DESIGNATED_INITIALIZER; passwordCheckManager
authService:(AuthenticationService*)authService
syncService:(SyncSetupService*)syncService
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
// The consumer for the Safety Check mediator. // The consumer for the Safety Check mediator.
@property(nonatomic, weak) id<SafetyCheckConsumer> consumer; @property(nonatomic, weak) id<SafetyCheckConsumer> consumer;
// Handler used to navigate inside the safety check.
@property(nonatomic, weak) id<SafetyCheckNavigationCommands> handler;
@end @end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_H_ #endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_MEDIATOR_H_
...@@ -9,8 +9,18 @@ ...@@ -9,8 +9,18 @@
// controller. // controller.
@protocol SafetyCheckNavigationCommands @protocol SafetyCheckNavigationCommands
// TODO(crbug.com/1078782): Add navigation commands for updates, passwords, and // Shows password issues page.
// safe browsing. - (void)showPasswordIssuesPage;
// Opens Chrome page in App Store for updates.
- (void)showUpdateOnAppStorePage;
// Shows page with Safe Browsing preference toggle.
- (void)showSafeBrowsingPreferencePage;
// Shows the error popover with the corresponding |text|.
- (void)showErrorInfoFrom:(UIButton*)buttonView
withText:(NSAttributedString*)text;
@end @end
......
...@@ -15,6 +15,13 @@ ...@@ -15,6 +15,13 @@
// Called when item is tapped. // Called when item is tapped.
- (void)didSelectItem:(TableViewItem*)item; - (void)didSelectItem:(TableViewItem*)item;
// Checks if |item| should have an error popover.
- (BOOL)isItemWithErrorInfo:(TableViewItem*)item;
// Notifies the mediator that an info button was tapped for |itemType|.
- (void)infoButtonWasTapped:(UIButton*)buttonView
usingItemType:(NSInteger)itemType;
@end @end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_SERVICE_DELEGATE_H_ #endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_SERVICE_DELEGATE_H_
...@@ -10,13 +10,10 @@ ...@@ -10,13 +10,10 @@
@protocol SafetyCheckServiceDelegate; @protocol SafetyCheckServiceDelegate;
@class SafetyCheckTableViewController; @class SafetyCheckTableViewController;
@protocol SafetyCheckTableViewControllerModelDelegate;
// The accessibility identifier of the privacy settings collection view. // The accessibility identifier of the privacy settings collection view.
extern NSString* const kSafetyCheckTableViewId; extern NSString* const kSafetyCheckTableViewId;
@protocol SafetyCheckNavigationCommands;
// Delegate for presentation events related to // Delegate for presentation events related to
// SafetyCheckTableViewController. // SafetyCheckTableViewController.
@protocol SafetyCheckTableViewControllerPresentationDelegate @protocol SafetyCheckTableViewControllerPresentationDelegate
...@@ -40,9 +37,6 @@ extern NSString* const kSafetyCheckTableViewId; ...@@ -40,9 +37,6 @@ extern NSString* const kSafetyCheckTableViewId;
// Handler for taps on items on the safety check page. // Handler for taps on items on the safety check page.
@property(nonatomic, weak) id<SafetyCheckServiceDelegate> serviceDelegate; @property(nonatomic, weak) id<SafetyCheckServiceDelegate> serviceDelegate;
// Handler used to navigate inside the safety check.
@property(nonatomic, weak) id<SafetyCheckNavigationCommands> handler;
@end @end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_TABLE_VIEW_CONTROLLER_H_ #endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_TABLE_VIEW_CONTROLLER_H_
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#import "base/mac/foundation_util.h" #import "base/mac/foundation_util.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/settings/cells/settings_check_cell.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_service_delegate.h" #import "ios/chrome/browser/ui/settings/safety_check/safety_check_service_delegate.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#include "ios/chrome/browser/ui/ui_feature_flags.h" #include "ios/chrome/browser/ui/ui_feature_flags.h"
...@@ -97,4 +99,31 @@ typedef NS_ENUM(NSInteger, SectionIdentifier) { ...@@ -97,4 +99,31 @@ typedef NS_ENUM(NSInteger, SectionIdentifier) {
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
} }
#pragma mark - UITableViewDataSource
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
UITableViewCell* cell = [super tableView:tableView
cellForRowAtIndexPath:indexPath];
TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
if ([self.serviceDelegate isItemWithErrorInfo:item]) {
SettingsCheckCell* settingsCheckCell =
base::mac::ObjCCastStrict<SettingsCheckCell>(cell);
settingsCheckCell.infoButton.tag = item.type;
[settingsCheckCell.infoButton addTarget:self
action:@selector(didTapErrorInfoButton:)
forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
#pragma mark - Private
// Called when user tapped on the information button of an
// item. Shows popover with detailed description of an error if needed.
- (void)didTapErrorInfoButton:(UIButton*)buttonView {
[self.serviceDelegate infoButtonWasTapped:buttonView
usingItemType:buttonView.tag];
}
@end @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