diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index d07033c49ff..3a1a95a66f2 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2946,6 +2946,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
diff --git a/idea/src/META-INF/plugin.xml.172 b/idea/src/META-INF/plugin.xml.172
index 0b6483e17f2..f11b9cd2837 100644
--- a/idea/src/META-INF/plugin.xml.172
+++ b/idea/src/META-INF/plugin.xml.172
@@ -2946,6 +2946,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
diff --git a/idea/src/META-INF/plugin.xml.173 b/idea/src/META-INF/plugin.xml.173
index 37720d79315..c0a5e4a529f 100644
--- a/idea/src/META-INF/plugin.xml.173
+++ b/idea/src/META-INF/plugin.xml.173
@@ -2944,6 +2944,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
diff --git a/idea/src/META-INF/plugin.xml.182 b/idea/src/META-INF/plugin.xml.182
index 329fc96bf59..a0a0d901209 100644
--- a/idea/src/META-INF/plugin.xml.182
+++ b/idea/src/META-INF/plugin.xml.182
@@ -2945,6 +2945,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
diff --git a/idea/src/META-INF/plugin.xml.as31 b/idea/src/META-INF/plugin.xml.as31
index 3b3cadef1c6..db1b77e1f00 100644
--- a/idea/src/META-INF/plugin.xml.as31
+++ b/idea/src/META-INF/plugin.xml.as31
@@ -2944,6 +2944,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
diff --git a/idea/src/META-INF/plugin.xml.as32 b/idea/src/META-INF/plugin.xml.as32
index 73934004f5c..0d2c6928645 100644
--- a/idea/src/META-INF/plugin.xml.as32
+++ b/idea/src/META-INF/plugin.xml.as32
@@ -2944,6 +2944,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
@@ -2971,6 +2974,8 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
index ecb56b9a008..3bf866732c9 100644
--- a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt
@@ -27,6 +27,8 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.Alarm
import com.intellij.util.io.HttpRequests
import com.intellij.util.text.VersionComparatorUtil
+import org.jetbrains.kotlin.idea.update.PluginUpdateVerifier
+import org.jetbrains.kotlin.idea.update.verify
import java.io.File
import java.io.IOException
import java.io.PrintWriter
@@ -46,6 +48,8 @@ sealed class PluginUpdateStatus {
class CheckFailed(val message: String, val detail: String? = null) : PluginUpdateStatus()
+ class Unverified(val message: String, val updateStatus: Update) : PluginUpdateStatus()
+
fun mergeWith(other: PluginUpdateStatus): PluginUpdateStatus {
if (other is Update) {
when (this) {
@@ -57,7 +61,7 @@ sealed class PluginUpdateStatus {
return other
}
}
- is PluginUpdateStatus.CheckFailed -> {
+ is CheckFailed, is Unverified -> {
// proceed to return this
}
}
@@ -150,9 +154,14 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos
lastUpdateStatus = updateStatus
checkQueued = false
+ if (updateStatus is PluginUpdateStatus.Update) {
+ updateStatus = verify(updateStatus)
+ }
+
if (updateStatus !is PluginUpdateStatus.CheckFailed) {
recordSuccessfulUpdateCheck()
}
+
ApplicationManager.getApplication().invokeLater({
callback(updateStatus)
}, ModalityState.any())
diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt
index ea2abf70c92..e8101bc181e 100644
--- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt
@@ -156,6 +156,11 @@ class KotlinUpdatesSettingsConfigurable : SearchableConfigurable, Configurable.N
is PluginUpdateStatus.CheckFailed ->
form.setUpdateStatus("Update check failed: ${pluginUpdateStatus.message}", false)
+
+ is PluginUpdateStatus.Unverified -> {
+ val version = pluginUpdateStatus.updateStatus.pluginDescriptor.version
+ form.setUpdateStatus("A new version $version is found. ${pluginUpdateStatus.message}", false)
+ }
}
false // do not auto-retry update check
diff --git a/idea/src/org/jetbrains/kotlin/idea/update/GooglePluginUpdateVerifier.kt.as32 b/idea/src/org/jetbrains/kotlin/idea/update/GooglePluginUpdateVerifier.kt.as32
new file mode 100644
index 00000000000..4456b06746e
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/update/GooglePluginUpdateVerifier.kt.as32
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.update
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor
+import org.intellij.lang.annotations.Language
+import java.util.*
+import javax.xml.bind.JAXBContext
+import javax.xml.bind.annotation.*
+import kotlin.collections.HashSet
+
+class GooglePluginUpdateVerifier : PluginUpdateVerifier() {
+ override fun verify(pluginDescriptor: IdeaPluginDescriptor): PluginVerifyResult? {
+ if (pluginDescriptor.pluginId.idString != "org.jetbrains.kotlin") {
+ return null
+ }
+
+ // val approvedKotlinVersions = fetchApprovedKotlinVersions()
+ // if (pluginDescriptor.version !in approvedKotlinVersions) {
+ // // TODO: Modify message
+ // return PluginVerifyResult.decline("Plugin is waiting for Google approve.")
+ // }
+
+ return PluginVerifyResult.accept()
+ }
+
+ private fun fetchApprovedKotlinVersions(): Set {
+ // TODO: Choose the host and do an actual request
+ // TODO: Fetching the full list each time might be time-consuming
+ // TODO: Process parsing and fetching errors
+
+ // TODO: Emulate long operation to check progress indicators. Remove after actual implementation is ready.
+ Thread.sleep(200)
+
+ @Language("XML")
+ val answer = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """.trimIndent()
+
+ val context = JAXBContext.newInstance(PluginCompatibility::class.java)
+ val unmarshaller = context.createUnmarshaller()
+ val pluginCompatibility = unmarshaller.unmarshal(answer.byteInputStream()) as PluginCompatibility
+
+ val approvedKotlinVersions = HashSet()
+ for (studioRelease in pluginCompatibility.releases()) {
+ for (ideaPlugin in studioRelease.plugins()) {
+ if (ideaPlugin.id == KOTLIN_PLUGIN_ID) {
+ val version = ideaPlugin.version
+ if (version != null) {
+ approvedKotlinVersions.add(version)
+ }
+ }
+ }
+ }
+
+ return approvedKotlinVersions
+ }
+
+ companion object {
+ private const val KOTLIN_PLUGIN_ID = "org.jetbrains.kotlin"
+
+ private fun PluginCompatibility.releases() = studioRelease ?: emptyArray()
+ private fun StudioRelease.plugins() = ideaPlugin ?: emptyArray()
+
+ @XmlRootElement(name = "plugin-compatibility")
+ @XmlAccessorType(XmlAccessType.FIELD)
+ class PluginCompatibility {
+ @XmlElement(name = "studio-release")
+ var studioRelease: Array? = null
+
+ override fun toString(): String {
+ return "PluginCompatibility(studioRelease=${Arrays.toString(studioRelease)})"
+ }
+ }
+
+ @XmlAccessorType(XmlAccessType.FIELD)
+ class StudioRelease {
+ @XmlAttribute(name = "until-build")
+ var untilBuild: String? = null
+ @XmlAttribute(name = "since-build")
+ var sinceBuild: String? = null
+ @XmlAttribute
+ var name: String? = null
+ @XmlAttribute
+ var channel: String? = null
+
+ @XmlElement(name = "idea-plugin")
+ var ideaPlugin: Array? = null
+
+ override fun toString(): String {
+ return "StudioRelease(" +
+ "untilBuild=$untilBuild, name=$name, ideaPlugin=${Arrays.toString(ideaPlugin)}, " +
+ "sinceBuild=$sinceBuild, channel=$channel" +
+ ")"
+ }
+ }
+
+ @XmlAccessorType(XmlAccessType.FIELD)
+ class IdeaPlugin {
+ @XmlAttribute
+ var id: String? = null
+ @XmlAttribute
+ var sha256: String? = null
+ @XmlAttribute
+ var channel: String? = null
+ @XmlAttribute
+ var version: String? = null
+
+ @XmlElement(name = "idea-version")
+ var ideaVersion: IdeaVersion? = null
+
+ override fun toString(): String {
+ return "IdeaPlugin(id=$id, sha256=$sha256, ideaVersion=$ideaVersion, channel=$channel, version=$version)"
+ }
+ }
+
+ @XmlAccessorType(XmlAccessType.FIELD)
+ class IdeaVersion {
+ @XmlAttribute(name = "until-build")
+ var untilBuild: String? = null
+ @XmlAttribute(name = "since-build")
+ var sinceBuild: String? = null
+
+ override fun toString(): String {
+ return "IdeaVersion(untilBuild=$untilBuild, sinceBuild=$sinceBuild)"
+ }
+ }
+ }
+}
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/update/PluginUpdateVerifier.kt b/idea/src/org/jetbrains/kotlin/idea/update/PluginUpdateVerifier.kt
new file mode 100644
index 00000000000..7e223c781d9
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/update/PluginUpdateVerifier.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.update
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor
+import com.intellij.openapi.extensions.ExtensionPointName
+
+abstract class PluginUpdateVerifier {
+ /**
+ * @param pluginDescriptor
+ * @return null means verifier is not responsible for the given plugin descriptor.
+ */
+ abstract fun verify(pluginDescriptor: IdeaPluginDescriptor): PluginVerifyResult?
+
+ companion object {
+ internal var EP_NAME = ExtensionPointName("org.jetbrains.kotlin.pluginUpdateVerifier")
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/update/PluginVerifyResult.kt b/idea/src/org/jetbrains/kotlin/idea/update/PluginVerifyResult.kt
new file mode 100644
index 00000000000..ecd96c0fe31
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/update/PluginVerifyResult.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.update
+
+class PluginVerifyResult {
+ var verified: Boolean = true
+
+ /**
+ * Short sentence explaining why plugin is available in JetBrains repository but can't be installed.
+ * Unapproved result should have a not-null declined message.
+ */
+ var declineMessage: String? = null
+
+ companion object {
+ fun decline(reason: String): PluginVerifyResult = PluginVerifyResult().apply {
+ declineMessage = reason
+ verified = false
+ }
+
+ fun accept() = PluginVerifyResult()
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/update/verify.kt b/idea/src/org/jetbrains/kotlin/idea/update/verify.kt
new file mode 100644
index 00000000000..19e3e6cf518
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/update/verify.kt
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.update
+
+import org.jetbrains.kotlin.idea.PluginUpdateStatus
+
+// Do an additional verification with PluginUpdateVerifier. Enabled only in AS 32.
+fun verify(updateStatus: PluginUpdateStatus.Update): PluginUpdateStatus {
+ return updateStatus
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/update/verify.kt.as32 b/idea/src/org/jetbrains/kotlin/idea/update/verify.kt.as32
new file mode 100644
index 00000000000..48b9630e851
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/update/verify.kt.as32
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.update
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor
+import org.jetbrains.kotlin.idea.PluginUpdateStatus
+
+// Do an additional verification with PluginUpdateVerifier. Enabled only in AS 32.
+fun verify(updateStatus: PluginUpdateStatus.Update): PluginUpdateStatus {
+ val pluginDescriptor: IdeaPluginDescriptor = updateStatus.pluginDescriptor
+ val pluginVerifiers = PluginUpdateVerifier.EP_NAME.extensions
+
+ val declineMessage = pluginVerifiers.asSequence()
+ .map { verifier ->
+ val verifyResult = verifier.verify(pluginDescriptor)
+ if (verifyResult != null && !verifyResult.verified) {
+ verifyResult.declineMessage
+ } else {
+ null
+ }
+ }
+ .firstOrNull { reason -> reason != null }
+
+ if (declineMessage != null) {
+ return PluginUpdateStatus.Unverified(declineMessage, updateStatus)
+ }
+
+ return updateStatus
+}
\ No newline at end of file