mirror of
https://github.com/Mibew/design.git
synced 2025-01-22 18:10:33 +03:00
Completely rewrite Invite JavaScript application
This commit is contained in:
parent
d357e505c9
commit
d24daf52eb
@ -16,6 +16,7 @@
|
||||
|
||||
default_app_js - Build JavaScript files related to default application
|
||||
chat_app_js - Build JavaScript files related to chat application
|
||||
invite_app_js - Build JavaScript files related to invite application
|
||||
thread_log_app_js - Build JavaScript files related to thread log application
|
||||
users_app_js - Build JavaScript files related to users application
|
||||
|
||||
@ -270,6 +271,16 @@
|
||||
<echo>Chat JavaScript application built.</echo>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- Compile and concatenate JavaScript files related to invite application -->
|
||||
<target name="invite_app_js" depends="default_app_js">
|
||||
<antcall target="app_js">
|
||||
<param name="app_name" value="invite" />
|
||||
</antcall>
|
||||
<echo>Invite JavaScript application built.</echo>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- Compile and concatenate JavaScript files related to users application -->
|
||||
<target name="users_app_js" depends="default_app_js">
|
||||
<antcall target="app_js">
|
||||
@ -338,7 +349,7 @@
|
||||
</target>
|
||||
|
||||
<!-- Build all project -->
|
||||
<target name="all" depends="chat_app_js,thread_log_app_js,users_app_js,styles_all">
|
||||
<target name="all" depends="chat_app_js,invite_app_js,thread_log_app_js,users_app_js,styles_all">
|
||||
<echo>Mibew Messenger built.</echo>
|
||||
</target>
|
||||
|
||||
|
9
src/messenger/webim/js/compiled/invite/app.js
Normal file
9
src/messenger/webim/js/compiled/invite/app.js
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
This file is part of Mibew Messenger project.
|
||||
http://mibew.org
|
||||
|
||||
Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
License: http://mibew.org/license.php
|
||||
*/
|
||||
(function(c,a,e){a=new a.Marionette.Application;a.addInitializer(function(a){var d=new c.Server(e.extend({interactionType:MibewAPIInviteInteraction},a.server));d.callFunctionsPeriodically(function(){return[{"function":"invitationState",arguments:{"return":{invited:"invited",threadId:"threadId"},references:{},visitorId:a.visitorId}}]},function(b){0==b.errorCode&&(b.invited||window.close(),b.threadId&&(window.name="ImCenter"+b.threadId,window.location=a.chatLink+"?thread="+b.threadId))});d.runUpdater();
|
||||
c.Objects.server=d});c.Application=a})(Mibew,Backbone,_);
|
@ -0,0 +1,8 @@
|
||||
/*
|
||||
This file is part of Mibew Messenger project.
|
||||
http://mibew.org
|
||||
|
||||
Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
License: http://mibew.org/license.php
|
||||
*/
|
||||
MibewAPIInviteInteraction=function(){this.obligatoryArguments={"*":{"return":{},references:{},visitorId:null},result:{errorCode:0}};this.reservedFunctionNames=["result"]};MibewAPIInviteInteraction.prototype=new MibewAPIInteraction;
|
15
src/messenger/webim/js/compiled/invite_app.js
Normal file
15
src/messenger/webim/js/compiled/invite_app.js
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
This file is part of Mibew Messenger project.
|
||||
http://mibew.org
|
||||
Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
License: http://mibew.org/license.php
|
||||
*/
|
||||
MibewAPIInviteInteraction=function(){this.obligatoryArguments={"*":{"return":{},references:{},visitorId:null},result:{errorCode:0}};this.reservedFunctionNames=["result"]};MibewAPIInviteInteraction.prototype=new MibewAPIInteraction;
|
||||
/*
|
||||
This file is part of Mibew Messenger project.
|
||||
http://mibew.org
|
||||
Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
License: http://mibew.org/license.php
|
||||
*/
|
||||
(function(c,a,e){a=new a.Marionette.Application;a.addInitializer(function(a){var d=new c.Server(e.extend({interactionType:MibewAPIInviteInteraction},a.server));d.callFunctionsPeriodically(function(){return[{"function":"invitationState",arguments:{"return":{invited:"invited",threadId:"threadId"},references:{},visitorId:a.visitorId}}]},function(b){0==b.errorCode&&(b.invited||window.close(),b.threadId&&(window.name="ImCenter"+b.threadId,window.location=a.chatLink+"?thread="+b.threadId))});d.runUpdater();
|
||||
c.Objects.server=d});c.Application=a})(Mibew,Backbone,_);
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
This file is part of Mibew Messenger project.
|
||||
http://mibew.org
|
||||
|
||||
Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
License: http://mibew.org/license.php
|
||||
*/
|
||||
Ajax.InviteUpdater=Class.create();
|
||||
Class.inherit(Ajax.InviteUpdater,Ajax.Base,{initialize:function(a){this.setOptions(a);this._options.onComplete=this.requestComplete.bind(this);this._options.onException=this.handleException.bind(this);this._options.onTimeout=this.handleTimeout.bind(this);this._options.updateParams=this.updateParams.bind(this);this._options.handleError=this.handleError.bind(this);this._options.updateContent=this.updateContent.bind(this);this._options.timeout=5E3;this.frequency=this._options.frequency||2;this.updater=
|
||||
{};this.update()},handleException:function(){this._options.handleError&&this._options.handleError("offline, reconnecting");this.stopUpdate();this.timer=setTimeout(this.update.bind(this),1E3)},handleTimeout:function(){this._options.handleError&&this._options.handleError("timeout, reconnecting");this.stopUpdate();this.timer=setTimeout(this.update.bind(this),1E3)},stopUpdate:function(){if(this.updater._options)this.updater._options.onComplete=void 0;clearTimeout(this.timer)},update:function(){if(this._options.updateParams)this._options.parameters=
|
||||
this._options.updateParams();this.updater=new Ajax.Request(this._options.url,this._options)},requestComplete:function(a){try{var b=Ajax.getXml(a);b?(this._options.updateContent||Ajax.emptyFunction)(b):this._options.handleError&&this._options.handleError("reconnecting")}catch(c){}this.timer=setTimeout(this.update.bind(this),this.frequency*1E3)},updateParams:function(){return"visitor="+this._options.visitor},handleError:function(){},updateContent:function(a){if(a.tagName=="invitation"){var b=NodeUtils.getNodeValue(a,
|
||||
"invited"),a=NodeUtils.getNodeValue(a,"threadid");if(b=="0")this.stopUpdate(),window.close();else if(a!="0")this.stopUpdate(),window.name="ImCenter"+a,window.location=this._options.agentservl+"?thread="+a}}});EventHelper.register(window,"onload",function(){new Ajax.InviteUpdater({}.extend(updaterOptions||{}))});
|
67
src/messenger/webim/js/source/invite/app.js
Normal file
67
src/messenger/webim/js/source/invite/app.js
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @preserve This file is part of Mibew Messenger project.
|
||||
* http://mibew.org
|
||||
*
|
||||
* Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
* License: http://mibew.org/license.php
|
||||
*/
|
||||
|
||||
(function(Mibew, Backbone, _){
|
||||
|
||||
// Create application instance
|
||||
var App = new Backbone.Marionette.Application();
|
||||
|
||||
// Initialize application
|
||||
App.addInitializer(function(options){
|
||||
|
||||
// Initialize Server
|
||||
var server = new Mibew.Server(_.extend(
|
||||
{
|
||||
'interactionType': MibewAPIInviteInteraction
|
||||
},
|
||||
options.server
|
||||
));
|
||||
|
||||
// Periodically call update function at the server side
|
||||
server.callFunctionsPeriodically(
|
||||
function() {
|
||||
// Build functions list
|
||||
return [
|
||||
{
|
||||
"function": "invitationState",
|
||||
"arguments": {
|
||||
"return": {
|
||||
'invited': 'invited',
|
||||
'threadId': 'threadId'
|
||||
},
|
||||
"references": {},
|
||||
"visitorId": options.visitorId
|
||||
}
|
||||
}
|
||||
];
|
||||
},
|
||||
function(args) {
|
||||
if (args.errorCode == 0) {
|
||||
if (!args.invited) {
|
||||
// Visitor not invited any more.
|
||||
// Invitation vindow should be closed.
|
||||
window.close();
|
||||
}
|
||||
if (args.threadId) {
|
||||
// Invitation accepted.
|
||||
// Redirect agent to chat page
|
||||
window.name = 'ImCenter' + args.threadId;
|
||||
window.location= options.chatLink
|
||||
+ '?thread=' + args.threadId;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
server.runUpdater();
|
||||
Mibew.Objects.server = server;
|
||||
});
|
||||
|
||||
Mibew.Application = App;
|
||||
|
||||
})(Mibew, Backbone, _);
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @preserve This file is part of Mibew Messenger project.
|
||||
* http://mibew.org
|
||||
*
|
||||
* Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
* License: http://mibew.org/license.php
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents Invitation waiting Window to core interaction type
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
MibewAPIInviteInteraction = function() {
|
||||
this.obligatoryArguments = {
|
||||
'*': {
|
||||
'return': {},
|
||||
'references': {},
|
||||
'visitorId': null
|
||||
},
|
||||
'result': {
|
||||
'errorCode': 0
|
||||
}
|
||||
};
|
||||
|
||||
this.reservedFunctionNames = [
|
||||
'result'
|
||||
];
|
||||
}
|
||||
MibewAPIInviteInteraction.prototype = new MibewAPIInteraction();
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* @preserve This file is part of Mibew Messenger project.
|
||||
* http://mibew.org
|
||||
*
|
||||
* Copyright (c) 2005-2011 Mibew Messenger Community
|
||||
* License: http://mibew.org/license.php
|
||||
*/
|
||||
|
||||
Ajax.InviteUpdater = Class.create();
|
||||
Class.inherit( Ajax.InviteUpdater, Ajax.Base, {
|
||||
|
||||
initialize: function(_options) {
|
||||
this.setOptions(_options);
|
||||
this._options.onComplete = this.requestComplete.bind(this);
|
||||
this._options.onException = this.handleException.bind(this);
|
||||
this._options.onTimeout = this.handleTimeout.bind(this);
|
||||
this._options.updateParams = this.updateParams.bind(this);
|
||||
this._options.handleError = this.handleError.bind(this);
|
||||
this._options.updateContent = this.updateContent.bind(this);
|
||||
this._options.timeout = 5000;
|
||||
this.frequency = (this._options.frequency || 2);
|
||||
this.updater = {};
|
||||
this.update();
|
||||
},
|
||||
|
||||
handleException: function(_request, ex) {
|
||||
if( this._options.handleError )
|
||||
this._options.handleError("offline, reconnecting");
|
||||
this.stopUpdate();
|
||||
this.timer = setTimeout(this.update.bind(this), 1000);
|
||||
},
|
||||
|
||||
handleTimeout: function(_request) {
|
||||
if( this._options.handleError )
|
||||
this._options.handleError("timeout, reconnecting");
|
||||
this.stopUpdate();
|
||||
this.timer = setTimeout(this.update.bind(this), 1000);
|
||||
},
|
||||
|
||||
stopUpdate: function() {
|
||||
if( this.updater._options )
|
||||
this.updater._options.onComplete = undefined;
|
||||
clearTimeout(this.timer);
|
||||
},
|
||||
|
||||
update: function() {
|
||||
if( this._options.updateParams )
|
||||
this._options.parameters = (this._options.updateParams)();
|
||||
this.updater = new Ajax.Request(this._options.url, this._options);
|
||||
},
|
||||
|
||||
requestComplete: function(presponse) {
|
||||
try {
|
||||
var xmlRoot = Ajax.getXml(presponse);
|
||||
if( xmlRoot ) {
|
||||
(this._options.updateContent || Ajax.emptyFunction)( xmlRoot );
|
||||
} else {
|
||||
if( this._options.handleError )
|
||||
this._options.handleError("reconnecting");
|
||||
}
|
||||
} catch(e) {
|
||||
}
|
||||
this.timer = setTimeout(this.update.bind(this), this.frequency * 1000);
|
||||
},
|
||||
|
||||
updateParams: function() {
|
||||
return "visitor=" + this._options.visitor;
|
||||
},
|
||||
|
||||
handleError: function(s) {
|
||||
},
|
||||
|
||||
updateContent: function(root) {
|
||||
if( root.tagName == 'invitation' ) {
|
||||
var invited = NodeUtils.getNodeValue(root, "invited");
|
||||
var threadid = NodeUtils.getNodeValue(root, "threadid");
|
||||
if (invited == "0") {
|
||||
this.stopUpdate();
|
||||
window.close();
|
||||
}
|
||||
else if (threadid != "0") {
|
||||
this.stopUpdate();
|
||||
window.name = 'ImCenter' + threadid;
|
||||
window.location=this._options.agentservl + '?thread=' + threadid;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
EventHelper.register(window, 'onload', function(){
|
||||
new Ajax.InviteUpdater(({}).extend(updaterOptions || {}));
|
||||
});
|
130
src/messenger/webim/libs/classes/invite_processor.php
Normal file
130
src/messenger/webim/libs/classes/invite_processor.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Incapsulates invitation awaiting related api functions.
|
||||
*
|
||||
* Events triggered by the class (see description of the RequestProcessor class
|
||||
* for details):
|
||||
* - inviteRequestReceived
|
||||
* - inviteReceiveRequestError
|
||||
* - inviteCallError
|
||||
* - inviteFunctionCall
|
||||
*
|
||||
* Implements Singleton pattern
|
||||
*/
|
||||
class InviteProcessor extends ClientSideProcessor {
|
||||
|
||||
/**
|
||||
* An instance of the InviteProcessor class
|
||||
* @var InviteProcessor
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Return an instance of the InviteProcessor class.
|
||||
* @return InviteProcessor
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* Do not use directly __construct method! Use
|
||||
* InviteProcessor::getInstance() instead!
|
||||
* @todo Think about why the method is not protected
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct(array(
|
||||
'signature' => '',
|
||||
'trusted_signatures' => array(''),
|
||||
'event_prefix' => 'invite'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an instance of the MibewAPI class.
|
||||
*
|
||||
* @return MibewAPI
|
||||
*/
|
||||
protected function getMibewAPIInstance() {
|
||||
return MibewAPI::getAPI('MibewAPIInviteInteraction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub for sendAsyncRequest method.
|
||||
*
|
||||
* Actually request not send to client side. This method is ONLY STUB.
|
||||
* @return boolean Always true
|
||||
*/
|
||||
protected function sendAsyncRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub for call method.
|
||||
*
|
||||
* Actually nothing can be called at client side. This method is ONLY STUB.
|
||||
* @return boolean Always false.
|
||||
*/
|
||||
public function call() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns visitor invitation state. API function
|
||||
*
|
||||
* @param array $args Associative array of arguments. It must contains
|
||||
* following keys:
|
||||
* - 'visitorId': Id of the invited visitor
|
||||
* @return array Array of results. It contains following keys:
|
||||
* - 'invited': boolean, indicates if visitor is invited
|
||||
* - 'threadId': thread id related to visitor or false if there is no thread
|
||||
*/
|
||||
protected function apiInvitationState($args) {
|
||||
$operator = get_logged_in();
|
||||
if (!$operator) {
|
||||
throw new ThreadProcessorException(
|
||||
"Operator not logged in!",
|
||||
InviteProcessorException::ERROR_AGENT_NOT_LOGGED_IN
|
||||
);
|
||||
}
|
||||
|
||||
$invitation = invitation_state($args['visitorId']);
|
||||
return array(
|
||||
'invited' => (bool)$invitation['invited'],
|
||||
'threadId' => ($invitation['threadid'] ? $invitation['threadid'] : false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for invite processor exceptions
|
||||
*/
|
||||
class InviteProcessorException extends RequestProcessorException {
|
||||
/**
|
||||
* Operator is not logged in
|
||||
*/
|
||||
const ERROR_AGENT_NOT_LOGGED_IN = 1;
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements Mibew Core - Mibew invitation waiting window interaction
|
||||
*/
|
||||
class MibewAPIInviteInteraction extends MibewAPIInteraction {
|
||||
/**
|
||||
* Defines obligatory arguments and default values for them
|
||||
* @var array
|
||||
* @see MibewAPIInteraction::$obligatoryArgumnents
|
||||
*/
|
||||
protected $obligatoryArguments = array(
|
||||
'*' => array(
|
||||
'references' => array(),
|
||||
'return' => array(),
|
||||
'visitorId' => null
|
||||
),
|
||||
'result' => array(
|
||||
'errorCode' => 0
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Reserved function's names
|
||||
* @var array
|
||||
* @see MibewAPIInteraction::$reservedFunctionNames
|
||||
*/
|
||||
public $reservedFunctionNames = array(
|
||||
'result'
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
@ -18,20 +18,14 @@
|
||||
require_once('../libs/init.php');
|
||||
require_once('../libs/invitation.php');
|
||||
require_once('../libs/operator.php');
|
||||
require_once('../libs/classes/mibew_api.php');
|
||||
require_once('../libs/classes/mibew_api_interaction.php');
|
||||
require_once('../libs/classes/mibew_api_invite_interaction.php');
|
||||
require_once('../libs/classes/mibew_api_execution_context.php');
|
||||
require_once('../libs/classes/client_side_processor.php');
|
||||
require_once('../libs/classes/invite_processor.php');
|
||||
|
||||
$operator = check_login();
|
||||
|
||||
$visitorid = verifyparam("visitor", "/^\d{1,8}$/");
|
||||
|
||||
$errors = array();
|
||||
|
||||
$invitation = invitation_state($visitorid);
|
||||
|
||||
start_xml_output();
|
||||
echo '<invitation>';
|
||||
echo '<invited>' . ($invitation['invited'] ? $invitation['invited'] : '0') . '</invited>';
|
||||
echo '<threadid>' . ($invitation['threadid'] ? $invitation['threadid'] : '0') . '</threadid>';
|
||||
echo '</invitation>';
|
||||
exit;
|
||||
$processor = InviteProcessor::getInstance();
|
||||
$processor->receiveRequest($_POST['data']);
|
||||
|
||||
?>
|
@ -19,14 +19,37 @@ $page['title'] = getlocal("invitation.title");
|
||||
|
||||
function tpl_header() { global $page, $webimroot;
|
||||
?>
|
||||
<script type="text/javascript" language="javascript" src="<?php echo $webimroot ?>/js/compiled/common.js"></script>
|
||||
<script type="text/javascript" language="javascript"><!--
|
||||
var updaterOptions = {
|
||||
url:"<?php echo $webimroot ?>/operator/invitationstate.php",wroot:"<?php echo $webimroot ?>",
|
||||
agentservl:"<?php echo $webimroot ?>/operator/agent.php", frequency:<?php echo $page['frequency'] ?>,
|
||||
visitor: "<?php echo $page['visitor'] ?>" };
|
||||
|
||||
<!-- External libs -->
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/libs/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/libs/json2.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/libs/underscore-min.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/libs/backbone-min.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/libs/backbone.marionette.min.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/libs/handlebars.js"></script>
|
||||
|
||||
<!-- Application files -->
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/compiled/mibewapi.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/compiled/default_app.js"></script>
|
||||
<script type="text/javascript" src="<?php echo $webimroot ?>/js/compiled/invite_app.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
jQuery(document).ready(function(){
|
||||
Mibew.Application.start({
|
||||
server: {
|
||||
url: "<?php echo $webimroot ?>/operator/invitationstate.php",
|
||||
requestsFrequency: <?php echo $page['frequency'] ?>
|
||||
},
|
||||
|
||||
visitorId: "<?php echo $page['visitor'] ?>",
|
||||
|
||||
chatLink: "<?php echo $webimroot ?>/operator/agent.php"
|
||||
});
|
||||
});
|
||||
//--></script>
|
||||
<script type="text/javascript" language="javascript" src="<?php echo $webimroot ?>/js/compiled/invite_op.js"></script>
|
||||
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user