source: josm/trunk/src/org/openstreetmap/josm/actions/UploadAction.java @ 11709

Last change on this file since 11709 was 11709, checked in by bastiK, 2 years ago

fixed #12731 - Add an option to completely prevent upload of a layer : e.g. "never" to upload=true/false

to set this option, add XML attribute upload='never' to .osm file

  • Property svn:eol-style set to native
File size: 9.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.util.LinkedList;
10import java.util.List;
11
12import javax.swing.JOptionPane;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.actions.upload.ApiPreconditionCheckerHook;
16import org.openstreetmap.josm.actions.upload.DiscardTagsHook;
17import org.openstreetmap.josm.actions.upload.FixDataHook;
18import org.openstreetmap.josm.actions.upload.RelationUploadOrderHook;
19import org.openstreetmap.josm.actions.upload.UploadHook;
20import org.openstreetmap.josm.actions.upload.ValidateUploadHook;
21import org.openstreetmap.josm.data.APIDataSet;
22import org.openstreetmap.josm.data.conflict.ConflictCollection;
23import org.openstreetmap.josm.gui.HelpAwareOptionPane;
24import org.openstreetmap.josm.gui.io.UploadDialog;
25import org.openstreetmap.josm.gui.io.UploadPrimitivesTask;
26import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
27import org.openstreetmap.josm.gui.layer.OsmDataLayer;
28import org.openstreetmap.josm.gui.util.GuiHelper;
29import org.openstreetmap.josm.tools.ImageProvider;
30import org.openstreetmap.josm.tools.Shortcut;
31
32/**
33 * Action that opens a connection to the osm server and uploads all changes.
34 *
35 * An dialog is displayed asking the user to specify a rectangle to grab.
36 * The url and account settings from the preferences are used.
37 *
38 * If the upload fails this action offers various options to resolve conflicts.
39 *
40 * @author imi
41 */
42public class UploadAction extends JosmAction {
43    /**
44     * The list of upload hooks. These hooks will be called one after the other
45     * when the user wants to upload data. Plugins can insert their own hooks here
46     * if they want to be able to veto an upload.
47     *
48     * Be default, the standard upload dialog is the only element in the list.
49     * Plugins should normally insert their code before that, so that the upload
50     * dialog is the last thing shown before upload really starts; on occasion
51     * however, a plugin might also want to insert something after that.
52     */
53    private static final List<UploadHook> uploadHooks = new LinkedList<>();
54    private static final List<UploadHook> lateUploadHooks = new LinkedList<>();
55
56    static {
57        /**
58         * Calls validator before upload.
59         */
60        uploadHooks.add(new ValidateUploadHook());
61
62        /**
63         * Fixes database errors
64         */
65        uploadHooks.add(new FixDataHook());
66
67        /**
68         * Checks server capabilities before upload.
69         */
70        uploadHooks.add(new ApiPreconditionCheckerHook());
71
72        /**
73         * Adjusts the upload order of new relations
74         */
75        uploadHooks.add(new RelationUploadOrderHook());
76
77        /**
78         * Removes discardable tags like created_by on modified objects
79         */
80        lateUploadHooks.add(new DiscardTagsHook());
81    }
82
83    /**
84     * Registers an upload hook. Adds the hook at the first position of the upload hooks.
85     *
86     * @param hook the upload hook. Ignored if null.
87     */
88    public static void registerUploadHook(UploadHook hook) {
89        registerUploadHook(hook, false);
90    }
91
92    /**
93     * Registers an upload hook. Adds the hook at the first position of the upload hooks.
94     *
95     * @param hook the upload hook. Ignored if null.
96     * @param late true, if the hook should be executed after the upload dialog
97     * has been confirmed. Late upload hooks should in general succeed and not
98     * abort the upload.
99     */
100    public static void registerUploadHook(UploadHook hook, boolean late) {
101        if (hook == null) return;
102        if (late) {
103            if (!lateUploadHooks.contains(hook)) {
104                lateUploadHooks.add(0, hook);
105            }
106        } else {
107            if (!uploadHooks.contains(hook)) {
108                uploadHooks.add(0, hook);
109            }
110        }
111    }
112
113    /**
114     * Unregisters an upload hook. Removes the hook from the list of upload hooks.
115     *
116     * @param hook the upload hook. Ignored if null.
117     */
118    public static void unregisterUploadHook(UploadHook hook) {
119        if (hook == null) return;
120        if (uploadHooks.contains(hook)) {
121            uploadHooks.remove(hook);
122        }
123        if (lateUploadHooks.contains(hook)) {
124            lateUploadHooks.remove(hook);
125        }
126    }
127
128    /**
129     * Constructs a new {@code UploadAction}.
130     */
131    public UploadAction() {
132        super(tr("Upload data"), "upload", tr("Upload all changes in the active data layer to the OSM server"),
133                Shortcut.registerShortcut("file:upload", tr("File: {0}", tr("Upload data")), KeyEvent.VK_UP, Shortcut.CTRL_SHIFT), true);
134        putValue("help", ht("/Action/Upload"));
135    }
136
137    /**
138     * Refreshes the enabled state
139     *
140     */
141    @Override
142    protected void updateEnabledState() {
143        OsmDataLayer editLayer = getLayerManager().getEditLayer();
144        setEnabled(editLayer != null && editLayer.isUploadable());
145    }
146
147    public static boolean checkPreUploadConditions(AbstractModifiableLayer layer) {
148        return checkPreUploadConditions(layer,
149                layer instanceof OsmDataLayer ? new APIDataSet(((OsmDataLayer) layer).data) : null);
150    }
151
152    protected static void alertUnresolvedConflicts(OsmDataLayer layer) {
153        HelpAwareOptionPane.showOptionDialog(
154                Main.parent,
155                tr("<html>The data to be uploaded participates in unresolved conflicts of layer ''{0}''.<br>"
156                        + "You have to resolve them first.</html>", layer.getName()
157                ),
158                tr("Warning"),
159                JOptionPane.WARNING_MESSAGE,
160                ht("/Action/Upload#PrimitivesParticipateInConflicts")
161        );
162    }
163
164    /**
165     * Warn user about discouraged upload, propose to cancel operation.
166     * @param layer incriminated layer
167     * @return true if the user wants to cancel, false if they want to continue
168     */
169    public static boolean warnUploadDiscouraged(AbstractModifiableLayer layer) {
170        return GuiHelper.warnUser(tr("Upload discouraged"),
171                "<html>" +
172                tr("You are about to upload data from the layer ''{0}''.<br /><br />"+
173                    "Sending data from this layer is <b>strongly discouraged</b>. If you continue,<br />"+
174                    "it may require you subsequently have to revert your changes, or force other contributors to.<br /><br />"+
175                    "Are you sure you want to continue?", layer.getName())+
176                "</html>",
177                ImageProvider.get("upload"), tr("Ignore this hint and upload anyway"));
178    }
179
180    /**
181     * Check whether the preconditions are met to upload data in <code>apiData</code>.
182     * Makes sure upload is allowed, primitives in <code>apiData</code> don't participate in conflicts and
183     * runs the installed {@link UploadHook}s.
184     *
185     * @param layer the source layer of the data to be uploaded
186     * @param apiData the data to be uploaded
187     * @return true, if the preconditions are met; false, otherwise
188     */
189    public static boolean checkPreUploadConditions(AbstractModifiableLayer layer, APIDataSet apiData) {
190        if (layer.isUploadDiscouraged() && warnUploadDiscouraged(layer)) {
191            return false;
192        }
193        if (layer instanceof OsmDataLayer) {
194            OsmDataLayer osmLayer = (OsmDataLayer) layer;
195            ConflictCollection conflicts = osmLayer.getConflicts();
196            if (apiData.participatesInConflict(conflicts)) {
197                alertUnresolvedConflicts(osmLayer);
198                return false;
199            }
200        }
201        // Call all upload hooks in sequence.
202        // FIXME: this should become an asynchronous task
203        //
204        if (apiData != null) {
205            for (UploadHook hook : uploadHooks) {
206                if (!hook.checkUpload(apiData))
207                    return false;
208            }
209        }
210
211        return true;
212    }
213
214    /**
215     * Uploads data to the OSM API.
216     *
217     * @param layer the source layer for the data to upload
218     * @param apiData the primitives to be added, updated, or deleted
219     */
220    public void uploadData(final OsmDataLayer layer, APIDataSet apiData) {
221        if (apiData.isEmpty()) {
222            JOptionPane.showMessageDialog(
223                    Main.parent,
224                    tr("No changes to upload."),
225                    tr("Warning"),
226                    JOptionPane.INFORMATION_MESSAGE
227            );
228            return;
229        }
230        if (!checkPreUploadConditions(layer, apiData))
231            return;
232
233        final UploadDialog dialog = UploadDialog.getUploadDialog();
234        dialog.setChangesetTags(layer.data);
235        dialog.setUploadedPrimitives(apiData);
236        dialog.setVisible(true);
237        dialog.rememberUserInput();
238        if (dialog.isCanceled())
239            return;
240
241        for (UploadHook hook : lateUploadHooks) {
242            if (!hook.checkUpload(apiData))
243                return;
244        }
245
246        Main.worker.execute(
247                new UploadPrimitivesTask(
248                        UploadDialog.getUploadDialog().getUploadStrategySpecification(),
249                        layer,
250                        apiData,
251                        UploadDialog.getUploadDialog().getChangeset()
252                )
253        );
254    }
255
256    @Override
257    public void actionPerformed(ActionEvent e) {
258        if (!isEnabled())
259            return;
260        if (Main.map == null) {
261            JOptionPane.showMessageDialog(
262                    Main.parent,
263                    tr("Nothing to upload. Get some data first."),
264                    tr("Warning"),
265                    JOptionPane.WARNING_MESSAGE
266            );
267            return;
268        }
269        APIDataSet apiData = new APIDataSet(Main.getLayerManager().getEditDataSet());
270        uploadData(Main.getLayerManager().getEditLayer(), apiData);
271    }
272}
Note: See TracBrowser for help on using the repository browser.