no message
This commit is contained in:
parent
360ad75a13
commit
6a3b7f41df
@ -31,7 +31,7 @@
|
||||
|
||||
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
// Override point for customization after application launch.
|
||||
|
||||
/*
|
||||
[PNObjectConfig initSharedInstanceForEnvironments:@{EnvironmentDevelopment : @"http://packman.local/app_dev.php/api/v1/",
|
||||
EnvironmentStage : @"https://packman.ppreview.it/app_stage.php/api/v1/",
|
||||
EnvironmentProduction : @"http://packman.ppreview.it/app_stage.php/api/v1/"
|
||||
@ -40,7 +40,20 @@
|
||||
[[PNObjectConfig sharedInstance] setClientID:@"1_pqjo2w5k7j4g8skco408oc048w8so0ws840gcg8k8gwsgk0g4" clientSecret:@"10w0vg2v6eggooc4wks4w4s0wkwok0wkck0w888so0o80g88w8" forEnv:EnvironmentProduction];
|
||||
#ifdef DEBUG
|
||||
[[PNObjectConfig sharedInstance] setEnvironment:EnvironmentStage];
|
||||
#endif
|
||||
#endif*/
|
||||
|
||||
[PNObjectConfig initSharedInstanceForEnvironments:@{EnvironmentDevelopment : @"http://bmwcallingweb.local/app_dev.php/api/v1/",
|
||||
EnvironmentStage : @"http://bmwcallingweb.ppreview.it/app_dev.php/api/v1/",
|
||||
EnvironmentProduction : @"http://bmwcallingweb.ppreview.it/app_dev.php/api/v1/"
|
||||
} userSubclass:[PNUser class] withOauthMode:OAuthModePassword];
|
||||
|
||||
[[PNObjectConfig sharedInstance] setClientID:@"1_pqjo2w5k7j4g8skco408oc048w8so0ws840gcg8k8gwsgk0g4" clientSecret:@"10w0vg2v6eggooc4wks4w4s0wkwok0wkck0w888so0o80g88w8" forEnv:EnvironmentStage];
|
||||
|
||||
|
||||
[[PNObjectConfig sharedInstance] setOauthUserName:@"admin" oauthPassword:@"admin" forEnv:EnvironmentStage];
|
||||
|
||||
|
||||
//[[PNObjectConfig sharedInstance] setHTTPHeaderValue:@"XMLHttpRequest" forKey:@"X-Request-With"];
|
||||
|
||||
|
||||
|
||||
|
||||
@ -150,14 +150,14 @@
|
||||
|
||||
}];*/
|
||||
|
||||
[PNUser socialUserFromViewController:self blockSuccess:^(PNUser * _Nullable responseObject) {
|
||||
/*[PNUser socialUserFromViewController:self blockSuccess:^(PNUser * _Nullable responseObject) {
|
||||
|
||||
NSLog(@"%@",[[PNUser currentUser] JSONFormObject]);
|
||||
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
|
||||
}];
|
||||
}];*/
|
||||
|
||||
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'PNObject'
|
||||
s.version = '1.0.2'
|
||||
s.version = '1.1.0'
|
||||
s.summary = 'PNObject is a simple replica of the more complex ParseObject'
|
||||
|
||||
|
||||
|
||||
@ -44,13 +44,19 @@ extern NSString* _Nonnull const EnvironmentDevelopment;
|
||||
extern NSString* _Nonnull const Client_ID;
|
||||
extern NSString* _Nonnull const Client_Secret;
|
||||
|
||||
typedef NS_ENUM(NSInteger, OAuthMode) {
|
||||
OAuthModeNo = 0,
|
||||
OAuthModeClientCredential,
|
||||
OAuthModePassword
|
||||
};
|
||||
|
||||
@interface PNObjectConfig : NSObject
|
||||
|
||||
/**
|
||||
* gets singleton object.
|
||||
* @return singleton
|
||||
*/
|
||||
+ (instancetype _Nonnull) sharedInstance;
|
||||
+ (instancetype _Nullable) sharedInstance;
|
||||
|
||||
/**
|
||||
* Oauth is NO by default
|
||||
@ -90,11 +96,11 @@ extern NSString* _Nonnull const Client_Secret;
|
||||
* PNObjectConfigEnvStage : @"ttps://stage.it/api/v1",
|
||||
* PNObjectConfigEnvProduction : @"ttps://production.it/api/v1"
|
||||
* }
|
||||
* @param oauthEnabled <#oauthEnabled description#>
|
||||
* @param oauthMode <#oauthEnabled description#>
|
||||
*
|
||||
* @return singleton
|
||||
*/
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary * _Nonnull) endpointUrlsForEnvironments withOauth:(BOOL) oauthEnabled;
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary * _Nonnull) endpointUrlsForEnvironments withOauthMode:(OAuthMode) oauthMode;
|
||||
|
||||
/**
|
||||
* <#Description#>
|
||||
@ -106,11 +112,11 @@ extern NSString* _Nonnull const Client_Secret;
|
||||
* PNObjectConfigEnvProduction : @"ttps://production.it/api/v1"
|
||||
* }
|
||||
* @param userSubClass <#userSubClass description#>
|
||||
* @param oauthEnabled <#oauthEnabled description#>
|
||||
* @param oauthMode <#oauthEnabled description#>
|
||||
*
|
||||
* @return <#return value description#>
|
||||
*/
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary * _Nonnull) endpointUrlsForEnvironments userSubclass:(Class _Nonnull) userSubClass withOauth:(BOOL) oauthEnabled;
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary * _Nonnull) endpointUrlsForEnvironments userSubclass:(Class _Nonnull) userSubClass withOauthMode:(OAuthMode) oauthMode;
|
||||
/**
|
||||
* <#Description#>
|
||||
*
|
||||
@ -134,8 +140,13 @@ extern NSString* _Nonnull const Client_Secret;
|
||||
*/
|
||||
- (void) removeHTTPHeaderValueForKey:(NSString * _Nonnull) key;
|
||||
|
||||
|
||||
|
||||
- (void) setClientID:(NSString * _Nonnull) clientID clientSecret:(NSString* _Nonnull) clientSecret forEnv:(NSString * _Nonnull) environment;
|
||||
|
||||
|
||||
- (void) setOauthUserName:(NSString * _Nonnull)oauthUserName oauthPassword:(NSString* _Nonnull) oauthPassword forEnv:(NSString *) environment;
|
||||
|
||||
/**
|
||||
* <#Description#>
|
||||
*
|
||||
@ -246,4 +257,4 @@ extern NSString* _Nonnull const Client_Secret;
|
||||
*/
|
||||
@property (nonatomic) NSInteger minPasswordLenght;
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@ -48,10 +48,12 @@ NSString* const EnvironmentDevelopment = @"PNObjectConfigDevelopment";
|
||||
NSString* const BaseUrl = @"base_url";
|
||||
NSString* const Client_ID = @"client_id";
|
||||
NSString* const Client_Secret = @"client_secret";
|
||||
NSString* const Client_Username = @"client_username";
|
||||
NSString* const Client_Password = @"client_password";
|
||||
|
||||
@interface PNObjectConfig()
|
||||
|
||||
@property (nonatomic) BOOL oauthEnabled;
|
||||
@property (nonatomic) OAuthMode oauthMode;
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary *configuration;
|
||||
@property (nonatomic, strong) NSMutableDictionary *headerFields;
|
||||
@ -59,6 +61,9 @@ NSString* const Client_Secret = @"client_secret";
|
||||
@property (nonatomic, strong) NSString *currentEndPointBaseUrl;
|
||||
@property (nonatomic, strong) NSString *currentOAuthClientID;
|
||||
@property (nonatomic, strong) NSString *currentOAuthClientSecret;
|
||||
@property (nonatomic, strong) NSString *currentOAuthUserName;
|
||||
@property (nonatomic, strong) NSString *currentOAuthPassword;
|
||||
|
||||
@property (nonatomic, strong) AFOAuth2Manager *authManager;
|
||||
|
||||
|
||||
@ -76,38 +81,32 @@ static bool isFirstAccess = YES;
|
||||
|
||||
#pragma mark - Public Method
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
+ (instancetype _Nullable)sharedInstance
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
isFirstAccess = NO;
|
||||
SINGLETON_PNObjectConfig = [[super allocWithZone:NULL] initWithUserSubclass:[PNUser class] withOauth:NO];
|
||||
});
|
||||
|
||||
return SINGLETON_PNObjectConfig;
|
||||
}
|
||||
|
||||
#pragma mark - Life Cycle
|
||||
|
||||
+ (instancetype) initSharedInstanceForEnvironments:(NSDictionary *) endpointUrlsForEnvironments {
|
||||
return [self initSharedInstanceForEnvironments:endpointUrlsForEnvironments userSubclass:[PNUser class] withOauth:NO];
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary *) endpointUrlsForEnvironments {
|
||||
return [self initSharedInstanceForEnvironments:endpointUrlsForEnvironments userSubclass:[PNUser class] withOauthMode:OAuthModeClientCredential];
|
||||
}
|
||||
|
||||
+ (instancetype) initSharedInstanceForEnvironments:(NSDictionary *)endpointUrlsForEnvironments andUserSubclass:(Class)userSubClass {
|
||||
return [self initSharedInstanceForEnvironments:endpointUrlsForEnvironments userSubclass:userSubClass withOauth:NO];
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary *)endpointUrlsForEnvironments andUserSubclass:(Class)userSubClass {
|
||||
return [self initSharedInstanceForEnvironments:endpointUrlsForEnvironments userSubclass:userSubClass withOauthMode:OAuthModeClientCredential];
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype) initSharedInstanceForEnvironments:(NSDictionary *) endpointUrlsForEnvironments withOauth:(BOOL) oauthEnabled {
|
||||
return [self initSharedInstanceForEnvironments:endpointUrlsForEnvironments userSubclass:[PNUser class] withOauth:oauthEnabled];
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary *) endpointUrlsForEnvironments withOauthMode:(OAuthMode) oauthMode {
|
||||
return [self initSharedInstanceForEnvironments:endpointUrlsForEnvironments userSubclass:[PNUser class] withOauthMode:oauthMode];
|
||||
}
|
||||
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary * _Nonnull) endpointUrlsForEnvironments userSubclass:(Class _Nonnull) userSubClass withOauth:(BOOL) oauthEnabled {
|
||||
+ (instancetype _Nonnull) initSharedInstanceForEnvironments:(NSDictionary * _Nonnull) endpointUrlsForEnvironments userSubclass:(Class _Nonnull) userSubClass withOauthMode:(OAuthMode) oauthMode {
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
isFirstAccess = NO;
|
||||
SINGLETON_PNObjectConfig = [[super allocWithZone:NULL] initWithUserSubclass:userSubClass withOauth:oauthEnabled];
|
||||
SINGLETON_PNObjectConfig = [[super allocWithZone:NULL] initWithUserSubclass:userSubClass withOauthMode:oauthMode];
|
||||
|
||||
if (SINGLETON_PNObjectConfig) {
|
||||
|
||||
@ -152,7 +151,7 @@ static bool isFirstAccess = YES;
|
||||
return [[PNObjectConfig alloc] init];
|
||||
}
|
||||
|
||||
- (id) initWithUserSubclass:(Class _Nonnull) userSubClass withOauth:(BOOL) oauthEnabled
|
||||
- (id) initWithUserSubclass:(Class _Nonnull) userSubClass withOauthMode:(OAuthMode) oauthMode
|
||||
{
|
||||
if(SINGLETON_PNObjectConfig){
|
||||
return SINGLETON_PNObjectConfig;
|
||||
@ -163,7 +162,7 @@ static bool isFirstAccess = YES;
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_oauthEnabled = oauthEnabled;
|
||||
_oauthMode = oauthMode;
|
||||
_userSubClass = userSubClass;
|
||||
_configuration = [[NSMutableDictionary alloc] init];
|
||||
_minPasswordLenght = minPassLenght;
|
||||
@ -180,23 +179,28 @@ static bool isFirstAccess = YES;
|
||||
NSData *key = [[NSString getRandString:256] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
[DDDKeychainWrapper setData:key forKey:PNObjectEncryptionKey];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (_oauthEnabled) {
|
||||
AFOAuthCredential *clientCredential = [AFOAuthCredential retrieveCredentialWithIdentifier:PNObjectServiceClientCredentialIdentifier];
|
||||
|
||||
if (clientCredential) {
|
||||
_currentOauthCredential = clientCredential;
|
||||
switch (_oauthMode) {
|
||||
case OAuthModePassword:
|
||||
case OAuthModeClientCredential:
|
||||
default: {
|
||||
|
||||
AFOAuthCredential *clientCredential = [AFOAuthCredential retrieveCredentialWithIdentifier:PNObjectServiceClientCredentialIdentifier];
|
||||
|
||||
if (clientCredential) {
|
||||
_currentOauthCredential = clientCredential;
|
||||
}
|
||||
|
||||
AFOAuthCredential *userCredential = [AFOAuthCredential retrieveCredentialWithIdentifier:PNObjectServiceUserCredentialIdentifier];
|
||||
|
||||
if (userCredential) {
|
||||
_currentOauthCredential = userCredential;
|
||||
}
|
||||
}
|
||||
|
||||
AFOAuthCredential *userCredential = [AFOAuthCredential retrieveCredentialWithIdentifier:PNObjectServiceUserCredentialIdentifier];
|
||||
|
||||
if (userCredential) {
|
||||
_currentOauthCredential = userCredential;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
@ -208,11 +212,17 @@ static bool isFirstAccess = YES;
|
||||
_currentEndPointBaseUrl = nil;
|
||||
_currentOAuthClientID = nil;
|
||||
_currentOAuthClientSecret = nil;
|
||||
_currentOAuthUserName = nil;
|
||||
_currentOAuthPassword = nil;
|
||||
|
||||
if ([_configuration objectForKey:environment]) {
|
||||
_currentEndPointBaseUrl = [[_configuration objectForKey:_currentEnv] objectForKey:BaseUrl];
|
||||
_currentOAuthClientID = [[_configuration objectForKey:_currentEnv] objectForKey:Client_ID];
|
||||
_currentOAuthClientSecret = [[_configuration objectForKey:_currentEnv] objectForKey:Client_Secret];
|
||||
if ([[_configuration objectForKey:_currentEnv] containsValueForKey:Client_Username] && [[_configuration objectForKey:_currentEnv] containsValueForKey:Client_Password]) {
|
||||
_currentOAuthUserName = [[_configuration objectForKey:_currentEnv] objectForKey:Client_Username];
|
||||
_currentOAuthPassword = [[_configuration objectForKey:_currentEnv] objectForKey:Client_Password];
|
||||
}
|
||||
}
|
||||
|
||||
NSLogDebug(@"%@",[[_configuration objectForKey:_currentEnv] objectForKey:BaseUrl]);
|
||||
@ -265,17 +275,42 @@ static bool isFirstAccess = YES;
|
||||
if (!_authManager) {
|
||||
_authManager = [AFOAuth2Manager manager];
|
||||
|
||||
if (_oauthEnabled && _currentOAuthClientID && _currentOAuthClientSecret) {
|
||||
|
||||
if (![_authManager clientID]) {
|
||||
_authManager = [AFOAuth2Manager managerWithBaseURL:[NSURL URLWithString:_currentEndPointBaseUrl] clientID:_currentOAuthClientID secret:_currentOAuthClientSecret];
|
||||
switch (_oauthMode) {
|
||||
case OAuthModeClientCredential:{
|
||||
if (_currentOAuthClientID && _currentOAuthClientSecret) {
|
||||
|
||||
|
||||
if (![_authManager clientID]) {
|
||||
_authManager = [AFOAuth2Manager managerWithBaseURL:[NSURL URLWithString:_currentEndPointBaseUrl] clientID:_currentOAuthClientID secret:_currentOAuthClientSecret];
|
||||
}
|
||||
|
||||
[_authManager setUseHTTPBasicAuthentication:NO];
|
||||
|
||||
canTryRefreh = YES;
|
||||
}
|
||||
}
|
||||
|
||||
[_authManager setUseHTTPBasicAuthentication:NO];
|
||||
|
||||
canTryRefreh = YES;
|
||||
break;
|
||||
case OAuthModePassword:{
|
||||
if (_currentOAuthClientID && _currentOAuthClientSecret && _currentOAuthUserName && _currentOAuthPassword) {
|
||||
|
||||
if (![_authManager clientID]) {
|
||||
_authManager = [AFOAuth2Manager managerWithBaseURL:[NSURL URLWithString:_currentEndPointBaseUrl] clientID:_currentOAuthClientID secret:_currentOAuthClientSecret];
|
||||
}
|
||||
|
||||
[_authManager setUseHTTPBasicAuthentication:NO];
|
||||
|
||||
canTryRefreh = YES;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OAuthModeNo:
|
||||
default:{
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
for (NSString *key in [_headerFields allKeys]) {
|
||||
|
||||
[_httpSerializer setValue:[_headerFields objectForKey:key] forHTTPHeaderField:key];
|
||||
@ -508,27 +543,61 @@ static bool isFirstAccess = YES;
|
||||
}];
|
||||
}
|
||||
else {
|
||||
[_authManager authenticateUsingOAuthWithURLString:[_currentEndPointBaseUrl stringByAppendingString:@"oauth-token"] scope:nil success:^(AFOAuthCredential * _Nonnull credential) {
|
||||
_currentOauthCredential = credential;
|
||||
|
||||
[AFOAuthCredential storeCredential:_currentOauthCredential withIdentifier:PNObjectServiceClientCredentialIdentifier];
|
||||
|
||||
[_httpSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_jsonSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_authManager.requestSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_manager.requestSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PNObjectLocalNotificationRefreshTokenClientCredentialSuccess object:nil];
|
||||
if (success) {
|
||||
success(YES);
|
||||
switch (_oauthMode) {
|
||||
case OAuthModeClientCredential:{
|
||||
[_authManager authenticateUsingOAuthWithURLString:[_currentEndPointBaseUrl stringByAppendingString:@"oauth-token"] scope:nil success:^(AFOAuthCredential * _Nonnull credential) {
|
||||
_currentOauthCredential = credential;
|
||||
|
||||
[AFOAuthCredential storeCredential:_currentOauthCredential withIdentifier:PNObjectServiceClientCredentialIdentifier];
|
||||
|
||||
[_httpSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_jsonSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_authManager.requestSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_manager.requestSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PNObjectLocalNotificationRefreshTokenClientCredentialSuccess object:nil];
|
||||
if (success) {
|
||||
success(YES);
|
||||
}
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PNObjectLocalNotificationRefreshTokenClientCredentialFail object:nil];
|
||||
if (failure) {
|
||||
failure(error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PNObjectLocalNotificationRefreshTokenClientCredentialFail object:nil];
|
||||
if (failure) {
|
||||
failure(error);
|
||||
break;
|
||||
case OAuthModePassword:{
|
||||
|
||||
[_authManager authenticateUsingOAuthWithURLString:[_currentEndPointBaseUrl stringByAppendingString:@"oauth-token"] username:_currentOAuthUserName password:_currentOAuthPassword scope:nil success:^(AFOAuthCredential * _Nonnull credential) {
|
||||
_currentOauthCredential = credential;
|
||||
|
||||
[AFOAuthCredential storeCredential:_currentOauthCredential withIdentifier:PNObjectServiceClientCredentialIdentifier];
|
||||
|
||||
[_httpSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_jsonSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_authManager.requestSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
[_manager.requestSerializer setAuthorizationHeaderFieldWithCredential:_currentOauthCredential];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PNObjectLocalNotificationRefreshTokenClientCredentialSuccess object:nil];
|
||||
if (success) {
|
||||
success(YES);
|
||||
}
|
||||
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PNObjectLocalNotificationRefreshTokenClientCredentialFail object:nil];
|
||||
if (failure) {
|
||||
failure(error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
break;
|
||||
case OAuthModeNo:
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -563,4 +632,20 @@ static bool isFirstAccess = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setOauthUserName:(NSString * _Nonnull)oauthUserName oauthPassword:(NSString* _Nonnull) oauthPassword forEnv:(NSString *) environment {
|
||||
|
||||
if ([_configuration objectForKey:environment]) {
|
||||
|
||||
NSMutableDictionary *currentConfigurationDict = [[NSMutableDictionary alloc] initWithDictionary:[_configuration objectForKey:environment]];
|
||||
[currentConfigurationDict setObject:oauthUserName forKey:Client_Username];
|
||||
[currentConfigurationDict setObject:oauthPassword forKey:Client_Password];
|
||||
|
||||
[_configuration setObject:currentConfigurationDict forKey:environment];
|
||||
|
||||
if (_currentEnv == environment) {
|
||||
[self setEnvironment:environment];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user