mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 06:50:54 +07:00
[platform] refactoring: configuration of the in-product ZenDesk feedback form moved to ExternalProductResourceUrls (IJPL-204)
IntelliJ IDEA Community and Ultimate are migrated to use the new approach. Since other JetBrains IDEs didn't use this feature, and the corresponding method wasn't part of public API, no fallback implementation in LegacyExternalProductResourceUrls is provided. GitOrigin-RevId: a50912b65d4fc44a3fcf7e59d615092c0372b581
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8149d16d1b
commit
48f0103a52
@@ -18,18 +18,6 @@
|
||||
|
||||
<help webhelp-url="https://www.jetbrains.com/help/idea/"/>
|
||||
<documentation url="https://www.jetbrains.com/idea/resources/"/>
|
||||
<feedback zendesk-form-id="360001912739" zendesk-url="https://jbsintellij.zendesk.com">
|
||||
<field id="28147552" value="ij_idea"/>
|
||||
<field id="28102551" value="sl_unknown"/> <!-- country -->
|
||||
<field id="29444529" type="rating"/>
|
||||
<field id="28500325" type="build"/>
|
||||
<field id="28151042" type="os"/>
|
||||
<field id="28500645" type="timezone"/>
|
||||
<field id="28351649" type="eval"/>
|
||||
<field id="360021010939" type="systeminfo"/>
|
||||
<field id="22996310" type="needsupport"/>
|
||||
<field id="28116681" type="topic"/>
|
||||
</feedback>
|
||||
<whatsnew url="https://www.jetbrains.com/idea/whatsnew/" show-on-update="true"/>
|
||||
<keymap win="https://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard.pdf"
|
||||
mac="https://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard_Mac.pdf"/>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
package com.intellij.idea.customization.base
|
||||
|
||||
import com.intellij.platform.ide.impl.customization.BaseJetBrainsExternalProductResourceUrls
|
||||
import com.intellij.platform.ide.impl.customization.ZenDeskFeedbackFormData
|
||||
import com.intellij.platform.ide.impl.customization.ZenDeskFeedbackFormFieldIds
|
||||
|
||||
class IntelliJIdeaExternalResourceUrls : BaseJetBrainsExternalProductResourceUrls() {
|
||||
override val basePatchDownloadUrl: String
|
||||
@@ -12,4 +14,23 @@ class IntelliJIdeaExternalResourceUrls : BaseJetBrainsExternalProductResourceUrl
|
||||
|
||||
override val shortProductNameUsedInForms: String
|
||||
get() = "IDEA"
|
||||
|
||||
override val zenDeskFeedbackFormData: ZenDeskFeedbackFormData
|
||||
get() = object : ZenDeskFeedbackFormData {
|
||||
override val formUrl: String = "https://jbsintellij.zendesk.com"
|
||||
override val formId: Long = 360001912739
|
||||
override val productId: String = "ij_idea"
|
||||
override val fieldIds = object : ZenDeskFeedbackFormFieldIds {
|
||||
override val product: Long = 28147552
|
||||
override val country: Long = 28102551
|
||||
override val rating: Long = 29444529
|
||||
override val build: Long = 28500325
|
||||
override val os: Long = 28151042
|
||||
override val timezone: Long = 28500645
|
||||
override val eval: Long = 28351649
|
||||
override val systemInfo: Long = 360021010939
|
||||
override val needSupport: Long = 22996310
|
||||
override val topic: Long = 28116681
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,6 @@ public final class ApplicationInfoImpl extends ApplicationInfoEx {
|
||||
|
||||
private String mySubscriptionFormId;
|
||||
private boolean mySubscriptionTipsAvailable;
|
||||
private XmlElement myFeedbackForm;
|
||||
|
||||
private String myDefaultLightLaf;
|
||||
private String myDefaultClassicLightLaf;
|
||||
@@ -201,9 +200,6 @@ public final class ApplicationInfoImpl extends ApplicationInfoEx {
|
||||
|
||||
case "feedback": {
|
||||
myFeedbackUrl = child.getAttributeValue("url");
|
||||
if (child.getAttributeValue("zendesk-form-id") != null) {
|
||||
myFeedbackForm = child;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -748,11 +744,6 @@ public final class ApplicationInfoImpl extends ApplicationInfoEx {
|
||||
return override != null ? override : myDefaultClassicDarkLaf;
|
||||
}
|
||||
|
||||
public @Nullable ZenDeskForm getFeedbackForm() {
|
||||
XmlElement v = myFeedbackForm;
|
||||
return v == null ? null : ZenDeskForm.parse(v);
|
||||
}
|
||||
|
||||
private static final class UpdateUrlsImpl implements UpdateUrls {
|
||||
private final String myCheckingUrl;
|
||||
private final String myPatchesUrl;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.application.impl
|
||||
|
||||
import com.intellij.util.xml.dom.XmlElement
|
||||
|
||||
data class ZenDeskField(val id: Long, val type: String?, val value: String?) {
|
||||
companion object {
|
||||
fun parse(element: XmlElement): ZenDeskField {
|
||||
val id = element.getAttributeValue("id")!!.toLong()
|
||||
val type = element.getAttributeValue("type")
|
||||
val value = element.getAttributeValue("value")
|
||||
return ZenDeskField(id, type, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ZenDeskForm(val id: Long, val url: String, val fields: List<ZenDeskField>) {
|
||||
companion object {
|
||||
@JvmStatic fun parse(element: XmlElement): ZenDeskForm {
|
||||
val id = element.getAttributeValue("zendesk-form-id")!!.toLong()
|
||||
val url = element.getAttributeValue("zendesk-url")!!
|
||||
val fields = mutableListOf<ZenDeskField>()
|
||||
|
||||
for (child in element.children) {
|
||||
if (child.name == "field") {
|
||||
fields.add(ZenDeskField.parse(child))
|
||||
}
|
||||
}
|
||||
return ZenDeskForm(id, url, fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
package com.intellij.platform.ide.customization
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.Url
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
@@ -88,4 +90,15 @@ interface FeedbackReporter {
|
||||
* extension point.
|
||||
*/
|
||||
fun feedbackFormUrl(description: String): Url
|
||||
|
||||
/**
|
||||
* Override this function to show a custom form when "Submit Feedback" action is invoked or when the IDE requests a user to provide
|
||||
* feedback during the evaluation period.
|
||||
* @param requestedForEvaluation `true` if the form is shown by the IDE during the evaluation period and `false` if user explicitly
|
||||
* invoked "Submit Feedback" action.
|
||||
* @return `true` if the custom form was shown, and `false` otherwise;
|
||||
* in the latter case, the default way with opening [feedbackFormUrl] in the browser will be used.
|
||||
*/
|
||||
@RequiresEdt
|
||||
fun showFeedbackForm(project: Project?, requestedForEvaluation: Boolean): Boolean = false
|
||||
}
|
||||
@@ -4,15 +4,11 @@ package com.intellij.ide.actions;
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.ide.FeedbackDescriptionProvider;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.feedback.FeedbackForm;
|
||||
import com.intellij.idea.ActionsBundle;
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationInfo;
|
||||
import com.intellij.openapi.application.ex.ApplicationInfoEx;
|
||||
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
|
||||
import com.intellij.openapi.application.impl.ZenDeskForm;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -20,7 +16,6 @@ import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.platform.ide.customization.ExternalProductResourceUrls;
|
||||
import com.intellij.platform.ide.customization.FeedbackReporter;
|
||||
import com.intellij.ui.LicensingFacade;
|
||||
@@ -55,12 +50,12 @@ public class SendFeedbackAction extends AnAction implements DumbAware {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
ZenDeskForm feedbackForm = ((ApplicationInfoImpl)ApplicationInfo.getInstance()).getFeedbackForm();
|
||||
if (Registry.is("ide.in.product.feedback") && feedbackForm != null) {
|
||||
new FeedbackForm(e.getProject(), feedbackForm, false).show();
|
||||
}
|
||||
else {
|
||||
submit(e.getProject());
|
||||
FeedbackReporter feedbackReporter = ExternalProductResourceUrls.getInstance().getFeedbackReporter();
|
||||
if (feedbackReporter != null) {
|
||||
boolean formShown = feedbackReporter.showFeedbackForm(e.getProject(), false);
|
||||
if (!formShown) {
|
||||
submit(e.getProject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,9 @@ import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.application.ApplicationBundle
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
import com.intellij.openapi.application.ex.ApplicationInfoEx
|
||||
import com.intellij.openapi.application.impl.ZenDeskForm
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.platform.ide.impl.customization.ZenDeskFeedbackFormData
|
||||
import com.intellij.ui.CollectionComboBoxModel
|
||||
import com.intellij.ui.LicensingFacade
|
||||
import com.intellij.ui.PopupBorder
|
||||
@@ -24,8 +23,7 @@ import com.intellij.ui.components.JBTextArea
|
||||
import com.intellij.ui.components.TextComponentEmptyText
|
||||
import com.intellij.ui.components.dialog
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.ui.layout.selectedValueMatches
|
||||
import com.intellij.util.ui.JBFont
|
||||
import com.intellij.util.ui.JBUI
|
||||
import org.jetbrains.annotations.Nls
|
||||
@@ -54,7 +52,7 @@ private val topicOptions = listOf(
|
||||
|
||||
class FeedbackForm(
|
||||
private val project: Project?,
|
||||
val form: ZenDeskForm,
|
||||
private val zenDeskFormData: ZenDeskFeedbackFormData,
|
||||
val isEvaluation: Boolean
|
||||
) : DialogWrapper(project, false) {
|
||||
private var details = ""
|
||||
@@ -230,15 +228,12 @@ class FeedbackForm(
|
||||
val systemInfo = if (shareSystemInformation) AboutDialog(project).extendedAboutText else ""
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
ZenDeskRequests().submit(
|
||||
form,
|
||||
zenDeskFormData,
|
||||
email,
|
||||
ApplicationNamesInfo.getInstance().fullProductName + " Feedback",
|
||||
details.ifEmpty { "No details" },
|
||||
mapOf(
|
||||
"systeminfo" to systemInfo,
|
||||
"needsupport" to needSupport
|
||||
) + (ratingComponent?.let { mapOf("rating" to it.myRating) } ?: mapOf()) + (topic?.let { mapOf("topic" to it.id) } ?: emptyMap())
|
||||
, onDone = {
|
||||
CustomFieldValues(systemInfo, needSupport, ratingComponent?.myRating, topic?.id),
|
||||
onDone = {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
var message = ApplicationBundle.message("feedback.form.thanks", ApplicationNamesInfo.getInstance().fullProductName)
|
||||
if (isEvaluation) {
|
||||
|
||||
@@ -3,9 +3,9 @@ package com.intellij.ide.feedback
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.openapi.application.impl.ZenDeskForm
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.platform.ide.impl.customization.ZenDeskFeedbackFormData
|
||||
import com.intellij.ui.LicensingFacade
|
||||
import com.intellij.util.io.HttpRequests
|
||||
import java.io.IOException
|
||||
@@ -14,34 +14,40 @@ import java.net.HttpURLConnection
|
||||
class ZenDeskRequests {
|
||||
private val objectMapper by lazy { ObjectMapper() }
|
||||
|
||||
fun submit(form: ZenDeskForm, email: String, subject: String, text: String, fieldData: Map<String, Any>, onDone: () -> Unit, onError: () -> Unit) {
|
||||
val customFields = mutableListOf<ZenDeskCustomField>()
|
||||
for (field in form.fields) {
|
||||
val value = field.value
|
||||
?: when (field.type) {
|
||||
"build" -> ApplicationInfo.getInstance().build.asString()
|
||||
"os" -> getOsForZenDesk()
|
||||
"timezone" -> System.getProperty("user.timezone")
|
||||
"eval" -> LicensingFacade.getInstance()?.isEvaluationLicense?.toString()
|
||||
"needsupport" -> if (fieldData[field.type] == true) "share_problems_or_ask_about_missing_features" else "provide_a_feedback"
|
||||
else -> fieldData[field.type]
|
||||
}
|
||||
value?.let {
|
||||
customFields.add(ZenDeskCustomField(field.id, it))
|
||||
internal fun submit(zenDeskFormData: ZenDeskFeedbackFormData, email: String, subject: String, text: String,
|
||||
customFieldValues: CustomFieldValues, onDone: () -> Unit, onError: () -> Unit) {
|
||||
val fieldIds = zenDeskFormData.fieldIds
|
||||
val customFields = listOfNotNull(
|
||||
ZenDeskCustomField(fieldIds.product, zenDeskFormData.productId),
|
||||
ZenDeskCustomField(fieldIds.country, "sl_unknown"),
|
||||
customFieldValues.rating?.let { rating ->
|
||||
ZenDeskCustomField(fieldIds.rating, rating)
|
||||
},
|
||||
ZenDeskCustomField(fieldIds.build, ApplicationInfo.getInstance().build.asString()),
|
||||
ZenDeskCustomField(fieldIds.os, getOsForZenDesk()),
|
||||
ZenDeskCustomField(fieldIds.timezone, System.getProperty("user.timezone")),
|
||||
LicensingFacade.getInstance()?.isEvaluationLicense?.let { isEval ->
|
||||
ZenDeskCustomField(fieldIds.eval, isEval.toString())
|
||||
},
|
||||
ZenDeskCustomField(fieldIds.systemInfo, customFieldValues.systemInfo),
|
||||
ZenDeskCustomField(fieldIds.needSupport,
|
||||
if (customFieldValues.needSupport) "share_problems_or_ask_about_missing_features" else "provide_a_feedback"),
|
||||
customFieldValues.topicId?.let { topicId ->
|
||||
ZenDeskCustomField(fieldIds.topic, topicId)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val request = ZenDeskRequest(
|
||||
ZenDeskRequester("anonymous", email),
|
||||
subject,
|
||||
ZenDeskComment(text),
|
||||
form.id,
|
||||
zenDeskFormData.formId,
|
||||
customFields
|
||||
)
|
||||
val requestData = objectMapper.writeValueAsString(mapOf("request" to request))
|
||||
try {
|
||||
HttpRequests
|
||||
.post("${form.url}/api/v2/requests", "application/json")
|
||||
.post("${zenDeskFormData.formUrl}/api/v2/requests", "application/json")
|
||||
.productNameAsUserAgent()
|
||||
.accept("application/json")
|
||||
.connect {
|
||||
@@ -81,6 +87,13 @@ class ZenDeskRequests {
|
||||
}
|
||||
}
|
||||
|
||||
internal data class CustomFieldValues(
|
||||
val systemInfo: String,
|
||||
val needSupport: Boolean,
|
||||
val rating: Int?,
|
||||
val topicId: String?
|
||||
)
|
||||
|
||||
private class ZenDeskComment(val body: String)
|
||||
|
||||
private class ZenDeskRequester(val name: String, val email: String)
|
||||
|
||||
@@ -31,6 +31,12 @@ abstract class BaseJetBrainsExternalProductResourceUrls : ExternalProductResourc
|
||||
open val intellijSupportFormId: Int
|
||||
get() = 66731
|
||||
|
||||
/**
|
||||
* Return a non-null value from this property to enable the in-product form for "Submit Feedback" action and evaluation feedback
|
||||
*/
|
||||
open val zenDeskFeedbackFormData: ZenDeskFeedbackFormData?
|
||||
get() = null
|
||||
|
||||
override val updatesMetadataXmlUrl: String
|
||||
get() = "https://www.jetbrains.com/updates/updates.xml"
|
||||
|
||||
@@ -56,7 +62,7 @@ abstract class BaseJetBrainsExternalProductResourceUrls : ExternalProductResourc
|
||||
}
|
||||
|
||||
override val feedbackReporter: FeedbackReporter?
|
||||
get() = JetBrainsFeedbackReporter(shortProductNameUsedInForms)
|
||||
get() = JetBrainsFeedbackReporter(shortProductNameUsedInForms, zenDeskFeedbackFormData)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.impl.customization
|
||||
|
||||
import com.intellij.ide.feedback.FeedbackForm
|
||||
import com.intellij.openapi.application.ex.ApplicationInfoEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.ide.customization.FeedbackReporter
|
||||
import com.intellij.ui.LicensingFacade
|
||||
import com.intellij.util.Url
|
||||
import com.intellij.util.Urls
|
||||
|
||||
internal class JetBrainsFeedbackReporter(private val productName: String) : FeedbackReporter {
|
||||
internal class JetBrainsFeedbackReporter(private val productName: String,
|
||||
private val zenDeskFormData: ZenDeskFeedbackFormData?) : FeedbackReporter {
|
||||
override val destinationDescription: String
|
||||
get() = "jetbrains.com"
|
||||
|
||||
@@ -23,4 +27,39 @@ internal class JetBrainsFeedbackReporter(private val productName: String) : Feed
|
||||
"eval" to (LicensingFacade.getInstance()?.isEvaluationLicense == true).toString(),
|
||||
))
|
||||
}
|
||||
|
||||
override fun showFeedbackForm(project: Project?, requestedForEvaluation: Boolean): Boolean {
|
||||
if (Registry.`is`("ide.in.product.feedback") && zenDeskFormData != null) {
|
||||
FeedbackForm(project, zenDeskFormData, requestedForEvaluation).show()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information about ZenDesk form used to send feedback.
|
||||
*/
|
||||
interface ZenDeskFeedbackFormData {
|
||||
val formUrl: String
|
||||
val formId: Long
|
||||
val productId: String
|
||||
val fieldIds: ZenDeskFeedbackFormFieldIds
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of elements in ZenDesk feedback form.
|
||||
* They are used in JSON sent to zendesk.com, see [ZenDeskRequests][com.intellij.ide.feedback.ZenDeskRequests] for implementation details.
|
||||
*/
|
||||
interface ZenDeskFeedbackFormFieldIds {
|
||||
val product: Long
|
||||
val country: Long
|
||||
val rating: Long
|
||||
val build: Long
|
||||
val os: Long
|
||||
val timezone: Long
|
||||
val eval: Long
|
||||
val systemInfo: Long
|
||||
val needSupport: Long
|
||||
val topic: Long
|
||||
}
|
||||
Reference in New Issue
Block a user