refactor PathUtil - it is a big difference now, whether we find sdk home from plugin or from compiler
This commit is contained in:
committed by
Evgeny Gerashchenko
parent
e5589e1037
commit
843fb8d7a9
@@ -24,15 +24,15 @@
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.highlighter.DeclarationHintSupport</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.OutdatedKotlinRuntimeNotification</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<module-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.project.K2JSModuleComponent</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.jet.plugin.OutdatedKotlinRuntimeNotification</implementation-class>
|
||||
</component>
|
||||
</module-components>
|
||||
|
||||
<actions>
|
||||
|
||||
@@ -14,19 +14,16 @@ package org.jetbrains.jet.plugin;/*
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||
import com.intellij.ide.plugins.PluginManager;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.AbstractProjectComponent;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleComponent;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
@@ -38,6 +35,7 @@ import com.intellij.util.text.VersionComparatorUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.plugin.quickfix.ConfigureKotlinLibraryNotificationProvider;
|
||||
import org.jetbrains.jet.plugin.util.PluginPathUtil;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
@@ -50,36 +48,40 @@ import java.util.jar.JarFile;
|
||||
* @author Evgeny Gerashchenko
|
||||
* @since 5/22/12
|
||||
*/
|
||||
public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent {
|
||||
public class OutdatedKotlinRuntimeNotification implements ModuleComponent {
|
||||
private static final String UNKNOWN_VERSION = "UNKNOWN";
|
||||
private static final String SUPPRESSED_PROPERTY_NAME = "oudtdated.runtime.suppressed.plugin.version";
|
||||
private static final String SUPPRESSED_PROPERTY_NAME_PATTERN = "oudtdated.runtime.suppressed.plugin.version[%s]";
|
||||
|
||||
public OutdatedKotlinRuntimeNotification(final Project project) {
|
||||
super(project);
|
||||
private final Module myModule;
|
||||
private final String mySuppressedPropertyName;
|
||||
|
||||
public OutdatedKotlinRuntimeNotification(final Module module) {
|
||||
myModule = module;
|
||||
mySuppressedPropertyName = String.format(SUPPRESSED_PROPERTY_NAME_PATTERN, module.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectOpened() {
|
||||
if (ApplicationManager.getApplication().isInternal()) return;
|
||||
String runtimeVersion = getRuntimeVersion();
|
||||
final String pluginVersion = getPluginVersion();
|
||||
String runtimeVersion = getRuntimeVersion(getKotlinRuntimeJar());
|
||||
if (runtimeVersion == null) return; // runtime is not present in project
|
||||
if ("@snapshot@".equals(pluginVersion)) return; // plugin is run from sources, can't compare versions
|
||||
final String sdkVersion = getRuntimeVersion(getRuntimeFromSdk());
|
||||
if (sdkVersion == null || "snapshot".equals(sdkVersion)) return; // plugin is run from sources, can't compare versions
|
||||
|
||||
// user already clicked suppress
|
||||
if (pluginVersion.equals(PropertiesComponent.getInstance(myProject).getValue(SUPPRESSED_PROPERTY_NAME))) return;
|
||||
if (sdkVersion.equals(PropertiesComponent.getInstance(myModule.getProject()).getValue(mySuppressedPropertyName))) return;
|
||||
|
||||
boolean isRuntimeOutdated = "snapshot".equals(runtimeVersion)
|
||||
|| UNKNOWN_VERSION.equals(runtimeVersion)
|
||||
|| runtimeVersion.startsWith("internal-") != pluginVersion.startsWith("internal-")
|
||||
|| VersionComparatorUtil.compare(pluginVersion, runtimeVersion) > 0;
|
||||
|| runtimeVersion.startsWith("internal-") != sdkVersion.startsWith("internal-")
|
||||
|| VersionComparatorUtil.compare(sdkVersion, runtimeVersion) > 0;
|
||||
|
||||
if (!isRuntimeOutdated) return;
|
||||
|
||||
String message = String.format("<p>Your version of Kotlin runtime library is %s, while plugin version is %s." +
|
||||
String message = String.format("<p>Your version of Kotlin runtime library in module \"%s\" is %s, while Kotlin SDK version in this module is %s." +
|
||||
" Runtime library should be updated to avoid compatibility problems.</p>" +
|
||||
"<p><a href=\"update\">Update Runtime</a> <a href=\"ignore\">Ignore</a></p>",
|
||||
UNKNOWN_VERSION.equals(runtimeVersion) ? "older than 0.1.2296" : runtimeVersion, pluginVersion);
|
||||
myModule.getName(), UNKNOWN_VERSION.equals(runtimeVersion) ? "older than 0.1.2296" : runtimeVersion, sdkVersion);
|
||||
Notifications.Bus.notify(new Notification("Outdated Kotlin Runtime", "Outdated Kotlin Runtime",
|
||||
message,
|
||||
NotificationType.WARNING, new NotificationListener() {
|
||||
@@ -90,7 +92,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
updateRuntime();
|
||||
}
|
||||
else if ("ignore".equals(event.getDescription())) {
|
||||
PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion);
|
||||
PropertiesComponent.getInstance(myModule.getProject()).setValue(mySuppressedPropertyName, sdkVersion);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
@@ -98,16 +100,17 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
notification.expire();
|
||||
}
|
||||
}
|
||||
}), myProject);
|
||||
}
|
||||
}), myModule.getProject());
|
||||
}
|
||||
|
||||
private void updateRuntime() {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
File runtimePath = PathUtil.getDefaultRuntimePath();
|
||||
File runtimePath = PluginPathUtil.getRuntimePath(myModule);
|
||||
if (runtimePath == null) {
|
||||
Messages.showErrorDialog(myProject, "kotlin-runtime.jar is not found. Make sure plugin is properly installed.",
|
||||
Messages.showErrorDialog(myModule.getProject(),
|
||||
"kotlin-runtime.jar is not found. Make sure plugin is properly installed.",
|
||||
"No Runtime Found");
|
||||
return;
|
||||
}
|
||||
@@ -130,8 +133,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getRuntimeVersion() {
|
||||
VirtualFile kotlinRuntimeJar = getKotlinRuntimeJar();
|
||||
private static String getRuntimeVersion(@Nullable final VirtualFile kotlinRuntimeJar) {
|
||||
if (kotlinRuntimeJar == null) return null;
|
||||
VirtualFile manifestFile = kotlinRuntimeJar.findFileByRelativePath(JarFile.MANIFEST_NAME);
|
||||
if (manifestFile != null) {
|
||||
@@ -145,7 +147,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
|
||||
@Nullable
|
||||
private VirtualFile getKotlinRuntimeJar() {
|
||||
LibraryTable table = ProjectLibraryTable.getInstance(myProject);
|
||||
LibraryTable table = ModuleRootManager.getInstance(myModule).getModifiableModel().getModuleLibraryTable();
|
||||
Library kotlinRuntime = table.getLibraryByName(ConfigureKotlinLibraryNotificationProvider.LIBRARY_NAME);
|
||||
if (kotlinRuntime != null) {
|
||||
for (VirtualFile root : kotlinRuntime.getFiles(OrderRootType.CLASSES)) {
|
||||
@@ -157,10 +159,27 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private VirtualFile getRuntimeFromSdk() {
|
||||
final File runtimePath = PluginPathUtil.getRuntimePath(myModule);
|
||||
return runtimePath == null ? null : PathUtil.jarFileOrDirectoryToVirtualFile(runtimePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moduleAdded() {}
|
||||
|
||||
@Override
|
||||
public void projectClosed() {}
|
||||
|
||||
@Override
|
||||
public void initComponent() {}
|
||||
|
||||
@Override
|
||||
public void disposeComponent() {}
|
||||
|
||||
@NotNull
|
||||
private static String getPluginVersion() {
|
||||
IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin"));
|
||||
assert plugin != null : "How can it be? Kotlin plugin is available, but its component is running. Complete nonsense.";
|
||||
return plugin.getVersion();
|
||||
@Override
|
||||
public String getComponentName() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,11 @@ package org.jetbrains.jet.plugin.compiler;
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import jet.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
import org.jetbrains.jet.plugin.sdk.KotlinSdkUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
|
||||
import static com.intellij.openapi.compiler.CompilerMessageCategory.ERROR;
|
||||
|
||||
@@ -37,7 +36,7 @@ public final class CompilerEnvironment {
|
||||
public static CompilerEnvironment getEnvironmentFor(@NotNull CompileContext compileContext, @NotNull Module module, boolean tests) {
|
||||
VirtualFile mainOutput = compileContext.getModuleOutputDirectory(module);
|
||||
final VirtualFile outputDir = tests ? compileContext.getModuleOutputDirectoryForTests(module) : mainOutput;
|
||||
File kotlinHome = PathUtil.getDefaultCompilerPath();
|
||||
File kotlinHome = KotlinSdkUtil.getSDKHomeFor(module);
|
||||
return new CompilerEnvironment(kotlinHome, outputDir);
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -55,7 +55,7 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.plugin.JetPluginUtil;
|
||||
import org.jetbrains.jet.plugin.sdk.KotlinSdkUtil;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
import org.jetbrains.jet.plugin.util.PluginPathUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
@@ -105,7 +105,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific
|
||||
return null;
|
||||
}
|
||||
|
||||
private Library findOrCreateRuntimeLibrary() {
|
||||
private Library findOrCreateRuntimeLibrary(@NotNull Module module) {
|
||||
LibraryTable table = ProjectLibraryTable.getInstance(myProject);
|
||||
Library kotlinRuntime = table.getLibraryByName(LIBRARY_NAME);
|
||||
if (kotlinRuntime != null) {
|
||||
@@ -116,7 +116,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific
|
||||
}
|
||||
}
|
||||
|
||||
File runtimePath = PathUtil.getDefaultRuntimePath();
|
||||
File runtimePath = PluginPathUtil.getRuntimePath(module);
|
||||
if (runtimePath == null) {
|
||||
Messages.showErrorDialog(myProject, "kotlin-runtime.jar is not found. Make sure plugin is properly installed.",
|
||||
"No Runtime Found");
|
||||
@@ -192,7 +192,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Library library = findOrCreateRuntimeLibrary();
|
||||
Library library = findOrCreateRuntimeLibrary(module);
|
||||
if (library != null) {
|
||||
ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
|
||||
if (model.findLibraryOrderEntry(library) == null) {
|
||||
@@ -214,7 +214,7 @@ public class ConfigureKotlinLibraryNotificationProvider implements EditorNotific
|
||||
/* package */ static void addJdkAnnotations(Module module) {
|
||||
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
|
||||
assert sdk != null;
|
||||
File annotationsIoFile = PathUtil.getJdkAnnotationsPath();
|
||||
File annotationsIoFile = PluginPathUtil.getJdkAnnotationsPath(module);
|
||||
if (annotationsIoFile != null) {
|
||||
VirtualFile jdkAnnotationsJar = LocalFileSystem.getInstance().findFileByIoFile(annotationsIoFile);
|
||||
if (jdkAnnotationsJar != null) {
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.intellij.psi.impl.PsiModificationTrackerImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.plugin.project.K2JSModuleComponent;
|
||||
import org.jetbrains.jet.plugin.util.PluginPathUtil;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
@@ -65,7 +66,7 @@ public final class JsModuleSetUp {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!copyJsLibFiles(rootDir)) return;
|
||||
if (!copyJsLibFiles(module, rootDir)) return;
|
||||
|
||||
setUpK2JSModuleComponent(module);
|
||||
setUpLibraryAsSourceLibrary(module, rootDir);
|
||||
@@ -108,9 +109,9 @@ public final class JsModuleSetUp {
|
||||
DaemonCodeAnalyzer.getInstance(module.getProject()).restart();
|
||||
}
|
||||
|
||||
private static boolean copyJsLibFiles(@NotNull File rootDir) {
|
||||
File jsLibJarPath = PathUtil.getDefaultJsLibJarPath();
|
||||
File jsLibJsPath = PathUtil.getDefaultJsLibJsPath();
|
||||
private static boolean copyJsLibFiles(@NotNull Module module, @NotNull File rootDir) {
|
||||
File jsLibJarPath = PluginPathUtil.getJsLibJarPath(module);
|
||||
File jsLibJsPath = PluginPathUtil.getJsLibJsPath(module);
|
||||
if ((jsLibJarPath == null) || (jsLibJsPath == null)) {
|
||||
notifyFailure("JavaScript library not found. Make sure plugin is installed properly.");
|
||||
return false;
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -46,7 +47,6 @@ public class KotlinSdkUtil {
|
||||
return new KotlinSdkProperties("");
|
||||
}
|
||||
};
|
||||
@NotNull private static final String KOTLIN_COMPILER_JAR = "kotlin-compiler.jar";
|
||||
@NotNull private static final String[] KOTLIN_COMPILER_JAR_ENTRY_NAMES = {
|
||||
"org/jetbrains/jet/cli/KotlinCompiler.class",
|
||||
"org/jetbrains/jet/cli/jvm/K2JVMCompiler.class"
|
||||
@@ -59,7 +59,7 @@ public class KotlinSdkUtil {
|
||||
}
|
||||
|
||||
private static boolean isSDKHome(@NotNull final File dir) {
|
||||
return dir.isDirectory() && isKotlinCompilerJar(getKotlinCompilerJar(dir));
|
||||
return dir.isDirectory() && isKotlinCompilerJar(PathUtil.getCompilerPath(dir));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -77,7 +77,8 @@ public class KotlinSdkUtil {
|
||||
}
|
||||
catch (final IOException e) {
|
||||
try {
|
||||
return getJarImplementationVersion(getKotlinCompilerJar(new File(sdkHomePath)));
|
||||
final File compilerJar = PathUtil.getCompilerPath(new File(sdkHomePath));
|
||||
return compilerJar == null ? null : getJarImplementationVersion(compilerJar);
|
||||
}
|
||||
catch (final IOException e1) {
|
||||
return null;
|
||||
@@ -97,15 +98,10 @@ public class KotlinSdkUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File getKotlinCompilerJar(@NotNull final File sdkHome) {
|
||||
return new File(new File(sdkHome, "lib"), KOTLIN_COMPILER_JAR);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String detectSDKVersion(@NotNull final List<VirtualFile> jars) {
|
||||
for (final VirtualFile jar : jars) {
|
||||
if (jar.getName().equals(KOTLIN_COMPILER_JAR) && isKotlinCompilerJar(new File(jar.getPath()))) {
|
||||
if (jar.getName().equals(PathUtil.KOTLIN_COMPILER_JAR) && isKotlinCompilerJar(new File(jar.getPath()))) {
|
||||
final VirtualFile libDir = jar.getParent();
|
||||
if (libDir != null) {
|
||||
final VirtualFile sdkHomeDir = libDir.getParent();
|
||||
@@ -119,22 +115,29 @@ public class KotlinSdkUtil {
|
||||
}
|
||||
|
||||
public static boolean isSDKConfiguredFor(@NotNull final Module module) {
|
||||
final GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false);
|
||||
return containsKotlinCompilerJar(FilenameIndex.getVirtualFilesByName(module.getProject(), KOTLIN_COMPILER_JAR, scope));
|
||||
return getSDKHomeFor(module) != null;
|
||||
}
|
||||
|
||||
private static boolean containsKotlinCompilerJar(@NotNull final Collection<VirtualFile> jars) {
|
||||
@Nullable
|
||||
public static File getSDKHomeFor(@NotNull final Module module) {
|
||||
final GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false);
|
||||
return findKotlinCompilerJar(FilenameIndex.getVirtualFilesByName(module.getProject(), PathUtil.KOTLIN_COMPILER_JAR, scope));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static File findKotlinCompilerJar(@NotNull final Collection<VirtualFile> jars) {
|
||||
for (final VirtualFile jar : jars) {
|
||||
if (isKotlinCompilerJar(new File(jar.getPath()))) {
|
||||
return true;
|
||||
final File file = new File(jar.getPath());
|
||||
if (isKotlinCompilerJar(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isKotlinCompilerJar(@NotNull final File jar) {
|
||||
private static boolean isKotlinCompilerJar(@Nullable final File jar) {
|
||||
try {
|
||||
return doIsKotlinCompilerJar(jar);
|
||||
return jar != null && doIsKotlinCompilerJar(jar);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.plugin.sdk.KotlinSdkUtil;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Maxim.Manuylov
|
||||
* Date: 20.05.12
|
||||
*/
|
||||
public class PluginPathUtil {
|
||||
private PluginPathUtil() {}
|
||||
|
||||
@Nullable
|
||||
public static File getRuntimePath(@NotNull final Module module) {
|
||||
return PathUtil.getRuntimePath(KotlinSdkUtil.getSDKHomeFor(module));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getJsLibJsPath(@NotNull final Module module) {
|
||||
return PathUtil.getJsLibJsPath(KotlinSdkUtil.getSDKHomeFor(module));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getJsLibJarPath(@NotNull final Module module) {
|
||||
return PathUtil.getJsLibJarPath(KotlinSdkUtil.getSDKHomeFor(module));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getJdkAnnotationsPath(@NotNull final Module module) {
|
||||
return PathUtil.getJdkAnnotationsPath(KotlinSdkUtil.getSDKHomeFor(module));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user