Skip to content

Commit 43fea65

Browse files
committed
Plugins::WebBrowser: add import bookmark
1 parent 5c50b1e commit 43fea65

File tree

3 files changed

+386
-10
lines changed

3 files changed

+386
-10
lines changed

Plugins/WebBrowser/Bookmark/BookmarkDatabase.cpp

Lines changed: 367 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -625,13 +625,6 @@ BookmarkItem CBookmarkDatabase::bookmarkFromQuery(const QSqlQuery &query)
625625
return item;
626626
}
627627

628-
bool CBookmarkDatabase::importFromHtml(const QString &filename)
629-
{
630-
// 实现HTML导入(Chrome/Firefox格式)
631-
// 这里简化为从历史记录导入
632-
return true;
633-
}
634-
635628
bool CBookmarkDatabase::exportToHtml(const QString &filename)
636629
{
637630
QFile file(filename);
@@ -665,11 +658,376 @@ bool CBookmarkDatabase::exportToHtml(const QString &filename)
665658
<< "ADD_DATE=\"" << timestamp << "\" "
666659
<< "LAST_VISIT=\"" << timestamp << "\" "
667660
<< "LAST_MODIFIED=\"" << timestamp << "\">"
668-
<< item.title << "</A>" << "\n";
661+
<< item.title << "</A></DT>" << "\n";
669662
}
670663

671-
out << "</DL><p>" << "\n";
664+
out << "</p></DL>" << "\n";
672665

673666
file.close();
674667
return true;
675668
}
669+
670+
bool CBookmarkDatabase::importFromHtml(const QString &filename)
671+
{
672+
QFile file(filename);
673+
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
674+
qWarning() << "Failed to open file for import:" << filename;
675+
return false;
676+
}
677+
678+
QTextStream in(&file);
679+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
680+
in.setEncoding(QStringConverter::Utf8);
681+
#else
682+
in.setCodec("UTF-8");
683+
#endif
684+
685+
QString content = in.readAll();
686+
file.close();
687+
688+
// 开始事务
689+
m_database.transaction();
690+
691+
try {
692+
int importedCount = parseHtmlBookmarks(content);
693+
694+
if (!m_database.commit()) {
695+
throw QString("Failed to commit transaction: %1").arg(m_database.lastError().text());
696+
}
697+
698+
emit bookmarksChanged();
699+
700+
qDebug(log) << "Successfully imported" << importedCount << "bookmarks";
701+
return true;
702+
703+
} catch (const QString &error) {
704+
m_database.rollback();
705+
qWarning(log) << "Import failed:" << error;
706+
return false;
707+
}
708+
}
709+
710+
int CBookmarkDatabase::parseHtmlBookmarks(const QString &htmlContent)
711+
{
712+
int importedCount = 0;
713+
714+
// 解析 HTML 书签文件
715+
QDomDocument doc;
716+
if (!doc.setContent(htmlContent)) {
717+
throw QString("Invalid HTML format");
718+
}
719+
720+
// 查找根节点
721+
QDomElement root = doc.documentElement();
722+
if (root.isNull()) {
723+
throw QString("No root element found");
724+
}
725+
726+
// 查找 DL 标签(书签列表)
727+
QDomElement dlElement = findFirstElement(root, "DL");
728+
if (dlElement.isNull()) {
729+
throw QString("No bookmarks list found");
730+
}
731+
732+
// 递归解析书签
733+
QMap<QString, int> folderMap; // 文件夹路径 -> 文件夹ID
734+
folderMap["/"] = 0; // 根目录
735+
736+
importedCount = parseBookmarkList(dlElement, "/", folderMap);
737+
738+
return importedCount;
739+
}
740+
741+
int CBookmarkDatabase::parseBookmarkList(const QDomElement &dlElement,
742+
const QString &currentPath,
743+
QMap<QString, int> &folderMap)
744+
{
745+
int importedCount = 0;
746+
QDomNode child = dlElement.firstChild();
747+
748+
while (!child.isNull()) {
749+
QDomElement element = child.toElement();
750+
751+
if (!element.isNull()) {
752+
QString tagName = element.tagName().toUpper();
753+
754+
if (tagName == "DT") {
755+
// 处理 DT 节点
756+
importedCount += parseDtElement(element, currentPath, folderMap);
757+
} else if (tagName == "DL") {
758+
// 嵌套的 DL 标签,继续解析
759+
importedCount += parseBookmarkList(element, currentPath, folderMap);
760+
}
761+
}
762+
763+
child = child.nextSibling();
764+
}
765+
766+
return importedCount;
767+
}
768+
769+
int CBookmarkDatabase::parseDtElement(const QDomElement &dtElement,
770+
const QString &currentPath,
771+
QMap<QString, int> &folderMap)
772+
{
773+
int importedCount = 0;
774+
775+
// 查找子节点
776+
QDomNode child = dtElement.firstChild();
777+
while (!child.isNull()) {
778+
QDomElement element = child.toElement();
779+
780+
if (!element.isNull()) {
781+
QString tagName = element.tagName().toUpper();
782+
783+
if (tagName == "A") {
784+
// 书签链接
785+
importedCount += importBookmark(element, currentPath);
786+
} else if (tagName == "H3") {
787+
// 文件夹
788+
importedCount += importFolder(element, currentPath, folderMap);
789+
} else if (tagName == "DL") {
790+
// 嵌套的书签列表
791+
importedCount += parseBookmarkList(element, currentPath, folderMap);
792+
}
793+
}
794+
795+
child = child.nextSibling();
796+
}
797+
798+
return importedCount;
799+
}
800+
801+
int CBookmarkDatabase::importBookmark(const QDomElement &aElement,
802+
const QString &folderPath)
803+
{
804+
// 获取链接属性
805+
QString url = aElement.attribute("HREF");
806+
QString title = aElement.text();
807+
808+
if (url.isEmpty() || title.isEmpty()) {
809+
return 0;
810+
}
811+
812+
// 获取时间戳
813+
QString addDateStr = aElement.attribute("ADD_DATE");
814+
QString lastVisitStr = aElement.attribute("LAST_VISIT");
815+
QString lastModifiedStr = aElement.attribute("LAST_MODIFIED");
816+
817+
QDateTime addDate = parseTimestamp(addDateStr);
818+
QDateTime lastVisit = parseTimestamp(lastVisitStr);
819+
QDateTime lastModified = parseTimestamp(lastModifiedStr);
820+
821+
// 创建书签对象
822+
BookmarkItem item;
823+
item.url = url;
824+
item.title = title;
825+
item.createdTime = addDate.isValid() ? addDate : QDateTime::currentDateTime();
826+
item.lastVisitTime = lastVisit;
827+
item.modifiedTime = lastModified.isValid() ? lastModified : item.createdTime;
828+
829+
// 获取图标(如果有)
830+
QString iconUrl = aElement.attribute("ICON");
831+
if (!iconUrl.isEmpty()) {
832+
item.iconUrl = iconUrl;
833+
834+
// 尝试获取ICON_URI属性
835+
QString iconUri = aElement.attribute("ICON_URI");
836+
if (!iconUri.isEmpty()) {
837+
item.iconUrl = iconUri;
838+
}
839+
}
840+
841+
// 获取标签(如果有)
842+
QString tags = aElement.attribute("TAGS");
843+
if (!tags.isEmpty()) {
844+
item.tags = tags.split(",");
845+
}
846+
847+
// 查找或创建文件夹
848+
int folderId = getOrCreateFolder(folderPath);
849+
item.folderId = folderId;
850+
851+
// 检查是否已存在
852+
auto lstExisting = getBookmarkByUrl(url);
853+
if (lstExisting.isEmpty()) {
854+
// 插入新书签
855+
if (addBookmark(item)) {
856+
qDebug(log) << "Imported bookmark:" << title;
857+
return 1;
858+
}
859+
} else {
860+
foreach(auto exist, lstExisting) {
861+
// 更新现有书签
862+
exist.title = item.title;
863+
exist.iconUrl = item.iconUrl;
864+
exist.tags = item.tags;
865+
exist.folderId = item.folderId;
866+
exist.lastVisitTime = item.lastVisitTime;
867+
exist.modifiedTime = QDateTime::currentDateTime();
868+
869+
if (updateBookmark(exist)) {
870+
qDebug(log) << "Updated existing bookmark:" << title;
871+
}
872+
}
873+
874+
return lstExisting.count();
875+
}
876+
877+
return 0;
878+
}
879+
880+
int CBookmarkDatabase::importFolder(const QDomElement &h3Element,
881+
const QString &parentPath,
882+
QMap<QString, int> &folderMap)
883+
{
884+
QString folderName = h3Element.text();
885+
if (folderName.isEmpty()) {
886+
return 0;
887+
}
888+
889+
// 构建完整路径
890+
QString folderPath = parentPath;
891+
if (!parentPath.endsWith("/")) {
892+
folderPath += "/";
893+
}
894+
folderPath += folderName;
895+
896+
// 获取文件夹属性
897+
QString addDateStr = h3Element.attribute("ADD_DATE");
898+
QString lastModifiedStr = h3Element.attribute("LAST_MODIFIED");
899+
900+
QDateTime addDate = parseTimestamp(addDateStr);
901+
QDateTime lastModified = parseTimestamp(lastModifiedStr);
902+
903+
// 查找父文件夹ID
904+
int parentFolderId = folderMap.value(parentPath, 0);
905+
906+
// 创建文件夹
907+
int folderId = getOrCreateFolder(folderPath, parentFolderId);
908+
folderMap[folderPath] = folderId;
909+
910+
qDebug(log) << "Imported folder:" << folderName << "Path:" << folderPath << "ID:" << folderId;
911+
912+
// 处理子节点
913+
int importedCount = 0;
914+
QDomNode nextSibling = h3Element.nextSibling();
915+
916+
while (!nextSibling.isNull()) {
917+
QDomElement element = nextSibling.toElement();
918+
919+
if (!element.isNull()) {
920+
QString tagName = element.tagName().toUpper();
921+
922+
if (tagName == "DL") {
923+
// 递归处理子文件夹
924+
importedCount += parseBookmarkList(element, folderPath, folderMap);
925+
break;
926+
}
927+
}
928+
929+
nextSibling = nextSibling.nextSibling();
930+
}
931+
932+
return importedCount;
933+
}
934+
935+
int CBookmarkDatabase::getOrCreateFolder(const QString &folderPath, int parentFolderId)
936+
{
937+
if (folderPath.isEmpty() || folderPath == "/") {
938+
return 0; // 根目录
939+
}
940+
941+
// 分割路径
942+
QStringList pathParts = folderPath.split("/", Qt::SkipEmptyParts);
943+
if (pathParts.isEmpty()) {
944+
return 0;
945+
}
946+
947+
QString folderName = pathParts.last();
948+
949+
// 检查文件夹是否已存在
950+
QSqlQuery query(m_database);
951+
query.prepare("SELECT id FROM bookmark_folders WHERE name = :name AND parent_id = :parent_id");
952+
query.bindValue(":name", folderName);
953+
query.bindValue(":parent_id", parentFolderId);
954+
955+
if (query.exec() && query.next()) {
956+
return query.value(0).toInt();
957+
}
958+
959+
// 创建新文件夹
960+
query.prepare(
961+
"INSERT INTO bookmark_folders (name, parent_id, created_time) "
962+
"VALUES (:name, :parent_id, :created_time)"
963+
);
964+
query.bindValue(":name", folderName);
965+
query.bindValue(":parent_id", parentFolderId);
966+
query.bindValue(":created_time", QDateTime::currentDateTime());
967+
968+
if (query.exec()) {
969+
return query.lastInsertId().toInt();
970+
}
971+
972+
qWarning() << "Failed to create folder:" << folderName << query.lastError().text();
973+
return 0; // 失败时返回根目录
974+
}
975+
976+
int CBookmarkDatabase::getOrCreateFolder(const QString &folderPath)
977+
{
978+
if (folderPath.isEmpty() || folderPath == "/") {
979+
return 0;
980+
}
981+
982+
QStringList pathParts = folderPath.split("/", Qt::SkipEmptyParts);
983+
int currentParentId = 0;
984+
985+
// 逐级创建文件夹
986+
for (const QString &part : pathParts) {
987+
if (part.isEmpty()) continue;
988+
989+
currentParentId = getOrCreateFolder(part, currentParentId);
990+
}
991+
992+
return currentParentId;
993+
}
994+
995+
QDateTime CBookmarkDatabase::parseTimestamp(const QString &timestampStr)
996+
{
997+
if (timestampStr.isEmpty()) {
998+
return QDateTime();
999+
}
1000+
1001+
bool ok;
1002+
qint64 timestamp = timestampStr.toLongLong(&ok);
1003+
1004+
if (!ok) {
1005+
return QDateTime();
1006+
}
1007+
1008+
// 处理不同精度的时间戳
1009+
if (timestamp > 10000000000) {
1010+
// 毫秒时间戳
1011+
return QDateTime::fromMSecsSinceEpoch(timestamp);
1012+
} else {
1013+
// 秒时间戳
1014+
return QDateTime::fromSecsSinceEpoch(timestamp);
1015+
}
1016+
}
1017+
1018+
QDomElement CBookmarkDatabase::findFirstElement(const QDomElement &parent, const QString &tagName)
1019+
{
1020+
QDomNode child = parent.firstChild();
1021+
1022+
while (!child.isNull()) {
1023+
QDomElement element = child.toElement();
1024+
1025+
if (!element.isNull() && element.tagName().toUpper() == tagName.toUpper()) {
1026+
return element;
1027+
}
1028+
1029+
child = child.nextSibling();
1030+
}
1031+
1032+
return QDomElement();
1033+
}

0 commit comments

Comments
 (0)