JS IDE: Replace indication file with module component
Introduce K2JSModuleComponent Partially replace references to project with references to module (Should be able to tell which facade to use based on what module we are in) #KT-2064 fixed
This commit is contained in:
@@ -29,6 +29,12 @@
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<module-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.project.K2JSModuleComponent</implementation-class>
|
||||
</component>
|
||||
</module-components>
|
||||
|
||||
<actions>
|
||||
<action id="Kotlin.NewFile" class="org.jetbrains.jet.plugin.actions.NewKotlinFileAction">
|
||||
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFromTemplate"/>
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.intellij.openapi.compiler.CompileScope;
|
||||
import com.intellij.openapi.compiler.TranslatingCompiler;
|
||||
import com.intellij.openapi.compiler.ex.CompileContextEx;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkType;
|
||||
import com.intellij.openapi.projectRoots.JdkUtil;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
@@ -61,8 +60,8 @@ public class JetCompiler implements TranslatingCompiler {
|
||||
if (!(virtualFile.getFileType() instanceof JetFileType)) {
|
||||
return false;
|
||||
}
|
||||
Project project = compileContext.getProject();
|
||||
if (project != null && JsModuleDetector.isJsProject(project)) {
|
||||
Module module = compileContext.getModuleByFile(virtualFile);
|
||||
if (module != null && JsModuleDetector.isJsModule(module)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -51,11 +51,11 @@ public final class K2JSCompiler implements TranslatingCompiler {
|
||||
if (!(file.getFileType() instanceof JetFileType)) {
|
||||
return false;
|
||||
}
|
||||
Project project = context.getProject();
|
||||
if (project == null) {
|
||||
Module module = context.getModuleByFile(file);
|
||||
if (module == null) {
|
||||
return false;
|
||||
}
|
||||
return JsModuleDetector.isJsProject(project);
|
||||
return JsModuleDetector.isJsModule(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,14 +142,14 @@ public final class K2JSCompiler implements TranslatingCompiler {
|
||||
}
|
||||
|
||||
private static void addLibLocationAndTarget(@NotNull Project project, @NotNull ArrayList<String> args) {
|
||||
Pair<String, String> data = JsModuleDetector.getLibLocationAndTargetForProject(project);
|
||||
if (data.first != null) {
|
||||
Pair<String, String> libLocationAndTarget = JsModuleDetector.getLibLocationAndTargetForProject(project);
|
||||
if (libLocationAndTarget.first != null) {
|
||||
args.add("-libzip");
|
||||
args.add(data.first);
|
||||
args.add(libLocationAndTarget.first);
|
||||
}
|
||||
if (data.second != null) {
|
||||
if (libLocationAndTarget.second != null) {
|
||||
args.add("-target");
|
||||
args.add(data.second);
|
||||
args.add(libLocationAndTarget.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ public final class K2JSRunnerUtils {
|
||||
|
||||
@NotNull
|
||||
public static Module getJsModule(@NotNull Project project) {
|
||||
//TODO Should not be there, we should know what module we are in
|
||||
Module[] modules = ModuleManager.getInstance(project).getModules();
|
||||
if (modules.length != 1) {
|
||||
throw new UnsupportedOperationException("Kotlin to JavaScript translator temporarily does not support multiple modules.");
|
||||
|
||||
@@ -71,7 +71,7 @@ public class JetSourceNavigationHelper {
|
||||
}
|
||||
final Project project = declaration.getProject();
|
||||
final List<JetFile> libraryFiles = findAllSourceFilesWhichContainIdentifier(declaration);
|
||||
BindingContext bindingContext = AnalyzerFacadeProvider.getAnalyzerFacadeForProject(project).analyzeFiles(
|
||||
BindingContext bindingContext = AnalyzerFacadeProvider.getAnalyzerFacadeForProject().analyzeFiles(
|
||||
project,
|
||||
libraryFiles,
|
||||
Collections.<AnalyzerScriptParameter>emptyList(),
|
||||
|
||||
@@ -16,31 +16,64 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.project;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.analyzer.AnalyzerFacade;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
|
||||
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
|
||||
|
||||
/**
|
||||
* @author Pavel Talanov
|
||||
*/
|
||||
public final class AnalyzerFacadeProvider {
|
||||
|
||||
private final static Logger LOG = Logger.getInstance(AnalyzerFacade.class);
|
||||
|
||||
private AnalyzerFacadeProvider() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalyzerFacade getAnalyzerFacadeForFile(@NotNull JetFile file) {
|
||||
return getAnalyzerFacadeForProject(file.getProject());
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile == null) {
|
||||
logErrorIfNotTests("No virtual file for " + file.getName() + " with text:\n" + file.getText());
|
||||
return getDefaultAnalyzerFacade();
|
||||
}
|
||||
Module moduleForFile = ProjectFileIndex.SERVICE.getInstance(file.getProject()).getModuleForFile(virtualFile);
|
||||
if (moduleForFile == null) {
|
||||
logErrorIfNotTests("File " + virtualFile.getPath() + " is not under any module. Cannot determine which facade to use.");
|
||||
return getDefaultAnalyzerFacade();
|
||||
}
|
||||
return getAnalyzerFacadeForModule(moduleForFile);
|
||||
}
|
||||
|
||||
private static void logErrorIfNotTests(@NotNull String message) {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
LOG.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AnalyzerFacade getAnalyzerFacadeForProject(@NotNull Project project) {
|
||||
if (JsModuleDetector.isJsProject(project)) {
|
||||
private static AnalyzerFacade getDefaultAnalyzerFacade() {
|
||||
LOG.info("Using default analyzer facade");
|
||||
return AnalyzerFacadeForJVM.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static AnalyzerFacade getAnalyzerFacadeForModule(@NotNull Module module) {
|
||||
if (JsModuleDetector.isJsModule(module)) {
|
||||
return JSAnalyzerFacadeForIDEA.INSTANCE;
|
||||
}
|
||||
return AnalyzerFacadeForJVM.INSTANCE;
|
||||
}
|
||||
|
||||
//TODO should remove all calls to this method
|
||||
@NotNull
|
||||
public static AnalyzerFacade getAnalyzerFacadeForProject() {
|
||||
return AnalyzerFacadeForJVM.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,77 +16,47 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.project;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Pavel Talanov
|
||||
* <p/>
|
||||
* This class has utility functions to determine whether the project (or module) is js project.
|
||||
*/
|
||||
public final class JsModuleDetector {
|
||||
|
||||
public static final String INDICATION_FILE_NAME = ".kotlin-js";
|
||||
|
||||
private JsModuleDetector() {
|
||||
}
|
||||
|
||||
public static boolean isJsProject(@NotNull Project project) {
|
||||
return findIndicationFileInContentRoots(project) != null;
|
||||
public static boolean isJsModule(@NotNull Module module) {
|
||||
return K2JSModuleComponent.getInstance(module).isJavaScriptModule();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Pair<String, String> getLibLocationAndTargetForProject(@NotNull Project project) {
|
||||
Module module = getJSModule(project);
|
||||
if (module == null) {
|
||||
return Pair.empty();
|
||||
}
|
||||
K2JSModuleComponent jsModuleComponent = K2JSModuleComponent.getInstance(module);
|
||||
String pathToJavaScriptLibrary = jsModuleComponent.getPathToJavaScriptLibrary();
|
||||
String basePath = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath();
|
||||
return Pair.create(basePath + pathToJavaScriptLibrary, jsModuleComponent.getEcmaVersion().toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile findIndicationFileInContentRoots(@NotNull Project project) {
|
||||
VirtualFile[] roots = ProjectRootManager.getInstance(project).getContentRoots();
|
||||
for (VirtualFile root : roots) {
|
||||
for (VirtualFile child : root.getChildren()) {
|
||||
if (child.getName().equals(INDICATION_FILE_NAME)) {
|
||||
return child;
|
||||
}
|
||||
private static Module getJSModule(@NotNull Project project) {
|
||||
Module[] modules = ModuleManager.getInstance(project).getModules();
|
||||
for (Module module : modules) {
|
||||
if (isJsModule(module)) {
|
||||
return module;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO: refactor
|
||||
@NotNull
|
||||
public static Pair<String, String> getLibLocationAndTargetForProject(@NotNull Project project) {
|
||||
VirtualFile indicationFile = findIndicationFileInContentRoots(project);
|
||||
Logger logger = Logger.getInstance(JsModuleDetector.class);
|
||||
if (indicationFile == null) {
|
||||
logger.error("Indication file not found for project " + project.getName());
|
||||
return Pair.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream stream = indicationFile.getInputStream();
|
||||
String text = FileUtil.loadTextAndClose(stream);
|
||||
if (text.isEmpty()) {
|
||||
logger.error("Indication file is corrupted for project " + project.getName());
|
||||
return Pair.empty();
|
||||
}
|
||||
String[] lines = text.split("\n");
|
||||
if (lines.length == 0) {
|
||||
logger.error("Indication file " + indicationFile.getPath() + "is empty");
|
||||
return Pair.empty();
|
||||
}
|
||||
String pathToLibFile = lines[0];
|
||||
String version = lines.length >= 2 ? lines[1] : null;
|
||||
String pathToIndicationFileLocation = indicationFile.getParent().getPath();
|
||||
return new Pair<String, String>(pathToIndicationFileLocation + "/" + pathToLibFile, version);
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Could not open file " + indicationFile.getPath());
|
||||
return Pair.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2010-2012 JetBrains s.r.o.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.project;
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleComponent;
|
||||
import com.intellij.openapi.roots.impl.storage.ClasspathStorage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.k2js.config.EcmaVersion;
|
||||
|
||||
/**
|
||||
* @author Pavel Talanov
|
||||
*/
|
||||
@State(
|
||||
name = "K2JSModule",
|
||||
storages = @Storage(
|
||||
id = ClasspathStorage.DEFAULT_STORAGE,
|
||||
file = "$MODULE_FILE$"
|
||||
)
|
||||
)
|
||||
public final class K2JSModuleComponent implements ModuleComponent, PersistentStateComponent<K2JSModuleComponent> {
|
||||
|
||||
@NotNull
|
||||
public static K2JSModuleComponent getInstance(@NotNull Module module) {
|
||||
return module.getComponent(K2JSModuleComponent.class);
|
||||
}
|
||||
|
||||
private boolean isJavaScriptModule;
|
||||
|
||||
@Nullable
|
||||
private String pathToJavaScriptLibrary;
|
||||
|
||||
@NotNull
|
||||
private EcmaVersion ecmaVersion;
|
||||
|
||||
@NotNull
|
||||
public EcmaVersion getEcmaVersion() {
|
||||
return ecmaVersion;
|
||||
}
|
||||
|
||||
public K2JSModuleComponent() {
|
||||
this.isJavaScriptModule = false;
|
||||
this.pathToJavaScriptLibrary = null;
|
||||
this.ecmaVersion = EcmaVersion.defaultVersion();
|
||||
}
|
||||
|
||||
public void setEcmaVersion(@NotNull EcmaVersion ecmaVersion) {
|
||||
this.ecmaVersion = ecmaVersion;
|
||||
}
|
||||
|
||||
public boolean isJavaScriptModule() {
|
||||
return isJavaScriptModule;
|
||||
}
|
||||
|
||||
public void setJavaScriptModule(boolean javaScriptModule) {
|
||||
isJavaScriptModule = javaScriptModule;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPathToJavaScriptLibrary() {
|
||||
return pathToJavaScriptLibrary;
|
||||
}
|
||||
|
||||
public void setPathToJavaScriptLibrary(@Nullable String pathToJavaScriptLibrary) {
|
||||
this.pathToJavaScriptLibrary = pathToJavaScriptLibrary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectOpened() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectClosed() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moduleAdded() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initComponent() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeComponent() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getComponentName() {
|
||||
return "Kotlin to JavaScript module configuration";
|
||||
}
|
||||
|
||||
@Override
|
||||
public K2JSModuleComponent getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(K2JSModuleComponent state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -56,7 +56,7 @@ import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.jetbrains.jet.plugin.project.JsModuleDetector.isJsProject;
|
||||
import static org.jetbrains.jet.plugin.project.JsModuleDetector.isJsModule;
|
||||
|
||||
public class ConfigureKotlinLibraryNotificationProvider implements EditorNotifications.Provider<EditorNotificationPanel> {
|
||||
private static final Key<EditorNotificationPanel> KEY = Key.create("configure.kotlin.library");
|
||||
@@ -86,7 +86,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific
|
||||
|
||||
if (isMavenModule(module)) return null;
|
||||
|
||||
if (isJsProject(myProject)) return null;
|
||||
if (isJsModule(module)) return null;
|
||||
|
||||
GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false);
|
||||
if (JavaPsiFacade.getInstance(myProject).findClass("jet.JetObject", scope) == null) {
|
||||
@@ -178,7 +178,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific
|
||||
}
|
||||
|
||||
private void setUpJSModule(@NotNull Module module) {
|
||||
JsModuleSetUp.doSetUpModule(module.getProject(), new Runnable() {
|
||||
JsModuleSetUp.doSetUpModule(module, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateNotifications();
|
||||
|
||||
@@ -20,14 +20,11 @@ import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.plugin.k2jsrun.K2JSRunnerUtils;
|
||||
import org.jetbrains.jet.plugin.project.JsModuleDetector;
|
||||
import org.jetbrains.jet.plugin.project.K2JSModuleComponent;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
@@ -45,13 +42,13 @@ public final class JsModuleSetUp {
|
||||
private JsModuleSetUp() {
|
||||
}
|
||||
|
||||
public static void doSetUpModule(@Nullable Project project, @NotNull Runnable continuation) {
|
||||
if (project == null) {
|
||||
notifyFailure("Internal error: Project not found.");
|
||||
public static void doSetUpModule(@Nullable Module module, @NotNull Runnable continuation) {
|
||||
if (module == null) {
|
||||
notifyFailure("Internal error: Module not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
File rootDir = getRootDir(project);
|
||||
File rootDir = getRootDir(module);
|
||||
if (!rootDir.isDirectory()) {
|
||||
notifyFailure("Internal error: Broken content root.");
|
||||
return;
|
||||
@@ -59,19 +56,11 @@ public final class JsModuleSetUp {
|
||||
|
||||
if (!copyJsLibFiles(rootDir)) return;
|
||||
|
||||
File file = new File(rootDir, JsModuleDetector.INDICATION_FILE_NAME);
|
||||
if (file.exists()) {
|
||||
notifyInfo("File " + file.getName() + " already exists.");
|
||||
// If the notification in the editor did not disappear due to
|
||||
// slow file system events, this will remove the notification
|
||||
// when the user clicks for the second time
|
||||
refreshRootDir(project, continuation);
|
||||
return;
|
||||
}
|
||||
K2JSModuleComponent jsModuleComponent = K2JSModuleComponent.getInstance(module);
|
||||
jsModuleComponent.setJavaScriptModule(true);
|
||||
jsModuleComponent.setPathToJavaScriptLibrary("/lib/" + PathUtil.JS_LIB_JAR_NAME);
|
||||
|
||||
createIndicationFile(file);
|
||||
|
||||
refreshRootDir(project, continuation);
|
||||
refreshRootDir(module, continuation);
|
||||
}
|
||||
|
||||
private static boolean copyJsLibFiles(@NotNull File rootDir) {
|
||||
@@ -85,19 +74,10 @@ public final class JsModuleSetUp {
|
||||
return doCopyJsLibFiles(Arrays.asList(jsLibJarPath, jsLibJsPath), rootDir);
|
||||
}
|
||||
|
||||
private static void refreshRootDir(@NotNull Project project, @NotNull Runnable continuation) {
|
||||
private static void refreshRootDir(@NotNull Module project, @NotNull Runnable continuation) {
|
||||
getContentRoot(project).refresh(true, true, continuation);
|
||||
}
|
||||
|
||||
private static void createIndicationFile(@NotNull File file) {
|
||||
try {
|
||||
FileUtil.writeToFile(file, "lib/" + PathUtil.JS_LIB_JAR_NAME);
|
||||
}
|
||||
catch (IOException e) {
|
||||
notifyFailure("Failed to write file " + file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doCopyJsLibFiles(@NotNull List<File> files, @NotNull File rootDir) {
|
||||
try {
|
||||
File lib = new File(rootDir, "lib");
|
||||
@@ -113,14 +93,13 @@ public final class JsModuleSetUp {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File getRootDir(@NotNull Project project) {
|
||||
VirtualFile contentRoot = getContentRoot(project);
|
||||
private static File getRootDir(@NotNull Module module) {
|
||||
VirtualFile contentRoot = getContentRoot(module);
|
||||
return new File(contentRoot.getPath());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static VirtualFile getContentRoot(@NotNull Project project) {
|
||||
Module module = K2JSRunnerUtils.getJsModule(project);
|
||||
private static VirtualFile getContentRoot(@NotNull Module module) {
|
||||
return ModuleRootManager.getInstance(module).getContentRoots()[0];
|
||||
}
|
||||
|
||||
@@ -129,10 +108,4 @@ public final class JsModuleSetUp {
|
||||
message,
|
||||
NotificationType.ERROR));
|
||||
}
|
||||
|
||||
public static void notifyInfo(@NotNull String message) {
|
||||
Notifications.Bus.notify(new Notification("Set Up Kotlin to JavaScript Module", "Information",
|
||||
message,
|
||||
NotificationType.INFORMATION));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class JetRunConfigurationProducer extends RuntimeConfigurationProducer im
|
||||
return null;
|
||||
}
|
||||
|
||||
if (JsModuleDetector.isJsProject(module.getProject())) {
|
||||
if (JsModuleDetector.isJsModule(module)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user