@@ -3656,7 +3656,7 @@ public synchronized void revisionDelete(Boolean hidecontent, Boolean hideuser, B
36563656 if (!temp .getClass ().equals (clazz ))
36573657 throw new IllegalArgumentException ("All Events to be RevisionDeleted must be of the same type." );
36583658 // TODO: Apparently you can RevisionDelete old files (i.e.
3659- // pseudo-LogEntries from getImageHistory and the file archive, but
3659+ // pseudo-LogEntries from getFileHistory and the file archive, but
36603660 // I have no idea how to get the necessary ID parameter.
36613661 // You can also RevisionDelete deleted revisions, but I need to
36623662 // test this first.
@@ -4203,7 +4203,7 @@ public List<Map<String, Object>> getFileMetadata(SequencedCollection<String> fil
42034203 * Commons).
42044204 *
42054205 * @param files the files for checking duplicates (may contain "File")
4206- * @return the duplicates of those files
4206+ * @return the duplicates of those files, in order of input
42074207 * @throws IOException or UncheckedIOException if a network error occurs
42084208 * @throws IllegalArgumentException if any of the files has an invalid title
42094209 * @since 0.18
@@ -4231,25 +4231,28 @@ public List<List<String>> getDuplicates(List<String> files) throws IOException
42314231 }
42324232
42334233 /**
4234- * Returns the upload history of an image . This is not the same as
4234+ * Returns the upload histories of a list of files . This is not the same as
42354235 * {@linkplain #getLogEntries(String, String, Wiki.RequestHelper) fetching
4236- * a page's upload log}, as the image may have been deleted. This returns
4237- * only the live history of an image.
4236+ * each file's upload log individually}, as the file may have been deleted.
4237+ * This returns only the live history of the files. The log entries so
4238+ * returned have the archive filename as details.
42384239 *
4239- * @param title the title of the image (may contain File)
4240- * @return the image history of the image
4240+ * @param titles a bunch of file titles (may contain File)
4241+ * @return the history of the files, in order of input
42414242 * @throws IOException or UncheckedIOException if a network error occurs
4242- * @since 0.20
4243+ * @since 0.39
4244+ * @see #fileRevert(String, String)
42434245 */
4244- public List <LogEntry > getImageHistory ( String title ) throws IOException
4246+ public List <List < LogEntry >> getFileHistory ( SequencedCollection < String > titles ) throws IOException
42454247 {
42464248 Map <String , String > getparams = new HashMap <>();
4249+ List <String > files2 = new ArrayList <>(titles .size ());
4250+ for (String title : titles )
4251+ files2 .add ("File:" + removeNamespace (normalize (title ), FILE_NAMESPACE ));
42474252 getparams .put ("prop" , "imageinfo" );
4248- getparams .put ("iiprop" , "timestamp|user|comment|parsedcomment" );
4249- getparams .put ("titles" , "File:" + removeNamespace (normalize (title ), FILE_NAMESPACE ));
4253+ getparams .put ("iiprop" , "timestamp|user|comment|parsedcomment|archivename" );
42504254
4251- String prefixtitle = namespaceIdentifier (FILE_NAMESPACE ) + ":" + title ;
4252- List <LogEntry > history = makeListQuery ("ii" , getparams , null , "getImageHistory" , -1 , (line , results ) ->
4255+ List <List <LogEntry >> history = makeVectorizedQuery ("ii" , getparams , files2 , "getFileHistory" , -1 , (line , results ) ->
42534256 {
42544257 if (line .contains ("missing=\" \" " ))
42554258 return ;
@@ -4259,18 +4262,24 @@ public List<LogEntry> getImageHistory(String title) throws IOException
42594262 {
42604263 int b = line .indexOf ('>' , a );
42614264 String temp = line .substring (a , b );
4262- LogEntry le = parseLogEntry (temp , null , UPLOAD_LOG , "overwrite" , prefixtitle );
4265+ LogEntry le = parseLogEntry (temp , null , UPLOAD_LOG , "overwrite" , parseAttribute (line , "title" , 0 ));
4266+ le .details = new HashMap <>();
4267+ le .details .put ("archivename" , parseAttribute (temp , "archivename" , 0 ));
42634268 results .add (le );
42644269 }
42654270 });
42664271
4267- // crude hack: action adjusting for first image (in the history, not our list)
4268- int size = history .size ();
4269- if (size == 0 )
4270- return Collections .emptyList ();
4271- LogEntry last = history .get (size - 1 );
4272- last .action = "upload" ;
4273- history .set (size - 1 , last );
4272+ // adjust actions
4273+ for (int i = 0 ; i < history .size (); i ++)
4274+ {
4275+ List <Wiki .LogEntry > history2 = history .get (i );
4276+ int size = history2 .size ();
4277+ if (size == 0 )
4278+ continue ;
4279+ LogEntry last = history2 .get (size - 1 );
4280+ last .action = "upload" ;
4281+ history2 .set (size - 1 , last );
4282+ }
42744283 return history ;
42754284 }
42764285
@@ -4547,6 +4556,48 @@ public synchronized void upload(URL url, String filename, String contents, Strin
45474556 checkErrorsAndUpdateStatus (response , "upload" , null , null );
45484557 log (Level .INFO , "upload" , "Successfully uploaded to File:" + filename + "." );
45494558 }
4559+
4560+ /**
4561+ * Reverts a file to the given previous revision.
4562+ * @param filename the target file name (may contain File)
4563+ * @param filerev a file revision LogEntry from {@link #getFileHistory(List)}
4564+ * @param reason the reason for reverting the file
4565+ * @throws IOException or UncheckedIOException if a network error occurs
4566+ * @throws SecurityException if not logged in
4567+ * @throws CredentialException if (page is protected OR file is on a central
4568+ * repository) and we can't revert the file
4569+ * @throws CredentialExpiredException if cookies have expired
4570+ * @throws AccountLockedException if user is blocked
4571+ * @since 0.39
4572+ */
4573+ public synchronized void fileRevert (String filename , Wiki .LogEntry filerev , String reason ) throws IOException , LoginException
4574+ {
4575+ filename = removeNamespace (filename , FILE_NAMESPACE );
4576+ checkPermissions ("upload" , "upload_by_url" );
4577+ throttle ();
4578+
4579+ // protection
4580+ Map <String , Object > info = getPageInfo (List .of ("File:" + filename )).get (0 );
4581+ if (!checkRights (info , "upload" ))
4582+ {
4583+ CredentialException ex = new CredentialException ("Permission denied: page is protected." );
4584+ log (Level .WARNING , "upload" , "Cannot revert file - permission denied." + ex );
4585+ throw ex ;
4586+ }
4587+
4588+ // send and build request
4589+ Map <String , String > getparams = new HashMap <>();
4590+ getparams .put ("action" , "filerevert" );
4591+ getparams .put ("filename" , normalize (filename ));
4592+ Map <String , Object > postparams = new HashMap <>();
4593+ postparams .put ("token" , getToken ("csrf" ));
4594+ postparams .put ("comment" , reason );
4595+ postparams .put ("archivename" , filerev .getDetails ().get ("archivename" ));
4596+
4597+ String response = makeApiCall (getparams , postparams , "fileRevert" );
4598+ checkErrorsAndUpdateStatus (response , "fileRevert" , null , null );
4599+ log (Level .INFO , "fileRevert" , "Successfully reverted File:" + filename + "." );
4600+ }
45504601
45514602 // USER METHODS
45524603
@@ -6042,7 +6093,7 @@ public List<LogEntry> getLogEntries(String logtype, String action, Wiki.RequestH
60426093
60436094 /**
60446095 * Parses xml generated by <code>getLogEntries()</code>,
6045- * <code>getImageHistory ()</code> and <code>getBlockList()</code> into {@link Wiki.LogEntry}
6096+ * <code>getFileHistory ()</code> and <code>getBlockList()</code> into {@link Wiki.LogEntry}
60466097 * objects. Override this if you want custom log types. NOTE: if
60476098 * RevisionDelete was used on a log entry, the relevant values will be
60486099 * null.
@@ -6090,7 +6141,8 @@ else if (reasonhidden)
60906141 }
60916142 else
60926143 {
6093- reason = parseAttribute (xml , "comment" , 0 );
6144+ // space is important, if comment comes after parsedcomment
6145+ reason = parseAttribute (xml , " comment" , 0 );
60946146 parsedreason = parseAttribute (xml , "parsedcomment" , 0 );
60956147 }
60966148
@@ -6102,7 +6154,7 @@ else if (userhidden)
61026154 user = Wiki .Event .USER_DELETED ;
61036155
61046156 // generic target name
6105- // space is important -- commons.getImageHistory ("File:Chief1.gif");
6157+ // space is important -- commons.getFileHistory ("File:Chief1.gif");
61066158 if (target == null && xml .contains (" title=\" " ))
61076159 target = parseAttribute (xml , "title" , 0 );
61086160 else if (actionhidden )
0 commit comments