1 /* 2 * SVNCommandHandler.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.studio.client.workbench.views.vcs.svn; 16 17 import java.util.ArrayList; 18 19 import com.google.inject.Inject; 20 import com.google.inject.Provider; 21 import org.rstudio.core.client.command.CommandBinder; 22 import org.rstudio.core.client.command.Handler; 23 import org.rstudio.core.client.files.FileSystemItem; 24 import org.rstudio.core.client.widget.MessageDialog; 25 import org.rstudio.core.client.widget.Operation; 26 import org.rstudio.core.client.widget.OperationWithInput; 27 import org.rstudio.studio.client.RStudioGinjector; 28 import org.rstudio.studio.client.common.GlobalDisplay; 29 import org.rstudio.studio.client.common.SimpleRequestCallback; 30 import org.rstudio.studio.client.common.console.ConsoleProcess; 31 import org.rstudio.studio.client.common.vcs.ProcessResult; 32 import org.rstudio.studio.client.common.vcs.SVNServerOperations; 33 import org.rstudio.studio.client.common.vcs.StatusAndPath; 34 import org.rstudio.studio.client.common.vcs.ignore.Ignore; 35 import org.rstudio.studio.client.common.vcs.ignore.IgnoreList; 36 import org.rstudio.studio.client.server.ServerRequestCallback; 37 import org.rstudio.studio.client.workbench.commands.Commands; 38 import org.rstudio.studio.client.workbench.views.vcs.common.ConsoleProgressDialog; 39 import org.rstudio.studio.client.workbench.views.vcs.common.ProcessCallback; 40 import org.rstudio.studio.client.workbench.views.vcs.common.VCSFileOpener; 41 import org.rstudio.studio.client.workbench.views.vcs.svn.commit.SVNCommitDialog; 42 import org.rstudio.studio.client.workbench.views.vcs.svn.model.SVNState; 43 44 import com.google.gwt.core.client.GWT; 45 import com.google.gwt.user.client.Command; 46 47 public class SVNCommandHandler 48 { 49 public interface Binder extends CommandBinder<Commands, SVNCommandHandler> 50 { 51 } 52 SVNCommandHandler(SVNPresenterDisplay display, GlobalDisplay globalDisplay, Commands commands, SVNServerOperations server, SVNState svnState, VCSFileOpener vcsFileOpener)53 public SVNCommandHandler(SVNPresenterDisplay display, 54 GlobalDisplay globalDisplay, 55 Commands commands, 56 SVNServerOperations server, 57 SVNState svnState, 58 VCSFileOpener vcsFileOpener) 59 { 60 display_ = display; 61 globalDisplay_ = globalDisplay; 62 commands_ = commands; 63 server_ = server; 64 svnState_ = svnState; 65 vcsFileOpener_ = vcsFileOpener; 66 GWT.<Binder>create(Binder.class).bind(commands, this); 67 68 RStudioGinjector.INSTANCE.injectMembers(this); 69 } 70 71 @Inject initialize(Provider<SVNCommitDialog> pCommitDialog, Provider<Ignore> pIgnore)72 void initialize(Provider<SVNCommitDialog> pCommitDialog, 73 Provider<Ignore> pIgnore) 74 { 75 pCommitDialog_ = pCommitDialog; 76 pIgnore_ = pIgnore; 77 } 78 setFilesCommandsEnabled(boolean enabled)79 public void setFilesCommandsEnabled(boolean enabled) 80 { 81 commands_.vcsAddFiles().setEnabled(enabled); 82 commands_.vcsRemoveFiles().setEnabled(enabled); 83 commands_.vcsRevert().setEnabled(enabled); 84 commands_.vcsIgnore().setEnabled(enabled); 85 commands_.vcsResolve().setEnabled(enabled); 86 } 87 88 // onVcsPull and onVcsCommit and not direct command handlers because they 89 // are handled globally in the main frame (by BaseVcsPresenter or the 90 // satellite frame) 91 onVcsPull()92 public void onVcsPull() 93 { 94 server_.svnUpdate(new SimpleRequestCallback<ConsoleProcess>() 95 { 96 @Override 97 public void onResponseReceived(ConsoleProcess response) 98 { 99 new ConsoleProgressDialog(response, server_).showModal(); 100 } 101 }); 102 } 103 onVcsPullRebase()104 public void onVcsPullRebase() 105 { 106 server_.svnUpdate(new SimpleRequestCallback<ConsoleProcess>() 107 { 108 @Override 109 public void onResponseReceived(ConsoleProcess response) 110 { 111 new ConsoleProgressDialog(response, server_).showModal(); 112 } 113 }); 114 } 115 116 onVcsCommit()117 public void onVcsCommit() 118 { 119 pCommitDialog_.get().showModal(); 120 } 121 onVcsIgnore()122 public void onVcsIgnore() 123 { 124 // special case for a single directory with property changes 125 ArrayList<StatusAndPath> items = display_.getSelectedItems(); 126 if (items.size() == 1) 127 { 128 StatusAndPath item = items.get(0); 129 if (item.isDirectory() && item.getStatus() == "M") 130 { 131 String path = item.getPath(); 132 if (path == ".") 133 path = ""; 134 IgnoreList ignoreList = new IgnoreList(path, new ArrayList<>()); 135 pIgnore_.get().showDialog(ignoreList, ignoreStrategy_); 136 return; 137 } 138 } 139 140 // standard case 141 ArrayList<String> paths = getPathArray(); 142 pIgnore_.get().showDialog(paths, ignoreStrategy_); 143 144 } 145 146 private final Ignore.Strategy ignoreStrategy_ = new Ignore.Strategy() { 147 148 @Override 149 public String getDialogCaption() 150 { 151 return "SVN Ignore"; 152 } 153 154 @Override 155 public String getIgnoresCaption() 156 { 157 return "svn:ignore"; 158 } 159 160 @Override 161 public String getHelpLinkName() 162 { 163 return "svn_ignore_help"; 164 } 165 166 @Override 167 public Filter getFilter() 168 { 169 return null; 170 } 171 172 173 @Override 174 public void getIgnores(String path, 175 ServerRequestCallback<ProcessResult> requestCallback) 176 { 177 server_.svnGetIgnores(path, requestCallback); 178 } 179 180 @Override 181 public void setIgnores(String path, String ignores, 182 ServerRequestCallback<ProcessResult> requestCallback) 183 { 184 server_.svnSetIgnores(path, ignores, requestCallback); 185 } 186 }; 187 188 @Handler onVcsOpen()189 void onVcsOpen() 190 { 191 vcsFileOpener_.openFiles(display_.getSelectedItems()); 192 } 193 194 @Handler onVcsRefresh()195 void onVcsRefresh() 196 { 197 display_.getChangelistTable().showProgress(); 198 svnState_.refresh(true); 199 } 200 201 @Handler onVcsRefreshNoError()202 void onVcsRefreshNoError() 203 { 204 display_.getChangelistTable().showProgress(); 205 svnState_.refresh(false); 206 } 207 208 @Handler onVcsAddFiles()209 void onVcsAddFiles() 210 { 211 ArrayList<String> paths = getPathArray(); 212 213 if (paths.size() > 0) 214 server_.svnAdd(paths, new ProcessCallback("SVN Add")); 215 } 216 217 @Handler onVcsRemoveFiles()218 void onVcsRemoveFiles() 219 { 220 ArrayList<String> paths = getPathArray(); 221 222 if (paths.size() > 0) 223 server_.svnDelete(paths, new ProcessCallback("SVN Delete")); 224 } 225 226 @Handler onVcsRevert()227 void onVcsRevert() 228 { 229 final ArrayList<String> paths = getPathArray(); 230 if (paths.size() == 0) 231 return; 232 233 doRevert(paths, new Command() { 234 @Override 235 public void execute() 236 { 237 display_.getChangelistTable().selectNextUnselectedItem(); 238 display_.getChangelistTable().focus(); 239 } 240 241 }); 242 } 243 244 @Handler onVcsResolve()245 void onVcsResolve() 246 { 247 ArrayList<StatusAndPath> items = display_.getSelectedItems(); 248 249 if (items.size() == 0) 250 return; 251 252 final ArrayList<String> paths = new ArrayList<>(); 253 254 boolean conflict = false; 255 for (StatusAndPath item : items) 256 { 257 paths.add(item.getPath()); 258 if ("C".equals(item.getStatus())) 259 conflict = true; 260 else if (item.isDirectory()) 261 conflict = true; 262 } 263 264 Operation resolveOperation = new Operation() 265 { 266 @Override 267 public void execute() 268 { 269 new SVNResolveDialog( 270 paths.size(), 271 "Resolve", 272 new OperationWithInput<String>() 273 { 274 @Override 275 public void execute(String input) 276 { 277 server_.svnResolve( 278 input, paths, 279 new ProcessCallback("SVN Resolve")); 280 } 281 }).showModal(); 282 } 283 }; 284 285 if (conflict) 286 { 287 resolveOperation.execute(); 288 } 289 else 290 { 291 String message = 292 (paths.size() > 1 ? 293 "None of the selected paths appear to have conflicts." : 294 "The selected path does not appear to have conflicts.") + 295 "\n\nDo you want to resolve anyway?"; 296 297 globalDisplay_.showYesNoMessage(GlobalDisplay.MSG_WARNING, 298 "No Conflicts Detected", 299 message, 300 resolveOperation, 301 true); 302 } 303 } 304 revertFile(FileSystemItem file)305 public void revertFile(FileSystemItem file) 306 { 307 // build an ArrayList<String> so we can call the core helper 308 ArrayList<String> revertList = new ArrayList<>(); 309 for (StatusAndPath item : svnState_.getStatus()) 310 { 311 if (item.getRawPath() == file.getPath()) 312 { 313 revertList.add(item.getPath()); 314 break; 315 } 316 } 317 318 if (revertList.size() > 0) 319 { 320 doRevert(revertList, null); 321 } 322 else 323 { 324 globalDisplay_.showMessage(MessageDialog.INFO, 325 "No Changes to Revert", 326 "There are no changes to the file \"" + 327 file.getName() + "\" to revert."); 328 } 329 330 } 331 doRevert(final ArrayList<String> revertList, final Command onRevertConfirmed)332 private void doRevert(final ArrayList<String> revertList, 333 final Command onRevertConfirmed) 334 { 335 String noun = revertList.size() == 1 ? "file" : "files"; 336 globalDisplay_.showYesNoMessage( 337 GlobalDisplay.MSG_WARNING, 338 "Revert Changes", 339 "Changes to the selected " + noun + " will be reverted.\n\n" + 340 "Are you sure you want to continue?", 341 new Operation() 342 { 343 @Override 344 public void execute() 345 { 346 if (onRevertConfirmed != null) 347 onRevertConfirmed.execute(); 348 349 server_.svnRevert(revertList, 350 new ProcessCallback("SVN Revert")); 351 352 } 353 }, 354 false); 355 } 356 357 getPathArray()358 private ArrayList<String> getPathArray() 359 { 360 ArrayList<StatusAndPath> items = display_.getSelectedItems(); 361 ArrayList<String> paths = new ArrayList<>(); 362 for (StatusAndPath item : items) 363 paths.add(item.getPath()); 364 return paths; 365 } 366 367 private final SVNPresenterDisplay display_; 368 private final GlobalDisplay globalDisplay_; 369 private final Commands commands_; 370 private final SVNServerOperations server_; 371 private final SVNState svnState_; 372 private final VCSFileOpener vcsFileOpener_; 373 private Provider<SVNCommitDialog> pCommitDialog_; 374 private Provider<Ignore> pIgnore_; 375 }