bookmarks/BookmarksManager.cpp
changeset 15 5ea3798f1248
parent 12 d26902edeef5
equal deleted inserted replaced
14:1d6c4b7a8fbd 15:5ea3798f1248
     1 /*
     1 /*
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
     2  * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     3  * All rights reserved.
     4 *
     4  *
     5 * This program is free software: you can redistribute it and/or modify
     5  * This program is free software: you can redistribute it and/or modify
     6 * it under the terms of the GNU Lesser General Public License as published by
     6  * it under the terms of the GNU Lesser General Public License as published by
     7 * the Free Software Foundation, version 2.1 of the License.
     7  * the Free Software Foundation, version 2.1 of the License.
     8 * 
     8  * 
     9 * This program is distributed in the hope that it will be useful,
     9  * This program is distributed in the hope that it will be useful,
    10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12 * GNU Lesser General Public License for more details.
    12  * GNU Lesser General Public License for more details.
    13 *
    13  *
    14 * You should have received a copy of the GNU Lesser General Public License
    14  * You should have received a copy of the GNU Lesser General Public License
    15 * along with this program.  If not, 
    15  * along with this program.  If not, 
    16 * see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
    16  * see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
    17 *
    17  *
    18 * Description: This implements the bookmarks API's.
    18  * Description: This implements the bookmarks API's.
    19 *
    19  *
    20 */
    20  */
    21 
    21 
    22 #include<QString>
    22 #include<QString>
    23 #include<QFile>
    23 #include<QFile>
    24 #include<QFileInfo>
    24 #include<QFileInfo>
    25 #include<QDebug>
    25 #include<QDebug>
    26 #include<QSqlDatabase>
       
    27 #include<QSqlQuery>
    26 #include<QSqlQuery>
    28 #include<QSqlError>
    27 #include<QSqlError>
    29 #include<QWidget>
    28 #include<QWidget>
    30 #include<QtGui>
    29 #include<QtGui>
    31 
    30 
       
    31 #include "../../../app/browserui/bedrockProvisioning/bedrockprovisioning.h"
    32 #include "bookmarksapi.h"
    32 #include "bookmarksapi.h"
    33 #include "BookmarksManager.h"
    33 #include "BookmarksManager.h"
    34 #include "BookmarkFav.h"
    34 #include "BookmarkFav.h"
    35 #include "BookmarkResults.h"
    35 #include "BookmarkResults.h"
    36 #include "TagResults.h"
    36 #include "TagResults.h"
    37 #include "xbelreader.h"
    37 #include "xbelreader.h"
    38 #include "xbelwriter.h"
    38 #include "xbelwriter.h"
    39 #include "defaultBookmarks.xml.h"
    39 #include "defaultBookmarks.xml.h"
    40 
    40 
       
    41 namespace 
       
    42 {
       
    43     const QString BOOKMARK_IMPORT_FILENAME = "bookmarks.xml";
       
    44     const QString DEFAULT_BOOKMARKS_IMPORT_FILENAME = "defaultBookmarks.xml";
       
    45     const QString TAGS_TABLE_NAME = "tags";
       
    46     const QString BOOKMARKS_TABLE_NAME = "bookmarks";
       
    47 }
       
    48 
       
    49 BookmarksManager* BookmarksManager::instance() 
       
    50 {
       
    51     static BookmarksManager* s_instance;
       
    52     if(!s_instance) {
       
    53         s_instance = new BookmarksManager();
       
    54         if(s_instance->needsImport())
       
    55             s_instance->importDefaultBookmarks();
       
    56     }
       
    57     Q_ASSERT(s_instance);
       
    58     return s_instance;    
       
    59 }
       
    60 
    41 BookmarksManager::BookmarksManager(QWidget *parent) :
    61 BookmarksManager::BookmarksManager(QWidget *parent) :
    42     QObject(parent)
    62     QObject(parent)
    43 {
    63 {
    44     setObjectName("bookmarksManager");
    64     setObjectName("bookmarksManager");
       
    65     
       
    66     m_needsImport = false;
    45 
    67 
    46     m_db = QSqlDatabase::database(BOOKMARKS_DB_NAME);
    68     m_db = QSqlDatabase::database(BOOKMARKS_DB_NAME);
    47     if (!m_db.isValid()) {
    69     if (!m_db.isValid()) {
    48         m_db = QSqlDatabase::addDatabase("QSQLITE", BOOKMARKS_DB_NAME);
    70         m_db = QSqlDatabase::addDatabase("QSQLITE", BOOKMARKS_DB_NAME);
    49         m_db.setDatabaseName(BOOKMARKS_DB_FILE);
    71         m_db.setDatabaseName(BOOKMARKS_DB_FILE);
    50     }
    72     }
    51     if (m_db.open()) {
    73     if (m_db.open()) {
    52         // TODO: Do we need a flag so that this gets called only once OR when
    74         // TODO: Do we need a flag so that this gets called only once OR when
    53         // creating a database tables "IF NOT EXISTS" is good enough
    75         // creating a database tables "IF NOT EXISTS" is good enough
    54         createBookmarksSchema();
    76         if(!doesTableExist(BOOKMARKS_TABLE_NAME) || !doesTableExist(TAGS_TABLE_NAME)) {
    55     }
    77             createBookmarksSchema();
    56 }
    78             m_needsImport = true;
    57 
    79         }
    58 BookmarksManager::~BookmarksManager() {
    80     }
       
    81 }
       
    82 
       
    83 BookmarksManager::~BookmarksManager()
       
    84 {
    59     m_db.close();
    85     m_db.close();
    60     QSqlDatabase::removeDatabase(BOOKMARKS_DB_NAME);
    86     QSqlDatabase::removeDatabase(BOOKMARKS_DB_NAME);
       
    87 }
       
    88 
       
    89 bool BookmarksManager::createFile(QString filename, QString fileContents) 
       
    90 {
       
    91     QFile file(filename);
       
    92     
       
    93     if(file.exists())
       
    94         file.remove();
       
    95     if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
       
    96         return false;
       
    97 
       
    98     QTextStream out(&file); 
       
    99     out << fileContents;
       
   100     file.flush();
       
   101     file.close();
       
   102     return file.exists();
       
   103 }
       
   104 
       
   105 void BookmarksManager::deleteFile(QString filename) {
       
   106     QFile::remove(filename);   
       
   107 }
       
   108 
       
   109 bool BookmarksManager::doesTableExist(QString tableName)
       
   110 {
       
   111     bool retVal = false;
       
   112     
       
   113     if (!m_db.isValid() || !m_db.isOpen())
       
   114         return false;
       
   115     
       
   116     QSqlQuery query(m_db);
       
   117     query.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name=:tableName");
       
   118     query.bindValue(":tableName", QVariant(tableName));
       
   119     if (query.exec()) {
       
   120         if (query.next()) 
       
   121             retVal = true;
       
   122         query.finish();
       
   123     }
       
   124     return retVal;
    61 }
   125 }
    62 
   126 
    63 /*
   127 /*
    64  * +-------------+       +----------+
   128  * +-------------+       +----------+
    65  * |Bookmarks(ID)|* <-> *|Tags(BMID)|
   129  * |Bookmarks(ID)|* <-> *|Tags(BMID)|
    66  * +-------------+       +----------+
   130  * +-------------+       +----------+
    67  */
   131  */
    68 void BookmarksManager::createBookmarksSchema() {
   132 void BookmarksManager::createBookmarksSchema()
       
   133 {
    69     // Bookmarks
   134     // Bookmarks
    70     if (!doQuery("CREATE TABLE IF NOT EXISTS bookmarks("
   135     if (!doQuery("CREATE TABLE IF NOT EXISTS bookmarks("
    71                     "id INTEGER PRIMARY KEY,"
   136         "id INTEGER PRIMARY KEY,"
    72                     "title text, "
   137         "title text, "
    73                     "url text,"
   138         "url text,"
    74                     "sortIndex int DEFAULT 0)")) {
   139         "sortIndex int DEFAULT 0)")) {
    75         // TODO: do some error handling here!
   140         // TODO: do some error handling here!
    76         return;
   141         return;
    77     }
   142     }
    78     // Make sorting faster
   143     // Make sorting faster
    79     if (!doQuery("CREATE INDEX IF NOT EXISTS bm_sort_idx ON bookmarks(sortIndex ASC)")) {
   144     if (!doQuery(
       
   145             "CREATE INDEX IF NOT EXISTS bm_sort_idx ON bookmarks(sortIndex ASC)")) {
    80         // TODO: do some error handling here!
   146         // TODO: do some error handling here!
    81         return;
   147         return;
    82     }
   148     }
    83     // We do a lot of lookups by id
   149     // We do a lot of lookups by id
    84     if (!doQuery("CREATE INDEX IF NOT EXISTS bm_id_idx ON bookmarks(id ASC)")) {
   150     if (!doQuery("CREATE INDEX IF NOT EXISTS bm_id_idx ON bookmarks(id ASC)")) {
    88 
   154 
    89     // Tags
   155     // Tags
    90     // Note: foreign key constraints are not enforced in the current version of sqlite
   156     // Note: foreign key constraints are not enforced in the current version of sqlite
    91     // that we are using.
   157     // that we are using.
    92     if (!doQuery("CREATE TABLE IF NOT EXISTS tags("
   158     if (!doQuery("CREATE TABLE IF NOT EXISTS tags("
    93                     "bmid INTEGER,"
   159         "bmid INTEGER,"
    94                     "tag text,"
   160         "tag text,"
    95                     "FOREIGN KEY(bmid) REFERENCES bookmarks(id))")) {
   161         "FOREIGN KEY(bmid) REFERENCES bookmarks(id))")) {
    96         // TODO: do some error handling here!
   162         // TODO: do some error handling here!
    97         return;
   163         return;
    98     }
   164     }
    99     // We do a lot of lookups, both by bookmark id and by tag
   165     // We do a lot of lookups, both by bookmark id and by tag
   100     if (!doQuery("CREATE INDEX IF NOT EXISTS tags_bmid_idx ON tags(bmid ASC)")) {
   166     if (!doQuery("CREATE INDEX IF NOT EXISTS tags_bmid_idx ON tags(bmid ASC)")) {
   105         // TODO: do some error handling here!
   171         // TODO: do some error handling here!
   106         return;
   172         return;
   107     }
   173     }
   108 }
   174 }
   109 
   175 
       
   176 int BookmarksManager::importDefaultBookmarks() 
       
   177 {
       
   178     int success = FAILURE;
       
   179     // TODO: When Jira Task BR-4939 (https://qtrequirements.europe.nokia.com/browse/BR-4939) is
       
   180     // finished, refactor this to get the path of the bookmarks.xml from that service.   
       
   181     //QString bookmarksFilename = BEDROCK_PROVISIONING::BedrockProvisioning::createBedrockProvisioning()->valueAsString("DataBaseDirectory"); 
       
   182     
       
   183     if(QFile::exists(QString("z:/private/10008d39/").append(BOOKMARK_IMPORT_FILENAME))) {
       
   184         success = importBookmarks(QString("z:/private/10008d39/").append(BOOKMARK_IMPORT_FILENAME));
       
   185         qDebug() << "Import of bookmarks.xml " << (success == SUCCESS ? "successful." : "failed.");
       
   186     } else if (QFile::exists(QString("c:/private/10008d39/").append(BOOKMARK_IMPORT_FILENAME))) {
       
   187         success = importBookmarks(QString("c:/private/10008d39/").append(BOOKMARK_IMPORT_FILENAME));
       
   188         qDebug() << "Import of bookmarks.xml " << (success == SUCCESS ? "successful." : "failed.");
       
   189     } else if(createFile(DEFAULT_BOOKMARKS_IMPORT_FILENAME, defaultBookmarksList)) {        
       
   190         success = importBookmarks(DEFAULT_BOOKMARKS_IMPORT_FILENAME);
       
   191         qDebug() << "bookmarks.xml not found. Import of defaultBookmarks.xml " << 
       
   192             (success == SUCCESS ? "successful." : "failed.");
       
   193     } else {
       
   194         qDebug() << "Could not import factory or default bookmarks.";
       
   195     }
       
   196     return success; 
       
   197 }
       
   198 
   110 // TODO refactor this - nothing except the schema creation can use it as is
   199 // TODO refactor this - nothing except the schema creation can use it as is
   111 bool BookmarksManager::doQuery(QString query) {
   200 bool BookmarksManager::doQuery(QString query)
       
   201 {
   112 #ifdef ENABLE_PERF_TRACE
   202 #ifdef ENABLE_PERF_TRACE
   113     PERF_DEBUG() << __PRETTY_FUNCTION__ << query << "\n";
   203     PERF_DEBUG() << __PRETTY_FUNCTION__ << query << "\n";
   114     unsigned int st = WrtPerfTracer::tracer()->startTimer();
   204     unsigned int st = WrtPerfTracer::tracer()->startTimer();
   115 #endif
   205 #endif
   116     QSqlQuery db_query(m_db);
   206     QSqlQuery db_query(m_db);
   117     bool ok = db_query.exec(query);
   207     bool ok = db_query.exec(query);
   118     if (!ok) {
   208     if (!ok) {
   119         qDebug() << "BookmarksManager::doQuery" << QString("ERR: %1 %2").arg(db_query.lastError().type()).arg(db_query.lastError().text()) << " Query: " << db_query.lastQuery();
   209         qDebug() << "BookmarksManager::doQuery" << QString("ERR: %1 %2").arg(
       
   210                 db_query.lastError().type()).arg(db_query.lastError().text())
       
   211                 << " Query: " << db_query.lastQuery();
   120     }
   212     }
   121 #ifdef ENABLE_PERF_TRACE
   213 #ifdef ENABLE_PERF_TRACE
   122     PERF_DEBUG() << __PRETTY_FUNCTION__ << WrtPerfTracer::tracer()->elapsedTime(st) << "\n";
   214     PERF_DEBUG() << __PRETTY_FUNCTION__ << WrtPerfTracer::tracer()->elapsedTime(st) << "\n";
   123 #endif
   215 #endif
   124     return ok;   
   216     return ok;
   125 }
   217 }
   126 
       
   127 
   218 
   128 /**==============================================================
   219 /**==============================================================
   129  * Description: Normalize a given url, if needed.
   220  * Description: Normalize a given url, if needed.
   130  * It adds http:// in front, if the url is relative.
   221  * It adds http:// in front, if the url is relative.
   131  ================================================================*/
   222  ================================================================*/
   147  ================================================================*/
   238  ================================================================*/
   148 int BookmarksManager::addBookmark(QString title, QString URL)
   239 int BookmarksManager::addBookmark(QString title, QString URL)
   149 {
   240 {
   150     int bookmarkId = 0;
   241     int bookmarkId = 0;
   151     
   242     
   152     if(URL.isEmpty()) {
   243     if (URL.isEmpty()) {
   153        bookmarkId = FAILURE;
   244         bookmarkId = FAILURE;
   154     }
   245     }
   155     
   246 
   156     if(bookmarkId != FAILURE) {
   247     if (bookmarkId != FAILURE) {
   157         // do some checking on parameters
   248         // do some checking on parameters
   158         QString updatedTitle = title;
   249         QString updatedTitle = title;
   159         QString updatedUrl = normalizeUrl(URL);
   250         QString updatedUrl = normalizeUrl(URL);
   160         int soIndex = 1;
   251         int soIndex = 1;
   161         if (title.isEmpty()) {
   252         if (title.isEmpty()) {
   163         }
   254         }
   164         if (m_db.isOpen()) {
   255         if (m_db.isOpen()) {
   165             QSqlQuery query(m_db);
   256             QSqlQuery query(m_db);
   166             m_db.transaction();
   257             m_db.transaction();
   167             if (!query.exec("SELECT count(*) from bookmarks")) {
   258             if (!query.exec("SELECT count(*) from bookmarks")) {
   168                 lastErrMsg(query);
   259                    lastErrMsg(query);
   169                 m_db.rollback();
   260                    m_db.rollback();
   170                 return DATABASEERROR;
   261                    return DATABASEERROR;
   171             }
   262             }
   172             if(query.next()) {
   263             if(query.next()) {
   173                 query.prepare("UPDATE bookmarks SET sortIndex=sortIndex+1 WHERE sortIndex >= :sIndex");
   264                    query.prepare("UPDATE bookmarks SET sortIndex=sortIndex+1 WHERE sortIndex >= :sIndex");
   174                 query.bindValue(":sIndex", soIndex);
   265                    query.bindValue(":sIndex", soIndex);
   175                 if (!query.exec()) {
   266                    if (!query.exec()) {
   176                     lastErrMsg(query);
   267                         lastErrMsg(query);
   177                     m_db.rollback();
   268                         m_db.rollback();
   178                     return DATABASEERROR;
   269                         return DATABASEERROR;
   179                  }
   270                    }
   180            } 
   271             } 
   181            query.prepare("INSERT INTO bookmarks (title, url, sortIndex) "
   272             query.prepare("INSERT INTO bookmarks (title, url, sortIndex) "
   182                                "VALUES (:title, :url, :sIndex)");
   273                     "VALUES (:title, :url, :sIndex)");
   183             query.bindValue(":title", QVariant(updatedTitle));
   274             query.bindValue(":title", QVariant(updatedTitle));
   184             query.bindValue(":url",    QVariant(updatedUrl));
   275             query.bindValue(":url",    QVariant(updatedUrl));
   185             query.bindValue(":sIndex",  QVariant(soIndex));
   276             query.bindValue(":sIndex",  QVariant(soIndex));
   186             if (!query.exec()) {
   277             if (!query.exec()) {
   187                 lastErrMsg(query);
   278                 lastErrMsg(query);
   188                 m_db.rollback();
   279                 m_db.rollback();
   189                 return DATABASEERROR;
   280                 return DATABASEERROR;
   190            }
   281             }
   191            // Note: lastInsertId() is not thread-safe
   282             // Note: lastInsertId() is not thread-safe
   192             bookmarkId = query.lastInsertId().toInt();
   283             bookmarkId = query.lastInsertId().toInt();
   193             if (!m_db.commit()) {
   284             if (!m_db.commit()) {
   194                 qDebug() << m_db.lastError().text();
   285                 qDebug() << m_db.lastError().text();
   195                 m_db.rollback();
   286                 m_db.rollback();
   196                return DATABASEERROR;
   287                 return DATABASEERROR;
   197             }
   288             }
   198         } else {
   289         } else {
   199             bookmarkId = FAILURE;
   290               bookmarkId = FAILURE;
   200         }
   291         }
   201       }
   292       }
   202     return bookmarkId;
   293     return bookmarkId;
   203 }
   294 }
   204 
   295 
   215 int BookmarksManager::importBookmarks(QString xbelFilePath)
   306 int BookmarksManager::importBookmarks(QString xbelFilePath)
   216 {
   307 {
   217     XbelReader *reader = new XbelReader(this);
   308     XbelReader *reader = new XbelReader(this);
   218     bool retVal = false;
   309     bool retVal = false;
   219     
   310     
   220     if(xbelFilePath.isEmpty() || !QFile::exists(xbelFilePath)) {
   311     if (xbelFilePath.isEmpty() || !QFile::exists(xbelFilePath)) {
   221         xbelFilePath = "c:\\data\\temp.xml";
   312         xbelFilePath = DEFAULT_BOOKMARKS_IMPORT_FILENAME;
   222         QFile file(xbelFilePath);
   313         QFile file(xbelFilePath);
   223         if(file.exists())
   314         if (file.exists())
   224             file.remove();
   315             file.remove();
   225         if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
   316         if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
   226             return false;
   317             return false;
   227         QTextStream out(&file); 
   318         QTextStream out(&file);
   228         out << defaultBookmarksList;
   319         out << defaultBookmarksList;
   229         out.flush();
   320         out.flush();
   230         file.close();
   321         file.close();
   231     } 
   322     }
   232     QFile file(xbelFilePath);
   323     QFile file(xbelFilePath);
   233     if(file.exists()) {
   324     if (file.exists()) {
   234         file.open(QIODevice::ReadOnly | QIODevice::Text);
   325         file.open(QIODevice::ReadOnly | QIODevice::Text);
   235         retVal = reader->read(&file);
   326         retVal = reader->read(&file);
   236         file.close();
   327         file.close();
   237     }
   328     }
   238     if(reader)
   329     if(QFile::exists(DEFAULT_BOOKMARKS_IMPORT_FILENAME)) {
       
   330         QFile::remove(DEFAULT_BOOKMARKS_IMPORT_FILENAME);
       
   331     }
       
   332     if (reader)
   239         delete reader;
   333         delete reader;
   240     return retVal ? SUCCESS : FAILURE;
   334     return retVal ? SUCCESS : FAILURE;
   241 }
   335 }
   242 
   336 
   243 /**==============================================================
   337 /**==============================================================
   252  ==============================================================*/
   346  ==============================================================*/
   253 int BookmarksManager::exportBookmarks(QString xbelFilePath)
   347 int BookmarksManager::exportBookmarks(QString xbelFilePath)
   254 {
   348 {
   255     XbelWriter *writer = new XbelWriter(this);
   349     XbelWriter *writer = new XbelWriter(this);
   256     bool retVal = false;
   350     bool retVal = false;
   257 
   351     
   258     if(xbelFilePath.isEmpty()) {
   352     if (xbelFilePath.isEmpty()) {
   259         xbelFilePath = "c:\\data\\myBookmarks.xml";
   353         xbelFilePath = "c:\\data\\myBookmarks.xml";
   260     }  
   354     }
   261 
   355 
   262     QFile file(xbelFilePath);
   356     QFile file(xbelFilePath);
   263     if(file.exists()) {
   357     if (file.exists()) {
   264         file.remove(xbelFilePath);
   358         file.remove(xbelFilePath);
   265     }
   359     }
   266     file.open(QIODevice::WriteOnly | QIODevice::Text);
   360     file.open(QIODevice::WriteOnly | QIODevice::Text);
   267     retVal = writer->writeFile(&file);
   361     retVal = writer->writeFile(&file);
   268     file.flush();
   362     file.flush();
   269     file.close();
   363     file.close();
   270     
   364     
   271     if(writer)
   365     if (writer)
   272         delete writer;
   366         delete writer;
   273     return retVal ? SUCCESS : FAILURE;
   367     return retVal ? SUCCESS : FAILURE;
   274     
   368     
   275 }
   369 }
   276 
   370 
   277 /**==============================================================
   371 /**==============================================================
   278  * Description: Modify the existing bookmark. Given id of the bookmark
   372  * Description: Modify the existing bookmark. Given id of the bookmark
   279  * and new title or url, it modifies the existing bookmark.
   373  * and new title or url, it modifies the existing bookmark.
   280  * Returns : Success(0) or Failure(-1 or -2).
   374  * Returns : Success(0) or Failure(-1 or -2).
   281  ================================================================*/
   375  ================================================================*/
   282 int BookmarksManager::modifyBookmark(int origBookmarkId, QString newTitle, QString newURL)
   376 int BookmarksManager::modifyBookmark(int origBookmarkId, QString newTitle,
       
   377         QString newURL)
   283 {
   378 {
   284     int retVal = SUCCESS;
   379     int retVal = SUCCESS;
   285     
   380     
   286     // Client has to at least pass title or url
   381     // Client has to at least pass title or url
   287     if(newTitle.isEmpty() && newURL.isEmpty())
   382     if (newTitle.isEmpty() && newURL.isEmpty())
   288        retVal = FAILURE;
   383         retVal = FAILURE;
   289      
   384     
   290     if(retVal == SUCCESS) {
   385     if (retVal == SUCCESS) {
   291     
   386         
   292         if (m_db.isOpen()) { 
   387         if (m_db.isOpen()) {
   293               QSqlQuery query(m_db);
   388             QSqlQuery query(m_db);
   294             
   389             
   295             if(newTitle.isEmpty()) {        
   390             if (newTitle.isEmpty()) {
   296                 query.prepare("UPDATE bookmarks SET url=:newurl WHERE id=:bmId");
   391                 query.prepare(
       
   392                         "UPDATE bookmarks SET url=:newurl WHERE id=:bmId");
   297                 query.bindValue(":newurl", normalizeUrl(newURL));
   393                 query.bindValue(":newurl", normalizeUrl(newURL));
   298                 query.bindValue(":bmId", origBookmarkId);
   394                 query.bindValue(":bmId", origBookmarkId);
   299             } else if(newURL.isEmpty()) { 
   395             } else if (newURL.isEmpty()) {
   300                 query.prepare("UPDATE bookmarks SET title=:newTitle WHERE id=:bmId");
   396                 query.prepare(
       
   397                         "UPDATE bookmarks SET title=:newTitle WHERE id=:bmId");
   301                 query.bindValue(":newTitle", newTitle);
   398                 query.bindValue(":newTitle", newTitle);
   302                 query.bindValue(":bmId", origBookmarkId);
   399                 query.bindValue(":bmId", origBookmarkId);
   303             } else {
   400             } else {
   304                 query.prepare("UPDATE bookmarks SET url=:newurl, title=:newTitle WHERE id=:bmId");
   401                 query.prepare(
       
   402                         "UPDATE bookmarks SET url=:newurl, title=:newTitle WHERE id=:bmId");
   305                 query.bindValue(":newurl", normalizeUrl(newURL));
   403                 query.bindValue(":newurl", normalizeUrl(newURL));
   306                 query.bindValue(":newTitle", newTitle);
   404                 query.bindValue(":newTitle", newTitle);
   307                 query.bindValue(":bmId", origBookmarkId);
   405                 query.bindValue(":bmId", origBookmarkId);
   308             }
   406             }
   309             if (!query.exec()) {
   407             if (!query.exec()) {
   310                 lastErrMsg(query);
   408                 lastErrMsg(query);
   311                 return DATABASEERROR;
   409                 return DATABASEERROR;
   312             } 
   410             }
   313             if (query.numRowsAffected() == 0) {
   411             if (query.numRowsAffected() == 0) {
   314                 // No update happened - must be an invalid id
   412                 // No update happened - must be an invalid id
   315                 // TODO: shall we return some other status
   413                 // TODO: shall we return some other status
   316                 retVal = FAILURE;
   414                 retVal = FAILURE;
   317             }
   415             }
   318         } else 
   416         } else
   319             retVal = FAILURE;
   417             retVal = FAILURE;
   320     }
   418     }
   321     return retVal;
   419     return retVal;
   322 }
   420 }
   323 
   421 
   326  * Returns: SUCCESS(0) or Failure(-1 or -2)
   424  * Returns: SUCCESS(0) or Failure(-1 or -2)
   327  ======================================================================================*/
   425  ======================================================================================*/
   328 int BookmarksManager::deleteBookmark(int bookmarkId)
   426 int BookmarksManager::deleteBookmark(int bookmarkId)
   329 {
   427 {
   330     int retVal = SUCCESS;
   428     int retVal = SUCCESS;
   331    
   429     
   332     if (m_db.isOpen()) {      
   430     if (m_db.isOpen()) {
   333     
   431         
   334         QSqlQuery query(m_db);
   432         QSqlQuery query(m_db);
   335          
   433         
   336         // TODO: Need to think about whether we need to get sortIndex and update all the 
   434         // TODO: Need to think about whether we need to get sortIndex and update all the 
   337         // rows after the deletion or not
   435         // rows after the deletion or not
   338 
   436 
   339         // TODO: check if transaction() has been supported 
   437         // TODO: check if transaction() has been supported 
   340         // by calling hasfeature() function first
   438         // by calling hasfeature() function first
   345         if (!query.exec()) {
   443         if (!query.exec()) {
   346             lastErrMsg(query);
   444             lastErrMsg(query);
   347             m_db.rollback();
   445             m_db.rollback();
   348             return DATABASEERROR;
   446             return DATABASEERROR;
   349         }
   447         }
   350         
   448 
   351         query.prepare("DELETE FROM tags WHERE bmid=:bmId");
   449         query.prepare("DELETE FROM tags WHERE bmid=:bmId");
   352         query.bindValue(":bmId", bookmarkId);
   450         query.bindValue(":bmId", bookmarkId);
   353         if (!query.exec()) {
   451         if (!query.exec()) {
   354             lastErrMsg(query);
   452             lastErrMsg(query);
   355             m_db.rollback();
   453             m_db.rollback();
   356             return DATABASEERROR;
   454             return DATABASEERROR;
   357         } 
   455         }
   358         if (!m_db.commit()) {
   456         if (!m_db.commit()) {
   359             qDebug() << m_db.lastError().text();
   457             qDebug() << m_db.lastError().text();
   360             m_db.rollback();
   458             m_db.rollback();
   361             return DATABASEERROR;
   459             return DATABASEERROR;
   362         }         
   460         }
   363     } else 
   461     } else
   364         retVal = FAILURE;
   462         retVal = FAILURE;
   365     
   463     
   366     return retVal;     
   464     return retVal;
   367 }
   465 }
   368 
   466 
   369 /**===================================================================================
   467 /**===================================================================================
   370  * Description: Delete all records from the bookmarks table as well as all the tags.
   468  * Description: Delete all records from the bookmarks table as well as all the tags.
   371  * Returns: SUCCESS(0) or FAILURE(-1 or -2)
   469  * Returns: SUCCESS(0) or FAILURE(-1 or -2)
   372  ======================================================================================*/
   470  ======================================================================================*/
   373 int BookmarksManager::clearAll()
   471 int BookmarksManager::clearAll()
   374 {
   472 {
   375     int retVal = SUCCESS;
   473     int retVal = SUCCESS;
   376     
   474     
   377     if (m_db.isOpen()) {        
   475     if (m_db.isOpen()) {
   378        QSqlQuery query(m_db);
   476         QSqlQuery query(m_db);
   379            
   477         
   380        // TODO: check if transaction() has been supported 
   478         // TODO: check if transaction() has been supported 
   381        // by calling hasfeature() function first
   479         // by calling hasfeature() function first
   382        m_db.transaction();
   480         m_db.transaction();
   383        
   481         
   384        if(!query.exec("DELETE FROM bookmarks")) {  
   482         if (!query.exec("DELETE FROM bookmarks")) {
   385             lastErrMsg(query);
   483             lastErrMsg(query);
   386             m_db.rollback();
   484             m_db.rollback();
   387             retVal = DATABASEERROR;
   485             retVal = DATABASEERROR;
   388        } 
   486         }
   389        if (retVal == SUCCESS && !query.exec("DELETE FROM tags")) {
   487         if (retVal == SUCCESS && !query.exec("DELETE FROM tags")) {
   390             lastErrMsg(query);
   488             lastErrMsg(query);
   391             m_db.rollback();
   489             m_db.rollback();
   392             retVal = DATABASEERROR;
   490             retVal = DATABASEERROR;
   393        } 
   491         }
   394        if (retVal == SUCCESS && !m_db.commit()) {
   492         if (retVal == SUCCESS && !m_db.commit()) {
   395            qDebug() << m_db.lastError().text();
   493             qDebug() << m_db.lastError().text();
   396            m_db.rollback();
   494             m_db.rollback();
   397            retVal = DATABASEERROR;
   495             retVal = DATABASEERROR;
   398        }
   496         }
   399     } else 
   497     } else
   400         retVal = FAILURE;
   498         retVal = FAILURE;
   401     
   499     
   402     return retVal;     
   500     return retVal;
   403 }
   501 }
   404 
   502 
   405 /**==============================================================
   503 /**==============================================================
   406  * Description: Deletes a single tag associated with the bookmark
   504  * Description: Deletes a single tag associated with the bookmark
   407  * Returns: SUCCESS(0) or FAILURE (-1 or -2)
   505  * Returns: SUCCESS(0) or FAILURE (-1 or -2)
   408  ===============================================================*/
   506  ===============================================================*/
   409 int BookmarksManager::deleteTag(int bookmarkId, QString tagToDelete)
   507 int BookmarksManager::deleteTag(int bookmarkId, QString tagToDelete)
   410 {
   508 {
   411     int retVal = SUCCESS;
   509     int retVal = SUCCESS;
   412  
   510     
   413     if(tagToDelete.isEmpty()|| bookmarkId < 0)
   511     if (tagToDelete.isEmpty() || bookmarkId < 0)
   414           retVal = FAILURE;
   512         retVal = FAILURE;
   415     
   513     
   416     if (retVal == SUCCESS) {
   514     if (retVal == SUCCESS) {
   417         if (m_db.isOpen()) {
   515         if (m_db.isOpen()) {
   418             QSqlQuery query(m_db);
   516             QSqlQuery query(m_db);
   419          
   517             
   420             query.prepare("DELETE FROM tags WHERE bmid=:bmId AND tag=:tag");
   518             query.prepare("DELETE FROM tags WHERE bmid=:bmId AND tag=:tag");
   421             query.bindValue(":bmId", bookmarkId);
   519             query.bindValue(":bmId", bookmarkId);
   422             query.bindValue(":tag", tagToDelete);
   520             query.bindValue(":tag", tagToDelete);
   423             if (!query.exec()) {
   521             if (!query.exec()) {
   424                 lastErrMsg(query);
   522                 lastErrMsg(query);
   425                 retVal = DATABASEERROR;
   523                 retVal = DATABASEERROR;
   426             }
   524             }
   427         } else 
   525         } else
   428             retVal = FAILURE;
   526             retVal = FAILURE;
   429     }
   527     }
   430     
   528     
   431     return retVal;     
   529     return retVal;
   432 }
   530 }
   433 
   531 
   434 /**================================================================
   532 /**================================================================
   435  * Description: Adds a single tag associated given the bookmark id
   533  * Description: Adds a single tag associated given the bookmark id
   436  * Returns: SUCCESS(0) or FAILURE (-1 or -2)
   534  * Returns: SUCCESS(0) or FAILURE (-1 or -2)
   437  ==================================================================*/
   535  ==================================================================*/
   438 int BookmarksManager::addTag(int bookmarkId, QString tagToAdd)
   536 int BookmarksManager::addTag(int bookmarkId, QString tagToAdd)
   439 {
   537 {
   440     int retVal = SUCCESS;
   538     int retVal = SUCCESS;
   441     
   539     
   442     if(tagToAdd.isEmpty()|| bookmarkId < 0)
   540     if (tagToAdd.isEmpty() || bookmarkId < 0)
   443        retVal = FAILURE;
   541         retVal = FAILURE;
   444     
   542     
   445     if(retVal == SUCCESS) {
   543     if (retVal == SUCCESS) {
   446         if (m_db.isOpen()) {     
   544         if (m_db.isOpen()) {
   447              QSqlQuery query(m_db);
   545             QSqlQuery query(m_db);
   448              
   546             
   449             query.prepare("INSERT INTO tags (bmid, tag) "
   547             query.prepare("INSERT INTO tags (bmid, tag) "
   450                      "VALUES (:id, :tag)");                      
   548                 "VALUES (:id, :tag)");
   451             query.bindValue(":id",   QVariant(bookmarkId));
   549             query.bindValue(":id", QVariant(bookmarkId));
   452             query.bindValue(":tag",  QVariant(tagToAdd));
   550             query.bindValue(":tag", QVariant(tagToAdd));
   453             
   551             
   454             if (!query.exec()) {
   552             if (!query.exec()) {
   455                 lastErrMsg(query);
   553                 lastErrMsg(query);
   456                 retVal = DATABASEERROR;
   554                 retVal = DATABASEERROR;
   457             } 
   555             }
   458         } else 
   556         } else
   459             retVal = FAILURE;
   557             retVal = FAILURE;
   460     }
   558     }
   461     
   559     
   462     return retVal;
   560     return retVal;
   463 }
   561 }
   467  * or not.
   565  * or not.
   468  * Returns: A pointer to BookmarkResults object or NULL.
   566  * Returns: A pointer to BookmarkResults object or NULL.
   469  ===============================================================*/
   567  ===============================================================*/
   470 BookmarkResults *BookmarksManager::findAllBookmarks()
   568 BookmarkResults *BookmarksManager::findAllBookmarks()
   471 {
   569 {
   472     BookmarkResults * results = NULL; 
   570     BookmarkResults * results = NULL;
   473     
   571     
   474     QString queryStr = QString("SELECT "
   572     QString queryStr = QString("SELECT "
   475                                " id, title, url, sortIndex " 
   573         " id, title, url, sortIndex "
   476                                " FROM bookmarks ORDER BY sortIndex");
   574         " FROM bookmarks ORDER BY sortIndex");
   477     if (m_db.isOpen()) {
   575     if (m_db.isOpen()) {
   478         QSqlQuery *query = new QSqlQuery(m_db);
   576         QSqlQuery *query = new QSqlQuery(m_db);
   479         if (query->exec(queryStr)) {
   577         if (query->exec(queryStr)) {
   480              results = new BookmarkResults(query);
   578             results = new BookmarkResults(query);
   481         } else {
   579         } else {
   482             qDebug() << query->lastError().text() << " Query: " << query->lastQuery();
   580             qDebug() << query->lastError().text() << " Query: "
       
   581                     << query->lastQuery();
   483             results = NULL;
   582             results = NULL;
   484         }
   583         }
   485     }
   584     }
   486     return results;
   585     return results;
   487 }
   586 }
   490  * Description: Finds all the distinct tags.
   589  * Description: Finds all the distinct tags.
   491  * Returns: A pointer to TagResults object or NULL.
   590  * Returns: A pointer to TagResults object or NULL.
   492  ===============================================================*/
   591  ===============================================================*/
   493 TagResults *BookmarksManager::findAllTags()
   592 TagResults *BookmarksManager::findAllTags()
   494 {
   593 {
   495     TagResults * results = NULL; 
   594     TagResults * results = NULL;
   496     
   595     
   497     if (m_db.isOpen()) {
   596     if (m_db.isOpen()) {
   498         QSqlQuery *query = new QSqlQuery(m_db);
   597         QSqlQuery *query = new QSqlQuery(m_db);
   499         if (query->exec("SELECT DISTINCT tag FROM tags")) {
   598         if (query->exec("SELECT DISTINCT tag FROM tags")) {
   500             // TODO: do we need javascript hack here like in findAllBookmarks API.
   599             // TODO: do we need javascript hack here like in findAllBookmarks API.
   501             results = new TagResults(query); 
   600             results = new TagResults(query);
   502         } else {
   601         } else {
   503             qDebug() << query->lastError().text() << " Query: " << query->lastQuery();
   602             qDebug() << query->lastError().text() << " Query: "
       
   603                     << query->lastQuery();
   504             results = NULL;
   604             results = NULL;
   505         }
   605         }
   506     }
   606     }
   507     return results;
   607     return results;
   508 }
   608 }
   509 
       
   510 
   609 
   511 /**==============================================================
   610 /**==============================================================
   512  * Description: Finds all the bookmarks associated with a given
   611  * Description: Finds all the bookmarks associated with a given
   513  * tag. 
   612  * tag. 
   514  * Returns: A pointer to BookmarkResults object or NULL.
   613  * Returns: A pointer to BookmarkResults object or NULL.
   515  ===============================================================*/
   614  ===============================================================*/
   516 BookmarkResults *BookmarksManager::findBookmarksByTag(QString tag)
   615 BookmarkResults *BookmarksManager::findBookmarksByTag(QString tag)
   517 {
   616 {
   518     BookmarkResults * results = NULL; 
   617     BookmarkResults * results = NULL;
   519     
   618     
   520     QString queryStr = QString("SELECT "
   619     QString queryStr = QString("SELECT "
   521                                " id, title, url, sortIndex " 
   620         " id, title, url, sortIndex "
   522                                " FROM bookmarks b JOIN"
   621         " FROM bookmarks b JOIN"
   523                                " tags t ON b.id=t.bmid WHERE" 
   622         " tags t ON b.id=t.bmid WHERE"
   524                                " t.tag=:tag");
   623         " t.tag=:tag");
   525      if (m_db.isOpen()) {
   624     if (m_db.isOpen()) {
   526          QSqlQuery *query = new QSqlQuery(m_db);
   625         QSqlQuery *query = new QSqlQuery(m_db);
   527          query->prepare(queryStr);
   626         query->prepare(queryStr);
   528          query->bindValue(":tag", tag);
   627         query->bindValue(":tag", tag);
   529          if (query->exec()) {
   628         if (query->exec()) {
   530              // TODO: do we need javascript hack here like in findAllBookmarks API.
   629             // TODO: do we need javascript hack here like in findAllBookmarks API.
   531              results = new BookmarkResults(query);
   630             results = new BookmarkResults(query);
   532          } else {
   631         } else {
   533             qDebug() << query->lastError().text() << " Query: " << query->lastQuery();
   632             qDebug() << query->lastError().text() << " Query: "
       
   633                     << query->lastQuery();
   534             results = NULL;
   634             results = NULL;
   535          }
   635         }
   536      }
   636     }
   537      return results;
   637     return results;
   538 }
   638 }
   539 
       
   540 
   639 
   541 /**==============================================================
   640 /**==============================================================
   542  * Description: Finds all the Tags associated with a given
   641  * Description: Finds all the Tags associated with a given
   543  * bookmarkID.
   642  * bookmarkID.
   544  * Returns: A pointer to TagResults object or NULL.
   643  * Returns: A pointer to TagResults object or NULL.
   545  ===============================================================*/
   644  ===============================================================*/
   546 TagResults *BookmarksManager::findTagsByBookmark(int bookmarkID)
   645 TagResults *BookmarksManager::findTagsByBookmark(int bookmarkID)
   547 {
   646 {
   548      TagResults * results = NULL; 
   647     TagResults * results = NULL;
   549     
   648     
   550      QString queryStr = QString("SELECT DISTINCT tag "
   649     QString queryStr = QString("SELECT DISTINCT tag "
   551                                  " FROM tags t JOIN"
   650         " FROM tags t JOIN"
   552                                  " bookmarks b ON t.bmid=b.id WHERE" 
   651         " bookmarks b ON t.bmid=b.id WHERE"
   553                                  " t.bmid=:id");
   652         " t.bmid=:id");
   554      if (m_db.isOpen()) {
   653     if (m_db.isOpen()) {
   555         QSqlQuery *query = new QSqlQuery(m_db);
   654         QSqlQuery *query = new QSqlQuery(m_db);
   556         query->prepare(queryStr);
   655         query->prepare(queryStr);
   557         query->bindValue(":id", bookmarkID);
   656         query->bindValue(":id", bookmarkID);
   558         if (query->exec()) {
   657         if (query->exec()) {
   559             // TODO: do we need javascript hack here like in findAllBookmarks API.
   658             // TODO: do we need javascript hack here like in findAllBookmarks API.
   560             results =  new TagResults(query); 
   659             results = new TagResults(query);
   561         } else {
   660         } else {
   562             qDebug() << query->lastError().text() << " Query: " << query->lastQuery();
   661             qDebug() << query->lastError().text() << " Query: "
       
   662                     << query->lastQuery();
   563             results = NULL;
   663             results = NULL;
   564         }
   664         }
   565      }
   665     }
   566      return results;
   666     return results;
   567 }
   667 }
   568 
   668 
   569 /**==============================================================
   669 /**==============================================================
   570  * Description: Finds all the Bookmarks that doesn't have any
   670  * Description: Finds all the Bookmarks that doesn't have any
   571  * tags associated with it. It is needed by export API.
   671  * tags associated with it. It is needed by export API.
   572  * Returns: A pointer to BookmarkResults object or NULL.
   672  * Returns: A pointer to BookmarkResults object or NULL.
   573  ===============================================================*/
   673  ===============================================================*/
   574 BookmarkResults *BookmarksManager::findUntaggedBookmarks()
   674 BookmarkResults *BookmarksManager::findUntaggedBookmarks()
   575 {   
   675 {
   576     BookmarkResults * results = NULL; 
   676     BookmarkResults * results = NULL;
   577        
   677     
   578     QString queryStr = QString("SELECT "
   678     QString queryStr = QString("SELECT "
   579                                " id, title, url, sortIndex " 
   679         " id, title, url, sortIndex "
   580                                " FROM bookmarks b LEFT OUTER JOIN"
   680         " FROM bookmarks b LEFT OUTER JOIN"
   581                                " tags t ON b.id=t.bmid WHERE" 
   681         " tags t ON b.id=t.bmid WHERE"
   582                                " t.bmid IS NULL ORDER BY sortIndex");
   682         " t.bmid IS NULL ORDER BY sortIndex");
   583     
   683     
   584     if (m_db.isOpen()) { 
   684     if (m_db.isOpen()) {
   585        QSqlQuery *query = new QSqlQuery(m_db);
   685         QSqlQuery *query = new QSqlQuery(m_db);
   586        if (query->exec(queryStr)) {
   686         if (query->exec(queryStr)) {
   587            // TODO: do we need javascript hack here like in findAllBookmarks API.
   687             // TODO: do we need javascript hack here like in findAllBookmarks API.
   588            results = new BookmarkResults(query);
   688             results = new BookmarkResults(query);
   589        } else {
   689         } else {
   590            qDebug() << query->lastError().text() << " Query: " << query->lastQuery();
   690             qDebug() << query->lastError().text() << " Query: "
   591            results = NULL;
   691                     << query->lastQuery();
   592        }
   692             results = NULL;
       
   693         }
   593     }
   694     }
   594     return results;
   695     return results;
   595 }
   696 }
   596 
       
   597 
   697 
   598 /**==============================================================
   698 /**==============================================================
   599  * Description: Reorder bookmarks. Moves a given bookmark to a 
   699  * Description: Reorder bookmarks. Moves a given bookmark to a 
   600  * passed new index.
   700  * passed new index.
   601  * Returns: SUCCESS(0) or FAILURE (-1 or -2)
   701  * Returns: SUCCESS(0) or FAILURE (-1 or -2)
   602  ===============================================================*/
   702  ===============================================================*/
   603 int BookmarksManager::reorderBookmark(int bookmarkID, int newIndex)
   703 int BookmarksManager::reorderBookmark(int bookmarkID, int newIndex)
   604 {   
   704 {
   605     if (newIndex <= 0) 
   705     if (newIndex <= 0)
   606         return FAILURE;
   706         return FAILURE;
   607 
   707 
   608     if (!m_db.isOpen())
   708     if (!m_db.isOpen())
   609     	return DATABASEERROR;
   709     	return DATABASEERROR;
   610 
   710 
   657  ===============================================================*/
   757  ===============================================================*/
   658 BookmarkFav* BookmarksManager::findBookmark(int bookmarkId)
   758 BookmarkFav* BookmarksManager::findBookmark(int bookmarkId)
   659 {
   759 {
   660     BookmarkFav * results = NULL;
   760     BookmarkFav * results = NULL;
   661     
   761     
   662   
   762     if (m_db.isOpen()) {
   663     if (m_db.isOpen()) { 
   763         QSqlQuery query(m_db);
   664         QSqlQuery query(m_db);  
   764         query.prepare(
   665         query.prepare("SELECT title, url, sortIndex FROM bookmarks WHERE id=:id");
   765                 "SELECT title, url, sortIndex FROM bookmarks WHERE id=:id");
   666         query.bindValue(":id", bookmarkId);
   766         query.bindValue(":id", bookmarkId);
   667         if (query.exec()) {
   767         if (query.exec()) {
   668             if (query.next()) 
   768             if (query.next())
   669                 results = new BookmarkFav(bookmarkId, query.value(0).toString(),
   769                 results = new BookmarkFav(bookmarkId,
   670                          query.value(1).toString(), query.value(2).toInt()); 
   770                         query.value(0).toString(), query.value(1).toString(),
       
   771                         query.value(2).toInt());
   671         } else {
   772         } else {
   672             lastErrMsg(query);
   773             lastErrMsg(query);
   673         }
   774         }
   674     }
   775     }
   675     return results;
   776     return results;
   702 
   803 
   703 /**==============================================================
   804 /**==============================================================
   704  * Description: Prints a last error message from the query.
   805  * Description: Prints a last error message from the query.
   705  * Returns: Nothing.
   806  * Returns: Nothing.
   706  ===============================================================*/
   807  ===============================================================*/
   707 void BookmarksManager::lastErrMsg(QSqlQuery& query) 
   808 void BookmarksManager::lastErrMsg(QSqlQuery& query)
   708 {
   809 {
   709     qDebug() << "BookmarksManager::lastErrMsg" << QString("ERR: %1 %2").arg(query.lastError().type()).arg(query.lastError().text()) << " Query: " << query.lastQuery();
   810     qDebug() << "BookmarksManager::lastErrMsg" << QString("ERR: %1 %2").arg(
   710 }
   811             query.lastError().type()).arg(query.lastError().text())
   711 
   812             << " Query: " << query.lastQuery();
       
   813 }
       
   814