contentmediautils.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #include "contentmediautils.h"
  2. #include <QDebug>
  3. #include <QSet>
  4. #include <QFileInfo>
  5. #include <QDir>
  6. #include <QHash>
  7. #include <notebookbackend/inotebookbackend.h>
  8. #include <notebook/node.h>
  9. #include <buffer/filetypehelper.h>
  10. #include <vtextedit/markdownutils.h>
  11. #include <vtextedit/textutils.h>
  12. #include <utils/pathutils.h>
  13. #include <utils/fileutils.h>
  14. #include <core/file.h>
  15. #include <core/exception.h>
  16. using namespace vnotex;
  17. void ContentMediaUtils::copyMediaFiles(Node *p_node,
  18. INotebookBackend *p_backend,
  19. const QString &p_destFilePath)
  20. {
  21. Q_ASSERT(p_node->hasContent());
  22. auto file = p_node->getContentFile();
  23. if (file->getContentType().isMarkdown()) {
  24. copyMarkdownMediaFiles(file->read(),
  25. PathUtils::parentDirPath(file->getContentPath()),
  26. p_backend,
  27. p_destFilePath);
  28. }
  29. }
  30. void ContentMediaUtils::copyMediaFiles(const QString &p_filePath,
  31. INotebookBackend *p_backend,
  32. const QString &p_destFilePath)
  33. {
  34. const auto &fileType = FileTypeHelper::getInst().getFileType(p_filePath);
  35. if (fileType.isMarkdown()) {
  36. copyMarkdownMediaFiles(FileUtils::readTextFile(p_filePath),
  37. PathUtils::parentDirPath(p_filePath),
  38. p_backend,
  39. p_destFilePath);
  40. }
  41. }
  42. void ContentMediaUtils::copyMediaFiles(const File *p_file,
  43. const QString &p_destFilePath)
  44. {
  45. if (p_file->getContentType().isMarkdown()) {
  46. copyMarkdownMediaFiles(p_file->read(),
  47. p_file->getResourcePath(),
  48. nullptr,
  49. p_destFilePath);
  50. }
  51. }
  52. void ContentMediaUtils::copyMarkdownMediaFiles(const QString &p_content,
  53. const QString &p_basePath,
  54. INotebookBackend *p_backend,
  55. const QString &p_destFilePath)
  56. {
  57. auto content = p_content;
  58. // Images.
  59. const auto images =
  60. vte::MarkdownUtils::fetchImagesFromMarkdownText(content,
  61. p_basePath,
  62. vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
  63. QDir destDir(PathUtils::parentDirPath(p_destFilePath));
  64. QSet<QString> handledImages;
  65. QHash<QString, QString> renamedImages;
  66. int lastPos = content.size();
  67. for (const auto &link : images) {
  68. Q_ASSERT(link.m_urlInLinkPos < lastPos);
  69. lastPos = link.m_urlInLinkPos;
  70. qDebug() << "link" << link.m_path << link.m_urlInLink;
  71. if (handledImages.contains(link.m_path)) {
  72. auto it = renamedImages.find(link.m_path);
  73. if (it != renamedImages.end()) {
  74. content.replace(link.m_urlInLinkPos, link.m_urlInLink.size(), it.value());
  75. }
  76. continue;
  77. }
  78. handledImages.insert(link.m_path);
  79. if (!QFileInfo::exists(link.m_path)) {
  80. qWarning() << "image of Markdown file does not exist" << link.m_path << link.m_urlInLink;
  81. continue;
  82. }
  83. // Get the relative path of the image and apply it to the dest file path.
  84. const auto decodedUrlInLink = vte::TextUtils::decodeUrl(link.m_urlInLink);
  85. const auto oldDestFilePath = destDir.filePath(decodedUrlInLink);
  86. destDir.mkpath(PathUtils::parentDirPath(oldDestFilePath));
  87. auto destFilePath = p_backend ? p_backend->renameIfExistsCaseInsensitive(oldDestFilePath)
  88. : FileUtils::renameIfExistsCaseInsensitive(oldDestFilePath);
  89. if (oldDestFilePath != destFilePath) {
  90. // Rename happens.
  91. const auto oldFileName = PathUtils::fileName(oldDestFilePath);
  92. const auto newFileName = PathUtils::fileName(destFilePath);
  93. qWarning() << QString("image name conflicts when copy, renamed from (%1) to (%2)").arg(oldFileName, newFileName);
  94. // Update the text content.
  95. const auto encodedOldFileName = PathUtils::fileName(link.m_urlInLink);
  96. const auto encodedNewFileName = vte::TextUtils::encodeUrl(newFileName);
  97. auto newUrlInLink(link.m_urlInLink);
  98. newUrlInLink.replace(newUrlInLink.size() - encodedOldFileName.size(),
  99. encodedOldFileName.size(),
  100. encodedNewFileName);
  101. content.replace(link.m_urlInLinkPos, link.m_urlInLink.size(), newUrlInLink);
  102. renamedImages.insert(link.m_path, newUrlInLink);
  103. }
  104. if (p_backend) {
  105. p_backend->copyFile(link.m_path, destFilePath);
  106. } else {
  107. FileUtils::copyFile(link.m_path, destFilePath);
  108. }
  109. }
  110. if (!renamedImages.isEmpty()) {
  111. if (p_backend) {
  112. p_backend->writeFile(p_destFilePath, content);
  113. } else {
  114. FileUtils::writeFile(p_destFilePath, content);
  115. }
  116. }
  117. }
  118. void ContentMediaUtils::removeMediaFiles(Node *p_node)
  119. {
  120. Q_ASSERT(p_node->hasContent());
  121. auto file = p_node->getContentFile();
  122. if (file->getContentType().isMarkdown()) {
  123. removeMarkdownMediaFiles(file.data(), p_node->getBackend());
  124. }
  125. }
  126. void ContentMediaUtils::removeMarkdownMediaFiles(const File *p_file, INotebookBackend *p_backend)
  127. {
  128. auto content = p_file->read();
  129. // Images.
  130. const auto images =
  131. vte::MarkdownUtils::fetchImagesFromMarkdownText(content,
  132. p_file->getResourcePath(),
  133. vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
  134. QSet<QString> handledImages;
  135. for (const auto &link : images) {
  136. if (handledImages.contains(link.m_path)) {
  137. continue;
  138. }
  139. handledImages.insert(link.m_path);
  140. if (!QFileInfo::exists(link.m_path)) {
  141. qWarning() << "image of Markdown file does not exist" << link.m_path << link.m_urlInLink;
  142. continue;
  143. }
  144. p_backend->removeFile(link.m_path);
  145. }
  146. }
  147. void ContentMediaUtils::copyAttachment(Node *p_node,
  148. INotebookBackend *p_backend,
  149. const QString &p_destFilePath,
  150. const QString &p_destAttachmentFolderPath)
  151. {
  152. Q_ASSERT(p_node->hasContent());
  153. Q_ASSERT(!p_node->getAttachmentFolder().isEmpty());
  154. // Copy the whole folder.
  155. const auto srcAttachmentFolderPath = p_node->fetchAttachmentFolderPath();
  156. try {
  157. if (p_backend) {
  158. p_backend->copyDir(srcAttachmentFolderPath, p_destAttachmentFolderPath);
  159. } else {
  160. FileUtils::copyDir(srcAttachmentFolderPath, p_destAttachmentFolderPath);
  161. }
  162. } catch (Exception &e) {
  163. qWarning() << "failed to copy attachment folder" << srcAttachmentFolderPath << e.what();
  164. return;
  165. }
  166. // Check if we need to modify links in content.
  167. // FIXME: check the whole relative path.
  168. if (p_node->getAttachmentFolder() == PathUtils::dirName(p_destAttachmentFolderPath)) {
  169. return;
  170. }
  171. auto file = p_node->getContentFile();
  172. if (file->getContentType().isMarkdown()) {
  173. fixMarkdownLinks(srcAttachmentFolderPath, p_backend, p_destFilePath, p_destAttachmentFolderPath);
  174. }
  175. }
  176. void ContentMediaUtils::fixMarkdownLinks(const QString &p_srcFolderPath,
  177. INotebookBackend *p_backend,
  178. const QString &p_destFilePath,
  179. const QString &p_destFolderPath)
  180. {
  181. // TODO.
  182. Q_UNUSED(p_srcFolderPath);
  183. Q_UNUSED(p_backend);
  184. Q_UNUSED(p_destFilePath);
  185. Q_UNUSED(p_destFolderPath);
  186. }