void BackupDialog::moveToAnotherFolder() { KUrl selectedURL = KDirSelectDialog::selectDirectory( /*startDir=*/Global::savesFolder(), /*localOnly=*/true, /*parent=*/0, /*caption=*/i18n("Choose a Folder Where to Move Baskets")); if (!selectedURL.isEmpty()) { QString folder = selectedURL.path(); QDir dir(folder); // The folder should not exists, or be empty (because KDirSelectDialog will likely create it anyway): if (dir.exists()) { // Get the content of the folder: QStringList content = dir.entryList(); if (content.count() > 2) { // "." and ".." int result = KMessageBox::questionYesNo( 0, "<qt>" + i18n("The folder <b>%1</b> is not empty. Do you want to override it?", folder), i18n("Override Folder?"), KGuiItem(i18n("&Override"), "document-save") ); if (result == KMessageBox::No) return; } Tools::deleteRecursively(folder); } FormatImporter copier; copier.moveFolder(Global::savesFolder(), folder); Backup::setFolderAndRestart(folder, i18n("Your baskets have been successfully moved to <b>%1</b>. %2 is going to be restarted to take this change into account.")); } }
void Archive::loadExtractedBaskets(const QString &extractionFolder, QDomNode &basketNode, QMap<QString, QString> &folderMap, Basket *parent) { bool basketSetAsCurrent = (parent != 0); QDomNode n = basketNode; while ( ! n.isNull() ) { QDomElement element = n.toElement(); if ( (!element.isNull()) && element.tagName() == "basket" ) { QString folderName = element.attribute("folderName"); if (!folderName.isEmpty()) { // Move the basket folder to its destination, while renaming it uniquely: QString newFolderName = folderMap[folderName]; FormatImporter copier; // The folder has been "reserved" by creating it. Avoid asking the user to override: QDir dir; dir.rmdir(Global::basketsFolder() + newFolderName); copier.moveFolder(extractionFolder + "baskets/" + folderName, Global::basketsFolder() + newFolderName); // Append and load the basket in the tree: Basket *basket = Global::bnpView->loadBasket(newFolderName); BasketListViewItem *basketItem = Global::bnpView->appendBasket(basket, (basket && parent ? Global::bnpView->listViewItemForBasket(parent) : 0)); basketItem->setExpanded(!XMLWork::trueOrFalse(element.attribute("folded", "false"), false)); QDomElement properties = XMLWork::getElement(element, "properties"); importBasketIcon(properties, extractionFolder); // Rename the icon fileName if necessary basket->loadProperties(properties); // Open the first basket of the archive: if (!basketSetAsCurrent) { Global::bnpView->setCurrentBasket(basket); basketSetAsCurrent = true; } QDomNode node = element.firstChild(); loadExtractedBaskets(extractionFolder, node, folderMap, basket); } } n = n.nextSibling(); } }
void Archive::importBasketIcon(QDomElement properties, const QString &extractionFolder) { QString iconName = XMLWork::getElementText(properties, "icon"); if (!iconName.isEmpty() && iconName != "basket") { QPixmap icon = KIconLoader::global()->loadIcon( iconName, KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, /*canReturnNull=*/true ); // The icon does not exists on that computer, import it: if (icon.isNull()) { QDir dir; dir.mkdir(Global::savesFolder() + "basket-icons/"); FormatImporter copier; // Only used to copy files synchronously // Of the icon path was eg. "/home/seb/icon.png", it was exported as "basket-icons/_home_seb_icon.png". // So we need to copy that image to "~/.kde/share/apps/basket/basket-icons/icon.png": int slashIndex = iconName.lastIndexOf('/'); QString iconFileName = (slashIndex < 0 ? iconName : iconName.right(slashIndex - 2)); QString source = extractionFolder + "basket-icons/" + iconName.replace('/', '_'); QString destination = Global::savesFolder() + "basket-icons/" + iconFileName; if (!dir.exists(destination)) copier.copyFolder(source, destination); // Replace the emblem path in the tags.xml copy: QDomElement iconElement = XMLWork::getElement(properties, "icon"); properties.removeChild(iconElement); QDomDocument document = properties.ownerDocument(); XMLWork::addElement(document, properties, "icon", destination); } } }
void Archive::importArchivedBackgroundImages(const QString &extractionFolder) { FormatImporter copier; // Only used to copy files synchronously QString destFolder = KGlobal::dirs()->saveLocation("data", "basket/backgrounds/"); QDir dir(extractionFolder + "backgrounds/", /*nameFilder=*/"*.png", /*sortSpec=*/QDir::Name | QDir::IgnoreCase, /*filterSpec=*/QDir::Files | QDir::NoSymLinks); QStringList files = dir.entryList(); for (QStringList::Iterator it = files.begin(); it != files.end(); ++it) { QString image = *it; if (!Global::backgroundManager->exists(image)) { // Copy images: QString imageSource = extractionFolder + "backgrounds/" + image; QString imageDest = destFolder + image; copier.copyFolder(imageSource, imageDest); // Copy configuration file: QString configSource = extractionFolder + "backgrounds/" + image + ".config"; QString configDest = destFolder + image; if (dir.exists(configSource)) copier.copyFolder(configSource, configDest); // Copy preview: QString previewSource = extractionFolder + "backgrounds/previews/" + image; QString previewDest = destFolder + "previews/" + image; if (dir.exists(previewSource)) { dir.mkdir(destFolder + "previews/"); // Make sure the folder exists! copier.copyFolder(previewSource, previewDest); } // Append image to database: Global::backgroundManager->addImage(imageDest); } } }
/** * When opening a basket archive that come from another computer, * it can contains tags that use icons (emblems) that are not present on that computer. * Fortunately, basket archives contains a copy of every used icons. * This method check for every emblems and import the missing ones. * It also modify the tags.xml copy for the emblems to point to the absolute path of the impported icons. */ void Archive::importTagEmblems(const QString &extractionFolder) { QDomDocument *document = XMLWork::openFile("basketTags", extractionFolder + "tags.xml"); if (document == 0) return; QDomElement docElem = document->documentElement(); QDir dir; dir.mkdir(Global::savesFolder() + "tag-emblems/"); FormatImporter copier; // Only used to copy files synchronously QDomNode node = docElem.firstChild(); while (!node.isNull()) { QDomElement element = node.toElement(); if ( (!element.isNull()) && element.tagName() == "tag" ) { QDomNode subNode = element.firstChild(); while (!subNode.isNull()) { QDomElement subElement = subNode.toElement(); if ( (!subElement.isNull()) && subElement.tagName() == "state" ) { QString emblemName = XMLWork::getElementText(subElement, "emblem"); if (!emblemName.isEmpty()) { QPixmap emblem = KIconLoader::global()->loadIcon(emblemName, KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, /*canReturnNull=*/true); // The icon does not exists on that computer, import it: if (emblem.isNull()) { // Of the emblem path was eg. "/home/seb/emblem.png", it was exported as "tag-emblems/_home_seb_emblem.png". // So we need to copy that image to "~/.kde/share/apps/basket/tag-emblems/emblem.png": int slashIndex = emblemName.lastIndexOf('/'); QString emblemFileName = (slashIndex < 0 ? emblemName : emblemName.right(slashIndex - 2)); QString source = extractionFolder + "tag-emblems/" + emblemName.replace('/', '_'); QString destination = Global::savesFolder() + "tag-emblems/" + emblemFileName; if (!dir.exists(destination) && dir.exists(source)) copier.copyFolder(source, destination); // Replace the emblem path in the tags.xml copy: QDomElement emblemElement = XMLWork::getElement(subElement, "emblem"); subElement.removeChild(emblemElement); XMLWork::addElement(*document, subElement, "emblem", destination); } } } subNode = subNode.nextSibling(); } } node = node.nextSibling(); } Basket::safelySaveToFile(extractionFolder + "tags.xml", document->toString()); }
void BackupDialog::restore() { // Get last backup folder: KConfig *config = KGlobal::config().data(); KConfigGroup configGroup(config, "Backups"); QString folder = configGroup.readEntry("lastFolder", QDir::homePath()) + "/"; // Ask a file name to the user: QString filter = "*.tar.gz|" + i18n("Tar Archives Compressed by Gzip") + "\n*|" + i18n("All Files"); QString path = KFileDialog::getOpenFileName(folder, filter, this, i18n("Open Basket Archive")); if (path.isEmpty()) // User has canceled return; // Before replacing the basket data folder with the backup content, we safely backup the current baskets to the home folder. // So if the backup is corrupted or something goes wrong while restoring (power cut...) the user will be able to restore the old working data: QString safetyPath = Backup::newSafetyFolder(); FormatImporter copier; copier.moveFolder(Global::savesFolder(), safetyPath); // Add the README file for user to cancel a bad restoration: QString readmePath = safetyPath + i18n("README.txt"); QFile file(readmePath); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << i18n("This is a safety copy of your baskets like they were before you started to restore the backup %1.", KUrl(path).fileName()) + "\n\n" << i18n("If the restoration was a success and you restored what you wanted to restore, you can remove this folder.") + "\n\n" << i18n("If something went wrong during the restoration process, you can re-use this folder to store your baskets and nothing will be lost.") + "\n\n" << i18n("Choose \"Basket\" -> \"Backup & Restore...\" -> \"Use Another Existing Folder...\" and select that folder.") + "\n"; file.close(); } QString message = "<p><nobr>" + i18n("Restoring <b>%1</b>. Please wait...", KUrl(path).fileName()) + "</nobr></p><p>" + i18n("If something goes wrong during the restoration process, read the file <b>%1</b>.", readmePath); KProgressDialog *dialog = new KProgressDialog(0, i18n("Restore Baskets"), message); dialog->setModal(/*modal=*/true); dialog->setAllowCancel(false); dialog->setAutoClose(true); dialog->show(); QProgressBar *progress = dialog->progressBar(); progress->setRange(0, 0/*Busy/Undefined*/); progress->setValue(0); progress->setTextVisible(false); // Uncompress: RestoreThread thread(path, Global::savesFolder()); thread.start(); while (thread.isRunning()) { progress->setValue(progress->value() + 1); // Or else, the animation is not played! kapp->processEvents(); usleep(300); // Not too long because if the restore process is finished, we wait for nothing } dialog->hide(); // The restore is finished, do not continue to show it while telling the user the application is going to be restarted delete dialog; // If we only hidden it, it reappeared just after having restored a small backup... Very strange. dialog = 0; // This was annoying since it is modal and the "BasKet Note Pads is going to be restarted" message was not reachable. //kapp->processEvents(); // Check for errors: if (!thread.success()) { // Restore the old baskets: QDir dir; dir.remove(readmePath); copier.moveFolder(safetyPath, Global::savesFolder()); // Tell the user: KMessageBox::error(0, i18n("This archive is either not a backup of baskets or is corrupted. It cannot be imported. Your old baskets have been preserved instead."), i18n("Restore Error")); return; } // Note: The safety backup is not removed now because the code can has been wrong, somehow, or the user perhapse restored an older backup by error... // The restore process will not be called very often (it is possible it will only be called once or twice arround the world during the next years). // So it is rare enough to force the user to remove the safety folder, but keep him in control and let him safely recover from restoration errors. Backup::setFolderAndRestart(Global::savesFolder()/*No change*/, i18n("Your backup has been successfuly restored to <b>%1</b>. %2 is going to be restarted to take this change into account.")); }
void FormatImporter::importBaskets() { kDebug() << "Import Baskets: Preparing..."; // Some preliminary preparations (create the destination folders and the basket tree file): QDir dirPrep; dirPrep.mkdir(Global::savesFolder()); dirPrep.mkdir(Global::basketsFolder()); QDomDocument document("basketTree"); QDomElement root = document.createElement("basketTree"); document.appendChild(root); // First up, establish a list of every baskets, ensure the old order (if any), and count them. QStringList baskets; // Read the 0.5.0 baskets order: QDomDocument *doc = XMLWork::openFile("container", Global::savesFolder() + "container.baskets"); if (doc != 0) { QDomElement docElem = doc->documentElement(); QDomElement basketsElem = XMLWork::getElement(docElem, "baskets"); QDomNode n = basketsElem.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); if ((!e.isNull()) && e.tagName() == "basket") baskets.append(e.text()); n = n.nextSibling(); } } // Then load the baskets that weren't loaded (import < 0.5.0 ones): QDir dir(Global::savesFolder(), QString::null, QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoSymLinks); QStringList list = dir.entryList(); if (list.count() > 2) // Pass "." and ".." for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) // For each folder if (*it != "." && *it != ".." && dir.exists(Global::savesFolder() + *it + "/.basket")) // If it can be a basket folder if (! (baskets.contains((*it) + "/")) && baskets.contains(*it) ) // And if it is not already in the imported baskets list baskets.append(*it); kDebug() << "Import Baskets: Found " << baskets.count() << " baskets to import."; // Import every baskets: int i = 0; for (QStringList::iterator it = baskets.begin(); it != baskets.end(); ++it) { ++i; kDebug() << "Import Baskets: Importing basket " << i << " of " << baskets.count() << "..."; // Move the folder to the new repository (normal basket) or copy the folder (mirorred folder): QString folderName = *it; if (folderName.startsWith("/")) { // It was a folder mirror: KMessageBox::information(0, i18n("<p>Folder mirroring is not possible anymore (see <a href='http://basket.kde.org/'>basket.kde.org</a> for more information).</p>" "<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 " "modifying one will not modify the other anymore as they are now separate entities.</p>", folderName), i18n("Folder Mirror Import"), "", KMessageBox::AllowLink); // Also modify folderName to be only the folder name and not the full path anymore: QString newFolderName = folderName; if (newFolderName.endsWith("/")) newFolderName = newFolderName.left(newFolderName.length() - 1); newFolderName = newFolderName.mid(newFolderName.lastIndexOf('/') + 1); newFolderName = Tools::fileNameForNewFile(newFolderName, Global::basketsFolder()); FormatImporter f; f.copyFolder(folderName, Global::basketsFolder() + newFolderName); folderName = newFolderName; } else dir.rename(Global::savesFolder() + folderName, Global::basketsFolder() + folderName); // Move the folder // Import the basket structure file and get the properties (to add them in the tree basket-properties cache): QDomElement properties = importBasket(folderName); // Add it to the XML document: QDomElement basketElement = document.createElement("basket"); root.appendChild(basketElement); basketElement.setAttribute("folderName", folderName); basketElement.appendChild(properties); } // Finalize (write to disk and delete now useless files): kDebug() << "Import Baskets: Finalizing..."; QFile file(Global::basketsFolder() + "baskets.xml"); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream.setCodec("UTF-8"); QString xml = document.toString(); stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; stream << xml; file.close(); } Tools::deleteRecursively(Global::savesFolder() + ".tmp"); dir.remove(Global::savesFolder() + "container.baskets"); kDebug() << "Import Baskets: Finished."; }