001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.actions;
003
004import java.awt.event.ActionEvent;
005import java.awt.event.KeyEvent;
006import java.io.IOException;
007
008import javax.json.Json;
009import javax.swing.JOptionPane;
010
011import org.apache.http.client.methods.CloseableHttpResponse;
012import org.apache.http.client.methods.HttpPost;
013import org.apache.http.entity.StringEntity;
014import org.apache.http.impl.client.CloseableHttpClient;
015import org.apache.http.impl.client.HttpClientBuilder;
016import org.apache.http.util.EntityUtils;
017import org.openstreetmap.josm.actions.JosmAction;
018import org.openstreetmap.josm.gui.Notification;
019import org.openstreetmap.josm.plugins.streetside.StreetsideLayer;
020import org.openstreetmap.josm.plugins.streetside.StreetsideLocationChangeset;
021import org.openstreetmap.josm.plugins.streetside.gui.StreetsideChangesetDialog;
022import org.openstreetmap.josm.plugins.streetside.utils.PluginState;
023import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;
024import org.openstreetmap.josm.plugins.streetside.utils.StreetsideURL.APIv3;
025import org.openstreetmap.josm.plugins.streetside.utils.StreetsideUtils;
026import org.openstreetmap.josm.plugins.streetside.utils.api.JsonLocationChangesetEncoder;
027import org.openstreetmap.josm.tools.I18n;
028import org.openstreetmap.josm.tools.ImageProvider;
029import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
030import org.openstreetmap.josm.tools.Logging;
031import org.openstreetmap.josm.tools.Shortcut;
032
033/**
034 * Imports a set of picture files into JOSM. They must be in jpg or png format.
035 */
036public class StreetsideSubmitCurrentChangesetAction extends JosmAction {
037
038  private static final long serialVersionUID = 4995924098228082806L;
039  private final StreetsideChangesetDialog changesetDialog;
040
041  /**
042   * Main constructor.
043   * @param changesetDialog Streetside changeset dialog
044   */
045  public StreetsideSubmitCurrentChangesetAction(StreetsideChangesetDialog changesetDialog) {
046    super(
047      I18n.tr("Submit changeset"),
048      new ImageProvider("dialogs", "streetside-upload").setSize(ImageSizes.DEFAULT),
049      I18n.tr("Submit the current changeset"),
050      // CHECKSTYLE.OFF: LineLength
051      Shortcut.registerShortcut("Submit changeset to Streetside", I18n.tr("Submit the current changeset to Streetside"), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE),
052      // CHECKSTYLE.ON: LineLength
053      false,
054      "streetsideSubmitChangeset",
055      false
056    );
057    this.changesetDialog = changesetDialog;
058    setEnabled(false);
059  }
060
061  @Override
062  public void actionPerformed(ActionEvent event) {
063    new Thread(() -> {
064      changesetDialog.setUploadPending(true);
065      String token = StreetsideProperties.ACCESS_TOKEN.get();
066      if (token != null && !token.trim().isEmpty()) {
067        PluginState.setSubmittingChangeset(true);
068        StreetsideUtils.updateHelpText();
069        HttpClientBuilder builder = HttpClientBuilder.create();
070        HttpPost httpPost = new HttpPost(APIv3.submitChangeset().toString());
071        httpPost.addHeader("content-type", "application/json");
072        httpPost.addHeader("Authorization", "Bearer " + token);
073        StreetsideLocationChangeset locationChangeset = StreetsideLayer.getInstance().getLocationChangeset();
074        String json = JsonLocationChangesetEncoder.encodeLocationChangeset(locationChangeset).build().toString();
075        Logging.info("Sending JSON to " + APIv3.submitChangeset() + "\n  " + json);
076        try (CloseableHttpClient httpClient = builder.build()) {
077          httpPost.setEntity(new StringEntity(json));
078          CloseableHttpResponse response = httpClient.execute(httpPost);
079          Logging.debug("HTTP request finished with response code " + response.getStatusLine().getStatusCode());
080          if (response.getStatusLine().getStatusCode() == 201) {
081            final String key = Json.createReader(response.getEntity().getContent()).readObject().getString("key");
082            final String state = Json.createReader(response.getEntity().getContent()).readObject().getString("state");
083            I18n.marktr("rejected");
084            I18n.marktr("pending");
085            I18n.marktr("approved");
086            final String message = I18n.tr("{0} images submitted, Changeset key: {1}, State: {2}", locationChangeset.size(), key, state);
087            Logging.debug(message);
088            new Notification(message)
089              .setDuration(Notification.TIME_LONG)
090              .setIcon("rejected".equals(state) ? JOptionPane.ERROR_MESSAGE : JOptionPane.INFORMATION_MESSAGE)
091              .show();
092            locationChangeset.cleanChangeset(); // TODO: Remove only uploaded changes. If the user made changes while uploading the changeset, these changes would also be removed, although they weren't uploaded. Alternatively: Disallow editing while uploading.
093          } else {
094            new Notification(
095              I18n.tr("Changeset upload failed with {0} error ''{1} {2}''!",
096                response.getStatusLine().getProtocolVersion(),
097                response.getStatusLine().getStatusCode(),
098                response.getStatusLine().getReasonPhrase()
099              )
100            ).setIcon(JOptionPane.ERROR_MESSAGE)
101              .setDuration(Notification.TIME_LONG)
102              .show();
103            Logging.error("Failed response " + EntityUtils.toString(response.getEntity()));
104          }
105        } catch (IOException e) {
106          Logging.log(Logging.LEVEL_ERROR, "Exception while trying to submit a changeset to streetside.com", e);
107          new Notification(
108            I18n.tr("An exception occured while trying to submit a changeset. If this happens repeatedly, consider reporting a bug via the Help menu. If this message appears for the first time, simply try it again. This might have been an issue with the internet connection.")
109          ).setDuration(Notification.TIME_LONG)
110            .setIcon(JOptionPane.ERROR_MESSAGE)
111            .show();
112        } finally {
113          PluginState.setSubmittingChangeset(false);
114        }
115      } else {
116        // TODO: currently no login for Microsoft
117        //PluginState.notLoggedInToStreetsideDialog();
118      }
119      changesetDialog.setUploadPending(false);
120    }, "Streetside changeset upload").start();
121  }
122}