diff --git a/include/bookmark.h b/include/bookmark.h new file mode 100644 index 000000000..7861fee31 --- /dev/null +++ b/include/bookmark.h @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef KIWIX_BOOKMARK_H +#define KIWIX_BOOKMARK_H + +#include + +namespace pugi { +class xml_node; +} + +namespace kiwix +{ + +/** + * A class to store information about a bookmark (an article in a book) + */ +class Bookmark +{ + public: + Bookmark(); + ~Bookmark(); + + void updateFromXml(const pugi::xml_node& node); + + const std::string& getBookId() const { return m_bookId; } + const std::string& getBookTitle() const { return m_bookTitle; } + const std::string& getUrl() const { return m_url; } + const std::string& getTitle() const { return m_title; } + const std::string& getLanguage() const { return m_language; } + const std::string& getDate() const { return m_date; } + + void setBookId(const std::string& bookId) { m_bookId = bookId; } + void setBookTitle(const std::string& bookTitle) { m_bookTitle = bookTitle; } + void setUrl(const std::string& url) { m_url = url; } + void setTitle(const std::string& title) { m_title = title; } + void setLanguage(const std::string& language) { m_language = language; } + void setDate(const std::string& date) { m_date = date; } + + protected: + std::string m_bookId; + std::string m_bookTitle; + std::string m_url; + std::string m_title; + std::string m_language; + std::string m_date; +}; + +} + +#endif diff --git a/include/library.h b/include/library.h index 9339cac0d..24f8fe5cd 100644 --- a/include/library.h +++ b/include/library.h @@ -24,12 +24,14 @@ #include #include +#include "book.h" +#include "bookmark.h" + #define KIWIX_LIBRARY_VERSION "20110515" namespace kiwix { -class Book; class OPDSDumper; enum supportedListSortBy { UNSORTED, TITLE, SIZE, DATE, CREATOR, PUBLISHER }; @@ -47,7 +49,9 @@ enum supportedListMode { */ class Library { - std::map books; + std::map m_books; + std::vector m_bookmarks; + public: Library(); ~Library(); @@ -64,6 +68,22 @@ class Library */ bool addBook(const Book& book); + /** + * Add a bookmark to the library. + * + * @param bookmark the book to add. + */ + void addBookmark(const Bookmark& bookmark); + + /** + * Remove a bookmarkk + * + * @param zimId The zimId of the bookmark. + * @param url The url of the bookmark. + * @return True if the bookmark has been removed. + */ + bool removeBookmark(const std::string& zimId, const std::string& url); + Book& getBookById(const std::string& id); /** @@ -78,10 +98,18 @@ class Library * Write the library to a file. * * @param path the path of the file to write to. - * @return True if the library has been correctly save. + * @return True if the library has been correctly saved. */ bool writeToFile(const std::string& path); + /** + * Write the library bookmarks to a file. + * + * @param path the path of the file to write to. + * @return True if the library has been correctly saved. + */ + bool writeBookmarksToFile(const std::string& path); + /** * Get the number of book in the library. * @@ -112,6 +140,13 @@ class Library */ std::vector getBooksPublishers(); + /** + * Get all bookmarks. + * + * @return A list of bookmarks + */ + const std::vector& getBookmarks() { return m_bookmarks; } + /** * Get all book ids of the books in the library. * diff --git a/include/libxml_dumper.h b/include/libxml_dumper.h index ff890a9da..09cce2d5a 100644 --- a/include/libxml_dumper.h +++ b/include/libxml_dumper.h @@ -49,6 +49,14 @@ class LibXMLDumper */ std::string dumpLibXMLContent(const std::vector& bookIds); + + /** + * Dump the bookmark of the library. + * + * @return The bookmark.xml content. + */ + std::string dumpLibXMLBookmark(); + /** * Set the base directory used. * @@ -68,6 +76,7 @@ class LibXMLDumper std::string baseDir; private: void handleBook(Book book, pugi::xml_node root_node); + void handleBookmark(Bookmark bookmark, pugi::xml_node root_node); }; } diff --git a/include/manager.h b/include/manager.h index 718bfb3a0..fae26ee6e 100644 --- a/include/manager.h +++ b/include/manager.h @@ -38,6 +38,7 @@ class LibraryManipulator { public: virtual ~LibraryManipulator() {} virtual bool addBookToLibrary(Book book) = 0; + virtual void addBookmarkToLibrary(Bookmark bookmark) = 0; }; class DefaultLibraryManipulator : public LibraryManipulator { @@ -48,6 +49,9 @@ class DefaultLibraryManipulator : public LibraryManipulator { bool addBookToLibrary(Book book) { return library->addBook(book); } + void addBookmarkToLibrary(Bookmark bookmark) { + library->addBookmark(bookmark); + } private: kiwix::Library* library; }; @@ -113,6 +117,15 @@ class Manager */ bool readOpds(const std::string& content, const std::string& urlHost); + + /** + * Load a bookmark file. + * + * @param path The path of the file to read. + * @return True if the content has been properly parsed. + */ + bool readBookmarkFile(const std::string& path); + /** * Add a book to the library. * diff --git a/include/meson.build b/include/meson.build index 819a1828b..a813dcf9c 100644 --- a/include/meson.build +++ b/include/meson.build @@ -1,5 +1,6 @@ headers = [ 'book.h', + 'bookmark.h', 'common.h', 'library.h', 'manager.h', diff --git a/src/bookmark.cpp b/src/bookmark.cpp new file mode 100644 index 000000000..2ca1b623f --- /dev/null +++ b/src/bookmark.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "bookmark.h" + +#include + +namespace kiwix +{ +/* Constructor */ +Bookmark::Bookmark() +{ +} + +/* Destructor */ +Bookmark::~Bookmark() +{ +} + +void Bookmark::updateFromXml(const pugi::xml_node& node) +{ + auto bookNode = node.child("book"); + m_bookId = bookNode.child("id").child_value(); + m_bookTitle = bookNode.child("title").child_value(); + m_language = bookNode.child("language").child_value(); + m_date = bookNode.child("date").child_value(); + m_title = node.child("title").child_value(); + m_url = node.child("url").child_value(); +} + +} diff --git a/src/library.cpp b/src/library.cpp index a56876c8d..c4e9a9f5b 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -44,31 +44,48 @@ bool Library::addBook(const Book& book) { /* Try to find it */ try { - auto& oldbook = books.at(book.getId()); + auto& oldbook = m_books.at(book.getId()); oldbook.update(book); return false; } catch (std::out_of_range&) { - books[book.getId()] = book; + m_books[book.getId()] = book; return true; } } +void Library::addBookmark(const Bookmark& bookmark) +{ + m_bookmarks.push_back(bookmark); +} + +bool Library::removeBookmark(const std::string& zimId, const std::string& url) +{ + for(auto it=m_bookmarks.begin(); it!=m_bookmarks.end(); it++) { + if (it->getBookId() == zimId && it->getUrl() == url) { + m_bookmarks.erase(it); + return true; + } + } + return false; +} + + bool Library::removeBookById(const std::string& id) { - return books.erase(id) == 1; + return m_books.erase(id) == 1; } Book& Library::getBookById(const std::string& id) { - return books.at(id); + return m_books.at(id); } unsigned int Library::getBookCount(const bool localBooks, const bool remoteBooks) { unsigned int result = 0; - for (auto& pair: books) { + for (auto& pair: m_books) { auto& book = pair.second; if ((!book.getPath().empty() && localBooks) || (book.getPath().empty() && remoteBooks)) { @@ -85,12 +102,17 @@ bool Library::writeToFile(const std::string& path) { return writeTextFile(path, dumper.dumpLibXMLContent(getBooksIds())); } +bool Library::writeBookmarksToFile(const std::string& path) { + LibXMLDumper dumper(this); + return writeTextFile(path, dumper.dumpLibXMLBookmark()); +} + std::vector Library::getBooksLanguages() { std::vector booksLanguages; std::map booksLanguagesMap; - for (auto& pair: books) { + for (auto& pair: m_books) { auto& book = pair.second; auto& language = book.getLanguage(); if (booksLanguagesMap.find(language) == booksLanguagesMap.end()) { @@ -109,7 +131,7 @@ std::vector Library::getBooksCreators() std::vector booksCreators; std::map booksCreatorsMap; - for (auto& pair: books) { + for (auto& pair: m_books) { auto& book = pair.second; auto& creator = book.getCreator(); if (booksCreatorsMap.find(creator) == booksCreatorsMap.end()) { @@ -128,7 +150,7 @@ std::vector Library::getBooksPublishers() std::vector booksPublishers; std::map booksPublishersMap; - for (auto& pair:books) { + for (auto& pair:m_books) { auto& book = pair.second; auto& publisher = book.getPublisher(); if (booksPublishersMap.find(publisher) == booksPublishersMap.end()) { @@ -146,7 +168,7 @@ std::vector Library::getBooksIds() { std::vector bookIds; - for (auto& pair: books) { + for (auto& pair: m_books) { bookIds.push_back(pair.first); } @@ -160,7 +182,7 @@ std::vector Library::filter(const std::string& search) } std::vector bookIds; - for(auto& pair:books) { + for(auto& pair:m_books) { auto& book = pair.second; if (matchRegex(book.getTitle(), "\\Q" + search + "\\E") || matchRegex(book.getDescription(), "\\Q" + search + "\\E")) { @@ -231,7 +253,7 @@ std::vector Library::listBooksIds( size_t maxSize) { std::vector bookIds; - for(auto& pair:books) { + for(auto& pair:m_books) { auto& book = pair.second; auto local = !book.getPath().empty(); if (mode & LOCAL && !local) diff --git a/src/libxml_dumper.cpp b/src/libxml_dumper.cpp index e7e9fbbb4..11680c111 100644 --- a/src/libxml_dumper.cpp +++ b/src/libxml_dumper.cpp @@ -87,7 +87,31 @@ void LibXMLDumper::handleBook(Book book, pugi::xml_node root_node) { ADD_ATTR_NOT_EMPTY(entry_node, "downloadId", book.getDownloadId()); } -string LibXMLDumper::dumpLibXMLContent(const std::vector& bookIds) +#define ADD_TEXT_ENTRY(node, child, value) (node).append_child((child)).append_child(pugi::node_pcdata).set_value((value).c_str()) + +void LibXMLDumper::handleBookmark(Bookmark bookmark, pugi::xml_node root_node) { + + auto entry_node = root_node.append_child("bookmark"); + auto book_node = entry_node.append_child("book"); + + try { + auto book = library->getBookById(bookmark.getBookId()); + ADD_TEXT_ENTRY(book_node, "id", book.getId()); + ADD_TEXT_ENTRY(book_node, "title", book.getTitle()); + ADD_TEXT_ENTRY(book_node, "language", book.getLanguage()); + ADD_TEXT_ENTRY(book_node, "date", book.getDate()); + } catch (...) { + ADD_TEXT_ENTRY(book_node, "id", bookmark.getBookId()); + ADD_TEXT_ENTRY(book_node, "title", bookmark.getBookTitle()); + ADD_TEXT_ENTRY(book_node, "language", bookmark.getLanguage()); + ADD_TEXT_ENTRY(book_node, "date", bookmark.getDate()); + } + ADD_TEXT_ENTRY(entry_node, "title", bookmark.getTitle()); + ADD_TEXT_ENTRY(entry_node, "url", bookmark.getUrl()); +} + + +std::string LibXMLDumper::dumpLibXMLContent(const std::vector& bookIds) { pugi::xml_document doc; @@ -101,8 +125,22 @@ string LibXMLDumper::dumpLibXMLContent(const std::vector& bookIds) handleBook(library->getBookById(bookId), libraryNode); } } - return nodeToString(libraryNode); } +std::string LibXMLDumper::dumpLibXMLBookmark() +{ + pugi::xml_document doc; + + /* Add the library node */ + pugi::xml_node bookmarksNode = doc.append_child("bookmarks"); + + if (library) { + for (auto& bookmark: library->getBookmarks()) { + handleBookmark(bookmark, bookmarksNode); + } + } + return nodeToString(bookmarksNode); +} + } diff --git a/src/manager.cpp b/src/manager.cpp index 4fb0637e2..c7c45b7e0 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -221,4 +221,27 @@ bool Manager::readBookFromPath(const std::string& path, kiwix::Book* book) return true; } +bool Manager::readBookmarkFile(const std::string& path) +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(path.c_str()); + + if (!result) { + return false; + } + + pugi::xml_node libraryNode = doc.child("bookmarks"); + + for (pugi::xml_node node = libraryNode.child("bookmark"); node; + node = node.next_sibling("bookmark")) { + kiwix::Bookmark bookmark; + + bookmark.updateFromXml(node); + + manipulator->addBookmarkToLibrary(bookmark); + } + + return true; +} + } diff --git a/src/meson.build b/src/meson.build index 94288310f..46f585b1c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ kiwix_sources = [ 'book.cpp', + 'bookmark.cpp', 'library.cpp', 'manager.cpp', 'libxml_dumper.cpp',