PageRenderTime 47ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/atlassian/uwc/converters/tikiwiki/AttachmentConverter.java

https://bitbucket.org/appfusions/universal-wiki-converter
Java | 893 lines | 569 code | 75 blank | 249 comment | 78 complexity | 9daf880d6911946f49628beaf9b6c1be MD5 | raw file
  1. package com.atlassian.uwc.converters.tikiwiki;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.io.UnsupportedEncodingException;
  7. import java.net.URLEncoder;
  8. import java.nio.channels.FileChannel;
  9. import java.sql.Connection;
  10. import java.sql.DriverManager;
  11. import java.sql.ResultSet;
  12. import java.sql.SQLException;
  13. import java.sql.SQLWarning;
  14. import java.sql.Statement;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. import java.util.Set;
  18. import java.util.TreeMap;
  19. import java.util.TreeSet;
  20. import java.util.Vector;
  21. import java.util.regex.Matcher;
  22. import java.util.regex.Pattern;
  23. import org.apache.log4j.Logger;
  24. import com.atlassian.uwc.converters.BaseConverter;
  25. import com.atlassian.uwc.ui.Attachment;
  26. import com.atlassian.uwc.ui.FileUtils;
  27. import com.atlassian.uwc.ui.Page;
  28. import com.atlassian.uwc.ui.UWCUserSettings.Setting;
  29. import com.atlassian.uwc.util.PropertyFileManager;
  30. /**
  31. * Prepares tikiwiki attachments for uploading to Confluence.
  32. * NOTE: This class was heavily influenced by TwikiPrepareAttachmentFilesConverter
  33. * Database methods were from MediawikiExporter
  34. * @author Laura Kolker
  35. */
  36. public class AttachmentConverter extends BaseConverter {
  37. private static final String PREF_COL = "value";
  38. protected static final String FILE_GAL_PREF_SQL = "select value from tiki_preferences where name='fgal_use_dir';";
  39. protected static final String IMAGE_GAL_PREF_SQL = "select value from tiki_preferences where name='gal_use_dir';";
  40. protected static String FILE_SEP = System.getProperty("file.separator");
  41. Logger log = Logger.getLogger(this.getClass());
  42. private Connection con; //DB connection
  43. /* properties fields */
  44. private String propertyLocation;
  45. protected TreeMap<String,String> properties;
  46. private String output;
  47. private String dbName;
  48. private String login;
  49. private String password;
  50. private String dbUrl;
  51. private String jdbcDriver;
  52. /* CONSTANTS */
  53. private static final String DEFAULT_PROPERTIES_LOCATION = "settings.tikiwiki.properties";
  54. private static final String DEFAULT_PROPERTIES_DIR = "conf/";
  55. protected static final String PROPERTIES_CONVERTGALLERY="Tikiwiki.gallery-conversion-on.switch";
  56. private static final String PROPERTIES_OUTPUTDIR = "Tikiwiki.output-dir.setting";
  57. private static final String PROPERTIES_DBNAME = "Tikiwiki.dbname.dbSetting";
  58. private static final String PROPERTIES_LOGIN = "Tikiwiki.login.dbSetting";
  59. private static final String PROPERTIES_PASSWORD = "Tikiwiki.password.dbSetting";
  60. private static final String PROPERTIES_DBURL = "Tikiwiki.connection-url.dbSetting";
  61. private static final String PROPERTIES_DRIVER = "Tikiwiki.driver.dbSetting";
  62. public enum GalleryType {
  63. IMAGE,
  64. FILE
  65. };
  66. public void convert(Page page) {
  67. log.debug("Converting Tikiwiki Attachments -- starting");
  68. // scan the page and create a list of attachments
  69. addAttachmentsToPage(page, this.getAttachmentDirectory());
  70. log.debug("Converting Tikiwiki Attachments -- complete");
  71. }
  72. /**
  73. * looks for attachments and attaches them
  74. * @param page object to attach page
  75. * s to
  76. */
  77. protected void addAttachmentsToPage(Page page, String attachDir) {
  78. //simplify image syntax to one code path
  79. String standardized = standardizeImageSyntax(page.getOriginalText());
  80. //two types of attachments, images uploaded to pages & gallery files
  81. //images uploaded to pages
  82. Vector<String> uploadPaths = getUploadPaths(standardized, attachDir);
  83. //gallery files
  84. Vector<String> paths = null;
  85. this.propertyLocation = getPropertyLocation();
  86. if(readDBProperties(this.propertyLocation) && isGalleryConversion()) {
  87. //fix the syntax
  88. standardized = replaceIdsWithNames(standardized);
  89. //get the paths to the files
  90. paths = getAllGalleryPaths(
  91. standardized, attachDir, uploadPaths);
  92. }
  93. if (paths == null) paths = uploadPaths; //no gallery paths
  94. attach(paths, page);
  95. if (Boolean.parseBoolean(getProperties().getProperty("attachments-attachall", "false")))//default is false
  96. addAttachmentsNotReferredInMarkup(page);
  97. //save changes to image syntax
  98. page.setConvertedText(standardized);
  99. closeDB();
  100. }
  101. private void addAttachmentsNotReferredInMarkup(Page page) {
  102. String defaultsql = "select filename, path, \"user\", created, comment from tiki_wiki_attachments where page ='"+page.getName()+"'"; //yes, this is ugly
  103. String sql = getProperties().getProperty("attachments-attachall-sql", defaultsql);
  104. sql = sql.replaceAll("[?]", page.getName());
  105. String defaultencoding = "utf-8";
  106. String encoding = getProperties().getProperty("attachments-attachall-dbencoding", defaultencoding);
  107. log.debug("Adding all attachments with sql: " + sql);
  108. // NOTE: postgres has a keyword "user", so we must use double quotes to "escape" unless we want the pg connect user...
  109. ResultSet rs = sql(sql);
  110. try {
  111. while (rs.next()) {
  112. // pageAttachments.add(new String[] { rs.getString(1), rs.getString(2) } );
  113. String attName = rs.getString(1);
  114. attName = new String(attName.getBytes(encoding), "utf-8");
  115. File attFile = new File(getAttachmentDirectory(), rs.getString(2));
  116. if (!attFile.exists() || !attFile.canRead()) {
  117. log.error("attachments '"+attName+"' (file '"+attFile.getAbsolutePath()+"' for page='"+page.getName()+"' not found or unreadable");
  118. }
  119. String attUser = rs.getString(3);
  120. Long attEpoch = rs.getLong(4);
  121. String attComment = rs.getString(5);
  122. page.addAttachment(new Attachment(attName, attFile, attUser, attEpoch, attComment));
  123. }
  124. } catch (Exception e) {
  125. log.error("failed retrieving attachments for page='"+page.getName()+"'", e);
  126. }
  127. }
  128. /**
  129. * translates any references to images with id numbers,
  130. * to images with filenames
  131. * @param input wiki syntax
  132. * @return input with ids translated to filenames
  133. */
  134. protected String replaceIdsWithNames(String input) {
  135. //fix image gallery ids
  136. String defaultsql = "select imageId,name from tiki_images;";
  137. String sql = getProperties().getProperty("attachments-imagegalids-sql", defaultsql);
  138. String colId = "imageId";
  139. String colName = "name";
  140. HashMap<String,String> imageNames = getIdsAndNames(sql, colId, colName);
  141. //fix file gallery ids
  142. defaultsql = "select fileId, fileName from tiki_files;";
  143. sql = getProperties().getProperty("attachments-filegalids-sql", defaultsql);
  144. colId = "fileId";
  145. colName = "fileName";
  146. HashMap<String,String> fileNames = getIdsAndNames(sql, colId, colName);
  147. //replace the input ids with filenames
  148. input = replaceImageIds(input, imageNames, GalleryType.IMAGE);
  149. input = replaceImageIds(input, fileNames, GalleryType.FILE);
  150. return input;
  151. }
  152. /**
  153. * gets a vector of all unique filepaths for gallery images or files.
  154. * @param input wiki syntax that refers to gallery attachments
  155. * @param attachDir directory where these files would live (attachment directory in the UI)
  156. * @param existingPaths vector of paths that will be combined with the
  157. * paths that will be found
  158. * @return vector containing the given existing paths, and any paths to gallery images and files.
  159. *
  160. */
  161. protected Vector<String> getAllGalleryPaths(String input, String attachDir, Vector<String> existingPaths) {
  162. //get image gallery paths
  163. Vector<String> imageGalleryPaths = getImageGalleryPaths(input, attachDir);
  164. //get file gallery paths
  165. Vector<String> fileGalleryPaths = getFileGalleryPaths(input, attachDir);
  166. //combine and uniquify
  167. Vector<String> paths = combineVectors(existingPaths, imageGalleryPaths);
  168. paths = combineVectors(paths, fileGalleryPaths);
  169. return paths;
  170. }
  171. /**
  172. * makes all image syntax follow the sames rules
  173. * regarding quotes and using http urls
  174. * @param input input with tikiwiki img syntax
  175. * @return all img syntax, standardized to the same rules
  176. */
  177. protected String standardizeImageSyntax(String input) {
  178. String standardized = standardizeQuotes(input);
  179. standardized = standardizeUrl(standardized);
  180. return standardized;
  181. }
  182. String noQuotes = "(\\{img src=)([^\"} ]+)([ }])";
  183. /**
  184. * makes all image syntax use the same quotes rules.
  185. * Example: If the input contains {img src=something}
  186. * then the return value would be {img src="something"}
  187. * @param input input with tikiwiki img syntax
  188. * @return input with all img syntax using the same rules for quotes
  189. */
  190. private String standardizeQuotes(String input) {
  191. String standardized = decodeEntities(input);
  192. String replacement = "{group1}\"{group2}\"{group3}";
  193. standardized = RegexUtil.loopRegex(standardized, noQuotes, replacement);
  194. return standardized;
  195. }
  196. String noProtocol = "(\\{img\\s+src=\")http.*?((tiki-download_file)|(img/wiki_up))";
  197. /**
  198. * removes all references to URLs in image syntax.
  199. * (We assume that we have access to the file system (through
  200. * the attachment directory setting in the UI), and to the
  201. * database (settings.tikiwiki.properties), and therefore,
  202. * references to the URL of this tikiwiki are unnecessary.
  203. * @param input tikiwiki input with img syntax
  204. * @return all img syntax has been standardized regarding urls
  205. */
  206. protected String standardizeUrl(String input) {
  207. String replacement = "{group1}{group2}";
  208. return RegexUtil.loopRegex(input, noProtocol, replacement);
  209. }
  210. String script = ".*\\.php\\?.*$";
  211. Pattern scriptPattern = Pattern.compile(script);
  212. /**
  213. * attaches given String Vector of paths to page
  214. * @param paths
  215. * @param page
  216. */
  217. protected void attach(Vector<String> paths, Page page) {
  218. //foreach path in paths
  219. for (String path : paths) {
  220. //get the complete path to the file
  221. log.debug("complete path = " + path);
  222. //confirm existance of file
  223. File file = new File(path);
  224. if (!file.exists() || file.isDirectory()) {
  225. if (path.contains("http://")) continue;
  226. Matcher scriptMatcher = scriptPattern.matcher(path);
  227. //if it's not a reference to a php script (which will need the DB to translate)
  228. if (!scriptMatcher.find()) { // give the user an error message
  229. String message = "Attachment '" + path + "' " +
  230. "does not exist or is a directory. Skipping.";
  231. log.warn(Feedback.BAD_FILE + ": " + message);
  232. addError(Feedback.BAD_FILE, message, true);
  233. }
  234. continue;
  235. }
  236. //attach the file
  237. log.info("adding attachment: " + file.getName());
  238. log.debug("attachment path: " + file.getPath());
  239. page.addAttachment(file);
  240. }
  241. }
  242. String imgSrc = "\\{img src=\"([^\"]+)\"";
  243. Pattern imgPattern = Pattern.compile(imgSrc);
  244. String notUploadPath = "show_image\\.php";
  245. Pattern notUploadPattern = Pattern.compile(notUploadPath);
  246. /**
  247. * @param input page text
  248. * @param attachDir directory to tikiwiki
  249. * @return list of absolute paths to images
  250. */
  251. protected Vector<String> getUploadPaths(String input, String attachDir) {
  252. log.debug("Getting Upload Paths");
  253. Vector<String> paths = new Vector<String>();
  254. String root = attachDir; //XXX this needs to be the directory to tikiwiki
  255. Matcher imgFinder = imgPattern.matcher(input);
  256. String localsep = FILE_SEP;
  257. //look for {img src= and get the filepath from there
  258. while (imgFinder.find()) {
  259. String path = imgFinder.group(1);
  260. path = path.replaceAll("[\\/\\\\]", "\\" + localsep);
  261. Matcher notUploadFinder = notUploadPattern.matcher(path);
  262. if (notUploadFinder.find()) continue;
  263. if (!path.startsWith(localsep) && !root.endsWith(localsep)) {
  264. root += localsep;
  265. }
  266. if (root.matches("[A-Za-z]:" +"\\"+localsep)) {
  267. root += localsep;
  268. }
  269. if (!path.startsWith("http://"))
  270. path = root + path;
  271. log.debug("upload path = " + path);
  272. paths.add(path);
  273. }
  274. return paths;
  275. }
  276. String entity = "&((?:quot)|(?:amp)|(?:lt)|(?:gt));"; //find ", &, <, > html entities
  277. Pattern entityPattern = Pattern.compile(entity);
  278. /**
  279. * @param input
  280. * @return input string with decoded entities
  281. * <br/>&amp;quot; becomes "
  282. * <br/>&amp;amp; become &
  283. * <br/>&amp;lt; become <
  284. * <br/>&amp;gt; become >
  285. */
  286. private String decodeEntities(String input) {
  287. Matcher entityFinder = entityPattern.matcher(input);
  288. StringBuffer sb = new StringBuffer();
  289. boolean found = false;
  290. while (entityFinder.find()) {
  291. found = true;
  292. String thisEntity = entityFinder.group(1);
  293. String replacement = "";
  294. if ("quot".equals(thisEntity)) replacement = "\"";
  295. else if("amp".equals(thisEntity)) replacement = "&";
  296. else if("lt".equals(thisEntity)) replacement = "<";
  297. else if("gt".equals(thisEntity)) replacement = ">";
  298. entityFinder.appendReplacement(sb, replacement);
  299. }
  300. if (found) {
  301. entityFinder.appendTail(sb);
  302. return sb.toString();
  303. }
  304. return input;
  305. }
  306. /**
  307. * gets all the image gallery paths for a given input and attachment directory
  308. * @param input
  309. * @param attachDir
  310. * @return vector of paths to relevant image gallery files
  311. */
  312. private Vector<String> getImageGalleryPaths(String input, String attachDir) {
  313. String baseDir = getImageGalleryDirectory(attachDir);
  314. Vector<String> systemPaths = getGalleryPaths(input, "tiki_images", "name", baseDir);
  315. return systemPaths;
  316. }
  317. /**
  318. * gets all the file gallery paths for a given input and attachment directory
  319. * @param input
  320. * @param attachDir
  321. * @return vector of paths to relevant file gallery files
  322. */
  323. private Vector<String> getFileGalleryPaths(String input, String attachDir){
  324. String baseDir = getFileGalleryDirectory(attachDir);
  325. Vector<String> systemPaths = getGalleryPaths(input, "tiki_files", "fileName", baseDir);
  326. Vector<String> dbPaths = downloadDbFilesToTmp(input, "tiki_files", "fileName", getTmpDir());
  327. return combineVectors(systemPaths, dbPaths);
  328. }
  329. /**
  330. * find the tikiwiki setting regarding where the image gallery files are
  331. * saved on the file system
  332. * @param attachDir the attachment directory setting that was passed in by the GUI.
  333. * useful if the database only has a relative path. Unimportant if the database has
  334. * an absolute path.
  335. * @return path to the directoy on the file system that contains image gallery files
  336. */
  337. protected String getImageGalleryDirectory(String attachDir) {
  338. return getDirectoryFromDbPrefs(IMAGE_GAL_PREF_SQL, attachDir);
  339. }
  340. /**
  341. * find the tikiwiki setting regarding where the file gallery files are
  342. * saved on the file system
  343. * @param attachDir the attachment directory setting that was passed in by the GUI.
  344. * useful if the database only has a relative path. Unimportant if the database has
  345. * an absolute path.
  346. * @return path to the directoy on the file system that contains file gallery files
  347. */
  348. protected String getFileGalleryDirectory(String attachDir) {
  349. return getDirectoryFromDbPrefs(FILE_GAL_PREF_SQL, attachDir);
  350. }
  351. /**
  352. * finds the directory for a gallery from the database,
  353. * @param sql SQL syntax that would find the directory from
  354. * the tikiwiki preferences table
  355. * @param attachDir attachment directory passed in by the GUI
  356. * @return path to a directory where files are stored
  357. */
  358. protected String getDirectoryFromDbPrefs(String sql, String attachDir) {
  359. connectToDB(this.properties);
  360. ResultSet results = this.sql(sql);
  361. String dir = null;
  362. try {
  363. while (results.next()) {
  364. dir = results.getString(PREF_COL);
  365. }
  366. } catch (SQLException e) {
  367. log.error("An error occurred while getting the image gallery directory.");
  368. e.printStackTrace();
  369. }
  370. dir = getAbsolutePath(dir, attachDir);
  371. return dir;
  372. }
  373. /**
  374. * gets the absolute path for a given input, and with a given parent directory.
  375. * @param input a directory that might be a relative path
  376. * @param attachDir a parent dir, for use if the input is a relative path
  377. * @return an absolute path to a directory
  378. */
  379. protected String getAbsolutePath(String input, String attachDir) {
  380. if (input.startsWith("./")) {
  381. input = input.substring(1); //remove .
  382. return attachDir + input;
  383. }
  384. return input;
  385. }
  386. String imgParam = "\\[\\^(.*)\\]";
  387. Pattern imgParamPattern = Pattern.compile(imgParam);
  388. /**
  389. * gets the paths to gallery attachments (files or images), referenced by the given input
  390. * @param input tikiwiki syntax that refers to attachments that would be found in a gallery
  391. * @param table the table name containing the paths for the particular gallery
  392. * @param column the column name for that table that would contain the file names
  393. * @param baseFileDir parent directory where the files would live on the file system
  394. * @return vector of absolute paths on the file system that the input refers to
  395. */
  396. protected Vector<String> getGalleryPaths(
  397. String input, String table, String column, String baseFileDir){
  398. log.debug("Getting Gallery Paths");
  399. Vector<String> paths = new Vector<String>();
  400. String decoded = decodeEntities(input);
  401. Matcher imgNameFinder = imgParamPattern.matcher(decoded);
  402. connectToDB(this.properties);
  403. String pathCol = "path";
  404. while (imgNameFinder.find()) {
  405. String name = imgNameFinder.group(1);
  406. name = name.trim();
  407. String filenameSql = "select "+pathCol+" from "+table+" where "+column+" like '" + name + "';";
  408. ResultSet filenameData = sql(filenameSql);
  409. try {
  410. String path = null;
  411. while (filenameData.next()) {
  412. path = filenameData.getString(pathCol);
  413. break; // I only want one filename. If we got multiple rows, too bad.
  414. }
  415. if (path == null) continue;
  416. if ("".equals(path)) continue;
  417. log.debug("baseFileDir = " + baseFileDir);
  418. log.debug("path = " + path);
  419. if (baseFileDir == null) baseFileDir = "";
  420. String fullpath = baseFileDir + path;
  421. log.debug("fullpath = " + fullpath);
  422. String newpath = copyFile(fullpath, this.output, name);
  423. if (newpath != null)
  424. paths.add(newpath);
  425. } catch (SQLException e) {
  426. log.error("Error while examining filename data.");
  427. e.printStackTrace();
  428. }
  429. }
  430. // closeDB(); //we're closing the connection at the end
  431. return paths;
  432. }
  433. /**
  434. * determines which attachments are saved in the database,
  435. * downloads them to a tmp directory,
  436. * and adds the path to the file (via the tmp directory) to the
  437. * return vector
  438. * @param input syntax with references to attachable files
  439. * @param table db table where images might be saved
  440. * @param column db column in the table where images might be saved
  441. * @param tmpDir directory where images should be saved to
  442. * @return Vector of paths to saved tmp images
  443. */
  444. protected Vector<String> downloadDbFilesToTmp(
  445. String input, String table, String column, String tmpDir) {
  446. //XXX Somewhere in here we need to copy the globbed data files that might exist
  447. //to a tmp directory, and put that path in the Vector
  448. log.debug("Downloading DB Files");
  449. log.debug("table = " + table);
  450. log.debug("col = " + column);
  451. log.debug("tmpDir = " + tmpDir);
  452. Vector<String> paths = new Vector<String>();
  453. String decoded = decodeEntities(input);
  454. Matcher imgNameFinder = imgParamPattern.matcher(decoded);
  455. connectToDB(this.properties);
  456. String dataCol = "data";
  457. while (imgNameFinder.find()) {
  458. String name = imgNameFinder.group(1);
  459. String filenameSql = "select "+dataCol+" from "+table+" where "+column+" like '" + name + "';";
  460. ResultSet filenameData = sql(filenameSql);
  461. try {
  462. byte[] data = null;
  463. while (filenameData.next()) {
  464. data = filenameData.getBytes(dataCol);
  465. break; // I only want one filename. If we got multiple rows, too bad.
  466. }
  467. if (data == null) continue;
  468. if (data.length == 0) continue;
  469. String newpath = tmpDir + FILE_SEP + name;
  470. boolean succeeded = FileUtils.writeFile(data, newpath);
  471. if (newpath != null && succeeded)
  472. paths.add(newpath);
  473. else
  474. log.error("There was a problem writing to the file.");
  475. } catch (SQLException e) {
  476. log.error("Error while examining filename data.");
  477. e.printStackTrace();
  478. }
  479. }
  480. // closeDB();
  481. return paths;
  482. }
  483. /**
  484. * copies file at fromFilePath to directory toDir with the name toFilename
  485. * @param fromFilePath
  486. * @param toDir
  487. * @param toFilename
  488. * @return path to new file
  489. */
  490. private String copyFile(String fromFilePath, String toDir, String toFilename) {
  491. //check that fromFilePath exists and is a file
  492. String root = this.getAttachmentDirectory(); //testing nullness so unit tests work
  493. if (!fromFilePath.startsWith(FILE_SEP) && !root.endsWith(FILE_SEP)) { //FIXME what about windows seperator (\\)?
  494. root += FILE_SEP;
  495. }
  496. File fromFile = new File(fromFilePath);
  497. if (!fromFile.exists() || fromFile.isDirectory()) {
  498. log.error("Attachment does not exist: " + fromFilePath);
  499. return null; //can't copy nonexistant file
  500. }
  501. //check that toDir exists and is a dir
  502. File toDirFile = new File(toDir);
  503. if (!toDirFile.exists()) {
  504. log.debug("Creating output directory: " + toDir);
  505. toDirFile.mkdir(); //try making the file
  506. }
  507. if (!toDirFile.exists() && !toDirFile.isDirectory()) {
  508. log.error("Cannot copy to this directory: " + toDir);
  509. return null; //otherwise give up
  510. }
  511. //create toFilePath
  512. if (!toDir.endsWith(FILE_SEP))
  513. toDir = toDir + FILE_SEP;
  514. String toFilePath = toDir + toFilename;
  515. File toFile = new File(toFilePath);
  516. if (toFile.exists()) {
  517. // FIXME more than one file by the same name, currently overwriting
  518. log.debug("File already exists: " + toFilePath);
  519. }
  520. //copy file
  521. boolean success = false;
  522. try {
  523. copy(fromFile, toFile);
  524. success = true;
  525. } catch (IOException e) {
  526. success = false;
  527. log.error("Could not copy: " + fromFilePath + " to " + toFilePath);
  528. e.printStackTrace();
  529. }
  530. log.debug("Copied file? " + success);
  531. return success?toFilePath:null; //return null if copy was unsuccessful
  532. }
  533. /**
  534. * copies files.
  535. * From the Java Developer's Almanac:
  536. * http://www.exampledepot.com/egs/java.nio/File2File.html
  537. * @param fromFile
  538. * @param toFile
  539. * @throws IOException
  540. */
  541. private void copy(File fromFile, File toFile) throws IOException{
  542. FileChannel srcChannel = new FileInputStream(fromFile).getChannel();
  543. FileChannel dstChannel = new FileOutputStream(toFile).getChannel();
  544. // Copy file contents from source to destination
  545. dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
  546. // Close the channels
  547. srcChannel.close();
  548. dstChannel.close();
  549. }
  550. /**
  551. * @return true if tikiwiki setting indicates gallery conversions should be attempted
  552. */
  553. protected boolean isGalleryConversion() {
  554. String isGC = this.properties.get(PROPERTIES_CONVERTGALLERY);
  555. return ("true".equals(isGC))?true:false;
  556. }
  557. /**
  558. * gets a map with id->name relationships that the sql refers to
  559. * @param sql sql that would return results that use colId and colName
  560. * as the columns for objects that have an id->name relationship
  561. * @param colId column used by the sql that refers to ids
  562. * @param colName column used by the sql that refers to names
  563. * @return map. keys = ids, values = names.
  564. */
  565. protected HashMap<String,String> getIdsAndNames(String sql, String colId, String colName) {
  566. HashMap<String,String> map = new HashMap<String,String>();
  567. connectToDB(properties);
  568. ResultSet imageData = sql(sql);
  569. try {
  570. while (imageData.next()) {
  571. String id = imageData.getString(colId);
  572. String name = imageData.getString(colName);
  573. map.put(id, name);
  574. }
  575. } catch (SQLException e) {
  576. log.error("Problem while examining image data.");
  577. e.printStackTrace();
  578. }
  579. // closeDB(); //we're closing this at then end
  580. return map;
  581. }
  582. String imgIdParam =
  583. "(" + //start capturing (group1)
  584. "\\{" + //open curly brace
  585. "img" + //the string 'img'
  586. "\\s+?" + //at least one space until
  587. "src=" + //the string 'src='
  588. "\"" + //a double quote
  589. "(" + //start capturing (group 2)
  590. "(?:show_image)" + //non capturing group and string 'show_image'
  591. "|" + //or
  592. "(?:tiki-download_file)" + //non captring group and string 'tiki-download_file.php'
  593. ")" + //end capturing (group 2)
  594. "\\.php\\?" + //the string '.php?'
  595. ")" + //end capturing (group1)
  596. "(?:" + //non capturing group
  597. "(?:" + //non capturing group
  598. "file" + //the string 'file'
  599. ")" + //close non capturing group
  600. "?" + //make previous group optional
  601. "(?i)" + //case insensitivity going forward
  602. "id=" + //the string 'id='
  603. ")" + //close non-capturing group
  604. "(" + //start capturing (group 2)
  605. "[^\"& ]+" + //slurp greedily anything not a double quote, space, or &
  606. ")"; //end capturing (group2)
  607. Pattern imgIdPattern = Pattern.compile(imgIdParam);
  608. /**
  609. * @param input tikiwiki syntax
  610. * @param idsAndNames map of id=>filename key value pairs
  611. * @param type replacing image gallery string or file gallery strings
  612. * @return input with id=num in img syntax, replaced with name=filename
  613. */
  614. protected String replaceImageIds(
  615. String input, HashMap<String, String> idsAndNames, GalleryType type) {
  616. String opt = getProperties().getProperty("attachments-imgId-regex", null);
  617. Matcher imgIdFinder = (opt != null) //if opt is not null
  618. ?Pattern.compile(opt).matcher(input)//use opt pattern
  619. :imgIdPattern.matcher(input); //otherwise use default pattern
  620. log.debug("Replacing ids with names");
  621. StringBuffer sb = new StringBuffer();
  622. boolean found = false;
  623. while (imgIdFinder.find()) {
  624. found = true;
  625. String pre = imgIdFinder.group(1);
  626. // have to check which file map we're using
  627. // (can't combine because they might have overlapping keys)
  628. String typeString = imgIdFinder.group(2);
  629. if (type == GalleryType.IMAGE && !typeString.startsWith("show_image"))
  630. continue;
  631. else if (type == GalleryType.FILE && !typeString.startsWith("tiki-download_file"))
  632. continue;
  633. //figure out the name that goes with this id
  634. String id = imgIdFinder.group(3);
  635. log.debug("..id = " + id);
  636. String name = idsAndNames.get(id);
  637. log.debug("..name= " + name);
  638. //create the replacement string
  639. String replacement = pre + "name=" + name;
  640. log.debug("..replacement = " + replacement);
  641. imgIdFinder.appendReplacement(sb, replacement);
  642. }
  643. if (found) {
  644. imgIdFinder.appendTail(sb);
  645. return sb.toString();
  646. }
  647. return input;
  648. }
  649. /**
  650. * combines elements from a and b into one vector. Common elements are combined as one in the new vector.
  651. * See AttachmentConverterTest.testCombineVectors
  652. * @param a
  653. * @param b
  654. * @return one Vector of all unique elements from a and b
  655. */
  656. protected Vector<String> combineVectors(Vector<String> a, Vector<String> b) {
  657. Set<String> c = new TreeSet<String>();
  658. c.addAll(a);
  659. c.addAll(b);
  660. Vector<String> d = new Vector<String>();
  661. d.addAll(c);
  662. return d;
  663. }
  664. /**
  665. * gets the database properties defined in a file at the given propLocation
  666. * @param propLocation
  667. * @return true if no problems were encountered.
  668. * false, if problems were encountered. If so, see log for error messages.
  669. */
  670. protected boolean readDBProperties(String propLocation) {
  671. TreeMap<String, String> props = null;
  672. try {
  673. props = PropertyFileManager.loadPropertiesFile(propLocation);
  674. } catch (IOException e) {
  675. String message = "Could not load file at '" + propLocation + "'.\n" +
  676. "Gallery attachments cannot be converted.\n" +
  677. "Note: File permissions may be too restrictive.";
  678. this.addError(Feedback.BAD_SETTINGS_FILE, message, true);
  679. log.error(message);
  680. return false;
  681. }
  682. if (props == null) {
  683. String message = "Properties file at '"+propLocation+"' could not be found.\n" +
  684. "Gallery attachments cannot be converted.";
  685. this.addError(Feedback.BAD_SETTINGS_FILE, message, true);
  686. log.error(message);
  687. return false;
  688. }
  689. if (props.isEmpty()) {
  690. String message = "Properties file at '" + propLocation + "' contains no settings!\n" +
  691. "Please customize this file if \n" +
  692. "you wish gallery attachments to be converted.";
  693. this.addError(Feedback.BAD_SETTINGS_FILE, message, true);
  694. log.warn(message);
  695. return false;
  696. }
  697. log.debug("Loaded file: " + propLocation);
  698. this.properties=props;
  699. return true;
  700. }
  701. /* Database Methods
  702. * From com.atlassian.uwc.exporters.MediawikiExporter
  703. */
  704. /**
  705. * connects to the database described by the given properties, unless the connection already exists.
  706. * @param props Map of properties. See example file export.mediawiki.properties
  707. */
  708. private void connectToDB(Map props) {
  709. //if we already have one, don't worry about it
  710. if (con != null) return;
  711. dbName = (String) props.get(PROPERTIES_DBNAME);
  712. dbUrl = (String) props.get(PROPERTIES_DBURL);
  713. login = (String) props.get(PROPERTIES_LOGIN);
  714. password = (String) props.get(PROPERTIES_PASSWORD);
  715. output = (String) props.get(PROPERTIES_OUTPUTDIR);
  716. jdbcDriver = (String) props.get(PROPERTIES_DRIVER);
  717. try {
  718. //load driver
  719. Class.forName(jdbcDriver);
  720. //connect to db
  721. String url = dbUrl + "/" + dbName;
  722. con = DriverManager.getConnection(url, login, password);
  723. } catch (ClassNotFoundException e) {
  724. String note = "Could not load JDBC driver: " + jdbcDriver;
  725. log.error(note);
  726. this.addError(Feedback.DB_DRIVER_FAILURE, note, true);
  727. e.printStackTrace();
  728. } catch (SQLException e) {
  729. String note = "Could not connect to database: " + dbName;
  730. log.error(note);
  731. this.addError(Feedback.DB_FAILURE, note, true);
  732. e.printStackTrace();
  733. }
  734. }
  735. /**
  736. * closes the currently opened database, if a connection still exists.
  737. */
  738. private void closeDB() {
  739. if (con != null) {
  740. try {
  741. con.close();
  742. con = null;
  743. } catch (SQLException e) {
  744. log.error("Error while closing JDBC connection");
  745. e.printStackTrace();
  746. }
  747. }
  748. }
  749. /**
  750. * Runs an sql SELECT query.
  751. * @param sql String, sql SELECT query
  752. * @return ResultSet of select query results
  753. */
  754. private ResultSet sql(String sql) {
  755. return sql(sql, false);
  756. }
  757. /**
  758. * runs an sql query.
  759. * @param sql the sql string to be run
  760. * @param isUpdate true if is an update, delete, or such type query. false, if just select
  761. * @return ResultSet object with results from select query, or null. (Notice, return will
  762. * always be null if isUpdate is true)
  763. */
  764. private ResultSet sql(String sql, boolean isUpdate) {
  765. Statement sqlStatement = null;
  766. String message = "";
  767. ResultSet result = null;
  768. try {
  769. message = "Creating statement: " + sql;
  770. sqlStatement = con.createStatement();
  771. log.debug(message);
  772. message = "Executing statement: " + sql;
  773. if (isUpdate) {
  774. sqlStatement.executeUpdate(sql);
  775. }
  776. else {
  777. result = sqlStatement.executeQuery(sql);
  778. }
  779. log.debug(message);
  780. SQLWarning warn = sqlStatement.getWarnings();
  781. while (warn != null) {
  782. log.warn(warn.getErrorCode() + "\n" +
  783. warn.getMessage() + "\n" +
  784. warn.getSQLState());
  785. warn = warn.getNextWarning();
  786. }
  787. } catch (SQLException e) {
  788. log.error("Error while: " + message);
  789. e.printStackTrace();
  790. }
  791. return result;
  792. }
  793. /**
  794. * @return property location. If null, will return the default.
  795. */
  796. public String getPropertyLocation() {
  797. if (this.propertyLocation == null)
  798. this.propertyLocation = DEFAULT_PROPERTIES_DIR + DEFAULT_PROPERTIES_LOCATION;
  799. return this.propertyLocation;
  800. }
  801. /**
  802. * sets the property location
  803. * @param location
  804. */
  805. public void setPropertyLocation (String location) {
  806. this.propertyLocation = location;
  807. }
  808. /**
  809. * the tmp directory is where attachments will be copied to if their
  810. * original incarnation does not have the filename we want to upload the attachment
  811. * with
  812. * @return the tmp directory
  813. */
  814. public String getTmpDir() {
  815. return this.properties.get(PROPERTIES_OUTPUTDIR);
  816. }
  817. }