File indexing completed on 2024-04-28 13:43:29
0001 /** 0002 * SPDX-FileCopyrightText: (C) 2003 by Sébastien Laoût <slaout@linux62.org> 0003 * SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "formatimporter.h" 0007 0008 #include <QApplication> 0009 #include <QLocale> 0010 #include <QtCore/QDir> 0011 #include <QtCore/QString> 0012 #include <QtCore/QStringList> 0013 #include <QtCore/QTextStream> 0014 #include <QtXml/QDomDocument> 0015 0016 #include <KIO/CopyJob> 0017 #include <KLocalizedString> 0018 #include <KMessageBox> 0019 0020 #include "basketscene.h" 0021 #include "bnpview.h" 0022 #include "global.h" 0023 #include "notecontent.h" 0024 #include "notefactory.h" 0025 #include "tools.h" 0026 #include "xmlwork.h" 0027 0028 bool FormatImporter::shouldImportBaskets() 0029 { 0030 // We should import if the application have not successfully loaded any basket... 0031 if (Global::bnpView->topLevelItemCount() >= 0) 0032 return false; 0033 0034 // ... And there is at least one folder in the save folder, with a ".basket" file inside that folder. 0035 QDir dir(Global::savesFolder(), QString(), QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoSymLinks); 0036 QStringList list = dir.entryList(); 0037 for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) 0038 if (*it != "." && *it != ".." && dir.exists(Global::savesFolder() + *it + "/.basket")) 0039 return true; 0040 0041 return false; 0042 } 0043 0044 void FormatImporter::copyFolder(const QString &folder, const QString &newFolder) 0045 { 0046 copyFinished = false; 0047 KIO::CopyJob *copyJob = KIO::copyAs(QUrl::fromLocalFile(folder), QUrl::fromLocalFile(newFolder), KIO::HideProgressInfo); 0048 connect(copyJob, &KIO::CopyJob::copyingDone, this, &FormatImporter::slotCopyingDone); 0049 while (!copyFinished) 0050 qApp->processEvents(); 0051 } 0052 0053 void FormatImporter::moveFolder(const QString &folder, const QString &newFolder) 0054 { 0055 copyFinished = false; 0056 KIO::CopyJob *copyJob = KIO::moveAs(QUrl::fromLocalFile(folder), QUrl::fromLocalFile(newFolder), KIO::HideProgressInfo); 0057 connect(copyJob, &KIO::CopyJob::copyingDone, this, &FormatImporter::slotCopyingDone); 0058 while (!copyFinished) 0059 qApp->processEvents(); 0060 } 0061 0062 void FormatImporter::slotCopyingDone(KIO::Job *) 0063 { 0064 // qDebug() << "Copy finished of " + from.path() + " to " + to.path(); 0065 copyFinished = true; 0066 } 0067 0068 void FormatImporter::importBaskets() 0069 { 0070 qDebug() << "Import Baskets: Preparing..."; 0071 0072 // Some preliminary preparations (create the destination folders and the basket tree file): 0073 QDir dirPrep; 0074 dirPrep.mkdir(Global::savesFolder()); 0075 dirPrep.mkdir(Global::basketsFolder()); 0076 QDomDocument document("basketTree"); 0077 QDomElement root = document.createElement("basketTree"); 0078 document.appendChild(root); 0079 0080 // First up, establish a list of every baskets, ensure the old order (if any), and count them. 0081 QStringList baskets; 0082 0083 // Read the 0.5.0 baskets order: 0084 QDomDocument *doc = XMLWork::openFile("container", Global::savesFolder() + "container.baskets"); 0085 if (doc != nullptr) { 0086 QDomElement docElem = doc->documentElement(); 0087 QDomElement basketsElem = XMLWork::getElement(docElem, "baskets"); 0088 QDomNode n = basketsElem.firstChild(); 0089 while (!n.isNull()) { 0090 QDomElement e = n.toElement(); 0091 if ((!e.isNull()) && e.tagName() == "basket") 0092 baskets.append(e.text()); 0093 n = n.nextSibling(); 0094 } 0095 } 0096 0097 // Then load the baskets that weren't loaded (import < 0.5.0 ones): 0098 QDir dir(Global::savesFolder(), QString(), QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoSymLinks); 0099 QStringList list = dir.entryList(); 0100 if (list.count() > 2) // Pass "." and ".." 0101 for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) // For each folder 0102 if (*it != "." && *it != ".." && dir.exists(Global::savesFolder() + *it + "/.basket")) // If it can be a basket folder 0103 if (!(baskets.contains((*it) + '/')) && baskets.contains(*it)) // And if it is not already in the imported baskets list 0104 baskets.append(*it); 0105 0106 qDebug() << "Import Baskets: Found " << baskets.count() << " baskets to import."; 0107 0108 // Import every baskets: 0109 int i = 0; 0110 for (QStringList::iterator it = baskets.begin(); it != baskets.end(); ++it) { 0111 ++i; 0112 qDebug() << "Import Baskets: Importing basket " << i << " of " << baskets.count() << "..."; 0113 0114 // Move the folder to the new repository (normal basket) or copy the folder (mirrored folder): 0115 QString folderName = *it; 0116 if (folderName.startsWith('/')) { // It was a folder mirror: 0117 KMessageBox::information(nullptr, 0118 i18n("<p>Folder mirroring is not possible anymore (see <a href='https://basket-notepads.github.io'>basket-notepads.github.io</a> for more information).</p>" 0119 "<p>The folder <b>%1</b> has been copied for the basket needs. You can either delete this folder or delete the basket, or use both. But remember that " 0120 "modifying one will not modify the other anymore as they are now separate entities.</p>", 0121 folderName), 0122 i18n("Folder Mirror Import"), 0123 QString(), 0124 KMessageBox::AllowLink); 0125 // Also modify folderName to be only the folder name and not the full path anymore: 0126 QString newFolderName = folderName; 0127 if (newFolderName.endsWith('/')) 0128 newFolderName = newFolderName.left(newFolderName.length() - 1); 0129 newFolderName = newFolderName.mid(newFolderName.lastIndexOf('/') + 1); 0130 newFolderName = Tools::fileNameForNewFile(newFolderName, Global::basketsFolder()); 0131 FormatImporter f; 0132 f.copyFolder(folderName, Global::basketsFolder() + newFolderName); 0133 folderName = newFolderName; 0134 } else 0135 dir.rename(Global::savesFolder() + folderName, Global::basketsFolder() + folderName); // Move the folder 0136 0137 // Import the basket structure file and get the properties (to add them in the tree basket-properties cache): 0138 QDomElement properties = importBasket(folderName); 0139 0140 // Add it to the XML document: 0141 QDomElement basketElement = document.createElement("basket"); 0142 root.appendChild(basketElement); 0143 basketElement.setAttribute("folderName", folderName); 0144 basketElement.appendChild(properties); 0145 } 0146 0147 // Finalize (write to disk and delete now useless files): 0148 qDebug() << "Import Baskets: Finalizing..."; 0149 0150 QFile file(Global::basketsFolder() + "baskets.xml"); 0151 if (file.open(QIODevice::WriteOnly)) { 0152 QTextStream stream(&file); 0153 stream.setCodec("UTF-8"); 0154 QString xml = document.toString(); 0155 stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; 0156 stream << xml; 0157 file.close(); 0158 } 0159 0160 Tools::deleteRecursively(Global::savesFolder() + ".tmp"); 0161 dir.remove(Global::savesFolder() + "container.baskets"); 0162 0163 qDebug() << "Import Baskets: Finished."; 0164 } 0165 0166 QDomElement FormatImporter::importBasket(const QString &folderName) 0167 { 0168 // Load the XML file: 0169 QDomDocument *document = XMLWork::openFile("basket", Global::basketsFolder() + folderName + "/.basket"); 0170 if (!document) { 0171 qDebug() << "Import Baskets: Failed to read the basket file!"; 0172 return QDomElement(); 0173 } 0174 QDomElement docElem = document->documentElement(); 0175 0176 // Import properties (change <background color=""> to <appearance backgroundColor="">, and figure out if is a checklist or not): 0177 QDomElement properties = XMLWork::getElement(docElem, "properties"); 0178 QDomElement background = XMLWork::getElement(properties, "background"); 0179 QColor backgroundColor = QColor(background.attribute("color")); 0180 if (backgroundColor.isValid() && (backgroundColor != qApp->palette().color(QPalette::Base))) { // Use the default color if it was already that color: 0181 QDomElement appearance = document->createElement("appearance"); 0182 appearance.setAttribute("backgroundColor", backgroundColor.name()); 0183 properties.appendChild(appearance); 0184 } 0185 QDomElement disposition = document->createElement("disposition"); 0186 disposition.setAttribute("mindMap", "false"); 0187 disposition.setAttribute("columnCount", "1"); 0188 disposition.setAttribute("free", "false"); 0189 bool isCheckList = XMLWork::trueOrFalse(XMLWork::getElementText(properties, "showCheckBoxes"), false); 0190 0191 // Insert all notes in a group (column): 1/ rename "items" to "group", 2/ add "notes" to root, 3/ move "group" into "notes" 0192 QDomElement column = XMLWork::getElement(docElem, "items"); 0193 column.setTagName("group"); 0194 QDomElement notes = document->createElement("notes"); 0195 notes.appendChild(column); 0196 docElem.appendChild(notes); 0197 0198 // Import notes from older representations: 0199 QDomNode n = column.firstChild(); 0200 while (!n.isNull()) { 0201 QDomElement e = n.toElement(); 0202 if (!e.isNull()) { 0203 e.setTagName("note"); 0204 QDomElement content = XMLWork::getElement(e, "content"); 0205 // Add Check tag: 0206 if (isCheckList) { 0207 bool isChecked = XMLWork::trueOrFalse(e.attribute("checked", "false")); 0208 XMLWork::addElement(*document, e, "tags", (isChecked ? "todo_done" : "todo_unchecked")); 0209 } 0210 // Import annotations as folded groups: 0211 QDomElement parentE = column; 0212 QString annotations = XMLWork::getElementText(e, "annotations", QString()); 0213 if (!annotations.isEmpty()) { 0214 QDomElement annotGroup = document->createElement("group"); 0215 column.insertBefore(annotGroup, e); 0216 annotGroup.setAttribute("folded", "true"); 0217 annotGroup.appendChild(e); 0218 parentE = annotGroup; 0219 // Create the text note and add it to the DOM tree: 0220 QDomElement annotNote = document->createElement("note"); 0221 annotNote.setAttribute("type", "text"); 0222 annotGroup.appendChild(annotNote); 0223 QString annotFileName = Tools::fileNameForNewFile("annotations1.txt", BasketScene::fullPathForFolderName(folderName)); 0224 QString annotFullPath = BasketScene::fullPathForFolderName(folderName) + '/' + annotFileName; 0225 QFile file(annotFullPath); 0226 if (file.open(QIODevice::WriteOnly)) { 0227 QTextStream stream(&file); 0228 stream << annotations; 0229 file.close(); 0230 } 0231 XMLWork::addElement(*document, annotNote, "content", annotFileName); 0232 n = annotGroup; 0233 } 0234 // Import Launchers from 0.3.x, 0.4.0 and 0.5.0-alphas: 0235 QString runCommand = e.attribute("runcommand"); // Keep compatibility with 0.4.0 and 0.5.0-alphas versions 0236 runCommand = XMLWork::getElementText(e, "action", runCommand); // Keep compatibility with 0.3.x versions 0237 if (!runCommand.isEmpty()) { // An import should be done 0238 // Prepare the launcher note: 0239 QString title = content.attribute("title", QString()); 0240 QString icon = content.attribute("icon", QString()); 0241 if (title.isEmpty()) 0242 title = runCommand; 0243 if (icon.isEmpty()) 0244 icon = NoteFactory::iconForCommand(runCommand); 0245 // Import the launcher note: 0246 // Adapted version of "QString launcherName = NoteFactory::createNoteLauncherFile(runCommand, title, icon, this)": 0247 QString launcherContent = QString( 0248 "[Desktop Entry]\n" 0249 "Exec=%1\n" 0250 "Name=%2\n" 0251 "Icon=%3\n" 0252 "Encoding=UTF-8\n" 0253 "Type=Application\n") 0254 .arg(runCommand, title, icon.isEmpty() ? QString("exec") : icon); 0255 QString launcherFileName = Tools::fileNameForNewFile("launcher.desktop", Global::basketsFolder() + folderName /*+ "/"*/); 0256 QString launcherFullPath = Global::basketsFolder() + folderName /*+ "/"*/ + launcherFileName; 0257 QFile file(launcherFullPath); 0258 if (file.open(QIODevice::WriteOnly)) { 0259 QTextStream stream(&file); 0260 stream.setCodec("UTF-8"); 0261 stream << launcherContent; 0262 file.close(); 0263 } 0264 // Add the element to the DOM: 0265 QDomElement launcherElem = document->createElement("note"); 0266 parentE.insertBefore(launcherElem, e); 0267 launcherElem.setAttribute("type", "launcher"); 0268 XMLWork::addElement(*document, launcherElem, "content", launcherFileName); 0269 } 0270 // Import unknown ns to 0.6.0: 0271 if (e.attribute("type") == "unknow") 0272 e.setAttribute("type", "unknown"); 0273 // Import links from version < 0.5.0: 0274 if (!content.attribute("autotitle").isEmpty() && content.attribute("autoTitle").isEmpty()) 0275 content.setAttribute("autoTitle", content.attribute("autotitle")); 0276 if (!content.attribute("autoicon").isEmpty() && content.attribute("autoIcon").isEmpty()) 0277 content.setAttribute("autoIcon", content.attribute("autoicon")); 0278 } 0279 n = n.nextSibling(); 0280 } 0281 0282 // Save the resulting XML file: 0283 QFile file(Global::basketsFolder() + folderName + "/.basket"); 0284 if (file.open(QIODevice::WriteOnly)) { 0285 QTextStream stream(&file); 0286 stream.setCodec("UTF-8"); 0287 // QString xml = document->toString(); 0288 // stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; 0289 // stream << xml; 0290 stream << document->toString(); // Document is ALREADY using UTF-8 0291 file.close(); 0292 } else 0293 qDebug() << "Import Baskets: Failed to save the basket file!"; 0294 0295 // Return the newly created properties (to put in the basket tree): 0296 return properties; 0297 } 0298 0299 #include "moc_formatimporter.cpp"