Commit 0c93289e authored by 汪洋's avatar 汪洋

使用GMPhobos3.0

parent cc5f889c
......@@ -35,7 +35,7 @@ PODS:
- GMNetService (0.1.4):
- AFNetworking (= 3.1.0)
- GMOCConstant (0.0.3)
- GMPhobos (0.2.24):
- GMPhobos (0.3.0):
- GMCache (~> 0.1.1)
- GMKit
- GMRefresh (0.1.5):
......@@ -66,7 +66,7 @@ SPEC CHECKSUMS:
GMKit: 04a30d67c6b5468f07c8d9f60d0f8b12dd90b162
GMNetService: 4f900345322a1f0f3269fc7edb1cb04dd81a5511
GMOCConstant: 39371248b4d8d54929391bfcd2c5883776436c4b
GMPhobos: 8d98e2480d078b6336340e612f5c786537ff2f70
GMPhobos: 1f5ab50a8879e014e92e2b270d40ffa6113e4044
GMRefresh: b9f0674b9b14c533ec2586147ea40a833ed5867d
JSONModel: 12523685c4b623553ccf844bbbf7007624317b2c
Masonry: a1a931a0d08870ed8ae415a2ea5ea68ebcac77df
......
......@@ -7,12 +7,11 @@
//
#import <Foundation/Foundation.h>
#import "PhobosPVProtocol.h"
#import <CoreLocation/CLLocation.h>
@interface Phobos : NSObject
@property (strong, nonatomic) UINavigationController *selectedNavigationController;
/**
* @brief 开启Phobos统计,默认以BATCH方式发送log.
*
......@@ -30,6 +29,17 @@
+ (void)setSharedClient:(Phobos *)client;
#pragma mark - SDK配置
// Phobos在处理业务端传递来的参数时会检查是否某个value为空,如果为空会调用这个block以通知业务层,业务层可以上报这个异常,以助解决问题
@property(nonatomic, copy) void (^captureNullExpection) (NSString *eventId, NSDictionary *info);
/**
网络状态 wifi=1, mobile=0, 不连通=-1
*/
@property (nonatomic, copy) NSString *netStatus;
/**
* @brief 设置是否打印sdk的log信息,默认不开启
*
......@@ -37,7 +47,7 @@
*
* @since 0.0.1
*/
- (void)setLogEnabled:(BOOL)value;
@property (assign, nonatomic) BOOL logEnabled;
/**
* @brief 设置当前登录用户的ID,如果没有默认为@""
......@@ -46,7 +56,7 @@
*
* @since 0.0.2
*/
- (void)setUserId:(NSInteger)userId;
@property (assign, nonatomic) NSInteger userId;
/*!
* @author zhaiguojun, 16-05-31
......@@ -57,9 +67,11 @@
*
* @since 0.2.7
*/
- (void)setCurrentCityId:(NSString *)currentCityId;
@property (strong, nonatomic) NSString *currentCityId;
@property (strong, nonatomic) CLLocation *gps;
- (void)setGPS:(NSDictionary *)gps;
#pragma mark - 事件采集
/**
* @brief 自定义事件,数量统计.
......@@ -73,6 +85,7 @@
+ (void)track:(NSString *)eventId attributes:(NSDictionary *)attributes;
+ (void)track:(NSString *)eventId attributes:(NSDictionary *)attributes sendNow:(BOOL)sendNow;
+ (void)track:(NSString *)eventId;
/**
* @author 翟国钧, 16-02-03 16:02:30
*
......@@ -85,22 +98,13 @@
+ (void)trackJsEvent:(NSString *)jsonString;
/**
* @brief 当页面加载时的PV处理
*
* @param pageName 页面唯一编码
* @param businessId 页面中的业务ID,比如美购详情页美购ID
* @param refererPageName 目标页面的上一个页面的pageName
*
* @since 0.0.8
* @brief PV事件开始。当controller viewWillAppear时调用
*/
- (void)onPageStart:(NSString *)pageName businessId:(NSString *)businessId referer:(NSString *)refererPageName inTime:(NSString *)time;
- (void)onPVStart:(UIResponder<PhobosPVProtocol> *)page;
/**
* @brief 当页面结束时的PV处理,在此方法中记录当前页面的停留时间和记录本次埋点事件
*
* @since 0.0.8
* @brief PV事件结束。当controller viewWillDisAppear时调用
*/
- (void)onPageEnd:(NSString *)pageName businessId:(NSString *)businessId referer:(NSString *)refererPageName inTime:(NSString *)time referrerId: (NSString *)referrerId;
- (void)onPVEnd:(UIResponder<PhobosPVProtocol> *)page;
/**
* @author 翟国钧, 16-03-08 11:03:45
......@@ -113,6 +117,6 @@
*
* @since 5.9.1
*/
- (void)simulativePageViewEventWithPageName:(NSString *)pageName BusinessId:(NSString *)bid referer:(NSString *)referer;
- (void)simulativePV:(NSString *)pageName businessId:(NSString *)bid referer:(NSString *)referer;
@end
......@@ -11,24 +11,20 @@
#import <AdSupport/AdSupport.h>
#import "PhobosUtil.h"
#import "PhobosConfig.h"
#import "UIViewController+Phobos.h"
#import "UIResponder+PhobosPV.h"
#import "UIDevice+Resolutions.h"
#import "PhobosUtil.h"
static Phobos *sharedClient = nil;
static NSString *sdkVersion = @"110";
@interface Phobos ()
@property (strong, nonatomic) UIViewController *visibleController;
@property (strong, nonatomic) NSDateFormatter *dateFormatter;
@property (strong, nonatomic) NSString *appName;
@property (strong, nonatomic) NSString *channelId;
@property (strong, nonatomic) NSString *currentCityId;
@property (strong, nonatomic) NSString *appVersion;
@property (assign, nonatomic) NSInteger userId;
@property (assign, nonatomic) BOOL logEnabled;
@property (strong, nonatomic) NSString *sessionId;
@property (strong, nonatomic) NSString *lng;
@property (strong, nonatomic) NSString *lat;
@end
......@@ -54,10 +50,9 @@ static NSString *sdkVersion = @"110";
_channelId = channelId;
_logEnabled = NO;
_userId = 0;
_lat = @"";
_lng = @"";
_netStatus = @"";
_currentCityId = @"";
_appVersion = [self getAppVersion];
_appVersion = [PhobosUtil getAppVersion];
[self setupNotification];
[self handleSessionStart];
......@@ -66,27 +61,9 @@ static NSString *sdkVersion = @"110";
return self;
}
- (void)setLogEnabled:(BOOL)value{
_logEnabled = value;
}
- (void)setUserId:(NSInteger)userId{
_userId = userId;
}
- (void)setCurrentCityId:(NSString *)currentCityId
{
_currentCityId = currentCityId;
}
- (void)setGPS:(NSDictionary *)gps {
@try {
NSString *lng = gps[@"lng"];
NSString *lat = gps[@"lat"];
_lng = lng;
_lat = lat;
} @catch (NSException *exception) {
phobosLog(exception);
- (void)dealloc{
if (self) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
}
......@@ -104,17 +81,20 @@ static NSString *sdkVersion = @"110";
}
}
- (UINavigationController *)selectedNavigationController{
if ([[UIApplication sharedApplication].keyWindow.rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabbar = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;
- (UIViewController *)visibleController {
id visibleController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([visibleController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabbar = (UITabBarController *)visibleController;
UINavigationController *navigationController = (UINavigationController *)tabbar.selectedViewController;
return navigationController;
}else{
return nil;
return navigationController.visibleViewController;
} else if ([visibleController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)visibleController;
return navigationController.visibleViewController;
} else {
return visibleController;
}
}
#pragma mark - notification handler
/**
* @brief 设置对APP的通知监听
......@@ -136,7 +116,7 @@ static NSString *sdkVersion = @"110";
- (void)handleSessionStart{
_sessionId = [[NSUUID UUID] UUIDString];
WMCacheService *cache = [WMCacheService sharedInstance];
[cache storeObjectAtDiskWithkey:PhobosBeginTime object:[self currentTime]];
[cache storeObjectAtDiskWithkey:PhobosBeginTime object:[PhobosUtil currentTime]];
}
/**
......@@ -204,11 +184,9 @@ static NSString *sdkVersion = @"110";
@author zhaiguojun 16-10-11 in (null)
*/
- (void)handlePVEventAppInForeground {
NSString *pageName = self.selectedNavigationController.visibleViewController.pageName;
NSString *businessId = self.selectedNavigationController.visibleViewController.businessId;
NSString *regerer = self.selectedNavigationController.visibleViewController.referer;
self.selectedNavigationController.visibleViewController.inTime = [self currentTime];
[self onPageStart:pageName businessId:businessId referer:regerer inTime:[self currentTime]];
if (self.visibleController != nil) {
[self onPVStart:self.visibleController];
}
}
/**
......@@ -216,16 +194,12 @@ static NSString *sdkVersion = @"110";
@author zhaiguojun 16-10-11 in (null)
*/
- (void)handlePVEventAppInBackgound {
NSString *pageName = self.selectedNavigationController.visibleViewController.pageName;
NSString *businessId = self.selectedNavigationController.visibleViewController.businessId;
NSString *referer = self.selectedNavigationController.visibleViewController.referer;
NSString *inTime = self.selectedNavigationController.visibleViewController.inTime;
NSString *referrerId = self.selectedNavigationController.visibleViewController.referrerId;
[self onPageEnd:pageName businessId:businessId referer:referer inTime:inTime referrerId:referrerId];
if (self.visibleController != nil) {
[self onPVEnd:self.visibleController];
}
}
#pragma mark - track event handler
#pragma mark - track event handler
+ (void)track:(NSString *)eventId{
[Phobos track:eventId attributes:@{}];
}
......@@ -273,26 +247,27 @@ static NSString *sdkVersion = @"110";
}
}
#pragma mark - pv
- (void)onPageStart:(NSString *)pageName businessId:(NSString *)businessId referer:(NSString *)refererPageName inTime:(NSString *)time{
//目前来说,变成了空方法,其主要功能是给当前VC的属性赋值,下个版本优化,挪到UIViewController+Phobos里去
#pragma mark - PV
- (void)onPVStart:(UIResponder<PhobosPVProtocol> *)page {
// 必须在此处调用一下referer,因为onControllerStart
[page initReferer];
page.inTime = [PhobosUtil currentTime];
}
- (void)onPageEnd:(NSString *)pageName businessId:(NSString *)businessId referer:(NSString *)refererPageName inTime:(NSString *)time referrerId: (NSString *)referrerId {
if (![self isNonEmpty:pageName]) {
- (void)onPVEnd:(UIResponder<PhobosPVProtocol> *)page {
if (![PhobosUtil isNonEmpty:page.pageName] || !page.needLogPV) {
return;
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
@try {
[dict setObject:[self currentTime] forKey:@"out"];
[dict setObject:time?:@"" forKey:@"in"];
[dict setObject:pageName forKey:@"page_name"];
[dict setObject:businessId?:@"" forKey:@"business_id"];
[dict setObject:refererPageName?:@"" forKey:@"referer"];
[dict setObject:[PhobosUtil currentTime] forKey:@"out"];
[dict setObject:page.inTime?:@"" forKey:@"in"];
[dict setObject:page.pageName forKey:@"page_name"];
[dict setObject:page.businessId?:@"" forKey:@"business_id"];
[dict setObject:page.referer?:@"" forKey:@"referer"];
[dict setObject:@(0) forKey:@"fake"];
[dict setObject:referrerId ? : @"" forKey:@"referrer_id"];
[dict setObject:page.referrerId ? : @"" forKey:@"referrer_id"];
[Phobos track:@"page_view" attributes:dict];
}
@catch (NSException *exception) {
......@@ -300,11 +275,12 @@ static NSString *sdkVersion = @"110";
}
}
- (void)simulativePageViewEventWithPageName:(NSString *)pageName BusinessId:(NSString *)bid referer:(NSString *)referer{
- (void)simulativePV:(NSString *)pageName businessId:(NSString *)bid referer:(NSString *)referer{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
@try {
[dict setObject:[self currentTime] forKey:@"in"];
[dict setObject:[self currentTime] forKey:@"out"];
// fake(模拟)的事件,所以in与out一样,就是这么规定的
[dict setObject:[PhobosUtil currentTime] forKey:@"in"];
[dict setObject:[PhobosUtil currentTime] forKey:@"out"];
[dict setObject:pageName?:@"" forKey:@"page_name"];
[dict setObject:bid?:@"" forKey:@"business_id"];
[dict setObject:referer?:@"" forKey:@"referer"];
......@@ -317,6 +293,9 @@ static NSString *sdkVersion = @"110";
}
}
#pragma mark - 事件存储、发送
/**
* @brief 将埋点时间封装成词典数据
*
......@@ -325,17 +304,19 @@ static NSString *sdkVersion = @"110";
* @since 0.0.1
*/
- (NSDictionary *)prepareDictionaryForEvent:(NSString *)eventId attributes:(NSDictionary *)attributes{
[self catchNullForEvent:eventId attributes:attributes];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
@try {
NSString *currentTime = [self currentTime];
NSString *currentTime = [PhobosUtil currentTime];
NSMutableDictionary *deviceParams = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString],@"idfa",
[[[UIDevice currentDevice] identifierForVendor] UUIDString],@"idfv",
[UIDevice deviceId],@"device_id",
@"ios",@"device_type",
@"Apple",@"manufacturer",
_lat,@"lat",
_lng,@"lng",nil];
@(self.gps.coordinate.latitude),@"lat",
@(self.gps.coordinate.longitude),@"lng",
_netStatus,@"is_WiFi",nil];
NSMutableDictionary *appParams = [NSMutableDictionary dictionaryWithObjectsAndKeys:
self.appName, @"name",
self.appVersion, @"version",
......@@ -402,7 +383,7 @@ static NSString *sdkVersion = @"110";
phobosLog([NSString stringWithFormat:@"array prepare to fly --✈: %@",array]);
}
@try {
NSData *JSON = [self encodeJSON:array];
NSData *JSON = [PhobosUtil encodeJSON:array];
NSData *compressedData = [PhobosUtil compressData:JSON];
if (compressedData) {
[PhobosUtil sendData:compressedData success:^(NSInteger code) {
......@@ -419,69 +400,21 @@ static NSString *sdkVersion = @"110";
}
#pragma mark - helpers
/**
* @brief 将对象转成JSON格式数据
*
* @param obj
*
* @return
*
* @since 0.0.1
*/
- (NSData *)encodeJSON:(id)obj {
NSData *data = [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil];
return data;
}
/**
* @brief 获取当前时间的毫秒数
*
* @return
*
* @since 0.0.1
*/
- (NSString *)currentTime {
NSDate *date = [NSDate date];
NSTimeInterval interval = [date timeIntervalSince1970];
NSString *timeIntervalStr = [NSString stringWithFormat:@"%ld",(long)interval];
return timeIntervalStr;
}
/**
* @brief 获取当前APP的版本号
*
* @return
*
* @since 0.0.1
*/
- (NSString *)getAppVersion{
NSString *buildVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
if (buildVersion) {
return buildVersion;
}else{
return @"";
}
}
- (BOOL)isNonEmpty:(NSString *)string {
if (!string) {
return NO;
}
NSMutableCharacterSet *emptyStringSet = [[NSMutableCharacterSet alloc] init];
[emptyStringSet formUnionWithCharacterSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[emptyStringSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @" "]];
if ([string length] == 0) {
return NO;
}
NSString* str = [string stringByTrimmingCharactersInSet:emptyStringSet];
return [str length] > 0;
}
- (void)dealloc{
if (self) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)catchNullForEvent:(NSString *)eventId attributes:(NSDictionary *)attributes {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@try {
for (NSString *key in attributes.allKeys) {
if ([attributes[key] isMemberOfClass:[NSNull class]]) {
if (self.captureNullExpection) {
self.captureNullExpection(eventId, attributes);
}
break;
}
}
} @catch (NSException *exception) {
}
});
}
@end
......@@ -32,4 +32,8 @@ typedef void (^SendDataSuccessBlock)(NSInteger code);
*/
+ (void)sendData:(NSData *)data success:(SendDataSuccessBlock)success;
+ (NSString *)currentTime;
+ (NSString *)getAppVersion;
+ (BOOL)isNonEmpty:(NSString *)string;
+ (NSData *)encodeJSON:(id)obj;
@end
......@@ -122,4 +122,64 @@
}
}
/**
* @brief 将对象转成JSON格式数据
*
* @param obj
*
* @return
*
* @since 0.0.1
*/
+ (NSData *)encodeJSON:(id)obj {
NSData *data = [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil];
return data;
}
/**
* @brief 获取当前时间的毫秒数
*
* @return
*
* @since 0.0.1
*/
+ (NSString *)currentTime {
NSDate *date = [NSDate date];
NSTimeInterval interval = [date timeIntervalSince1970];
NSString *timeIntervalStr = [NSString stringWithFormat:@"%ld",(long)interval];
return timeIntervalStr;
}
/**
* @brief 获取当前APP的版本号
*
* @return
*
* @since 0.0.1
*/
+ (NSString *)getAppVersion {
NSString *buildVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
if (buildVersion) {
return buildVersion;
}else{
return @"";
}
}
+ (BOOL)isNonEmpty:(NSString *)string {
if (!string) {
return NO;
}
NSMutableCharacterSet *emptyStringSet = [[NSMutableCharacterSet alloc] init];
[emptyStringSet formUnionWithCharacterSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[emptyStringSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @" "]];
if ([string length] == 0) {
return NO;
}
NSString* str = [string stringByTrimmingCharactersInSet:emptyStringSet];
return [str length] > 0;
}
@end
//
// UIViewController+Phobos.h
// Pods
//
// Created by wangyang on 16/7/12.
//
//
#import <UIKit/UIKit.h>
@interface UIViewController (Phobos)
/**
* @author 翟国钧, 16-02-24 17:02:22
*
* @brief 埋点的时候,有些埋点都需要业务id,比如DoctorId,针对那些只能在父类中埋点的业务,要在子类中设置当前id,然后在父类中取到
* 在一些详情页的分享和收藏的时候,由于分享、收藏的方法在basewebview里,所以,统一在里面做处理,但是需要在子类中把想要的参数传过去。包括:type(类型)、from(来自哪)、businessId(对应业务id)
* @since 5.9.1
*/
@property (nonatomic, copy, nonnull) NSString *businessId;
/**
* @author 翟国钧 in 16-02-25 19:02:32
*
* @brief 获取前一个页面的pageName
*
* @since 5.9.1
*/
@property (nonatomic, copy, nonnull) NSString *referer;
/**
* @author 翟国钧 in 16-02-25 19:02:32
*
* 埋点pv事件中当前页面的别名
* @since 5.9.1
*/
@property (nonatomic, copy, nonnull) NSString *pageName;
/**
当前VC.view 显示的时候的时间戳
@author zhaiguojun 16-10-12 in (null)
*/
@property (nonatomic, copy, nonnull) NSString *inTime;
/**
<#Description#>
*/
@property (nonatomic, copy, nonnull) NSString *referrerId;
@end
//
// UIViewController+Phobos.m
// Pods
//
// Created by wangyang on 16/7/12.
//
//
#import "UIViewController+Phobos.h"
#import <objc/runtime.h>
@implementation UIViewController (Phobos)
/**
* @author 翟国钧, 16-03-01 15:03:24
*
* @brief 取当前导航栈中当前VC的上级VC,如果该VC存在,就获取他的pageName
*
* @return 前一页的pageName
*
* @since 5.9.1
*/
- (void)setReferer:(NSString *)referer {
NSArray *navigationPool = self.navigationController.viewControllers;
NSInteger refererIndex = navigationPool.count - 2;
if (refererIndex < 0 ) {
return ;
}
UIViewController *controller = navigationPool[refererIndex];
objc_setAssociatedObject(self, @selector(referer), controller.pageName, OBJC_ASSOCIATION_COPY);
}
- (NSString *)referer
{
NSString *referer = objc_getAssociatedObject(self, @selector(referer));
return referer == nil ? @"" : referer;
}
- (NSString *)pageName {
NSString *name = objc_getAssociatedObject(self, @selector(pageName));
return name == nil ? @"" : name;
}
- (void)setPageName:(NSString *)pageName {
objc_setAssociatedObject(self, @selector(pageName), pageName, OBJC_ASSOCIATION_COPY);
}
- (NSString *)businessId {
NSString *businessId = objc_getAssociatedObject(self, @selector(businessId));
return businessId == nil ? @"" : businessId;
}
- (void)setBusinessId:(NSString *)businessId {
objc_setAssociatedObject(self, @selector(businessId), businessId, OBJC_ASSOCIATION_COPY);
}
- (void)setInTime:(NSString *)inTime {
objc_setAssociatedObject(self, @selector(inTime), inTime, OBJC_ASSOCIATION_COPY);
}
/**
这个地方inTime的值为nil的情况不做考虑,因为Phobos不会发送
*/
- (NSString *)inTime {
NSString *inTime = objc_getAssociatedObject(self, @selector(inTime));
return inTime;
}
- (void)setReferrerId:(NSString *)referrerId {
objc_setAssociatedObject(self, @selector(referrerId), referrerId, OBJC_ASSOCIATION_COPY);
}
- (NSString *)referrerId {
NSString *referrerId = objc_getAssociatedObject(self, @selector(referrerId));
return referrerId == nil ? @"" : referrerId;
}
@end
......@@ -35,7 +35,7 @@ PODS:
- GMNetService (0.1.4):
- AFNetworking (= 3.1.0)
- GMOCConstant (0.0.3)
- GMPhobos (0.2.24):
- GMPhobos (0.3.0):
- GMCache (~> 0.1.1)
- GMKit
- GMRefresh (0.1.5):
......@@ -66,7 +66,7 @@ SPEC CHECKSUMS:
GMKit: 04a30d67c6b5468f07c8d9f60d0f8b12dd90b162
GMNetService: 4f900345322a1f0f3269fc7edb1cb04dd81a5511
GMOCConstant: 39371248b4d8d54929391bfcd2c5883776436c4b
GMPhobos: 8d98e2480d078b6336340e612f5c786537ff2f70
GMPhobos: 1f5ab50a8879e014e92e2b270d40ffa6113e4044
GMRefresh: b9f0674b9b14c533ec2586147ea40a833ed5867d
JSONModel: 12523685c4b623553ccf844bbbf7007624317b2c
Masonry: a1a931a0d08870ed8ae415a2ea5ea68ebcac77df
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -4,8 +4,9 @@
#import "Phobos.h"
#import "PhobosConfig.h"
#import "PhobosPVProtocol.h"
#import "PhobosUtil.h"
#import "UIViewController+Phobos.h"
#import "UIResponder+PhobosPV.h"
FOUNDATION_EXPORT double GMPhobosVersionNumber;
FOUNDATION_EXPORT const unsigned char GMPhobosVersionString[];
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.2.24</string>
<string>0.3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
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