Fixes from live website
This commit is contained in:
parent
13982be9f6
commit
79c76995b9
@ -105,6 +105,7 @@ class Controller_Admin_Welcome extends Controller_Welcome {
|
||||
));
|
||||
|
||||
// We are a site administrator
|
||||
$output = '';
|
||||
if ($this->ao->rtm_id == NULL) {
|
||||
$rtmo = ORM::factory('RTM',array('account_id','=',$this->ao->id))->find();
|
||||
|
||||
@ -116,7 +117,7 @@ class Controller_Admin_Welcome extends Controller_Welcome {
|
||||
->set('o',$rtmo);
|
||||
|
||||
} else {
|
||||
$rtmo = ORM::factory('RTM',$this->ao->rmt_id);
|
||||
$rtmo = ORM::factory('RTM',$this->ao->rtm_id);
|
||||
}
|
||||
|
||||
if ($output)
|
||||
|
@ -10,14 +10,5 @@
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Controller_TemplateDefault_User extends Controller_TemplateDefault {
|
||||
public function after() {
|
||||
SystemMessage::add(array(
|
||||
'title'=>'Retire this class extension',
|
||||
'type'=>'info',
|
||||
'body'=>__METHOD__,
|
||||
));
|
||||
|
||||
return parent::after();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -36,7 +36,7 @@ class Model_RTM extends ORM_OSB {
|
||||
}
|
||||
|
||||
public function customers_direct() {
|
||||
return $this->customer->where_active()->find_all();
|
||||
return $this->customer->find_all();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
33
application/classes/Task/Account/Complete.php
Normal file
33
application/classes/Task/Account/Complete.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* Mark all accounts that have no outstanding invoices and active services as disabled.
|
||||
*
|
||||
* @package Account
|
||||
* @category Tasks
|
||||
* @author Deon George
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Task_Account_Complete extends Task {
|
||||
protected function _execute(array $params) {
|
||||
$c = 0;
|
||||
|
||||
$o = ORM::factory('Account')
|
||||
->where_active();
|
||||
|
||||
foreach ($o->find_all() as $ao) {
|
||||
if (count($ao->invoice->where_unprocessed()->find_all()) == 0 AND count($ao->service->where_active()->find_all()) == 0)
|
||||
// @todo Cant update status=0, problem with sessions in CLI
|
||||
echo $ao->id.',';
|
||||
|
||||
$ao->save();
|
||||
|
||||
if ($ao->saved())
|
||||
$c++;
|
||||
}
|
||||
|
||||
printf('%s services updated',$c);
|
||||
}
|
||||
}
|
||||
?>
|
@ -13,7 +13,7 @@ class Model_ADSL_Plan extends ORM_OSB {
|
||||
// Relationships
|
||||
// @todo This model should probably be joined with product_plugin_adsl
|
||||
protected $_belongs_to = array(
|
||||
'adsl_supplier_plan'=>array(),
|
||||
'adsl_supplier_plan'=>array('model'=>'ADSL_Supplier_Plan'),
|
||||
);
|
||||
|
||||
protected $_display_filters = array(
|
||||
|
@ -9,10 +9,10 @@
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Model_Adsl_Supplier extends ORM_OSB {
|
||||
class Model_ADSL_Supplier extends ORM_OSB {
|
||||
// Relationships
|
||||
protected $_has_many = array(
|
||||
'adsl_supplier_plan'=>array('foreign_key'=>'supplier_id','far_key'=>'id'),
|
||||
'adsl_supplier_plan'=>array('model'=>'ADSL_Supplier_Plan','foreign_key'=>'supplier_id','far_key'=>'id'),
|
||||
);
|
||||
|
||||
protected $_updated_column = FALSE;
|
||||
|
@ -9,13 +9,13 @@
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Model_Adsl_Supplier_Plan extends ORM_OSB {
|
||||
class Model_ADSL_Supplier_Plan extends ORM_OSB {
|
||||
// Relationships
|
||||
protected $_has_many = array(
|
||||
'adsl_plan'=>array('far_key'=>'id'),
|
||||
'adsl_plan'=>array('model'=>'ADSL_Plan','far_key'=>'id'),
|
||||
);
|
||||
protected $_belongs_to = array(
|
||||
'adsl_supplier'=>array('foreign_key'=>'supplier_id'),
|
||||
'adsl_supplier'=>array('model'=>'ADSL_Supplier','foreign_key'=>'supplier_id'),
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ class Model_Product_Plugin_Adsl extends Model_Product_Plugin {
|
||||
protected $_table_name = 'adsl_plan';
|
||||
|
||||
protected $_belongs_to = array(
|
||||
'adsl_supplier_plan'=>array(),
|
||||
'adsl_supplier_plan'=>array('model'=>'ADSL_Supplier_Plan'),
|
||||
);
|
||||
|
||||
protected $_display_filters = array(
|
||||
|
@ -33,7 +33,7 @@ class Service_Traffic_Adsl {
|
||||
// Our DB record must be the suffix of this class name
|
||||
$supplier = preg_replace('/^'.get_parent_class($this).'_/','',get_class($this));
|
||||
|
||||
$so = ORM::factory('Adsl_Supplier')
|
||||
$so = ORM::factory('ADSL_Supplier')
|
||||
->where('name','=',$supplier)
|
||||
->find();
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Controller_Cart extends Controller_TemplateDefault {
|
||||
protected $auth_required = FALSE;
|
||||
|
||||
/**
|
||||
* List the cart contents
|
||||
*/
|
||||
|
@ -69,7 +69,7 @@ abstract class Checkout_Plugin_Paypal extends Checkout_Plugin {
|
||||
return _('Thank you!');
|
||||
|
||||
if (! $debug_mode) {
|
||||
$request = Request::factory(sprintf('https://%s/cgi-bin/webscr',$cno->data['test_ipn'] ? $this->url_test : $this->url_prod))
|
||||
$request = Request::factory(sprintf('https://%s/cgi-bin/webscr',isset($cno->data['test_ipn']) ? $this->url_test : $this->url_prod))
|
||||
->method('POST');
|
||||
|
||||
$request->client()->options(Arr::merge($this->curlopts,array(
|
||||
@ -84,7 +84,6 @@ abstract class Checkout_Plugin_Paypal extends Checkout_Plugin {
|
||||
// Verify that the IPN is for us.
|
||||
// @todo This should be in the DB.
|
||||
if ($cno->data['business'] == 'deon_1260578114_biz@graytech.net.au') {
|
||||
|
||||
switch ($cno->data['payment_status']) {
|
||||
case 'Completed':
|
||||
// Our cart items total.
|
||||
|
@ -59,7 +59,7 @@ class Controller_Checkout extends Controller_TemplateDefault {
|
||||
$test_id = FALSE;
|
||||
$co = ORM::factory('Checkout',$this->request->param('id'));
|
||||
|
||||
if ((! $co->loaded() OR ! Request::current()->post()) AND ! $test_id=Kohana::$config->load('debug')->checkout_notify)
|
||||
if ((! $co->loaded() OR ! Request::current()->post()) AND ! ($test_id=Kohana::$config->load('debug')->checkout_notify))
|
||||
throw HTTP_Exception::factory(404,'Payment not found!');
|
||||
|
||||
$this->auto_render = FALSE;
|
||||
|
@ -175,14 +175,14 @@ class Export_Quicken extends Export {
|
||||
$qpo->MEMO = sprintf('Payment for invoice(s) %s (%s)',implode(':',$invoice_ids),$po->checkout->name);
|
||||
|
||||
// @todo Accounts/Payment should be configurable
|
||||
switch ($po->checkout->checkout_plugin) {
|
||||
switch ($po->checkout->plugin) {
|
||||
// @todo this is direct debit
|
||||
case 'MANUAL':
|
||||
case 'DD_EZYPAY':
|
||||
$qpo->PAYMETH = 'DirectDebit';
|
||||
$qpo->ACCNT = 'Ezypay';
|
||||
break;
|
||||
|
||||
case 'REMIT_CHECK':
|
||||
case 'REMIT_CHEQUE':
|
||||
$qpo->PAYMETH = 'Cheque';
|
||||
$qpo->ACCNT = 'Undeposited Funds';
|
||||
break;
|
||||
@ -192,7 +192,7 @@ class Export_Quicken extends Export {
|
||||
$qpo->ACCNT = 'Bendigo Bank';
|
||||
break;
|
||||
|
||||
case 'PAYPAL':
|
||||
case 'PAYPAL_CART':
|
||||
$qpo->PAYMETH = 'Paypal';
|
||||
$qpo->ACCNT = 'Paypal';
|
||||
break;
|
||||
|
81
modules/host/classes/Host/Plugin/Cpanel.php
Normal file
81
modules/host/classes/Host/Plugin/Cpanel.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class provides CPANEL support
|
||||
*
|
||||
* @package Host
|
||||
* @category Plugins
|
||||
* @author Deon George
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
abstract class Host_Plugin_Cpanel extends Host_Plugin {
|
||||
// Service Object
|
||||
# protected $protocol = '1.6.0.0';
|
||||
# protected $path = 'enterprise/control/agent.php';
|
||||
# protected $packet;
|
||||
# protected $xml;
|
||||
# protected $_loaded = FALSE;
|
||||
|
||||
// Manage UI Login Attributes
|
||||
protected $url = 'login';
|
||||
protected $login_acnt_field = '';
|
||||
protected $login_user_field = 'user';
|
||||
protected $login_pass_field = 'pass';
|
||||
|
||||
// Our required abstract classes
|
||||
public function serialize() {
|
||||
return (string)$this->_object;
|
||||
}
|
||||
public function unserialize($s) {
|
||||
$this->_object = XML::factory(NULL,NULL,$s);
|
||||
}
|
||||
|
||||
public function __get($index) {
|
||||
echo __METHOD__;die();
|
||||
}
|
||||
|
||||
public function admin_update() {
|
||||
echo __METHOD__;die();
|
||||
}
|
||||
|
||||
public function manage_button(Model_Service_Plugin_Host $spho,$t) {
|
||||
return $this->render_button($t,$spho->service_id,$spho->username_value(),substr(md5($spho->password_value()),0,8));
|
||||
}
|
||||
public function admin_manage_button(Model_Host_Server $hso,$t) {
|
||||
return $this->render_button($t,$hso->id,substr(md5($hso->manage_username),0,8),substr(md5($hso->manage_password),0,8));
|
||||
}
|
||||
|
||||
protected function render_button($t,$sid,$u,$p) {
|
||||
$debug = FALSE;
|
||||
$output = '';
|
||||
|
||||
$output .= Form::open(
|
||||
$debug ? 'debug/site' : sprintf('%s/%s',$this->hso->manage_url,$this->url),
|
||||
array('target'=>'w24','method'=>'post','id'=>sprintf('id_%s_%s',$sid,$t))
|
||||
);
|
||||
$output .= Form::input($this->login_user_field,$u,array('type'=>'hidden','id'=>sprintf('u_%s_%s',$sid,$t)));
|
||||
$output .= Form::input($this->login_pass_field,$p,array('type'=>'hidden','id'=>sprintf('p_%s_%s',$sid,$t)));
|
||||
$output .= Form::close();
|
||||
$output .= Form::button('submit',_('Manage'),array('class'=>'form_button','value'=>sprintf('%s:%s',$sid,$t)));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
protected function init() {
|
||||
echo __METHOD__;die();
|
||||
}
|
||||
|
||||
protected function server_command(XML $xml) {
|
||||
echo __METHOD__;die();
|
||||
}
|
||||
|
||||
public function loaded() {
|
||||
return $this->_loaded;
|
||||
}
|
||||
|
||||
private function render(XML $xml) {
|
||||
echo __METHOD__;die();
|
||||
}
|
||||
}
|
||||
?>
|
100
modules/host/classes/Host/Plugin/Cpanel/11.php
Normal file
100
modules/host/classes/Host/Plugin/Cpanel/11.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class provides CPANEL client support
|
||||
*
|
||||
* @package Host
|
||||
* @category Plugins
|
||||
* @author Deon George
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Host_Plugin_Cpanel_11 extends Host_Plugin_Cpanel {
|
||||
# protected $protocol = '1.6.3.0';
|
||||
|
||||
// @todo Get these default "templates" values out of the DB
|
||||
private $_template = array(
|
||||
);
|
||||
|
||||
/**
|
||||
* Get a Client Configuration
|
||||
*/
|
||||
public function cmd_getclient(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DNS Configuration
|
||||
*/
|
||||
public function cmd_getdns(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mail Configuration
|
||||
*/
|
||||
public function cmd_getmail(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Domain Configuration
|
||||
*/
|
||||
public function cmd_getdomain(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Reseller
|
||||
*/
|
||||
public function cmd_getreseller(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Domain Traffic
|
||||
*/
|
||||
public function cmd_gettraffic(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Mail
|
||||
*/
|
||||
public function cmd_disablemail(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provision a hosting service
|
||||
* @todo To implement
|
||||
*/
|
||||
public function provision(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
public function add_client(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
public function add_dnsdata(Model_Service $so,XML $dns) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
public function add_domain(Model_Service $so) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
public function newitem(Model_Service $so,$type) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
public function setexpire(Model_Service $so,$date=NULL) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
|
||||
public function setpasswd(Model_Service $so,$pw) {
|
||||
throw new Kohana_Exception('Not Implemented');
|
||||
}
|
||||
}
|
||||
?>
|
@ -14,7 +14,6 @@ class Task_Invoice_Complete extends Task {
|
||||
$c = 0;
|
||||
|
||||
$o = ORM::factory('Invoice')
|
||||
->where_active()
|
||||
->where_unprocessed();
|
||||
|
||||
foreach ($o->find_all() as $io) {
|
||||
|
@ -51,9 +51,6 @@ class Model_Payment extends ORM_OSB {
|
||||
* @param $inv number, to allocate payment to an invoice
|
||||
*/
|
||||
public function add_item($invnum) {
|
||||
if ($this->loaded() and ! $this->payment_items)
|
||||
throw new Kohana_Exception('Need to load payment_items?');
|
||||
|
||||
// Find our id, if it exists
|
||||
foreach ($this->payment_items as $pio)
|
||||
if ($pio->invoice_id == $invnum)
|
||||
@ -156,6 +153,7 @@ class Model_Payment extends ORM_OSB {
|
||||
/** LIST FUNCTIONS **/
|
||||
|
||||
public function list_unapplied() {
|
||||
return array();
|
||||
$pi = array();
|
||||
|
||||
// @todo database suffix needs to be dynamically calculated
|
||||
|
@ -181,7 +181,7 @@ class Model_Product extends ORM_OSB {
|
||||
*/
|
||||
public function availPriceGroups() {
|
||||
// @todo This needs to be worked out dynamically
|
||||
return array(0,2);
|
||||
return array(0,1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -496,7 +496,7 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
||||
public function action_listadslbilling() {
|
||||
$id = $this->request->param('id');
|
||||
|
||||
$aso = ORM::factory('Adsl_Supplier',$id);
|
||||
$aso = ORM::factory('ADSL_Supplier',$id);
|
||||
|
||||
// Process upload
|
||||
// @todo This should be separated out by supplier in case each supplier has a different format
|
||||
@ -616,37 +616,61 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
|
||||
if (! $start && preg_match('/^Item ID,/',$line)) {
|
||||
$start = true;
|
||||
continue;
|
||||
|
||||
// Items end after "Subtotal"
|
||||
} elseif ($start && ! $end && preg_match('/^Subtotal:,/',$line)) {
|
||||
$end = true;
|
||||
continue;
|
||||
|
||||
// If we havent started or not ended, continue
|
||||
} elseif (! $start || $end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo This is to workaround SEP2012 CSV invoice which had extra columns.
|
||||
if (count(explode(',',$line)) == 9)
|
||||
list($id,$ref,$unknown,$unknown,$unknown,$linedata,$q,$cost,$total) = explode(',',$line);
|
||||
elseif (count(explode(',',$line)) == 10)
|
||||
list($id,$ref,$unknown,$unknown,$unknown,$unknown,$linedata,$q,$cost,$total) = explode(',',$line);
|
||||
else
|
||||
list($id,$ref,$unknown,$linedata,$q,$cost,$total) = explode(',',$line);
|
||||
$record = explode(',',$line);
|
||||
|
||||
// Extract the phone number from the $linedata
|
||||
@list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$linedata)));
|
||||
// 1 = Item ID (ignore)
|
||||
// 2 = Reference ID (ignore - its not useful for us)
|
||||
// 3 = Category (always appears blank)
|
||||
// 4 = Item Description (has our service number, rental and excess charges description)
|
||||
// 0nnnnnnnnn - Monthly Internet Charge On Plan XXXXX For billing period (dd/mm/yyyy - dd/mm/yyyy) (7 FIELDED LINES)
|
||||
// 0nnnnnnnnn - Excess usage charges for March 2013 (8 FIELDED LINES)
|
||||
// 5 = Quantity
|
||||
// Always 1 for Plan Fees
|
||||
// 0nnnnnnnnn@graytech.net.au Excess Usage y GB (for excess charges)
|
||||
// 6 = Unit Price
|
||||
// Always 1 for Excess Usage (probably quantity)
|
||||
// 7 = Total Price
|
||||
// Unit price for Excess Usage
|
||||
// 8 = Total Price for Excess Usage
|
||||
|
||||
// If the description says Monthly Charge, we know its the monthly fee.
|
||||
if (preg_match('/^Monthly Charge/',$description))
|
||||
$result[$service]['cost'] = preg_replace('/\$/','',$total);
|
||||
// If the description says VISP credit, we know this is commission.
|
||||
elseif (preg_match('/^VISP Credit/',$description))
|
||||
$result[$service]['credit'] = preg_replace('/\$/','',$total);
|
||||
// If the description says Excess, we know this is excess charges.
|
||||
elseif (preg_match('/^Excess usage/',$description))
|
||||
$result[$service]['excess'] = preg_replace('/\$/','',$total);
|
||||
else
|
||||
if (! count($record) >= 7)
|
||||
throw Kohana_Exception('Format of CSV file changed? (:record)',array(':record'=>$record));
|
||||
|
||||
|
||||
if (preg_match('/Monthly Internet Charge On Plan /',$record[3])) {
|
||||
list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3])));
|
||||
$result[$service]['cost'] = str_replace('$','',$record[6]);
|
||||
|
||||
} elseif (preg_match('/VISP Credit/',$record[3])) {
|
||||
list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3])));
|
||||
$result[$service]['credit'] = str_replace('$','',$record[6]);
|
||||
|
||||
} elseif (preg_match('/Excess usage charges for /',$record[3])) {
|
||||
list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3])));
|
||||
$result[$service]['excess'] = str_replace('$','',$record[7]);
|
||||
|
||||
// Ignore Payment For Invoice lines
|
||||
} elseif (preg_match('/Payment For Invoice:/',$record[3])) {
|
||||
|
||||
} else {
|
||||
try {
|
||||
list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3])));
|
||||
$result[$service]['info'] = $line;
|
||||
} catch (Exception $e) {
|
||||
$result['000']['info'] = $line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
@ -11,7 +11,7 @@
|
||||
*/
|
||||
class Controller_Task_Service extends Controller_Task {
|
||||
private function _traffic_suppliers($active=FALSE) {
|
||||
$suppliers = ORM::factory('Adsl_Supplier');
|
||||
$suppliers = ORM::factory('ADSL_Supplier');
|
||||
|
||||
return $active ? $suppliers->list_active() : $suppliers->find_all();
|
||||
}
|
||||
|
@ -132,13 +132,13 @@ class Model_Service_Plugin_Ssl extends Model_Service_Plugin {
|
||||
}
|
||||
|
||||
public function renew() {
|
||||
$d = SSL::details($this->cert);
|
||||
$d = SSL::instance($this->cert);
|
||||
$ssl_conf = Kohana::$config->load('ssl');
|
||||
// @todo change this so an admin can force this.
|
||||
$force = TRUE;
|
||||
|
||||
// If our certificate is not old enough skip
|
||||
if ($d['validTo_time_t'] > time()+$ssl_conf['min_renew_days']*86400 AND ! $force)
|
||||
if ($d->get_valid_to() > time()+$ssl_conf['min_renew_days']*86400 AND ! $force)
|
||||
return FALSE;
|
||||
|
||||
$res = openssl_csr_sign($this->csr,$this->SSL_CA->sign_cert,$this->SSL_CA->sign_pk,$this->service->product->plugin()->days,array(
|
||||
|
Reference in New Issue
Block a user