mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[plugins] IJPL-249806 Missing project-trust gate lets Plugins Advertiser query Marketplace pre-trust
(cherry picked from commit 00b015bb260b66f11815270962173175dfbbf3bf) IJ-CR-214792 GitOrigin-RevId: 6ae16ea77313cb5e6ea32b563fc125c9c67a3b2b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8118f673a3
commit
6748fee73d
@@ -1896,7 +1896,7 @@ plugins.advertiser.action.try.ultimate=Try {0}
|
||||
plugins.advertiser.action.ignore.ultimate=Dismiss
|
||||
plugins.advertiser.no.suggested.plugins=No suggested plugins found
|
||||
plugins.advertiser.no.compatible.plugins.found=No compatible plugins found
|
||||
plugins.advertiser.check.progress=Checking suggested plugins for project
|
||||
plugins.advertiser.check.progress=Checking Suggested Plugins for Project
|
||||
plugins.advertiser.ultimate.features.detected=Features covered by Ultimate Subscription {0} are detected
|
||||
plugins.advertiser.missing.feature={0,choice,1#Plugin|2#Plugins} {4} supporting {1} ''{2}'' {0,choice,1#is|2#are} currently {3,choice,0#disabled|1#not installed}.
|
||||
plugins.advertiser.missing.features={0,choice,1#Plugin|2#Plugins} {3} supporting features ({1}) {0,choice,1#is|2#are} currently {2,choice,0#disabled|1#not installed}.
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ package com.intellij.ide.plugins.advertiser
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.platform.settings.CacheTag
|
||||
import com.intellij.platform.settings.Setting
|
||||
import com.intellij.platform.settings.objectSerializer
|
||||
@@ -21,6 +22,7 @@ import org.jetbrains.annotations.ApiStatus
|
||||
class PluginFeatureCacheService {
|
||||
companion object {
|
||||
fun getInstance(): PluginFeatureCacheService = service()
|
||||
suspend fun getInstanceAsync(): PluginFeatureCacheService = serviceAsync()
|
||||
}
|
||||
|
||||
private val factory = settingDescriptorFactory(PluginManagerCore.CORE_ID)
|
||||
|
||||
+10
-9
@@ -2,23 +2,24 @@
|
||||
package com.intellij.openapi.updateSettings.impl.pluginsAdvertisement
|
||||
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.ide.trustedProjects.TrustedProjects
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
|
||||
|
||||
internal class CheckSuggestedPluginsAction : DumbAwareAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return
|
||||
|
||||
runWithModalProgressBlocking(project, IdeBundle.message("plugins.advertiser.check.progress")) {
|
||||
PluginsAdvertiserStartupActivity().checkSuggestedPlugins(project = project, includeIgnored = true)
|
||||
}
|
||||
}
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.isEnabledAndVisible = e.project != null
|
||||
e.presentation.isEnabledAndVisible = e.project?.let { TrustedProjects.isProjectTrusted(it) } ?: false
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project?.takeIf { TrustedProjects.isProjectTrusted(it) } ?: return
|
||||
|
||||
runWithModalProgressBlocking(project, IdeBundle.message("plugins.advertiser.check.progress")) {
|
||||
checkSuggestedPlugins(project = project, includeIgnored = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-8
@@ -10,6 +10,7 @@ import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.ide.plugins.advertiser.PluginData
|
||||
import com.intellij.ide.plugins.marketplace.MarketplaceRequests
|
||||
import com.intellij.ide.plugins.pluginRequiresUltimatePluginButItsDisabled
|
||||
import com.intellij.ide.trustedProjects.TrustedProjects
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
@@ -36,6 +37,7 @@ import com.intellij.ui.EditorNotificationPanel
|
||||
import com.intellij.ui.EditorNotificationProvider
|
||||
import com.intellij.ui.EditorNotifications
|
||||
import com.intellij.ui.HyperlinkLabel
|
||||
import com.intellij.util.application
|
||||
import fleet.util.Either
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -56,14 +58,14 @@ import javax.swing.JLabel
|
||||
class PluginAdvertiserEditorNotificationProvider : EditorNotificationProvider, DumbAware {
|
||||
|
||||
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
|
||||
val app = ApplicationManager.getApplication()
|
||||
if (app.isUnitTestMode || app.isHeadlessEnvironment || tryUltimateIsDisabled()) {
|
||||
if (!TrustedProjects.isProjectTrusted(project)) return null
|
||||
|
||||
if (application.isHeadlessEnvironment || tryUltimateIsDisabled()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val providedSuggestion = SUGGESTION_EP_NAME.extensionList.asSequence()
|
||||
.mapNotNull { it.getSuggestion(project, file) }
|
||||
.firstOrNull()
|
||||
val providedSuggestion = SUGGESTION_EP_NAME.extensionList
|
||||
.firstNotNullOfOrNull { it.getSuggestion(project, file) }
|
||||
|
||||
val suggestionChoice = getSuggestionData(project = project,
|
||||
activeProductCode = service<ApplicationInfo>().build.productCode,
|
||||
@@ -307,8 +309,7 @@ private fun logSuggestionShown(project: Project, pluginIds: Collection<PluginId>
|
||||
}
|
||||
|
||||
private fun logSuggestedProducts(project: Project, suggestedIdes: List<SuggestedIde>) {
|
||||
for (ide in suggestedIdes) {
|
||||
val productCode = ide.productCode
|
||||
for ((_, productCode) in suggestedIdes) {
|
||||
if (!loggedIdeSuggestions.contains(productCode)) {
|
||||
FUSEventSource.EDITOR.logIdeSuggested(project, productCode)
|
||||
loggedIdeSuggestions.add(productCode)
|
||||
@@ -332,7 +333,7 @@ private fun getSuggestionData(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSuggestionDataByDetector(project: Project, suggestion: PluginAdvertisedByFileContent): AdvertiserSuggestion? {
|
||||
private fun getSuggestionDataByDetector(project: Project, suggestion: PluginAdvertisedByFileContent): AdvertiserSuggestion {
|
||||
val implementationName = "${FILE_HANDLER_KIND}:${suggestion.fileHandler.id}"
|
||||
|
||||
return AdvertiserSuggestion(
|
||||
|
||||
+110
-111
@@ -12,116 +12,43 @@ import com.intellij.ide.plugins.advertiser.PluginFeatureMap
|
||||
import com.intellij.ide.plugins.marketplace.MarketplaceRequests
|
||||
import com.intellij.ide.plugins.newui.PluginNodeModelBuilderFactory
|
||||
import com.intellij.ide.plugins.newui.PluginUiModel
|
||||
import com.intellij.ide.trustedProjects.TrustedProjects
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.rethrowControlFlowException
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.progress.runBlockingMaybeCancellable
|
||||
import com.intellij.openapi.extensions.ExtensionNotApplicableException
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.util.registry.RegistryManager
|
||||
import com.intellij.ui.EditorNotifications
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.intellij.util.io.computeDetached
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
private val isTestingMode : Boolean by lazy {
|
||||
private fun isTestingMode(): Boolean {
|
||||
val application = ApplicationManager.getApplication()
|
||||
application.isUnitTestMode || application.isHeadlessEnvironment || SystemProperties.getBooleanProperty("idea.is.playback", false)
|
||||
return application.isHeadlessEnvironment || SystemProperties.getBooleanProperty("idea.is.playback", false)
|
||||
}
|
||||
|
||||
internal class PluginsAdvertiserStartupActivity : ProjectActivity {
|
||||
|
||||
suspend fun checkSuggestedPlugins(project: Project, includeIgnored: Boolean) {
|
||||
if (isTestingMode) {
|
||||
return
|
||||
}
|
||||
|
||||
val customPlugins = computeDetached { RepositoryHelper.loadPluginsFromCustomRepositories(null, PluginNodeModelBuilderFactory) }
|
||||
|
||||
coroutineContext.ensureActive()
|
||||
|
||||
val extensionService = PluginFeatureCacheService.getInstance()
|
||||
val oldExtensions = extensionService.extensions.get()
|
||||
|
||||
val pluginAdvertiserService = PluginAdvertiserService.getInstance(project)
|
||||
pluginAdvertiserService.collectDependencyUnknownFeatures(includeIgnored)
|
||||
val unknownFeatures = UnknownFeaturesCollector.getInstance(project).unknownFeatures
|
||||
|
||||
if (oldExtensions != null && unknownFeatures.isEmpty()) {
|
||||
if (includeIgnored) {
|
||||
coroutineContext.ensureActive()
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
getPluginSuggestionNotificationGroup()
|
||||
.createNotification(IdeBundle.message("plugins.advertiser.no.suggested.plugins"), NotificationType.INFORMATION)
|
||||
.setDisplayId("advertiser.no.plugins")
|
||||
.notify(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val customPluginIds = customPlugins.map { it.pluginId.idString }.toSet()
|
||||
if (oldExtensions == null
|
||||
|| oldExtensions.isOutdated
|
||||
|| includeIgnored) {
|
||||
@Suppress("DEPRECATION")
|
||||
extensionService.extensions.set(PluginFeatureMap(
|
||||
featureMap = getFeatureMapFromMarketPlace(customPluginIds = customPluginIds, featureType = "com.intellij.fileTypeFactory"),
|
||||
lastUpdateTime = if (oldExtensions != null) System.currentTimeMillis() else 0L,
|
||||
))
|
||||
coroutineContext.ensureActive()
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
|
||||
val oldDependencies = extensionService.dependencies.get()
|
||||
if (oldDependencies == null
|
||||
|| oldDependencies.isOutdated
|
||||
|| includeIgnored) {
|
||||
extensionService.dependencies.set(PluginFeatureMap(
|
||||
featureMap = getFeatureMapFromMarketPlace(customPluginIds = customPluginIds, featureType = DEPENDENCY_SUPPORT_FEATURE),
|
||||
lastUpdateTime = if (oldDependencies != null) System.currentTimeMillis() else 0L,
|
||||
))
|
||||
}
|
||||
|
||||
// update information about file handlers when forced
|
||||
if (includeIgnored && PluginAdvertiserExtensionsStateService.getInstance().updateCompatibleFileHandlers()) {
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
|
||||
coroutineContext.ensureActive()
|
||||
|
||||
if (unknownFeatures.isNotEmpty()) {
|
||||
pluginAdvertiserService.run(
|
||||
customPlugins = customPlugins,
|
||||
unknownFeatures = unknownFeatures,
|
||||
includeIgnored = includeIgnored
|
||||
)
|
||||
}
|
||||
|
||||
notifyUnbundledPlugins(project)
|
||||
}
|
||||
catch (e: CancellationException) {
|
||||
throw e
|
||||
}
|
||||
catch (e: Exception) {
|
||||
if (e !is ControlFlowException) {
|
||||
thisLogger().info(e)
|
||||
}
|
||||
init {
|
||||
if (isTestingMode()) {
|
||||
throw ExtensionNotApplicableException.create()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun execute(project: Project) {
|
||||
if (!Registry.`is`("ide.show.plugin.suggestions.on.open", true)) {
|
||||
if (!RegistryManager.getInstanceAsync().`is`("ide.show.plugin.suggestions.on.open")) {
|
||||
thisLogger().debug("Plugin advertiser plugin suggestions disabled on open")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,41 +56,113 @@ internal class PluginsAdvertiserStartupActivity : ProjectActivity {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun findSuggestedPlugins(project: Project, customRepositories: Map<String, List<PluginUiModel>>): List<PluginUiModel> {
|
||||
return runBlockingMaybeCancellable {
|
||||
if (isTestingMode) {
|
||||
return@runBlockingMaybeCancellable emptyList()
|
||||
private val LOG = logger<PluginsAdvertiserStartupActivity>()
|
||||
|
||||
internal suspend fun checkSuggestedPlugins(project: Project, includeIgnored: Boolean) {
|
||||
if (!TrustedProjects.isProjectTrusted(project)) return
|
||||
|
||||
LOG.debug("Checking plugin advertiser plugin suggestions")
|
||||
|
||||
val customPlugins = computeDetached { RepositoryHelper.loadPluginsFromCustomRepositories(null, PluginNodeModelBuilderFactory) }
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
val extensionService = PluginFeatureCacheService.getInstanceAsync()
|
||||
val oldExtensions = extensionService.extensions.get()
|
||||
|
||||
val pluginAdvertiserService = PluginAdvertiserService.getInstance(project)
|
||||
pluginAdvertiserService.collectDependencyUnknownFeatures(includeIgnored)
|
||||
val unknownFeatures = project.serviceAsync<UnknownFeaturesCollector>().unknownFeatures
|
||||
|
||||
if (oldExtensions != null && unknownFeatures.isEmpty()) {
|
||||
if (includeIgnored) {
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
withContext(Dispatchers.EDT) {
|
||||
getPluginSuggestionNotificationGroup()
|
||||
.createNotification(IdeBundle.message("plugins.advertiser.no.suggested.plugins"), NotificationType.INFORMATION)
|
||||
.setDisplayId("advertiser.no.plugins")
|
||||
.notify(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val customPlugins = ArrayList<PluginUiModel>()
|
||||
for (value in customRepositories.values) {
|
||||
customPlugins.addAll(value)
|
||||
}
|
||||
|
||||
val pluginAdvertiserService = project.serviceAsync<PluginAdvertiserService>()
|
||||
pluginAdvertiserService.collectDependencyUnknownFeatures(true)
|
||||
|
||||
try {
|
||||
val customPluginIds = customPlugins.map { it.pluginId.idString }.toSet()
|
||||
val extensionService = serviceAsync<PluginFeatureCacheService>()
|
||||
val oldDependencies = extensionService.dependencies.get()
|
||||
extensionService.dependencies.set(PluginFeatureMap(
|
||||
getFeatureMapFromMarketPlace(customPluginIds, DEPENDENCY_SUPPORT_FEATURE),
|
||||
if (oldDependencies != null) System.currentTimeMillis() else 0L,
|
||||
))
|
||||
val unknownFeatures = UnknownFeaturesCollector.getInstance(project).unknownFeatures
|
||||
if (unknownFeatures.isNotEmpty()) {
|
||||
return@runBlockingMaybeCancellable pluginAdvertiserService.fetch(customPlugins, unknownFeatures, true)
|
||||
if (oldExtensions == null
|
||||
|| oldExtensions.isOutdated
|
||||
|| includeIgnored) {
|
||||
@Suppress("DEPRECATION")
|
||||
extensionService.extensions.set(PluginFeatureMap(
|
||||
featureMap = getFeatureMapFromMarketPlace(customPluginIds = customPluginIds, featureType = "com.intellij.fileTypeFactory"),
|
||||
lastUpdateTime = if (oldExtensions != null) System.currentTimeMillis() else 0L,
|
||||
))
|
||||
currentCoroutineContext().ensureActive()
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
|
||||
return@runBlockingMaybeCancellable emptyList()
|
||||
val oldDependencies = extensionService.dependencies.get()
|
||||
if (oldDependencies == null
|
||||
|| oldDependencies.isOutdated
|
||||
|| includeIgnored) {
|
||||
extensionService.dependencies.set(PluginFeatureMap(
|
||||
featureMap = getFeatureMapFromMarketPlace(customPluginIds = customPluginIds, featureType = DEPENDENCY_SUPPORT_FEATURE),
|
||||
lastUpdateTime = if (oldDependencies != null) System.currentTimeMillis() else 0L,
|
||||
))
|
||||
}
|
||||
|
||||
// update information about file handlers when forced
|
||||
if (includeIgnored && PluginAdvertiserExtensionsStateService.getInstance().updateCompatibleFileHandlers()) {
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
if (unknownFeatures.isNotEmpty()) {
|
||||
pluginAdvertiserService.run(
|
||||
customPlugins = customPlugins,
|
||||
unknownFeatures = unknownFeatures,
|
||||
includeIgnored = includeIgnored
|
||||
)
|
||||
}
|
||||
|
||||
notifyUnbundledPlugins(project)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
rethrowControlFlowException(e)
|
||||
LOG.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getFeatureMapFromMarketPlace(customPluginIds: Set<String>, featureType: String): Map<String, PluginDataSet> {
|
||||
if (isTestingMode) {
|
||||
return emptyMap()
|
||||
internal suspend fun findSuggestedPlugins(project: Project, customRepositories: Map<String, List<PluginUiModel>>): List<PluginUiModel> {
|
||||
if (isTestingMode()) return emptyList()
|
||||
|
||||
val customPlugins = ArrayList<PluginUiModel>()
|
||||
for (value in customRepositories.values) {
|
||||
customPlugins.addAll(value)
|
||||
}
|
||||
|
||||
val pluginAdvertiserService = project.serviceAsync<PluginAdvertiserService>()
|
||||
pluginAdvertiserService.collectDependencyUnknownFeatures(true)
|
||||
|
||||
val customPluginIds = customPlugins.map { it.pluginId.idString }.toSet()
|
||||
val extensionService = serviceAsync<PluginFeatureCacheService>()
|
||||
val oldDependencies = extensionService.dependencies.get()
|
||||
extensionService.dependencies.set(PluginFeatureMap(
|
||||
getFeatureMapFromMarketPlace(customPluginIds, DEPENDENCY_SUPPORT_FEATURE),
|
||||
if (oldDependencies != null) System.currentTimeMillis() else 0L,
|
||||
))
|
||||
val unknownFeatures = UnknownFeaturesCollector.getInstance(project).unknownFeatures
|
||||
if (unknownFeatures.isNotEmpty()) {
|
||||
return pluginAdvertiserService.fetch(customPlugins, unknownFeatures, true)
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private suspend fun getFeatureMapFromMarketPlace(customPluginIds: Set<String>, featureType: String): Map<String, PluginDataSet> {
|
||||
if (isTestingMode()) return emptyMap()
|
||||
|
||||
val params = mapOf("featureType" to featureType)
|
||||
val features = MarketplaceRequests.getInstance().getFeatures(params)
|
||||
return features
|
||||
|
||||
Reference in New Issue
Block a user