Refactoring: introduce parameter for getResolvedKotlinStdlibVersionByModuleData
This commit is contained in:
+10
-10
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.configure
|
package org.jetbrains.kotlin.android.configure
|
||||||
|
|
||||||
|
import com.android.builder.model.Library
|
||||||
import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys
|
import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys
|
||||||
import com.intellij.openapi.externalSystem.model.DataNode
|
import com.intellij.openapi.externalSystem.model.DataNode
|
||||||
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
import com.intellij.openapi.externalSystem.model.project.ModuleData
|
||||||
@@ -41,20 +42,19 @@ import org.gradle.tooling.model.UnsupportedMethodException
|
|||||||
import org.gradle.tooling.model.idea.IdeaProject
|
import org.gradle.tooling.model.idea.IdeaProject
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.KotlinGradleModelFacade
|
import org.jetbrains.kotlin.idea.inspections.gradle.KotlinGradleModelFacade
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findModulesByNames
|
import org.jetbrains.kotlin.idea.inspections.gradle.findModulesByNames
|
||||||
import com.android.builder.model.Library
|
|
||||||
|
|
||||||
class AndroidGradleModelFacade : KotlinGradleModelFacade {
|
class AndroidGradleModelFacade : KotlinGradleModelFacade {
|
||||||
override fun getResolvedKotlinStdlibVersionByModuleData(moduleData: DataNode<*>, libraryIds: List<String>): String? {
|
override fun getResolvedVersionByModuleData(moduleData: DataNode<*>, groupId: String, libraryIds: List<String>): String? {
|
||||||
ExternalSystemApiUtil
|
ExternalSystemApiUtil
|
||||||
.findAllRecursively(moduleData, AndroidProjectKeys.JAVA_MODULE_MODEL).asSequence()
|
.findAllRecursively(moduleData, AndroidProjectKeys.JAVA_MODULE_MODEL).asSequence()
|
||||||
.flatMap { it.data.jarLibraryDependencies.asSequence() }
|
.flatMap { it.data.jarLibraryDependencies.asSequence() }
|
||||||
.forEach {
|
.forEach {
|
||||||
val libraryName = it.name
|
val libraryName = it.name
|
||||||
for (libraryId in libraryIds) {
|
for (libraryId in libraryIds) {
|
||||||
val prefix = "$libraryId-"
|
val prefix = "$libraryId-"
|
||||||
if (libraryName.startsWith(prefix)) return libraryName.substringAfter(prefix)
|
if (libraryName.startsWith(prefix)) return libraryName.substringAfter(prefix)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -46,7 +46,7 @@ import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
|||||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedKotlinStdlibVersionByModuleData
|
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
||||||
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
||||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
||||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||||
@@ -168,7 +168,7 @@ fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind
|
|||||||
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
||||||
val detectedPlatforms =
|
val detectedPlatforms =
|
||||||
mavenLibraryIdToPlatform.entries
|
mavenLibraryIdToPlatform.entries
|
||||||
.filter { moduleNode.getResolvedKotlinStdlibVersionByModuleData(listOf(it.key)) != null }
|
.filter { moduleNode.getResolvedVersionByModuleData(KOTLIN_GROUP_ID, listOf(it.key)) != null }
|
||||||
.map { it.value }.distinct()
|
.map { it.value }.distinct()
|
||||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
|||||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedKotlinStdlibVersionByModuleData
|
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
||||||
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
import org.jetbrains.kotlin.idea.roots.migrateNonJvmSourceFolders
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
||||||
@@ -180,7 +180,7 @@ fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind
|
|||||||
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
||||||
val detectedPlatforms =
|
val detectedPlatforms =
|
||||||
mavenLibraryIdToPlatform.entries
|
mavenLibraryIdToPlatform.entries
|
||||||
.filter { moduleNode.getResolvedKotlinStdlibVersionByModuleData(listOf(it.key)) != null }
|
.filter { moduleNode.getResolvedVersionByModuleData(KOTLIN_GROUP_ID, listOf(it.key)) != null }
|
||||||
.map { it.value }.distinct()
|
.map { it.value }.distinct()
|
||||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
|||||||
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
import org.jetbrains.kotlin.idea.inspections.gradle.findAll
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedKotlinStdlibVersionByModuleData
|
import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedVersionByModuleData
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
|
||||||
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
|
||||||
@@ -179,7 +179,7 @@ fun detectPlatformByPlugin(moduleNode: DataNode<ModuleData>): TargetPlatformKind
|
|||||||
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
private fun detectPlatformByLibrary(moduleNode: DataNode<ModuleData>): TargetPlatformKind<*>? {
|
||||||
val detectedPlatforms =
|
val detectedPlatforms =
|
||||||
mavenLibraryIdToPlatform.entries
|
mavenLibraryIdToPlatform.entries
|
||||||
.filter { moduleNode.getResolvedKotlinStdlibVersionByModuleData(listOf(it.key)) != null }
|
.filter { moduleNode.getResolvedVersionByModuleData(KOTLIN_GROUP_ID, listOf(it.key)) != null }
|
||||||
.map { it.value }.distinct()
|
.map { it.value }.distinct()
|
||||||
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common }
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -61,10 +61,9 @@ class DeprecatedGradleDependencyInspection : GradleBaseInspection() {
|
|||||||
val libMarker = outdatedInfo.old.gradleMarker
|
val libMarker = outdatedInfo.old.gradleMarker
|
||||||
|
|
||||||
if (dependencyText.contains(libMarker)) {
|
if (dependencyText.contains(libMarker)) {
|
||||||
// Should be generified for any library, not exactly Kotlin stdlib
|
|
||||||
val libVersion =
|
val libVersion =
|
||||||
DifferentStdlibGradleVersionInspection.getResolvedKotlinStdlibVersion(
|
DifferentStdlibGradleVersionInspection.getResolvedLibVersion(
|
||||||
dependencyStatement.containingFile, listOf(outdatedInfo.old.name)
|
dependencyStatement.containingFile, outdatedInfo.old.groupId, listOf(outdatedInfo.old.name)
|
||||||
) ?: libraryVersionFromOrderEntry(dependencyStatement.containingFile, outdatedInfo.old.name)
|
) ?: libraryVersionFromOrderEntry(dependencyStatement.containingFile, outdatedInfo.old.name)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+12
-26
@@ -20,27 +20,26 @@ import com.intellij.openapi.externalSystem.model.DataNode
|
|||||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||||
import com.intellij.openapi.roots.ProjectRootManager
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.KOTLIN_GROUP_ID
|
||||||
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
|
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
|
||||||
import org.jetbrains.kotlin.idea.versions.*
|
import org.jetbrains.kotlin.idea.versions.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.plugins.gradle.codeInspection.GradleBaseInspection
|
import org.jetbrains.plugins.gradle.codeInspection.GradleBaseInspection
|
||||||
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor
|
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase
|
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement
|
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
|
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
|
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
|
||||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression
|
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
|
class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
|
||||||
override fun buildVisitor(): BaseInspectionVisitor = MyVisitor(
|
override fun buildVisitor(): BaseInspectionVisitor = MyVisitor(
|
||||||
listOf(MAVEN_STDLIB_ID, MAVEN_STDLIB_ID_JRE7, MAVEN_STDLIB_ID_JDK7, MAVEN_STDLIB_ID_JRE8, MAVEN_STDLIB_ID_JDK8)
|
KOTLIN_GROUP_ID, listOf(MAVEN_STDLIB_ID, MAVEN_STDLIB_ID_JRE7, MAVEN_STDLIB_ID_JDK7, MAVEN_STDLIB_ID_JRE8, MAVEN_STDLIB_ID_JDK8)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun buildErrorString(vararg args: Any) =
|
override fun buildErrorString(vararg args: Any) =
|
||||||
"Plugin version (${args[0]}) is not the same as library version (${args[1]})"
|
"Plugin version (${args[0]}) is not the same as library version (${args[1]})"
|
||||||
|
|
||||||
private abstract class VersionFinder(private val libraryIds: List<String>) : KotlinGradleInspectionVisitor() {
|
private abstract class VersionFinder(private val groupId: String, private val libraryIds: List<String>) :
|
||||||
|
KotlinGradleInspectionVisitor() {
|
||||||
protected abstract fun onFound(stdlibVersion: String, stdlibStatement: GrCallExpression)
|
protected abstract fun onFound(stdlibVersion: String, stdlibStatement: GrCallExpression)
|
||||||
|
|
||||||
override fun visitClosure(closure: GrClosableBlock) {
|
override fun visitClosure(closure: GrClosableBlock) {
|
||||||
@@ -52,13 +51,13 @@ class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
|
|||||||
if (dependenciesCall.parent !is PsiFile) return
|
if (dependenciesCall.parent !is PsiFile) return
|
||||||
|
|
||||||
val stdlibStatement = findLibraryStatement(closure, "org.jetbrains.kotlin", libraryIds) ?: return
|
val stdlibStatement = findLibraryStatement(closure, "org.jetbrains.kotlin", libraryIds) ?: return
|
||||||
val stdlibVersion = getResolvedKotlinStdlibVersion(closure.containingFile, libraryIds) ?: return
|
val stdlibVersion = getResolvedLibVersion(closure.containingFile, groupId, libraryIds) ?: return
|
||||||
|
|
||||||
onFound(stdlibVersion, stdlibStatement)
|
onFound(stdlibVersion, stdlibStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class MyVisitor(libraryIds: List<String>) : VersionFinder(libraryIds) {
|
private inner class MyVisitor(groupId: String, libraryIds: List<String>) : VersionFinder(groupId, libraryIds) {
|
||||||
override fun onFound(stdlibVersion: String, stdlibStatement: GrCallExpression) {
|
override fun onFound(stdlibVersion: String, stdlibStatement: GrCallExpression) {
|
||||||
val gradlePluginVersion = getResolvedKotlinGradleVersion(stdlibStatement.containingFile)
|
val gradlePluginVersion = getResolvedKotlinGradleVersion(stdlibStatement.containingFile)
|
||||||
|
|
||||||
@@ -69,33 +68,20 @@ class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getKotlinStdlibVersions(gradleFile: GroovyFileBase, libraryId: List<String>): Collection<String> {
|
|
||||||
val versions = LinkedHashSet<String>()
|
|
||||||
val visitor = object : VersionFinder(libraryId) {
|
|
||||||
override fun visitElement(element: GroovyPsiElement) {
|
|
||||||
element.acceptChildren(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFound(stdlibVersion: String, stdlibStatement: GrCallExpression) {
|
|
||||||
versions += stdlibVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gradleFile.accept(visitor)
|
|
||||||
return versions
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findLibraryStatement(closure: GrClosableBlock, libraryGroup: String, libraryIds: List<String>): GrCallExpression? {
|
private fun findLibraryStatement(closure: GrClosableBlock, libraryGroup: String, libraryIds: List<String>): GrCallExpression? {
|
||||||
return GradleHeuristicHelper.findStatementWithPrefixes(closure, PRODUCTION_DEPENDENCY_STATEMENTS).firstOrNull { statement ->
|
return GradleHeuristicHelper.findStatementWithPrefixes(closure, PRODUCTION_DEPENDENCY_STATEMENTS).firstOrNull { statement ->
|
||||||
libraryIds.any { it in statement.text } && statement.text.contains(libraryGroup)
|
libraryIds.any { it in statement.text } && statement.text.contains(libraryGroup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getResolvedKotlinStdlibVersion(file: PsiFile, libraryIds: List<String>): String? {
|
fun getResolvedLibVersion(file: PsiFile, groupId: String, libraryIds: List<String>): String? {
|
||||||
val projectStructureNode = findGradleProjectStructure(file) ?: return null
|
val projectStructureNode = findGradleProjectStructure(file) ?: return null
|
||||||
val module = ProjectRootManager.getInstance(file.project).fileIndex.getModuleForFile(file.virtualFile) ?: return null
|
val module = ProjectRootManager.getInstance(file.project).fileIndex.getModuleForFile(file.virtualFile) ?: return null
|
||||||
|
|
||||||
for (moduleData in projectStructureNode.findAll(ProjectKeys.MODULE).filter { it.data.internalName == module.name }) {
|
for (moduleData in projectStructureNode.findAll(ProjectKeys.MODULE).filter { it.data.internalName == module.name }) {
|
||||||
moduleData.node.getResolvedKotlinStdlibVersionByModuleData(libraryIds)?.let { return it }
|
moduleData.node.getResolvedVersionByModuleData(groupId, libraryIds)?.let {
|
||||||
|
return it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -103,8 +89,8 @@ class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun DataNode<*>.getResolvedKotlinStdlibVersionByModuleData(libraryIds: List<String>): String? {
|
internal fun DataNode<*>.getResolvedVersionByModuleData(groupId: String, libraryIds: List<String>): String? {
|
||||||
return KotlinGradleModelFacade.EP_NAME.extensions.asSequence()
|
return KotlinGradleModelFacade.EP_NAME.extensions.asSequence()
|
||||||
.mapNotNull { it.getResolvedKotlinStdlibVersionByModuleData(this, libraryIds) }
|
.mapNotNull { it.getResolvedVersionByModuleData(this, groupId, libraryIds) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
+3
-3
@@ -30,15 +30,15 @@ interface KotlinGradleModelFacade {
|
|||||||
val EP_NAME: ExtensionPointName<KotlinGradleModelFacade> = ExtensionPointName.create("org.jetbrains.kotlin.gradleModelFacade")
|
val EP_NAME: ExtensionPointName<KotlinGradleModelFacade> = ExtensionPointName.create("org.jetbrains.kotlin.gradleModelFacade")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getResolvedKotlinStdlibVersionByModuleData(moduleData: DataNode<*>, libraryIds: List<String>): String?
|
fun getResolvedVersionByModuleData(moduleData: DataNode<*>, groupId: String, libraryIds: List<String>): String?
|
||||||
fun getDependencyModules(ideModule: DataNode<ModuleData>, gradleIdeaProject: IdeaProject): Collection<DataNode<ModuleData>>
|
fun getDependencyModules(ideModule: DataNode<ModuleData>, gradleIdeaProject: IdeaProject): Collection<DataNode<ModuleData>>
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultGradleModelFacade : KotlinGradleModelFacade {
|
class DefaultGradleModelFacade : KotlinGradleModelFacade {
|
||||||
override fun getResolvedKotlinStdlibVersionByModuleData(moduleData: DataNode<*>, libraryIds: List<String>): String? {
|
override fun getResolvedVersionByModuleData(moduleData: DataNode<*>, groupId: String, libraryIds: List<String>): String? {
|
||||||
for (libraryDependencyData in ExternalSystemApiUtil.findAllRecursively(moduleData, ProjectKeys.LIBRARY_DEPENDENCY)) {
|
for (libraryDependencyData in ExternalSystemApiUtil.findAllRecursively(moduleData, ProjectKeys.LIBRARY_DEPENDENCY)) {
|
||||||
for (libraryId in libraryIds) {
|
for (libraryId in libraryIds) {
|
||||||
val libraryNameMarker = "org.jetbrains.kotlin:$libraryId:"
|
val libraryNameMarker = "$groupId:$libraryId:"
|
||||||
if (libraryDependencyData.data.externalName.startsWith(libraryNameMarker)) {
|
if (libraryDependencyData.data.externalName.startsWith(libraryNameMarker)) {
|
||||||
return libraryDependencyData.data.externalName.substringAfter(libraryNameMarker)
|
return libraryDependencyData.data.externalName.substringAfter(libraryNameMarker)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user