How to change the navigation of "Report Issue" button in marketplace

Hi, I would like to update the navigation event of the “Report Issue” button. This button is located in the marketplace entry. We would like to update this to point to a website where customers can log the issue, instead of an email address.

Thanks,
Luis

Hi, you may want to register and implement a custom ErrorReportSubmitter.
See IDE Infrastructure | IntelliJ Platform Plugin SDK and How to implement error reporting in an IntelliJ plugin | plugin-dev.com.
Per example, some plugins use it to submit and prefill issues to their GitHub project.

1 Like

I was using this implementation (highly inspired from the fantastic work of other plugin developers):

package lermitage.intellij.extra.icons.utils;

import com.intellij.ide.BrowserUtil;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diagnostic.SubmittedReportInfo;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.util.NlsActions;
import com.intellij.util.Consumer;
import com.intellij.util.ModalityUiUtil;
import org.apache.commons.lang3.SystemUtils;
import org.apache.http.client.utils.URIBuilder;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.Component;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Error reporter which prefills an issue on Extra Icons' GitHub repository.
 */
public class ExtraIconsErrorReportSubmitter extends ErrorReportSubmitter {

    private static final @NonNls Logger LOGGER = Logger.getInstance(IconUtils.class);
    private static final ResourceBundle i18n = I18nUtils.getResourceBundle();

    private static final int MAX_GITHUB_URI_LENGTH = 8192;

    @Override
    public @NlsActions.ActionText @NotNull String getReportActionText() {
        return i18n.getString("error.report.btn.title");
    }

    @Override
    public boolean submit(IdeaLoggingEvent @NotNull [] events,
                          @Nullable String additionalInfo,
                          @NotNull Component parentComponent,
                          @NotNull Consumer<? super SubmittedReportInfo> consumer) {
        try {
            URI uri = constructNewGitHubIssueUri(events, additionalInfo);
            ModalityUiUtil.invokeLaterIfNeeded(ModalityState.NON_MODAL, () -> BrowserUtil.browse(uri));
        } catch (Exception e) {
            LOGGER.error("Failed to prepare Extra Icons error reporter", e);
            return false;
        }
        return true;
    }

    @SuppressWarnings("HardCodedStringLiteral")
    URI constructNewGitHubIssueUri(IdeaLoggingEvent[] events, @Nullable String additionalInfo) throws URISyntaxException {
        URIBuilder uriBuilder;
        uriBuilder = new URIBuilder("https://github.com/jonathanlermitage/intellij-extra-icons-plugin/issues/new");
        String title = Stream.of(events)
            .map(event -> {
                Throwable throwable = event.getThrowable();
                String exceptionMessage = event.getThrowableText().lines().findFirst().orElse("");
                return (throwable != null ? exceptionMessage : event.getMessage()).stripTrailing();
            })
            .collect(Collectors.joining("; "));
        uriBuilder.setParameter("title", title);
        uriBuilder.setParameter("labels", "bug");

        URI uri;
        List<String> reportBodyLines = getReportBody(events, additionalInfo).lines().collect(Collectors.toList());
        do {
            // Let's cut the body gradually line-by-line until the resulting URI fits into the GitHub limits.
            // It's hard to predict the perfect exact cut in advance due to URL encoding.
            reportBodyLines = reportBodyLines.subList(0, reportBodyLines.size() - 1);
            uriBuilder.setParameter("body", reportBodyLines.stream().collect(Collectors.joining(System.lineSeparator())));
            uri = uriBuilder.build();
        } while (uri.toString().length() > MAX_GITHUB_URI_LENGTH);
        return uri;
    }

    private String getReportBody(
        IdeaLoggingEvent[] events,
        @Nullable String additionalInfo) {
        String reportBody = getBugTemplate();
        for (java.util.Map.Entry<String, String> entry : getTemplateVariables(events, additionalInfo).entrySet()) {
            reportBody = reportBody.replace("%" + entry.getKey() + "%", entry.getValue());
        }
        return reportBody;
    }

    @SuppressWarnings("HardCodedStringLiteral")
    private java.util.Map<String, String> getTemplateVariables(
        IdeaLoggingEvent[] events,
        @Nullable String additionalInfo) {
        Map<String, String> templateVariables = new HashMap<>();

        templateVariables.put("ide", ApplicationInfo.getInstance().getFullApplicationName());

        IdeaPluginDescriptor pluginDescriptor = PluginManagerCore.getPlugin(PluginId.getId("lermitage.intellij.extra.icons"));
        String pluginVersion = pluginDescriptor == null ? "<unknown>" : pluginDescriptor.getVersion();
        templateVariables.put("extraiconsPluginVersion", pluginVersion);

        String osName = SystemUtils.OS_NAME;
        String osVersion = SystemUtils.OS_VERSION;
        templateVariables.put("os", osName + " " + osVersion);

        templateVariables.put("additionalInfo", additionalInfo == null ? "N/A" : additionalInfo);

        String nl = System.lineSeparator();
        String stacktraces = Stream.of(events)
            .map(event -> {
                // This message is distinct from the throwable's message:
                // in `LOG.error(message, throwable)`, it's the first parameter.
                String messagePart = event.getMessage() != null ? (event.getMessage() + nl + nl) : "";
                String throwablePart = shortenExceptionsStack(event.getThrowableText().stripTrailing());
                return nl + messagePart + throwablePart + nl;
            })
            .collect(Collectors.joining(nl + nl));
        templateVariables.put("stacktraces", stacktraces);

        return templateVariables;
    }

    @SuppressWarnings("HardCodedStringLiteral")
    private String shortenExceptionsStack(String stackTrace) {
        String nl = System.lineSeparator();
        int rootCauseIndex = Math.max(
            stackTrace.lastIndexOf("Caused by:"),
            stackTrace.lastIndexOf("\tSuppressed:"));

        if (rootCauseIndex != -1) {
            String rootCauseStackTrace = stackTrace.substring(rootCauseIndex);
            String[] lines = stackTrace.substring(0, rootCauseIndex).split(nl);

            StringBuilder resultString = new StringBuilder();
            for (int i = 0; i < lines.length; i++) {
                if (lines[i].contains("Caused by:") || lines[i].contains("Suppressed:") || i == 0) {
                    resultString.append(lines[i]).append(nl);
                    if (i + 1 < lines.length) {
                        resultString.append(lines[i + 1]).append("...").append(nl);
                    }
                }
            }
            return resultString.append(rootCauseStackTrace).toString();
        }
        return stackTrace;
    }

    @SuppressWarnings("HardCodedStringLiteral")
    private String getBugTemplate() {
        return "## Running environment\n" +
            "- Extra Icons plugin version - %extraiconsPluginVersion%\n" +
            "- IDE - %ide%\n" +
            "- OS - %os%\n" +
            "\n" +
            "## Bug description\n" +
            "Please include steps to reproduce (like `go to...`/`click on...` etc.) + expected and actual behaviour.  \n" +
            "Please attach **IDE logs**. Open your IDE and go to <kbd>Help</kbd>, <kbd>Collect Logs and Diagnostic Data</kbd>.\n" +
            "\n" +
            "## IDE - additional info\n" +
            "%additionalInfo%\n" +
            "\n" +
            "## IDE - stack trace\n" +
            "```%stacktraces%\n";
    }
}

This prefills a GitHub issue with the plugin version, information on the IDE and the OS, and the stracktrace. There is a 8192 characters limit because everything is sent via the url, and some browsers do not accept more than 8192 characters.

I switched to a com.intellij.diagnostic.JetBrainsMarketplaceErrorReportSubmitter, but this was a bad idea (there are no notifications). I’ll revert to my old ErrorReportSubmitter.

Thanks @jonathanlermitage.1 I’ll give this a try!

Would you be interested in a library that simplifies extending this extension point?