Add Console classes
This commit is contained in:
parent
fc689e05bc
commit
9b8fb1d550
111
src/Core/Console.php
Normal file
111
src/Core/Console.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core;
|
||||
|
||||
/**
|
||||
* Description of Console
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Console extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
// Disables the default help handling
|
||||
protected $helpOptions = [];
|
||||
protected $customHelpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
|
||||
|
||||
$help = <<<HELP
|
||||
Usage: bin/console [--version] [-h|--help|-?] <command> [<args>] [-v]
|
||||
|
||||
Commands:
|
||||
config Edit site config
|
||||
createdoxygen Generate Doxygen headers
|
||||
docbloxerrorchecker Checks the file tree for DocBlox errors
|
||||
globalcommunityblock Silence remote profile from global community page
|
||||
help Show help about a command, e.g (bin/console help config)
|
||||
|
||||
Options:
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Executable: ' . $this->executable);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
$showHelp = false;
|
||||
$subHelp = false;
|
||||
$command = null;
|
||||
|
||||
if ($this->getOption('version')) {
|
||||
$this->out('Friendica Console version ' . FRIENDICA_VERSION);
|
||||
|
||||
return 0;
|
||||
} elseif ((count($this->options) === 0 || $this->getOption($this->customHelpOptions) === true || $this->getOption($this->customHelpOptions) === 1) && count($this->args) === 0
|
||||
) {
|
||||
$showHelp = true;
|
||||
} elseif (count($this->args) >= 2 && $this->getArgument(0) == 'help') {
|
||||
$command = $this->getArgument(1);
|
||||
$subHelp = true;
|
||||
array_shift($this->args);
|
||||
array_shift($this->args);
|
||||
} elseif (count($this->args) >= 1) {
|
||||
$command = $this->getArgument(0);
|
||||
array_shift($this->args);
|
||||
}
|
||||
|
||||
if (is_null($command)) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
$console = $this->getSubConsole($command);
|
||||
|
||||
if ($subHelp) {
|
||||
$console->setOption($this->customHelpOptions, true);
|
||||
}
|
||||
|
||||
return $console->execute();
|
||||
}
|
||||
|
||||
private function getSubConsole($command)
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Command: ' . $command);
|
||||
}
|
||||
|
||||
$subargs = $this->args;
|
||||
array_unshift($subargs, $this->executable);
|
||||
|
||||
$subconsole = null;
|
||||
|
||||
switch ($command) {
|
||||
case 'config' : $subconsole = new Console\Config($subargs);
|
||||
break;
|
||||
case 'createdoxygen' : $subconsole = new Console\CreateDoxygen($subargs);
|
||||
break;
|
||||
case 'docbloxerrorchecker' : $subconsole = new Console\DocBloxErrorChecker($subargs);
|
||||
break;
|
||||
case 'globalcommunityblock': $subconsole = new Console\GlobalCommunityBlock($subargs);
|
||||
break;
|
||||
default:
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Command ' . $command . ' doesn\'t exist');
|
||||
}
|
||||
|
||||
foreach ($this->options as $name => $value) {
|
||||
$subconsole->setOption($name, $value);
|
||||
}
|
||||
|
||||
return $subconsole;
|
||||
}
|
||||
|
||||
}
|
131
src/Core/Console/Config.php
Normal file
131
src/Core/Console/Config.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Asika\SimpleConsole\CommandArgsException;
|
||||
use dba;
|
||||
use Friendica\Core;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/text.php';
|
||||
|
||||
/**
|
||||
* @brief tool to access the system config from the CLI
|
||||
*
|
||||
* With this script you can access the system configuration of your node from
|
||||
* the CLI. You can do both, reading current values stored in the database and
|
||||
* set new values to config variables.
|
||||
*
|
||||
* Usage:
|
||||
* If you specify no parameters at the CLI, the script will list all config
|
||||
* variables defined.
|
||||
*
|
||||
* If you specify one parameter, the script will list all config variables
|
||||
* defined in this section of the configuration (e.g. "system").
|
||||
*
|
||||
* If you specify two parameters, the script will show you the current value
|
||||
* of the named configuration setting. (e.g. "system loglevel")
|
||||
*
|
||||
* If you specify three parameters, the named configuration setting will be
|
||||
* set to the value of the last parameter. (e.g. "system loglevel 0" will
|
||||
* disable logging)
|
||||
*
|
||||
* @author Tobias Diekershoff
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Config extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console config - Manage site configuration
|
||||
Synopsis
|
||||
bin/console config [-h|--help|-?] [-v]
|
||||
bin/console config <category> [-h|--help|-?] [-v]
|
||||
bin/console config <category> <key> [-h|--help|-?] [-v]
|
||||
bin/console config <category> <key> <value> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
bin/console config
|
||||
Lists all config values
|
||||
|
||||
bin/console config <category>
|
||||
Lists all config values in the provided category
|
||||
|
||||
bin/console config <category> <key>
|
||||
Shows the value of the provided key in the category
|
||||
|
||||
bin/console config <category> <key> <value>
|
||||
Set the value of the provided key in the category
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Executable: ' . $this->executable);
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 3) {
|
||||
throw new CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
if (count($this->args) == 3) {
|
||||
Core\Config::set($this->getArgument(0), $this->getArgument(1), $this->getArgument(2));
|
||||
$this->out("config[{$this->getArgument(0)}][{$this->getArgument(1)}] = " . Core\Config::get($this->getArgument(0),
|
||||
$this->getArgument(1)));
|
||||
}
|
||||
|
||||
if (count($this->args) == 2) {
|
||||
$this->out("config[{$this->getArgument(0)}][{$this->getArgument(1)}] = " . Core\Config::get($this->getArgument(0),
|
||||
$this->getArgument(1)));
|
||||
}
|
||||
|
||||
if (count($this->args) == 1) {
|
||||
Core\Config::load($this->getArgument(0));
|
||||
|
||||
$a = get_app();
|
||||
if (!is_null($a->config[$this->getArgument(0)])) {
|
||||
foreach ($a->config[$this->getArgument(0)] as $k => $x) {
|
||||
$this->out("config[{$this->getArgument(0)}][{$k}] = " . $x);
|
||||
}
|
||||
} else {
|
||||
$this->out('Config section ' . $this->getArgument(0) . ' returned nothing');
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$configs = dba::select('config');
|
||||
foreach ($configs as $config) {
|
||||
$this->out("config[{$config['cat']}][{$config['k']}] = " . $config['v']);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
148
src/Core/Console/CreateDoxygen.php
Normal file
148
src/Core/Console/CreateDoxygen.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* Description of CreateDoxygen
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class CreateDoxygen extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console createdoxygen - Generate Doxygen headers
|
||||
Usage
|
||||
bin/console createdoxygen <file> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Outputs the provided file with added Doxygen headers to functions
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$file = $this->getArgument(0);
|
||||
if (!file_exists($file)) {
|
||||
throw new \RuntimeException('Unable to find specified file.');
|
||||
}
|
||||
|
||||
$data = file_get_contents($file);
|
||||
|
||||
$lines = explode("\n", $data);
|
||||
|
||||
$previous = "";
|
||||
|
||||
foreach ($lines AS $line) {
|
||||
$line = rtrim(trim($line, "\r"));
|
||||
|
||||
if (strstr(strtolower($line), "function")) {
|
||||
$detect = strtolower(trim($line));
|
||||
$detect = implode(" ", explode(" ", $detect));
|
||||
|
||||
$found = false;
|
||||
|
||||
if (substr($detect, 0, 9) == "function ") {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if (substr($detect, 0, 19) == "protected function ") {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if (substr($detect, 0, 17) == "private function ") {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if (substr($detect, 0, 23) == "public static function ") {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if (substr($detect, 0, 24) == "private static function ") {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if (substr($detect, 0, 10) == "function (") {
|
||||
$found = false;
|
||||
}
|
||||
|
||||
if ($found && ( trim($previous) == "*/")) {
|
||||
$found = false;
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$this->out($this->addDocumentation($line));
|
||||
}
|
||||
}
|
||||
$this->out($line);
|
||||
$previous = $line;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a doxygen header
|
||||
*
|
||||
* @param string $line The current line of the document
|
||||
*
|
||||
* @return string added doxygen header
|
||||
*/
|
||||
private function addDocumentation($line)
|
||||
{
|
||||
$trimmed = ltrim($line);
|
||||
$length = strlen($line) - strlen($trimmed);
|
||||
$space = substr($line, 0, $length);
|
||||
|
||||
$block = $space . "/**\n" .
|
||||
$space . " * @brief \n" .
|
||||
$space . " *\n"; /**/
|
||||
|
||||
|
||||
$left = strpos($line, "(");
|
||||
$line = substr($line, $left + 1);
|
||||
|
||||
$right = strpos($line, ")");
|
||||
$line = trim(substr($line, 0, $right));
|
||||
|
||||
if ($line != "") {
|
||||
$parameters = explode(",", $line);
|
||||
foreach ($parameters AS $parameter) {
|
||||
$parameter = trim($parameter);
|
||||
$splitted = explode("=", $parameter);
|
||||
|
||||
$block .= $space . " * @param " . trim($splitted[0], "& ") . "\n";
|
||||
}
|
||||
if (count($parameters) > 0) $block .= $space . " *\n";
|
||||
}
|
||||
|
||||
$block .= $space . " * @return \n" .
|
||||
$space . " */\n";
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
}
|
192
src/Core/Console/DocBloxErrorChecker.php
Normal file
192
src/Core/Console/DocBloxErrorChecker.php
Normal file
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* When I installed docblox, I had the experience that it does not generate any output at all.
|
||||
* This script may be used to find that kind of problems with the documentation build process.
|
||||
* If docblox generates output, use another approach for debugging.
|
||||
*
|
||||
* Basically, docblox takes a list of files to build documentation from. This script assumes there is a file or set of files
|
||||
* breaking the build when it is included in that list. It tries to calculate the smallest list containing these files.
|
||||
* Unfortunatly, the original problem is NP-complete, so what the script does is a best guess only.
|
||||
*
|
||||
* So it starts with a list of all files in the project.
|
||||
* If that list can't be build, it cuts it in two parts and tries both parts independently. If only one of them breaks,
|
||||
* it takes that one and tries the same independently. If both break, it assumes this is the smallest set. This assumption
|
||||
* is not necessarily true. Maybe the smallest set consists of two files and both of them were in different parts when
|
||||
* the list was divided, but by now it is my best guess. To make this assumption better, the list is shuffled after every step.
|
||||
*
|
||||
* After that, the script tries to remove a file from the list. It tests if the list breaks and if so, it
|
||||
* assumes that the file it removed belongs to the set of erroneous files.
|
||||
* This is done for all files, so, in the end removing one file leads to a working doc build.
|
||||
*
|
||||
* @author Alexander Kampmann
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class DocBloxErrorChecker extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console docbloxerrorchecker - Checks the file tree for docblox errors
|
||||
Usage
|
||||
bin/console docbloxerrorchecker [-h|--help|-?] [-v]
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 0) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
if (!$this->commandExists('docblox')) {
|
||||
throw new \RuntimeException('DocBlox isn\'t available.');
|
||||
}
|
||||
|
||||
//return from util folder to frindica base dir
|
||||
$dir = get_app()->get_basepath();
|
||||
|
||||
//stack for dirs to search
|
||||
$dirstack = [];
|
||||
//list of source files
|
||||
$filelist = [];
|
||||
|
||||
//loop over all files in $dir
|
||||
while ($dh = opendir($dir)) {
|
||||
while ($file = readdir($dh)) {
|
||||
if (is_dir($dir . "/" . $file)) {
|
||||
//add to directory stack
|
||||
if (strpos($file, '.') !== 0) {
|
||||
array_push($dirstack, $dir . "/" . $file);
|
||||
$this->out('dir ' . $dir . '/' . $file);
|
||||
}
|
||||
} else {
|
||||
//test if it is a source file and add to filelist
|
||||
if (substr($file, strlen($file) - 4) == ".php") {
|
||||
array_push($filelist, $dir . "/" . $file);
|
||||
$this->out($dir . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
//look at the next dir
|
||||
$dir = array_pop($dirstack);
|
||||
}
|
||||
|
||||
//check the entire set
|
||||
if ($this->runs($filelist)) {
|
||||
throw new \RuntimeException("I can not detect a problem.");
|
||||
}
|
||||
|
||||
//check half of the set and discard if that half is okay
|
||||
$res = $filelist;
|
||||
$i = count($res);
|
||||
do {
|
||||
$this->out($i . '/' . count($filelist) . ' elements remaining.');
|
||||
$res = $this->reduce($res, count($res) / 2);
|
||||
shuffle($res);
|
||||
$i = count($res);
|
||||
} while (count($res) < $i);
|
||||
|
||||
//check one file after another
|
||||
$needed = [];
|
||||
|
||||
while (count($res) != 0) {
|
||||
$file = array_pop($res);
|
||||
|
||||
if ($this->runs(array_merge($res, $needed))) {
|
||||
$this->out('needs: ' . $file . ' and file count ' . count($needed));
|
||||
array_push($needed, $file);
|
||||
}
|
||||
}
|
||||
|
||||
$this->out('Smallest Set is: ' . $this->namesList($needed) . ' with ' . count($needed) . ' files. ');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function commandExists($command)
|
||||
{
|
||||
$prefix = strpos(strtolower(PHP_OS),'win') > -1 ? 'where' : 'which';
|
||||
exec("{$prefix} {$command}", $output = [], $returnVal = 0);
|
||||
return $returnVal === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generates a comma separated list of file names.
|
||||
*
|
||||
* @package util
|
||||
*
|
||||
* @param array $fileset Set of file names
|
||||
*
|
||||
* @return string comma-separated list of the file names
|
||||
*/
|
||||
private function namesList($fileset)
|
||||
{
|
||||
return implode(',', $fileset);
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions runs phpdoc on the provided list of files
|
||||
* @package util
|
||||
*
|
||||
* @param array $fileset Set of filenames
|
||||
*
|
||||
* @return bool true, if that set can be built
|
||||
*/
|
||||
private function runs($fileset)
|
||||
{
|
||||
$fsParam = $this->namesList($fileset);
|
||||
$this->exec('docblox -t phpdoc_out -f ' . $fsParam);
|
||||
if (file_exists("phpdoc_out/index.html")) {
|
||||
$this->out('Subset ' . $fsParam . ' is okay.');
|
||||
$this->exec('rm -r phpdoc_out');
|
||||
return true;
|
||||
} else {
|
||||
$this->out('Subset ' . $fsParam . ' failed.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions cuts down a fileset by removing files until it finally works.
|
||||
* it was meant to be recursive, but php's maximum stack size is to small. So it just simulates recursion.
|
||||
*
|
||||
* In that version, it does not necessarily generate the smallest set, because it may not alter the elements order enough.
|
||||
*
|
||||
* @package util
|
||||
*
|
||||
* @param array $fileset set of filenames
|
||||
* @param int $ps number of files in subsets
|
||||
*
|
||||
* @return array a part of $fileset, that crashes
|
||||
*/
|
||||
private function reduce($fileset, $ps)
|
||||
{
|
||||
//split array...
|
||||
$parts = array_chunk($fileset, $ps);
|
||||
//filter working subsets...
|
||||
$parts = array_filter($parts, [$this, 'runs']);
|
||||
//melt remaining parts together
|
||||
if (is_array($parts)) {
|
||||
return array_reduce($parts, "array_merge", []);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
70
src/Core/Console/GlobalCommunityBlock.php
Normal file
70
src/Core/Console/GlobalCommunityBlock.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Model\Contact;
|
||||
|
||||
/**
|
||||
* Description of GlobalCommunityBlock
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class GlobalCommunityBlock extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console globalcommunityblock - Silence remote profile from global community page
|
||||
Usage
|
||||
bin/console globalcommunityblock <profile_url> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
bin/console globalcommunityblock <profile_url>
|
||||
Silences the provided remote profile URL from the global community page
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
$contact_id = Contact::getIdForURL($argv[1]);
|
||||
if (!$contact_id) {
|
||||
throw new \RuntimeException(L10n::t('Could not find any contact entry for this URL (%s)', $nurl));
|
||||
}
|
||||
Contact::block($contact_id);
|
||||
$this->out(L10n::t('The contact has been blocked from the node'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue