Migrate major controllers to appframework
This commit is contained in:
parent
128b8454cc
commit
595b2fbf96
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ownCloud - Documents App
|
|
||||||
*
|
|
||||||
* @author Victor Dubiniuk
|
|
||||||
* @copyright 2013 Victor Dubiniuk victor.dubiniuk@gmail.com
|
|
||||||
*
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace OCA\Documents;
|
|
||||||
|
|
||||||
class Controller {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do security precheck
|
|
||||||
* @param bool callcheck - whether security token check is needed
|
|
||||||
* @return string userId of the currently logged in user
|
|
||||||
*/
|
|
||||||
public static function preDispatch($callcheck = true){
|
|
||||||
if ($callcheck){
|
|
||||||
\OCP\JSON::callCheck();
|
|
||||||
}
|
|
||||||
\OCP\JSON::checkAppEnabled('documents');
|
|
||||||
\OCP\JSON::checkLoggedIn();
|
|
||||||
return \OCP\User::getUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do security precheck for not logged in users
|
|
||||||
* @param bool callcheck - whether security token check is needed
|
|
||||||
*/
|
|
||||||
public static function preDispatchGuest($callcheck = true){
|
|
||||||
if ($callcheck){
|
|
||||||
\OCP\JSON::callCheck();
|
|
||||||
}
|
|
||||||
\OCP\JSON::checkAppEnabled('documents');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -25,6 +25,7 @@ class BadRequestException extends \Exception {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\OCP\JSON::checkAppEnabled('documents');
|
||||||
$response = array();
|
$response = array();
|
||||||
|
|
||||||
try{
|
try{
|
||||||
@ -38,10 +39,8 @@ try{
|
|||||||
$member = new Db\Member();
|
$member = new Db\Member();
|
||||||
$member->load($memberId);
|
$member->load($memberId);
|
||||||
|
|
||||||
if ($member->getIsGuest() || is_null($member->getIsGuest())){
|
if (!$member->getIsGuest()){
|
||||||
Controller::preDispatchGuest(false);
|
\OCP\JSON::checkLoggedIn();
|
||||||
} else {
|
|
||||||
Controller::preDispatch(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -35,9 +35,6 @@ OCP\App::addNavigationEntry(array(
|
|||||||
);
|
);
|
||||||
|
|
||||||
OC::$CLASSPATH['OCA\Documents\Controller'] = 'documents/ajax/controller.php';
|
OC::$CLASSPATH['OCA\Documents\Controller'] = 'documents/ajax/controller.php';
|
||||||
OC::$CLASSPATH['OCA\Documents\DocumentController'] = 'documents/ajax/documentController.php';
|
|
||||||
OC::$CLASSPATH['OCA\Documents\SessionController'] = 'documents/ajax/sessionController.php';
|
|
||||||
OC::$CLASSPATH['OCA\Documents\UserController'] = 'documents/ajax/userController.php';
|
|
||||||
|
|
||||||
//Script for registering file actions
|
//Script for registering file actions
|
||||||
OCP\Util::addScript('documents', 'viewer/viewer');
|
OCP\Util::addScript('documents', 'viewer/viewer');
|
||||||
|
66
appinfo/application.php
Normal file
66
appinfo/application.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ownCloud - Documents App
|
||||||
|
*
|
||||||
|
* @author Victor Dubiniuk
|
||||||
|
* @copyright 2014 Victor Dubiniuk victor.dubiniuk@gmail.com
|
||||||
|
*
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Documents\AppInfo;
|
||||||
|
|
||||||
|
use \OCP\AppFramework\App;
|
||||||
|
|
||||||
|
use \OCA\Documents\Controller\UserController;
|
||||||
|
use \OCA\Documents\Controller\SessionController;
|
||||||
|
use \OCA\Documents\Controller\DocumentController;
|
||||||
|
|
||||||
|
class Application extends App {
|
||||||
|
public function __construct (array $urlParams = array()) {
|
||||||
|
parent::__construct('documents', $urlParams);
|
||||||
|
|
||||||
|
$container = $this->getContainer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controllers
|
||||||
|
*/
|
||||||
|
$container->registerService('UserController', function($c) {
|
||||||
|
return new UserController(
|
||||||
|
$c->query('AppName'),
|
||||||
|
$c->query('Request')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
$container->registerService('SessionController', function($c) {
|
||||||
|
return new SessionController(
|
||||||
|
$c->query('AppName'),
|
||||||
|
$c->query('Request'),
|
||||||
|
$c->query('UserId')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
$container->registerService('DocumentController', function($c) {
|
||||||
|
return new DocumentController(
|
||||||
|
$c->query('AppName'),
|
||||||
|
$c->query('Request'),
|
||||||
|
$c->query('CoreConfig'),
|
||||||
|
$c->query('L10N'),
|
||||||
|
$c->query('UserId')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core
|
||||||
|
*/
|
||||||
|
|
||||||
|
$container->registerService('CoreConfig', function($c) {
|
||||||
|
return $c->query('ServerContainer')->getConfig();
|
||||||
|
});
|
||||||
|
$container->registerService('L10N', function($c) {
|
||||||
|
return $c->query('ServerContainer')->getL10N($c->query('AppName'));
|
||||||
|
});
|
||||||
|
$container->registerService('UserId', function() {
|
||||||
|
return \OCP\User::getUser();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,35 @@
|
|||||||
* ownCloud - Documents App
|
* ownCloud - Documents App
|
||||||
*
|
*
|
||||||
* @author Victor Dubiniuk
|
* @author Victor Dubiniuk
|
||||||
* @copyright 2013 Victor Dubiniuk victor.dubiniuk@gmail.com
|
* @copyright 2013-2014 Victor Dubiniuk victor.dubiniuk@gmail.com
|
||||||
*
|
*
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
* later.
|
* later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Documents;
|
||||||
|
|
||||||
|
$application = new \OCA\Documents\AppInfo\Application();
|
||||||
|
$application->registerRoutes($this, array(
|
||||||
|
'routes' => array(
|
||||||
|
//users
|
||||||
|
array('name' => 'user#getAvatar', 'url' => 'ajax/user/avatar', 'verb' => 'GET'),
|
||||||
|
array('name' => 'user#rename', 'url' => 'ajax/user/rename', 'verb' => 'POST'),
|
||||||
|
array('name' => 'user#disconnect', 'url' => 'ajax/user/disconnect/{memberId}', 'verb' => 'POST'),
|
||||||
|
array('name' => 'user#disconnectGuest', 'url' => 'ajax/user/disconnectGuest/{memberId}', 'verb' => 'POST'),
|
||||||
|
//session
|
||||||
|
array('name' => 'session#joinAsUser', 'url' => 'ajax/session/joinasuser/{fileId}', 'verb' => 'POST'),
|
||||||
|
array('name' => 'session#joinAsGuest', 'url' => 'ajax/session/joinasguest/{token}', 'verb' => 'POST'),
|
||||||
|
array('name' => 'session#save', 'url' => 'ajax/session/save', 'verb' => 'POST'),
|
||||||
|
//documents
|
||||||
|
array('name' => 'document#create', 'url' => 'ajax/documents/create', 'verb' => 'POST'),
|
||||||
|
array('name' => 'document#serve', 'url' => 'ajax/genesis/{esId}', 'verb' => array('GET', 'HEAD')),
|
||||||
|
array('name' => 'document#rename', 'url' => 'ajax/documents/rename/{fileId}', 'verb' => 'POST'),
|
||||||
|
array('name' => 'document#listAll', 'url' => 'ajax/documents/list', 'verb' => 'GET'),
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
/** @var $this \OC\Route\Router */
|
/** @var $this \OC\Route\Router */
|
||||||
|
|
||||||
$this->create('documents_ajax_admin', 'ajax/admin.php')
|
$this->create('documents_ajax_admin', 'ajax/admin.php')
|
||||||
@ -37,77 +60,7 @@ $this->create('documents_index', '')
|
|||||||
->get()
|
->get()
|
||||||
->actionInclude('documents/index.php');
|
->actionInclude('documents/index.php');
|
||||||
|
|
||||||
$this->create('documents_documents_create', 'ajax/documents/create')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\DocumentController', 'create')
|
|
||||||
;
|
|
||||||
$this->create('documents_genesis', 'ajax/genesis/{es_id}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\DocumentController', 'serve')
|
|
||||||
;
|
|
||||||
$this->create('documents_rename', 'ajax/documents/rename/{file_id}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\DocumentController', 'rename')
|
|
||||||
;
|
|
||||||
$this->create('documents_genesis', 'ajax/genesis/{es_id}')
|
|
||||||
->get()
|
|
||||||
->action('\OCA\Documents\DocumentController', 'serve')
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->create('documents_documents_list', 'ajax/documents/list')
|
|
||||||
->get()
|
|
||||||
->action('\OCA\Documents\DocumentController', 'listAll')
|
|
||||||
;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session routes
|
|
||||||
*/
|
|
||||||
$this->create('documents_session_joinasuser', 'ajax/session/joinasuser/{file_id}')
|
|
||||||
->get()
|
|
||||||
->action('\OCA\Documents\SessionController', 'joinAsUser')
|
|
||||||
;
|
|
||||||
$this->create('documents_session_joinasuser', 'ajax/session/joinasuser/{file_id}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\SessionController', 'joinAsUser')
|
|
||||||
;
|
|
||||||
$this->create('documents_session_joinasguest', 'ajax/session/joinasguest/{token}')
|
|
||||||
->get()
|
|
||||||
->action('\OCA\Documents\SessionController', 'joinAsGuest')
|
|
||||||
;
|
|
||||||
$this->create('documents_session_joinasguest', 'ajax/session/joinasguest/{token}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\SessionController', 'joinAsGuest')
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->create('documents_session_save', 'ajax/session/save')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\SessionController', 'save')
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->create('documents_otpoll', 'ajax/otpoll.php')
|
$this->create('documents_otpoll', 'ajax/otpoll.php')
|
||||||
->post()
|
->post()
|
||||||
->actionInclude('documents/ajax/otpoll.php')
|
->actionInclude('documents/ajax/otpoll.php')
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
|
||||||
* User routes
|
|
||||||
*/
|
|
||||||
$this->create('documents_user_avatar', 'ajax/user/avatar')
|
|
||||||
->get()
|
|
||||||
->action('\OCA\Documents\UserController', 'sendAvatar')
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->create('documents_user_rename', 'ajax/user/rename/{member_id}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\UserController', 'rename')
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->create('documents_user_disconnect', 'ajax/user/disconnect/{member_id}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\UserController', 'disconnectUser')
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->create('documents_user_disconnectGuest', 'ajax/user/disconnectGuest/{member_id}')
|
|
||||||
->post()
|
|
||||||
->action('\OCA\Documents\UserController', 'disconnectGuest')
|
|
||||||
;
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,86 +1,108 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ownCloud - Documents App
|
* ownCloud - Documents App
|
||||||
*
|
*
|
||||||
* @author Victor Dubiniuk
|
* @author Victor Dubiniuk
|
||||||
* @copyright 2013 Victor Dubiniuk victor.dubiniuk@gmail.com
|
* @copyright 2014 Victor Dubiniuk victor.dubiniuk@gmail.com
|
||||||
*
|
*
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
* later.
|
* later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace OCA\Documents;
|
namespace OCA\Documents\Controller;
|
||||||
|
|
||||||
|
use \OCP\AppFramework\Controller;
|
||||||
|
use \OCP\IRequest;
|
||||||
|
use \OCP\AppFramework\Http\JSONResponse;
|
||||||
|
|
||||||
|
use \OCA\Documents\Db;
|
||||||
|
use \OCA\Documents\File;
|
||||||
|
use \OCA\Documents\Helper;
|
||||||
|
use OCA\Documents\Filter;
|
||||||
|
|
||||||
class SessionController extends Controller{
|
class SessionController extends Controller{
|
||||||
|
|
||||||
public static function joinAsGuest($args){
|
protected $uid;
|
||||||
self::preDispatchGuest();
|
|
||||||
|
|
||||||
$uid = Helper::getArrayValueByKey($_POST, 'name');
|
public function __construct($appName, IRequest $request, $uid){
|
||||||
$uid = substr($uid, 0, 16);
|
parent::__construct($appName, $request);
|
||||||
|
$this->uid = $uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @PublicPage
|
||||||
|
*/
|
||||||
|
public function joinAsGuest($token, $name){
|
||||||
|
$uid = substr($name, 0, 16);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$token = Helper::getArrayValueByKey($args, 'token');
|
|
||||||
$file = File::getByShareToken($token);
|
$file = File::getByShareToken($token);
|
||||||
if ($file->isPasswordProtected() && !$file->checkPassword('')){
|
if ($file->isPasswordProtected() && !$file->checkPassword('')){
|
||||||
throw new \Exception('Not authorized');
|
throw new \Exception('Not authorized');
|
||||||
}
|
}
|
||||||
$session = Db\Session::start($uid, $file);
|
|
||||||
\OCP\JSON::success($session);
|
$response = array_merge(
|
||||||
|
Db\Session::start($uid, $file),
|
||||||
|
array('status'=>'success')
|
||||||
|
);
|
||||||
} catch (\Exception $e){
|
} catch (\Exception $e){
|
||||||
Helper::warnLog('Starting a session failed. Reason: ' . $e->getMessage());
|
Helper::warnLog('Starting a session failed. Reason: ' . $e->getMessage());
|
||||||
\OCP\JSON::error();
|
$response = array (
|
||||||
exit();
|
'status'=>'error'
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function joinAsUser($args){
|
return $response;
|
||||||
$uid = self::preDispatch();
|
}
|
||||||
$fileId = Helper::getArrayValueByKey($args, 'file_id');
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*/
|
||||||
|
public function joinAsUser($fileId){
|
||||||
try {
|
try {
|
||||||
$view = \OC\Files\Filesystem::getView();
|
$view = \OC\Files\Filesystem::getView();
|
||||||
$path = $view->getPath($fileId);
|
$path = $view->getPath($fileId);
|
||||||
|
|
||||||
if ($view->isUpdatable($path)) {
|
if ($view->isUpdatable($path)) {
|
||||||
$file = new File($fileId);
|
$file = new File($fileId);
|
||||||
$session = Db\Session::start($uid, $file);
|
$response = Db\Session::start($this->uid, $file);
|
||||||
\OCP\JSON::success($session);
|
|
||||||
} else {
|
} else {
|
||||||
$info = $view->getFileInfo($path);
|
$info = $view->getFileInfo($path);
|
||||||
\OCP\JSON::success(array(
|
$response = array(
|
||||||
'permissions' => $info['permissions'],
|
'permissions' => $info['permissions'],
|
||||||
'id' => $fileId
|
'id' => $fileId
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
exit();
|
$response = array_merge(
|
||||||
|
$response,
|
||||||
|
array('status'=>'success')
|
||||||
|
);
|
||||||
} catch (\Exception $e){
|
} catch (\Exception $e){
|
||||||
Helper::warnLog('Starting a session failed. Reason: ' . $e->getMessage());
|
Helper::warnLog('Starting a session failed. Reason: ' . $e->getMessage());
|
||||||
\OCP\JSON::error();
|
$response = array (
|
||||||
exit();
|
'status'=>'error'
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @PublicPage
|
||||||
* Store the document content to its origin
|
* Store the document content to its origin
|
||||||
*/
|
*/
|
||||||
public static function save(){
|
public function save(){
|
||||||
try {
|
try {
|
||||||
$esId = @$_SERVER['HTTP_WEBODF_SESSION_ID'];
|
$esId = $this->request->server['HTTP_WEBODF_SESSION_ID'];
|
||||||
if (!$esId){
|
if (!$esId){
|
||||||
throw new \Exception('Session id can not be empty');
|
throw new \Exception('Session id can not be empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
$memberId = @$_SERVER['HTTP_WEBODF_MEMBER_ID'];
|
$memberId = $this->request->server['HTTP_WEBODF_MEMBER_ID'];
|
||||||
$currentMember = new Db\Member();
|
$currentMember = new Db\Member();
|
||||||
$currentMember->load($memberId);
|
$currentMember->load($memberId);
|
||||||
if (is_null($currentMember->getIsGuest()) || $currentMember->getIsGuest()){
|
|
||||||
self::preDispatchGuest();
|
|
||||||
} else {
|
|
||||||
$uid = self::preDispatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
//check if member belongs to the session
|
//check if member belongs to the session
|
||||||
if ($esId != $currentMember->getEsId()){
|
if ($esId != $currentMember->getEsId()){
|
||||||
@ -88,8 +110,9 @@ class SessionController extends Controller{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extra info for future usage
|
// Extra info for future usage
|
||||||
// $sessionRevision = Helper::getArrayValueByKey($_SERVER, 'HTTP_WEBODF_SESSION_REVISION');
|
// $sessionRevision = $this->request->server['HTTP_WEBODF_SESSION_REVISION'];
|
||||||
|
|
||||||
|
//NB ouch! New document content is passed as an input stream content
|
||||||
$stream = fopen('php://input','r');
|
$stream = fopen('php://input','r');
|
||||||
if (!$stream){
|
if (!$stream){
|
||||||
throw new \Exception('New content missing');
|
throw new \Exception('New content missing');
|
||||||
@ -114,11 +137,13 @@ class SessionController extends Controller{
|
|||||||
} catch (\Exception $e){
|
} catch (\Exception $e){
|
||||||
//File was deleted or unshared. We need to save content as new file anyway
|
//File was deleted or unshared. We need to save content as new file anyway
|
||||||
//Sorry, but for guests it would be lost :(
|
//Sorry, but for guests it would be lost :(
|
||||||
if (isset($uid)){
|
if ($this->uid){
|
||||||
$view = new \OC\Files\View('/' . $uid . '/files');
|
$view = new \OC\Files\View('/' . $this->uid . '/files');
|
||||||
|
|
||||||
$dir = \OCP\Config::getUserValue(\OCP\User::getUser(), 'documents', 'save_path', '');
|
$dir = \OCP\Config::getUserValue($this->uid, 'documents', 'save_path', '');
|
||||||
$path = Helper::getNewFileName($view, $dir . 'New Document.odt');
|
$path = Helper::getNewFileName($view, $dir . 'New Document.odt');
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,12 +190,13 @@ class SessionController extends Controller{
|
|||||||
|
|
||||||
$view->touch($path);
|
$view->touch($path);
|
||||||
}
|
}
|
||||||
\OCP\JSON::success();
|
$response = array('status'=>'success');
|
||||||
} catch (\Exception $e){
|
} catch (\Exception $e){
|
||||||
Helper::warnLog('Saving failed. Reason:' . $e->getMessage());
|
Helper::warnLog('Saving failed. Reason:' . $e->getMessage());
|
||||||
//\OCP\JSON::error(array('message'=>$e->getMessage()));
|
|
||||||
\OC_Response::setStatus(500);
|
\OC_Response::setStatus(500);
|
||||||
|
$response = array();
|
||||||
}
|
}
|
||||||
exit();
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
@ -487,11 +487,17 @@ var documentsMain = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
changeNick: function(memberId, name, node){
|
changeNick: function(memberId, name, node){
|
||||||
var url = OC.generateUrl('apps/documents/ajax/user/rename/{member_id}', {member_id: memberId});
|
var url = OC.generateUrl('apps/documents/ajax/user/rename');
|
||||||
$.post(
|
$.ajax({
|
||||||
url,
|
url: url,
|
||||||
{ name : name },
|
type: "POST",
|
||||||
function(result) {
|
data: JSON.stringify({
|
||||||
|
name : name,
|
||||||
|
memberId : memberId
|
||||||
|
}),
|
||||||
|
contentType: 'application/json; charset=utf-8',
|
||||||
|
dataType:"json",
|
||||||
|
success: function(result) {
|
||||||
if (result && result.status === 'error') {
|
if (result && result.status === 'error') {
|
||||||
if (result.message){
|
if (result.message){
|
||||||
documentsMain.UI.notify(result.message);
|
documentsMain.UI.notify(result.message);
|
||||||
@ -499,7 +505,7 @@ var documentsMain = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onNickChange: function(memberId, fullNameNode){
|
onNickChange: function(memberId, fullNameNode){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user