pax_global_header00006660000000000000000000000064146233352270014521gustar00rootroot0000000000000052 comment=08174cebb6765a8a705060c0a62a67ebd7e535f3 libsocialcache-0.2.1/000077500000000000000000000000001462333522700144465ustar00rootroot00000000000000libsocialcache-0.2.1/.gitignore000066400000000000000000000011121462333522700164310ustar00rootroot00000000000000.qmake.stash *.o moc_* *.moc Makefile *.prl *.so *.so.* *.qm # project specific src/qml/socialcache.ts src/lib/pkgconfig/ tests/tst_abstractsocialcachedatabase/tst_abstractsocialcachedatabase tests/tst_dropboximage/tst_dropboximage tests/tst_facebookcontact/tst_facebookcontact tests/tst_facebookimage/tst_facebookimage tests/tst_facebooknotification/tst_facebooknotification tests/tst_facebookpost/tst_facebookpost tests/tst_onedriveimage/tst_onedriveimage tests/tst_socialimage/tst_socialimage tests/tst_socialnetworksync/tst_socialnetworksync tests/tst_twitterpost/tst_twitterpost libsocialcache-0.2.1/COPYING000066400000000000000000000636421462333522700155140ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! libsocialcache-0.2.1/README.md000066400000000000000000000004031462333522700157220ustar00rootroot00000000000000== libsocialcache == libsocialcache is a library and and a QML plugin that is used to manage cache from social networks. The library wraps databases that stores cached data, while the QML plugin offers higher models that can be used to access data directly.libsocialcache-0.2.1/common.pri000066400000000000000000000002771462333522700164600ustar00rootroot00000000000000isEmpty(COMMON_PRI_INCLUDED) { COMMON_PRI_INCLUDED = 1 DEFINES += 'PRIVILEGED_DATA_DIR=\'QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + \"/system/privileged/\"\'' } libsocialcache-0.2.1/libsocialcache.pro000066400000000000000000000001201462333522700201060ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = src tests OTHER_FILES += rpm/libsocialcache.spec libsocialcache-0.2.1/rpm/000077500000000000000000000000001462333522700152445ustar00rootroot00000000000000libsocialcache-0.2.1/rpm/libsocialcache.spec000077500000000000000000000041661462333522700210570ustar00rootroot00000000000000Name: libsocialcache Summary: A library that manages data from social networks Version: 0.2.1 Release: 1 License: BSD and LGPLv2+ URL: https://github.com/sailfishos/libsocialcache Source0: %{name}-%{version}.tar.bz2 BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Gui) BuildRequires: pkgconfig(Qt5Qml) BuildRequires: pkgconfig(Qt5Sql) BuildRequires: pkgconfig(Qt5DBus) BuildRequires: pkgconfig(Qt5Test) BuildRequires: qt5-qttools BuildRequires: qt5-qttools-linguist BuildRequires: pkgconfig(buteosyncfw5) BuildRequires: pkgconfig(libsailfishkeyprovider) Requires: qt5-plugin-sqldriver-sqlite %description libsocialcache is a library that manages data from social networks. It also provides higher level models with a QML plugin. %package qml-plugin-ts-devel Summary: Translation source for libsocialcache %description qml-plugin-ts-devel Translation source for socialcache qml plugin %package devel Summary: Development files for libsocialcache Requires: libsocialcache = %{version} %description devel This package contains development files for libsocialcache. %package qml-plugin Summary: QML plugin for libsocialcache %description qml-plugin This package contains the qml plugin for socialcache %package tests Summary: Unit tests for libsocialcache License: BSD Requires: %{name} = %{version}-%{release} %description tests This package contains unit tests for the libsocialcache library. %prep %setup -q -n %{name}-%{version} %build %qmake5 "VERSION=%{version}" %make_build %install %qmake5_install %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %{_libdir}/libsocialcache.so.* %license COPYING %files qml-plugin-ts-devel %{_datadir}/translations/source/socialcache.ts %files devel %{_includedir}/socialcache/*.h %{_libdir}/libsocialcache.so %{_libdir}/pkgconfig/socialcache.pc %files qml-plugin %{_libdir}/qt5/qml/org/nemomobile/socialcache/plugins.qmltypes %{_libdir}/qt5/qml/org/nemomobile/socialcache/qmldir %{_libdir}/qt5/qml/org/nemomobile/socialcache/libsocialcacheqml.so %{_datadir}/translations/socialcache_eng_en.qm %files tests /opt/tests/libsocialcache/ libsocialcache-0.2.1/src/000077500000000000000000000000001462333522700152355ustar00rootroot00000000000000libsocialcache-0.2.1/src/lib/000077500000000000000000000000001462333522700160035ustar00rootroot00000000000000libsocialcache-0.2.1/src/lib/abstractimagedownloader.cpp000066400000000000000000000332361462333522700234030ustar00rootroot00000000000000/* * Copyright (C) 2013 Lucien Xu * Copyright (C) 2013 - 2021 Jolla Pty Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "abstractimagedownloader.h" #include #include #include #include #include #include #include #include #include #include #include "abstractimagedownloader_p.h" // The AbstractImageDownloader is a class used to build image downloader objects // // An image downloader object is a QObject based object that lives in // a lower priority thread, downloads images from social networks // and updates a database. // // This object do not expose many methods. Instead, since it lives // in it's own thread, communications should be done using signals // and slots. // // To download an image, the AbstractImagesDownloader::queue slot // should be used, and when the download is completed, the // AbstractImagesDownloaderPrivate::imageDownloaded will be emitted. static int MAX_SIMULTANEOUS_DOWNLOAD = 5; static int MAX_BATCH_SAVE = 50; AbstractImageDownloaderPrivate::AbstractImageDownloaderPrivate(AbstractImageDownloader *q) : networkAccessManager(0), q_ptr(q), loadedCount(0) { } AbstractImageDownloaderPrivate::~AbstractImageDownloaderPrivate() { } void AbstractImageDownloaderPrivate::manageStack() { Q_Q(AbstractImageDownloader); while (runningReplies.count() < MAX_SIMULTANEOUS_DOWNLOAD && !stack.isEmpty()) { // Create a reply to download the image ImageInfo *info = stack.takeLast(); QString url = info->url; if (!info->redirectUrl.isEmpty()) { url = info->redirectUrl; } if (QNetworkReply *reply = q->createReply(url, info->requestsData.first())) { QTimer *timer = new QTimer(q); timer->setInterval(60000); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, q, &AbstractImageDownloader::timedOut); timer->start(); replyTimeouts.insert(timer, reply); reply->setProperty("timeoutTimer", QVariant::fromValue(timer)); QObject::connect(reply, SIGNAL(finished()), q, SLOT(slotFinished())); // For some reason, this fixes an issue with oopp sync plugins runningReplies.insert(reply, info); } else { // emit signal. Empty file signifies error. Q_FOREACH (const QVariantMap &metadata, info->requestsData) { emit q->imageDownloaded(info->url, QString(), metadata); } delete info; } } } bool AbstractImageDownloaderPrivate::writeImageData(ImageInfo *info, QNetworkReply *reply, QString *outFileName) { Q_Q(AbstractImageDownloader); qint64 bytesAvailable = reply->bytesAvailable(); if (bytesAvailable == 0) { qWarning() << Q_FUNC_INFO << "No image data available"; return false; } const QByteArray imageData = reply->readAll(); static const QMimeDatabase mimeDatabase; const QMimeType dataMimeType = mimeDatabase.mimeTypeForData(imageData); if (!dataMimeType.name().startsWith(QStringLiteral("image/"))) { qWarning() << "Downloaded file is not an image, mime type is" << dataMimeType.name(); if (dataMimeType.name() == "text/plain") { // might be some error explanation qDebug() << "Got text instead:" << imageData.left(200); } return false; } QString url = info->url; if (!info->redirectUrl.isEmpty()) { url = info->redirectUrl; } QString localFilePath = q->outputFile(url, info->requestsData.first(), dataMimeType.name()); QDir parentDir = QFileInfo(localFilePath).dir(); if (!parentDir.exists()) { parentDir.mkpath("."); } const QMimeType localFilePathMimeType = mimeDatabase.mimeTypesForFileName(localFilePath).value(0); if (localFilePathMimeType != dataMimeType) { // The destination file path has a file extension that does not match the mime type of the // downloaded content. const QFileInfo fileInfo(localFilePath); qWarning() << "Downloaded file" << fileInfo.fileName() << "has type" << dataMimeType.name() << "instead of expected" << localFilePathMimeType.name() << ", converting to" << localFilePathMimeType.name(); QImage image; if (!image.loadFromData(imageData)) { qWarning() << "Unable to read downloaded image data"; return false; } // QImage::save() will convert the image to the correct mime type when writing to file. if (!image.save(localFilePath)) { qWarning() << "Unable to save downloaded image data to file:" << localFilePath; return false; } } else { // The downloaded content matches the mime type of the destination file path, so save the // content directly to the file without converting it. QFile file(localFilePath); if (!file.open(QFile::WriteOnly)) { qWarning() << "Unable to write downloaded image data to file:" << localFilePath; return false; } file.write(imageData); file.close(); } *outFileName = localFilePath; return true; } void AbstractImageDownloader::slotFinished() { Q_D(AbstractImageDownloader); QNetworkReply *reply = qobject_cast(sender()); if (!reply) { qWarning() << Q_FUNC_INFO << "finished signal received with null reply"; d->manageStack(); return; } ImageInfo *info = d->runningReplies.take(reply); QTimer *timer = reply->property("timeoutTimer").value(); if (timer) { d->replyTimeouts.remove(timer); timer->stop(); timer->deleteLater(); } reply->deleteLater(); if (!info) { qWarning() << Q_FUNC_INFO << "No image info associated with reply"; d->manageStack(); return; } QByteArray redirectedUrl = reply->rawHeader("Location"); if (redirectedUrl.length() > 0) { // this is URL redirection info->redirectUrl = QString(redirectedUrl); d->stack.append(info); d->manageStack(); } else { QString fileName; if (d->writeImageData(info, reply, &fileName)) { dbQueueImage(info->url, info->requestsData.first(), fileName); Q_FOREACH (const QVariantMap &metadata, info->requestsData) { emit imageDownloaded(info->url, fileName, metadata); } } else { // the file is not in image format. Q_FOREACH (const QVariantMap &metadata, info->requestsData) { emit imageDownloaded(info->url, QString(), metadata); } } delete info; d->loadedCount ++; d->manageStack(); if (d->loadedCount > MAX_BATCH_SAVE || (d->runningReplies.isEmpty() && d->stack.isEmpty())) { dbWrite(); d->loadedCount = 0; } } } void AbstractImageDownloader::timedOut() { Q_D(AbstractImageDownloader); QTimer *timer = qobject_cast(sender()); if (timer) { QNetworkReply *reply = d->replyTimeouts.take(timer); if (reply) { reply->deleteLater(); timer->deleteLater(); ImageInfo *info = d->runningReplies.take(reply); qWarning() << Q_FUNC_INFO << "Image download request timed out"; Q_FOREACH (const QVariantMap &metadata, info->requestsData) { emit imageDownloaded(info->url, QString(), metadata); } } } d->manageStack(); } AbstractImageDownloader::AbstractImageDownloader(QObject *parent) : QObject(parent) , d_ptr(new AbstractImageDownloaderPrivate(this)) { Q_D(AbstractImageDownloader); d->networkAccessManager = new QNetworkAccessManager(this); } AbstractImageDownloader::AbstractImageDownloader(AbstractImageDownloaderPrivate &dd, QObject *parent) : QObject(parent), d_ptr(&dd) { Q_D(AbstractImageDownloader); d->networkAccessManager = new QNetworkAccessManager(this); } AbstractImageDownloader::~AbstractImageDownloader() { } void AbstractImageDownloader::queue(const QString &url, const QVariantMap &metadata) { Q_D(AbstractImageDownloader); if (!dbInit()) { qWarning() << Q_FUNC_INFO << "Cannot perform operation, database is not initialized"; emit imageDownloaded(url, QString(), metadata); // empty file signifies error. return; } Q_FOREACH (ImageInfo *info, d->runningReplies) { if (info->url == url) { qWarning() << Q_FUNC_INFO << "duplicate running request, appending metadata."; info->requestsData.append(metadata); return; } } ImageInfo *info = 0; for (int i = 0; i < d->stack.count(); ++i) { if (d->stack.at(i)->url == url) { qWarning() << Q_FUNC_INFO << "duplicate queued request, appending metadata."; info = d->stack.takeAt(i); info->requestsData.append(metadata); break; } } if (!info) { info = new ImageInfo(url, metadata); } d->stack.append(info); d->manageStack(); } QNetworkReply *AbstractImageDownloader::createReply(const QString &url, const QVariantMap &metadata) { Q_D(AbstractImageDownloader); QNetworkRequest request (url); for (QVariantMap::const_iterator iter = metadata.begin(); iter != metadata.end(); ++iter) { if (iter.key().startsWith("accessToken")) { request.setRawHeader(QString(QLatin1String("Authorization")).toUtf8(), QString(QLatin1String("Bearer ")).toUtf8() + iter.value().toString().toUtf8()); break; } } qWarning() << "AbstractImageDownloader::about to fetch image:" << url; return d->networkAccessManager->get(request); } static QString createOutputPath(SocialSyncInterface::DataType dataType, SocialSyncInterface::SocialNetwork socialNetwork, const QString &subdir, const QString &identifier, const QString &mimetype) { QString result = QString(PRIVILEGED_DATA_DIR) + QChar('/') + SocialSyncInterface::dataType(dataType) + QChar('/'); if (dataType == SocialSyncInterface::Contacts) { result += QStringLiteral("avatars") + QChar('/'); } result += SocialSyncInterface::socialNetwork(socialNetwork) + QChar('/') + subdir + QChar('/'); // do we want to support more image types? if (mimetype == QStringLiteral("image/png")) { result += identifier + QStringLiteral(".png"); } else { result += identifier + QStringLiteral(".jpg"); } return result; } QString AbstractImageDownloader::makeOutputFile(SocialSyncInterface::SocialNetwork socialNetwork, SocialSyncInterface::DataType dataType, const QString &identifier, const QString &mimetype) { if (identifier.isEmpty()) { return QString(); } QCryptographicHash hash (QCryptographicHash::Md5); hash.addData(identifier.toUtf8()); QByteArray hashedIdentifier = hash.result().toHex(); QString path = createOutputPath(dataType, socialNetwork, QString(hashedIdentifier.at(0)), identifier, mimetype); return path; } QString AbstractImageDownloader::makeUrlOutputFile(SocialSyncInterface::SocialNetwork socialNetwork, SocialSyncInterface::DataType dataType, const QString &identifier, const QString &remoteUrl, const QString &mimeType) { // this function hashes the remote URL in order to increase the // chance that a changed remote url will result in resynchronisation // of the image, due to output file path mismatch. if (identifier.isEmpty() || remoteUrl.isEmpty()) { return QString(); } QCryptographicHash urlHash(QCryptographicHash::Md5); urlHash.addData(remoteUrl.toUtf8()); QString hashedUrl = QString::fromUtf8(urlHash.result().toHex()); QCryptographicHash idHash(QCryptographicHash::Md5); idHash.addData(identifier.toUtf8()); QByteArray hashedId = idHash.result().toHex(); QString path = createOutputPath(dataType, socialNetwork, QString(hashedId.at(0)), hashedUrl, mimeType); return path; } bool AbstractImageDownloader::dbInit() { return true; } void AbstractImageDownloader::dbQueueImage(const QString &url, const QVariantMap &metadata, const QString &file) { Q_UNUSED(url) Q_UNUSED(metadata) Q_UNUSED(file) } void AbstractImageDownloader::dbWrite() { } libsocialcache-0.2.1/src/lib/abstractimagedownloader.h000066400000000000000000000055631462333522700230520ustar00rootroot00000000000000/* * Copyright (C) 2013 Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ABSTRACTIMAGEDOWNLOADER_H #define ABSTRACTIMAGEDOWNLOADER_H #include "socialsyncinterface.h" #include #include class QNetworkReply; class AbstractImageDownloaderPrivate; class AbstractImageDownloader : public QObject { Q_OBJECT public: AbstractImageDownloader(QObject *parent = 0); virtual ~AbstractImageDownloader(); public Q_SLOTS: void queue(const QString &url, const QVariantMap &data); Q_SIGNALS: void imageDownloaded(const QString &url, const QString &path, const QVariantMap &metadata); protected: explicit AbstractImageDownloader(AbstractImageDownloaderPrivate &dd, QObject *parent); static QString makeOutputFile(SocialSyncInterface::SocialNetwork socialNetwork, SocialSyncInterface::DataType dataType, const QString &identifier, const QString &mimeType); static QString makeUrlOutputFile(SocialSyncInterface::SocialNetwork socialNetwork, SocialSyncInterface::DataType dataType, const QString &identifier, const QString &remoteUrl, const QString &mimeType); virtual QNetworkReply * createReply(const QString &url, const QVariantMap &metadata); // Output file based on passed data virtual QString outputFile(const QString &url, const QVariantMap &metadata, const QString &mimetype) const = 0; // Init the database if not initialized // used to delay initialization of the database virtual bool dbInit(); // Queue an image in the database virtual void dbQueueImage(const QString &url, const QVariantMap &metadata, const QString &file); // Write in the database virtual void dbWrite(); QScopedPointer d_ptr; private Q_SLOTS: void slotFinished(); void timedOut(); private: Q_DECLARE_PRIVATE(AbstractImageDownloader) }; #endif // ABSTRACTIMAGEDOWNLOADER_H libsocialcache-0.2.1/src/lib/abstractimagedownloader_p.h000066400000000000000000000054561462333522700233720ustar00rootroot00000000000000/* * Copyright (C) 2013 Lucien XU * Copyright (C) 2013 - 2021 Jolla Pty Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #ifndef ABSTRACTIMAGEDOWNLOADER_P_H #define ABSTRACTIMAGEDOWNLOADER_P_H #include #include #include #include #include #include #include struct ImageInfo { ImageInfo(const QString &url, const QVariantMap &data) : url(url), requestsData(QList() << data) {} QString url; QString redirectUrl; QList requestsData; }; class QMimeDatabase; class AbstractImageDownloader; class AbstractImageDownloaderPrivate { public: explicit AbstractImageDownloaderPrivate(AbstractImageDownloader *q); virtual ~AbstractImageDownloaderPrivate(); QNetworkAccessManager *networkAccessManager; protected: AbstractImageDownloader * const q_ptr; private: void manageStack(); bool writeImageData(ImageInfo *imageInfo, QNetworkReply *reply, QString *outFileName); QMap runningReplies; QMap replyTimeouts; QList stack; int loadedCount; Q_DECLARE_PUBLIC(AbstractImageDownloader) }; #endif // ABSTRACTIMAGEDOWNLOADER_P_H libsocialcache-0.2.1/src/lib/abstractsocialcachedatabase.cpp000066400000000000000000000345071462333522700241670ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "abstractsocialcachedatabase.h" #include "abstractsocialcachedatabase_p.h" #include #include #include #include #include #include #include #include #include #include #include // AbstractSocialCacheDatabase // This class is the base class for all classes // that deals with database access. // // It provides a set of useful methods that should // be used to initialize the database, and write into // it. dbBeginTransaction, dbWrite and dbCommitTransaction // are particularly handy, and can make write operations // into db very fast. QThreadStorage > AbstractSocialCacheDatabasePrivate::globalThreadData; namespace { class ProcessMutexCleanup { public: ProcessMutexCleanup(AbstractSocialCacheDatabasePrivate::ThreadData *threadData) : threadData(threadData) { } ~ProcessMutexCleanup() { if (threadData) { threadData->mutex->unlock(); delete threadData->mutex; threadData->mutex = 0; } } void finalize() { threadData->mutex->unlock(); threadData = 0; } private: AbstractSocialCacheDatabasePrivate::ThreadData *threadData; }; } AbstractSocialCacheDatabasePrivate::AbstractSocialCacheDatabasePrivate( AbstractSocialCacheDatabase *q, const QString &serviceName, const QString &dataType, const QString &databaseFile, int version) : q_ptr(q) , serviceName(serviceName) , dataType(dataType) , filePath(QString(QLatin1String("%1/%2/%3")).arg(PRIVILEGED_DATA_DIR, dataType, databaseFile)) , version(version) , readStatus(AbstractSocialCacheDatabase::Null) , writeStatus(AbstractSocialCacheDatabase::Null) , asyncReadStatus(Null) , asyncWriteStatus(Null) , running(false) { setAutoDelete(false); } AbstractSocialCacheDatabasePrivate::~AbstractSocialCacheDatabasePrivate() { } bool AbstractSocialCacheDatabasePrivate::initializeThreadData(ThreadData *threadData) const { Q_Q(const AbstractSocialCacheDatabase); const QUuid uuid = QUuid::createUuid(); threadData->threadId = uuid.toByteArray().toBase64(); const QString connectionName = QString(QLatin1String("socialcache/%1/%2/%3")).arg( serviceName, dataType, uuid.toString()); threadData->mutex = new ProcessMutex(connectionName); if (!threadData->mutex->lock()) { qWarning() << Q_FUNC_INFO << "Error: unable to acquire mutex lock during database initialisation"; delete threadData->mutex; threadData->mutex = 0; return false; } ProcessMutexCleanup processMutexCleanup(threadData); bool createTables = false; QFileInfo fileInfo(filePath); if (!fileInfo.exists()) { createTables = true; QDir dir = fileInfo.dir(); if (!dir.exists()) { dir.mkpath("."); } QFile dbfile(filePath); if (!dbfile.open(QIODevice::ReadWrite)) { qWarning() << Q_FUNC_INFO << "Unable to create database" << filePath << "Service" << serviceName << "with data type" << dataType << "will be inactive"; return false; } dbfile.close(); } // open the database in which we store our synced image information threadData->database = QSqlDatabase::addDatabase("QSQLITE", connectionName); threadData->database.setDatabaseName(filePath); if (!threadData->database.open()) { qWarning() << Q_FUNC_INFO << "Unable to open database" << filePath << "Service" << serviceName << "with data type" << dataType << "will be inactive"; return false; } QSqlQuery query(threadData->database); query.exec(QStringLiteral("PRAGMA temp_store = MEMORY;")); query.exec(QStringLiteral("PRAGMA journal_mode = WAL;")); if (!query.exec(QLatin1String("PRAGMA user_version")) || !query.next()) { qWarning() << Q_FUNC_INFO << "Failed to query pragma_user version. Service" << serviceName << "with data type" << dataType << "will be inactive. Error" << query.lastError().text(); threadData->database.close(); return false; } const int databaseVersion = query.value(0).toInt(); query.finish(); if (databaseVersion < version) { createTables = true; qWarning() << Q_FUNC_INFO << "Version required is" << version << "while database is using" << databaseVersion; // DB needs to be recreated if (!q->dropTables(threadData->database)) { qWarning() << Q_FUNC_INFO << "Failed to update database" << filePath << "It is probably broken and need to be removed manually"; threadData->database.close(); return false; } } if (createTables) { if (!q->createTables(threadData->database)) { qWarning() << Q_FUNC_INFO << "Failed to update database" << filePath << "It is probably broken and need to be removed manually"; threadData->database.close(); return false; } else if (!query.exec(QString(QLatin1String("PRAGMA user_version=%1")).arg(version))) { qWarning() << Q_FUNC_INFO << "Failed to set database version" << filePath << query.lastError(); } } processMutexCleanup.finalize(); return true; } void AbstractSocialCacheDatabasePrivate::run() { Q_Q(AbstractSocialCacheDatabase); ThreadData &threadData = globalThreadData.localData()[filePath]; if (!threadData.mutex && !initializeThreadData(&threadData)) { return; } QMutexLocker locker(&mutex); for (;;) { if (asyncWriteStatus == Queued) { if (writeStatus == AbstractSocialCacheDatabase::Null) { asyncWriteStatus = Null; continue; } asyncWriteStatus = Executing; locker.unlock(); if (!threadData.mutex->lock()) { // Warning qWarning() << Q_FUNC_INFO << "Failed to acquire a lock on the database"; locker.relock(); break; } if (!threadData.database.transaction()) { qWarning() << Q_FUNC_INFO << "Failed to start a database transaction"; threadData.mutex->unlock(); locker.relock(); break; } bool success = q->write(); if (!success) { threadData.database.rollback(); } else if (!threadData.database.commit()) { qWarning() << Q_FUNC_INFO << "Failed to commit a database transaction"; qWarning() << threadData.database.lastError(); success = false; } threadData.mutex->unlock(); locker.relock(); if (asyncWriteStatus == Executing) { asyncWriteStatus = success ? Finished : Error; } } else if (asyncReadStatus == Queued) { if (readStatus == AbstractSocialCacheDatabase::Null) { asyncReadStatus = Null; continue; } asyncReadStatus = Executing; locker.unlock(); bool success = q->read(); locker.relock(); if (asyncReadStatus == Executing) { asyncReadStatus = success ? Finished : Error; } } else { running = false; QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); condition.wakeOne(); return; } } } AbstractSocialCacheDatabase::AbstractSocialCacheDatabase( const QString &serviceName, const QString &dataType, const QString &databaseFile, int version) : d_ptr(new AbstractSocialCacheDatabasePrivate( this, serviceName, dataType, databaseFile, version)) { } AbstractSocialCacheDatabase::AbstractSocialCacheDatabase(AbstractSocialCacheDatabasePrivate &dd) : d_ptr(&dd) { } AbstractSocialCacheDatabase::~AbstractSocialCacheDatabase() { } bool AbstractSocialCacheDatabase::isValid() const { Q_D(const AbstractSocialCacheDatabase); QSqlQuery query = prepare(QStringLiteral("PRAGMA user_version")); if (query.exec() && query.next()) { int userVersion = query.value(0).toInt(); query.finish(); return userVersion == d->version; } return false; } AbstractSocialCacheDatabase::Status AbstractSocialCacheDatabase::readStatus() const { return d_func()->readStatus; } AbstractSocialCacheDatabase::Status AbstractSocialCacheDatabase::writeStatus() const { return d_func()->writeStatus; } bool AbstractSocialCacheDatabase::event(QEvent *event) { if (event->type() == QEvent::UpdateRequest) { Q_D(AbstractSocialCacheDatabase); bool readDone = false; bool writeDone = false; QMutexLocker locker(&d->mutex); if (d->asyncReadStatus >= AbstractSocialCacheDatabasePrivate::Finished) { if (d->readStatus != Null) { d->readStatus = d->asyncReadStatus == AbstractSocialCacheDatabasePrivate::Finished ? Finished : Error; d->asyncReadStatus = AbstractSocialCacheDatabasePrivate::Null; readDone = true; } else { d->asyncReadStatus = AbstractSocialCacheDatabasePrivate::Null; } } if (d->asyncWriteStatus >= AbstractSocialCacheDatabasePrivate::Finished) { if (d->writeStatus != Null) { d->writeStatus = d->asyncWriteStatus == AbstractSocialCacheDatabasePrivate::Finished ? Finished : Error; d->asyncWriteStatus = AbstractSocialCacheDatabasePrivate::Null; writeDone = true; } else { d->asyncWriteStatus = AbstractSocialCacheDatabasePrivate::Null; } } locker.unlock(); if (readDone) { readFinished(); } if (writeDone) { writeFinished(); } return true; } else { return QObject::event(event); } } void AbstractSocialCacheDatabase::executeRead() { Q_D(AbstractSocialCacheDatabase); QMutexLocker locker(&d->mutex); d->readStatus = Executing; d->asyncReadStatus = AbstractSocialCacheDatabasePrivate::Queued; if (!d->running) { d->running = true; QThreadPool::globalInstance()->start(d); } } void AbstractSocialCacheDatabase::cancelRead() { Q_D(AbstractSocialCacheDatabase); QMutexLocker locker(&d->mutex); d->readStatus = Null; } void AbstractSocialCacheDatabase::executeWrite() { Q_D(AbstractSocialCacheDatabase); QMutexLocker locker(&d->mutex); d->writeStatus = Executing; d->asyncWriteStatus = AbstractSocialCacheDatabasePrivate::Queued; if (!d->running) { d->running = true; QThreadPool::globalInstance()->start(d); } } void AbstractSocialCacheDatabase::cancelWrite() { Q_D(AbstractSocialCacheDatabase); QMutexLocker locker(&d->mutex); d->writeStatus = Null; } bool AbstractSocialCacheDatabase::read() { return false; } bool AbstractSocialCacheDatabase::write() { return false; } void AbstractSocialCacheDatabase::readFinished() { } void AbstractSocialCacheDatabase::writeFinished() { } void AbstractSocialCacheDatabase::wait() { Q_D(AbstractSocialCacheDatabase); bool readDone = false; bool writeDone = false; QMutexLocker locker(&d->mutex); while (d->running) { d->condition.wait(&d->mutex); } if (d->asyncReadStatus >= AbstractSocialCacheDatabasePrivate::Finished) { d->readStatus = d->asyncReadStatus == AbstractSocialCacheDatabasePrivate::Finished ? Finished : Error; d->asyncReadStatus = AbstractSocialCacheDatabasePrivate::Null; readDone = true; } if (d->asyncWriteStatus >= AbstractSocialCacheDatabasePrivate::Finished) { d->writeStatus = d->asyncWriteStatus == AbstractSocialCacheDatabasePrivate::Finished ? Finished : Error; d->asyncWriteStatus = AbstractSocialCacheDatabasePrivate::Null; writeDone = true; } locker.unlock(); if (readDone) { readFinished(); } if (writeDone) { writeFinished(); } } QSqlQuery AbstractSocialCacheDatabase::prepare(const QString &query) const { Q_D(const AbstractSocialCacheDatabase); AbstractSocialCacheDatabasePrivate::ThreadData &threadData = AbstractSocialCacheDatabasePrivate::globalThreadData.localData()[d->filePath]; if (!threadData.mutex && ! d_func()->initializeThreadData(&threadData)) { return QSqlQuery(); } QHash::const_iterator it = threadData.preparedQueries.constFind(query); if (it != threadData.preparedQueries.constEnd()) { return *it; } QSqlQuery preparedQuery(threadData.database); if (!preparedQuery.prepare(query)) { qWarning() << Q_FUNC_INFO << "Failed to prepare query"; qWarning() << query; qWarning() << preparedQuery.lastError(); return QSqlQuery(); } else { threadData.preparedQueries.insert(query, preparedQuery); return preparedQuery; } } libsocialcache-0.2.1/src/lib/abstractsocialcachedatabase.h000066400000000000000000000044631462333522700236320ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ABSTRACTSOCIALCACHEDATABASE_H #define ABSTRACTSOCIALCACHEDATABASE_H #include #include QT_BEGIN_NAMESPACE class QSqlDatabase; class QSqlQuery; QT_END_NAMESPACE class AbstractSocialCacheDatabasePrivate; class AbstractSocialCacheDatabase : public QObject { Q_OBJECT public: enum Status { Null, Executing, Finished, Error }; explicit AbstractSocialCacheDatabase( const QString &serviceName, const QString &dataType, const QString &databaseFile, int version); virtual ~AbstractSocialCacheDatabase(); bool isValid() const; Status readStatus() const; Status writeStatus() const; bool event(QEvent *event); void wait(); Q_SIGNALS: void readStatusChanged(); void writeStatusChanged(); protected: void executeRead(); void cancelRead(); void executeWrite(); void cancelWrite(); virtual bool read(); virtual bool write(); virtual bool createTables(QSqlDatabase database) const = 0; virtual bool dropTables(QSqlDatabase database) const = 0; virtual void readFinished(); virtual void writeFinished(); QSqlQuery prepare(const QString &query) const; explicit AbstractSocialCacheDatabase(AbstractSocialCacheDatabasePrivate &dd); QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(AbstractSocialCacheDatabase) }; #endif // ABSTRACTSOCIALCACHEDATABASE_H libsocialcache-0.2.1/src/lib/abstractsocialcachedatabase_p.h000066400000000000000000000062551462333522700241520ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ABSTRACTSOCIALCACHEDATABASE_P_H #define ABSTRACTSOCIALCACHEDATABASE_P_H #include #include #include #include #include #include #include #include "semaphore_p.h" #include "abstractsocialcachedatabase.h" class AbstractSocialCacheDatabase; class AbstractSocialCacheDatabasePrivate : public QRunnable { protected: AbstractSocialCacheDatabase * const q_ptr; public: enum Status { Null, Queued, Executing, Finished, Error }; struct ThreadData { ThreadData() : mutex(0) {} ~ThreadData() { database.close(); delete mutex; } QSqlDatabase database; QHash preparedQueries; QString threadId; ProcessMutex *mutex; // Process (and thread) mutex to prevent concurrent write }; explicit AbstractSocialCacheDatabasePrivate( AbstractSocialCacheDatabase *q, const QString &serviceName, const QString &dataType, const QString &databaseFile, int version); virtual ~AbstractSocialCacheDatabasePrivate(); bool initializeThreadData(ThreadData *threadData) const; static QThreadStorage > globalThreadData; QMutex mutex; QWaitCondition condition; const QString serviceName; const QString dataType; const QString filePath; const int version; AbstractSocialCacheDatabase::Status readStatus; AbstractSocialCacheDatabase::Status writeStatus; Status asyncReadStatus; Status asyncWriteStatus; bool running; void run(); private: Q_DECLARE_PUBLIC(AbstractSocialCacheDatabase) }; #define executeSocialCacheQuery(query) \ if (!query.exec()) { \ qWarning() << Q_FUNC_INFO << "Failed to execute query"; \ qWarning() << query.lastQuery(); \ qWarning() << query.lastError(); \ success = false; \ } \ query.finish() #define executeBatchSocialCacheQuery(query) \ if (!query.execBatch()) { \ qWarning() << Q_FUNC_INFO << "Failed to execute query"; \ qWarning() << query.lastQuery(); \ qWarning() << query.lastError(); \ success = false; \ } \ query.finish() #endif // ABSTRACTSOCIALCACHEDATABASE_P_H libsocialcache-0.2.1/src/lib/abstractsocialpostcachedatabase.cpp000066400000000000000000000621651462333522700250760ustar00rootroot00000000000000/* * Copyright (C) 2013 Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "abstractsocialpostcachedatabase.h" #include "abstractsocialcachedatabase_p.h" #include "socialsyncinterface.h" #include #include #include #include #include static const char *INVALID = "invalid"; static const char *PHOTO = "photo"; static const char *VIDEO = "video"; static const int POST_DB_VERSION = 1; struct SocialPostImagePrivate { explicit SocialPostImagePrivate(const QString &url, SocialPostImage::ImageType type); QString url; SocialPostImage::ImageType type; }; SocialPostImagePrivate::SocialPostImagePrivate(const QString &url, SocialPostImage::ImageType type) : url(url), type(type) { } SocialPostImage::SocialPostImage() { } SocialPostImage::SocialPostImage(const QString &url, ImageType type) : d_ptr(new SocialPostImagePrivate(url, type)) { } SocialPostImage::~SocialPostImage() { } SocialPostImage::Ptr SocialPostImage::create(const QString &url, ImageType type) { return SocialPostImage::Ptr(new SocialPostImage(url, type)); } QString SocialPostImage::url() const { Q_D(const SocialPostImage); return d->url; } SocialPostImage::ImageType SocialPostImage::type() const { Q_D(const SocialPostImage); return d->type; } struct SocialPostPrivate { explicit SocialPostPrivate(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QVariantMap &extra = QVariantMap(), const QList &accounts = QList()); QString identifier; QString name; QString body; QDateTime timestamp; QMap images; QVariantMap extra; QList accounts; }; SocialPostPrivate::SocialPostPrivate(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QVariantMap &extra, const QList &accounts) : identifier(identifier), name(name), body(body), timestamp(timestamp) , extra(extra), accounts(accounts) { } SocialPost::SocialPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QMap &images, const QVariantMap &extra, const QList &accounts) : d_ptr(new SocialPostPrivate(identifier, body, name, timestamp ,extra, accounts)) { setImages(images); } SocialPost::~SocialPost() { } SocialPost::Ptr SocialPost::create(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QMap &images, const QVariantMap &extra, const QList &accounts) { return SocialPost::Ptr(new SocialPost(identifier, name, body, timestamp, images, extra, accounts)); } QString SocialPost::identifier() const { Q_D(const SocialPost); return d->identifier; } QString SocialPost::name() const { Q_D(const SocialPost); return d->name; } QString SocialPost::body() const { Q_D(const SocialPost); return d->body; } QDateTime SocialPost::timestamp() const { Q_D(const SocialPost); return d->timestamp; } QString SocialPost::icon() const { Q_D(const SocialPost); if (d->images.isEmpty()) { return QString(); } return d->images.value(0)->url(); } QList SocialPost::images() const { Q_D(const SocialPost); QList images; Q_FOREACH (int key, d->images.keys()) { if (key > 0) { images.append(d->images.value(key)); } } return images; } QMap SocialPost::allImages() const { Q_D(const SocialPost); return d->images; } void SocialPost::setImages(const QMap &images) { Q_D(SocialPost); d->images = images; } QVariantMap SocialPost::extra() const { Q_D(const SocialPost); return d->extra; } void SocialPost::setExtra(const QVariantMap &extra) { Q_D(SocialPost); d->extra = extra; } QList SocialPost::accounts() const { Q_D(const SocialPost); return d->accounts; } void SocialPost::setAccounts(const QList &accounts) { Q_D(SocialPost); d->accounts = accounts; } class AbstractSocialPostCacheDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: AbstractSocialPostCacheDatabasePrivate( AbstractSocialPostCacheDatabase *q, const QString &serviceName, const QString &databaseFile); private: struct { QMap insertPosts; QMultiMap mapPostsToAccounts; QList removePostsForAccount; QList removePosts; bool removeAll; } queue; QList asyncPosts; QList posts; QVariantList accountIdFilter; Q_DECLARE_PUBLIC(AbstractSocialPostCacheDatabase) }; AbstractSocialPostCacheDatabasePrivate::AbstractSocialPostCacheDatabasePrivate( AbstractSocialPostCacheDatabase *q, const QString &serviceName, const QString &databaseFile) : AbstractSocialCacheDatabasePrivate( q, serviceName, SocialSyncInterface::dataType(SocialSyncInterface::Posts), databaseFile, POST_DB_VERSION) { queue.removeAll = false; } AbstractSocialPostCacheDatabase::~AbstractSocialPostCacheDatabase() { cancelRead(); wait(); } AbstractSocialPostCacheDatabase::AbstractSocialPostCacheDatabase( const QString &serviceName, const QString &databaseFile) : AbstractSocialCacheDatabase( *(new AbstractSocialPostCacheDatabasePrivate(this, serviceName, databaseFile))) { } QVariantList AbstractSocialPostCacheDatabase::accountIdFilter() const { return d_func()->accountIdFilter; } void AbstractSocialPostCacheDatabase::setAccountIdFilter(const QVariantList &accountIds) { if (accountIds != d_func()->accountIdFilter) { d_func()->accountIdFilter = accountIds; emit accountIdFilterChanged(); } } QList AbstractSocialPostCacheDatabase::posts() const { return d_func()->posts; } void AbstractSocialPostCacheDatabase::addPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QString &icon, const QList > &images, const QVariantMap &extra, int account) { Q_D(AbstractSocialPostCacheDatabase); QMutexLocker locker(&d->mutex); QMap formattedImages; if (!icon.isEmpty()) { formattedImages.insert(0, SocialPostImage::create(icon, SocialPostImage::Photo)); } for (int i = 0; i < images.count(); i++) { const QPair &imagePair = images.at(i); formattedImages.insert(i + 1, SocialPostImage::create(imagePair.first, imagePair.second)); } d->queue.insertPosts.insert(identifier, SocialPost::create(identifier, name, body, timestamp, formattedImages, extra)); d->queue.mapPostsToAccounts.insert(identifier, account); } void AbstractSocialPostCacheDatabase::removePosts(int accountId) { Q_D(AbstractSocialPostCacheDatabase); QMutexLocker locker(&d->mutex); if (!d->queue.removePostsForAccount.contains(accountId)) { d->queue.removePostsForAccount.append(accountId); } } void AbstractSocialPostCacheDatabase::removePost(const QString &identifier) { Q_D(AbstractSocialPostCacheDatabase); QMutexLocker locker(&d->mutex); if (!d->queue.removePosts.contains(identifier)) { d->queue.removePosts.append(identifier); } d->queue.insertPosts.remove(identifier); } void AbstractSocialPostCacheDatabase::removeAll() { Q_D(AbstractSocialPostCacheDatabase); { QMutexLocker locker(&d->mutex); d->queue.insertPosts.clear(); d->queue.mapPostsToAccounts.clear(); d->queue.removePostsForAccount.clear(); d->queue.removeAll = true; } executeWrite(); } void AbstractSocialPostCacheDatabase::commit() { executeWrite(); } void AbstractSocialPostCacheDatabase::refresh() { executeRead(); } bool AbstractSocialPostCacheDatabase::read() { Q_D(AbstractSocialPostCacheDatabase); // This might be slow QString accountQueryString = QLatin1String( "SELECT account, postId " "FROM link_post_account"); if (!d->accountIdFilter.isEmpty()) { QStringList accountIds; for (int i=0; iaccountIdFilter.count(); i++) { if (d->accountIdFilter[i].type() == QVariant::Int) { accountIds << d->accountIdFilter[i].toString(); } } if (accountIds.count()) { accountQueryString += " WHERE account IN (" + accountIds.join(',') + ')'; } } QSqlQuery accountQuery = prepare(accountQueryString); if (!accountQuery.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from link_post_account table:" << accountQuery.lastError(); return false; } QStringList filteredPostIds; QHash > accounts; if (accountQuery.exec()) { while (accountQuery.next()) { int accountId = accountQuery.value(0).toInt(); QString postId = accountQuery.value(1).toString(); accounts[postId].append(accountId); filteredPostIds.append(postId); } } QString postQueryString = QLatin1String( "SELECT identifier, name, body, timestamp " "FROM posts"); if (!d->accountIdFilter.isEmpty()) { postQueryString += " WHERE identifier IN (\"" + filteredPostIds.join("\",\"") + "\")"; } postQueryString += " ORDER BY timestamp DESC"; QSqlQuery postQuery = prepare(postQueryString); QSqlQuery imageQuery = prepare(QLatin1String( "SELECT position, url, type " "FROM images " "WHERE postId = :postId " "ORDER BY position")); QSqlQuery extraQuery = prepare(QLatin1String( "SELECT key, value " "FROM extra " "WHERE postId = :postId")); if (!postQuery.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from posts table:" << postQuery.lastError(); return false; } QList posts; while (postQuery.next()) { QString identifier = postQuery.value(0).toString(); QString name = postQuery.value(1).toString(); QString body = postQuery.value(2).toString(); int timestamp = postQuery.value(3).toInt(); SocialPost::Ptr post = SocialPost::create(identifier, name, body, #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(timestamp)); #else QDateTime::fromTime_t(timestamp)); #endif imageQuery.bindValue(":postId", identifier); QMap images; if (imageQuery.exec()) { while (imageQuery.next()) { SocialPostImage::ImageType type = SocialPostImage::Invalid; QString typeString = imageQuery.value(2).toString(); if (typeString == QLatin1String(PHOTO)) { type = SocialPostImage::Photo; } else if (typeString == QLatin1String(VIDEO)) { type = SocialPostImage::Video; } int position = imageQuery.value(0).toInt(); SocialPostImage::Ptr image = SocialPostImage::create(imageQuery.value(1).toString(), type); images.insert(position, image); } post->setImages(images); } else { qWarning() << Q_FUNC_INFO << "Error reading from images table:" << imageQuery.lastError(); } extraQuery.bindValue(":postId", identifier); QVariantMap extra; if (extraQuery.exec()) { while (extraQuery.next()) { QString key = extraQuery.value(0).toString(); QVariant value = extraQuery.value(1); extra.insert(key, value); } } else { qWarning() << Q_FUNC_INFO << "Error reading from extra table:" << extraQuery.lastError(); } post->setExtra(extra); post->setAccounts(accounts[identifier]); posts.append(post); } QMutexLocker locker(&d->mutex); d->asyncPosts = posts; return true; } bool AbstractSocialPostCacheDatabase::write() { Q_D(AbstractSocialPostCacheDatabase); QMutexLocker locker(&d->mutex); const QMap insertPosts = d->queue.insertPosts; const QMultiMap mapPostsToAccounts = d->queue.mapPostsToAccounts; const QList removePostsForAccount = d->queue.removePostsForAccount; const QList removePosts = d->queue.removePosts; bool removeAll = d->queue.removeAll; d->queue.insertPosts.clear(); d->queue.mapPostsToAccounts.clear(); d->queue.removePostsForAccount.clear(); d->queue.removePosts.clear(); d->queue.removeAll = false; locker.unlock(); bool success = true; QSqlQuery query; // perform removals first. if (!removePosts.isEmpty()) { QVariantList postIds; Q_FOREACH (const QString postId, removePosts) { postIds.append(postId); } query = prepare(QStringLiteral( "DELETE FROM posts " "WHERE identifier = :postId")); query.bindValue(QStringLiteral(":postId"), postIds); executeBatchSocialCacheQuery(query); } if (!removePostsForAccount.isEmpty() || removeAll) { QVariantList accountIds; if (removeAll) { // query all accounts QSqlQuery accountQuery = prepare(QLatin1String( "SELECT DISTINCT account " "FROM link_post_account")); if (!accountQuery.exec()) { qWarning() << Q_FUNC_INFO << "Error querying account list from posts table:" << accountQuery.lastError(); return false; } while (accountQuery.next()) { accountIds.append(accountQuery.value(0).toInt()); } } else { Q_FOREACH (int accountId, removePostsForAccount) { accountIds.append(accountId); } } query = prepare(QStringLiteral( "DELETE FROM link_post_account " "WHERE account = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM extra " "WHERE postId NOT IN (" "SELECT postId FROM link_post_account)")); executeSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE postId NOT IN (" "SELECT postId FROM link_post_account)")); executeSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM posts " "WHERE identifier NOT IN (" "SELECT postId FROM link_post_account)")); executeSocialCacheQuery(query); } struct { QVariantList postIds; QVariantList names; QVariantList bodies; QVariantList timestamps; } posts; struct { QVariantList postIds; QVariantList positions; QVariantList urls; QVariantList types; } images; struct { QVariantList postIds; QVariantList keys; QVariantList values; } extras; struct { QVariantList postIds; QVariantList accountIds; } accounts; const QVariant invalidImageType = QLatin1String(INVALID); const QVariant photoImageType = QLatin1String(PHOTO); const QVariant videoImageType = QLatin1String(VIDEO); Q_FOREACH (const SocialPost::ConstPtr &post, insertPosts) { const QVariant postId = post->identifier(); posts.postIds.append(postId); posts.names.append(post->name()); posts.bodies.append(post->body()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) posts.timestamps.append(post->timestamp().toSecsSinceEpoch()); #else posts.timestamps.append(post->timestamp().toTime_t()); #endif const QMap postImages = post->allImages(); typedef QMap::const_iterator iterator; for (iterator it = postImages.begin(); it != postImages.end(); ++it) { images.postIds.append(postId); images.positions.append(it.key()); images.urls.append(it.value()->url()); switch (it.value()->type()) { case SocialPostImage::Photo: images.types.append(photoImageType); break; case SocialPostImage::Video: images.types.append(videoImageType); break; default: images.types.append(invalidImageType); break; } } const QVariantMap extra = post->extra(); for (QVariantMap::const_iterator it = extra.begin(); it != extra.end(); ++it) { extras.postIds.append(postId); extras.keys.append(it.key()); extras.values.append(it.value()); } } for (QMultiMap::const_iterator it = mapPostsToAccounts.begin(); it != mapPostsToAccounts.end(); ++it) { accounts.postIds.append(it.key()); accounts.accountIds.append(it.value()); } if (!posts.postIds.isEmpty()) { query = prepare(QStringLiteral( "DELETE FROM images " "WHERE postId = :postId")); query.bindValue(QStringLiteral(":postId"), posts.postIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM extra " "WHERE postId = :postId")); query.bindValue(QStringLiteral(":postId"), posts.postIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM link_post_account " "WHERE postId = :postId")); query.bindValue(QStringLiteral(":postId"), posts.postIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "INSERT OR REPLACE INTO posts (" " identifier, name, body, timestamp) " "VALUES (" " :postId, :name, :body, :timestamp)")); query.bindValue(QStringLiteral(":postId"), posts.postIds); query.bindValue(QStringLiteral(":name"), posts.names); query.bindValue(QStringLiteral(":body"), posts.bodies); query.bindValue(QStringLiteral(":timestamp"), posts.timestamps); executeBatchSocialCacheQuery(query); } if (!images.postIds.isEmpty()) { query = prepare(QStringLiteral( "INSERT INTO images (" " postId, position, url, type) " "VALUES (" " :postId, :position, :url, :type)")); query.bindValue(QStringLiteral(":postId"), images.postIds); query.bindValue(QStringLiteral(":position"), images.positions); query.bindValue(QStringLiteral(":url"), images.urls); query.bindValue(QStringLiteral(":type"), images.types); executeBatchSocialCacheQuery(query); } if (!extras.postIds.isEmpty()) { query = prepare(QStringLiteral( "INSERT INTO extra (" " postId, key, value) " "VALUES (" " :postId, :key, :value)")); query.bindValue(QStringLiteral(":postId"), extras.postIds); query.bindValue(QStringLiteral(":key"), extras.keys); query.bindValue(QStringLiteral(":value"), extras.values); executeBatchSocialCacheQuery(query); } if (!accounts.postIds.isEmpty()) { query = prepare(QStringLiteral( "INSERT INTO link_post_account (" " postId, account) " "VALUES (" " :postId, :account)")); query.bindValue(QStringLiteral(":postId"), accounts.postIds); query.bindValue(QStringLiteral(":account"), accounts.accountIds); executeBatchSocialCacheQuery(query); } return success; } bool AbstractSocialPostCacheDatabase::createTables(QSqlDatabase database) const { QSqlQuery query (database); // Heavily inspired from libeventfeeds // posts is composed of // * identifier is the identifier of the data (from social // network, like the facebook id) // * name is the displayed name of the poster. Twitter, that // requires both the name and "screen name" of the poster, // uses an extra field, passed to the extra table. // * body is the content of the entry. // * timestamp is the timestamp, converted to milliseconds // from epoch (makes sorting easier). query.prepare( "CREATE TABLE IF NOT EXISTS posts ("\ "identifier TEXT UNIQUE PRIMARY KEY,"\ "name TEXT,"\ "body TEXT,"\ "timestamp INTEGER)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create posts table" << query.lastError().text(); return false; } query.prepare("CREATE TABLE IF NOT EXISTS images ("\ "postId TEXT, "\ "position INTEGER, "\ "url TEXT, "\ "type TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create images table" << query.lastError().text(); return false; } query.prepare("CREATE TABLE IF NOT EXISTS extra ("\ "postId TEXT, "\ "key TEXT, "\ "value TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create extra table" << query.lastError().text(); return false; } query.prepare("CREATE TABLE IF NOT EXISTS link_post_account ("\ "postId TEXT, "\ "account INTEGER, "\ "CONSTRAINT id PRIMARY KEY (postId, account))"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create link_post_account table" << query.lastError().text(); return false; } return true; } bool AbstractSocialPostCacheDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS posts"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to delete posts table" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS images"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to delete images table" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS extra"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to delete extra table" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS link_post_account"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to delete link_post_account table" << query.lastError().text(); return false; } return true; } void AbstractSocialPostCacheDatabase::readFinished() { Q_D(AbstractSocialPostCacheDatabase); QMutexLocker locker(&d->mutex); d->posts = d->asyncPosts; d->asyncPosts.clear(); locker.unlock(); emit postsChanged(); } libsocialcache-0.2.1/src/lib/abstractsocialpostcachedatabase.h000066400000000000000000000110121462333522700245240ustar00rootroot00000000000000/* * Copyright (C) 2013 Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ABSTRACTSOCIALPOSTCACHEDATABASE_H #define ABSTRACTSOCIALPOSTCACHEDATABASE_H #include "abstractsocialcachedatabase.h" #include #include #include class SocialPostImagePrivate; class SocialPostImage { public: enum ImageType { Invalid, Photo, Video }; typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; explicit SocialPostImage(); virtual ~SocialPostImage(); QString url() const; ImageType type() const; static SocialPostImage::Ptr create(const QString &url, ImageType type); protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(SocialPostImage) explicit SocialPostImage(const QString &url, ImageType type); }; class SocialPostPrivate; class SocialPost { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~SocialPost(); static SocialPost::Ptr create(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QMap &images = QMap(), const QVariantMap &extra = QVariantMap(), const QList &accounts = QList()); QString identifier() const; QString name() const; QString body() const; QDateTime timestamp() const; QString icon() const; QList images() const; QMap allImages() const; void setImages(const QMap &images); QVariantMap extra() const; void setExtra(const QVariantMap &extra); QList accounts() const; void setAccounts(const QList &accounts); protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(SocialPost) explicit SocialPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QMap &images, const QVariantMap &extra = QVariantMap(), const QList &accounts = QList()); }; class AbstractSocialPostCacheDatabasePrivate; class AbstractSocialPostCacheDatabase: public AbstractSocialCacheDatabase { Q_OBJECT Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged) public: explicit AbstractSocialPostCacheDatabase( const QString &serviceName, const QString &databaseFile); ~AbstractSocialPostCacheDatabase(); QVariantList accountIdFilter() const; void setAccountIdFilter(const QVariantList &accountIds); QList posts() const; void addPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QString &icon, const QList > &images, const QVariantMap &extra, int account); void removePosts(int accountId); void removePost(const QString &identifier); void removeAll(); void commit(); void refresh(); Q_SIGNALS: void postsChanged(); void accountIdFilterChanged(); protected: bool read(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; void readFinished(); private: Q_DECLARE_PRIVATE(AbstractSocialPostCacheDatabase) }; #endif // ABSTRACTSOCIALPOSTCACHEDATABASE_H libsocialcache-0.2.1/src/lib/dropboximagesdatabase.cpp000066400000000000000000001407341462333522700230500ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dropboximagesdatabase.h" #include "abstractsocialcachedatabase.h" #include "socialsyncinterface.h" #include #include #include #include static const char *DB_NAME = "dropbox.db"; static const int VERSION = 1; struct DropboxUserPrivate { explicit DropboxUserPrivate(const QString &userId, const QDateTime &updatedTime, const QString &userName, int count = -1); QString userId; QDateTime updatedTime; QString userName; int count; }; DropboxUserPrivate::DropboxUserPrivate(const QString &userId, const QDateTime &updatedTime, const QString &userName, int count) : userId(userId), updatedTime(updatedTime), userName(userName), count(count) { } DropboxUser::DropboxUser(const QString &userId, const QDateTime &updatedTime, const QString &userName, int count) : d_ptr(new DropboxUserPrivate(userId, updatedTime, userName, count)) { } DropboxUser::~DropboxUser() { } DropboxUser::Ptr DropboxUser::create(const QString &userId, const QDateTime &updatedTime, const QString &userName, int count) { return DropboxUser::Ptr(new DropboxUser(userId, updatedTime, userName, count)); } QString DropboxUser::userId() const { Q_D(const DropboxUser); return d->userId; } QDateTime DropboxUser::updatedTime() const { Q_D(const DropboxUser); return d->updatedTime; } QString DropboxUser::userName() const { Q_D(const DropboxUser); return d->userName; } int DropboxUser::count() const { Q_D(const DropboxUser); return d->count; } struct DropboxAlbumPrivate { explicit DropboxAlbumPrivate(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash); QString albumId; QString userId; QDateTime createdTime; QDateTime updatedTime; QString albumName; int imageCount; QString hash; }; DropboxAlbumPrivate::DropboxAlbumPrivate(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash) : albumId(albumId), userId(userId), createdTime(createdTime) , updatedTime(updatedTime), albumName(albumName), imageCount(imageCount), hash(hash) { } DropboxAlbum::DropboxAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash) : d_ptr(new DropboxAlbumPrivate(albumId, userId, createdTime, updatedTime, albumName, imageCount, hash)) { } DropboxAlbum::~DropboxAlbum() { } DropboxAlbum::Ptr DropboxAlbum::create(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash) { return DropboxAlbum::Ptr(new DropboxAlbum(albumId, userId, createdTime, updatedTime, albumName, imageCount, hash)); } QString DropboxAlbum::albumId() const { Q_D(const DropboxAlbum); return d->albumId; } QString DropboxAlbum::userId() const { Q_D(const DropboxAlbum); return d->userId; } QDateTime DropboxAlbum::createdTime() const { Q_D(const DropboxAlbum); return d->createdTime; } QDateTime DropboxAlbum::updatedTime() const { Q_D(const DropboxAlbum); return d->updatedTime; } QString DropboxAlbum::albumName() const { Q_D(const DropboxAlbum); return d->albumName; } int DropboxAlbum::imageCount() const { Q_D(const DropboxAlbum); return d->imageCount; } QString DropboxAlbum::hash() const { Q_D(const DropboxAlbum); return d->hash; } struct DropboxImagePrivate { explicit DropboxImagePrivate(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account = -1, const QString &accessToken = QString()); QString imageId; QString albumId; QString userId; QDateTime createdTime; QDateTime updatedTime; QString imageName; int width; int height; QString thumbnailUrl; QString imageUrl; QString thumbnailFile; QString imageFile; int account; QString accessToken; }; DropboxImagePrivate::DropboxImagePrivate(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account, const QString &accessToken) : imageId(imageId), albumId(albumId), userId(userId) , createdTime(createdTime), updatedTime(updatedTime), imageName(imageName) , width(width), height(height), thumbnailUrl(thumbnailUrl) , imageUrl(imageUrl), thumbnailFile(thumbnailFile), imageFile(imageFile), account(account) , accessToken(accessToken) { } DropboxImage::DropboxImage(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account, const QString &accessToken) : d_ptr(new DropboxImagePrivate(imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, account, accessToken)) { } DropboxImage::~DropboxImage() { } DropboxImage::Ptr DropboxImage::create(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account, const QString &accessToken) { return DropboxImage::Ptr(new DropboxImage(imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, account, accessToken)); } QString DropboxImage::imageId() const { Q_D(const DropboxImage); return d->imageId; } QString DropboxImage::albumId() const { Q_D(const DropboxImage); return d->albumId; } QString DropboxImage::userId() const { Q_D(const DropboxImage); return d->userId; } QDateTime DropboxImage::createdTime() const { Q_D(const DropboxImage); return d->createdTime; } QDateTime DropboxImage::updatedTime() const { Q_D(const DropboxImage); return d->updatedTime; } QString DropboxImage::imageName() const { Q_D(const DropboxImage); return d->imageName; } int DropboxImage::width() const { Q_D(const DropboxImage); return d->width; } int DropboxImage::height() const { Q_D(const DropboxImage); return d->height; } QString DropboxImage::thumbnailUrl() const { Q_D(const DropboxImage); return d->thumbnailUrl; } QString DropboxImage::imageUrl() const { Q_D(const DropboxImage); return d->imageUrl; } QString DropboxImage::thumbnailFile() const { Q_D(const DropboxImage); return d->thumbnailFile; } QString DropboxImage::imageFile() const { Q_D(const DropboxImage); return d->imageFile; } int DropboxImage::account() const { Q_D(const DropboxImage); return d->account; } QString DropboxImage::accessToken() const { Q_D(const DropboxImage); return d->accessToken; } class DropboxImagesDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: enum QueryType { Users, Albums, UserImages, AlbumImages }; explicit DropboxImagesDatabasePrivate(DropboxImagesDatabase *q); ~DropboxImagesDatabasePrivate(); private: Q_DECLARE_PUBLIC(DropboxImagesDatabase) static void clearCachedImages(QSqlQuery &query); QList queryUsers() const; QList queryAlbums(const QString &userId) const; QList queryImages(const QString &userId, const QString &albumId); struct { QList purgeAccounts; QStringList removeUsers; QStringList removeAlbums; QStringList removeImages; QMap insertUsers; QMap insertAlbums; QMap insertImages; QMap syncAccounts; QMap updateThumbnailFiles; QMap updateImageFiles; } queue; struct { QueryType type; QString id; QList users; QList albums; QList images; } query; struct { QList users; QList albums; QList images; } result; }; DropboxImagesDatabasePrivate::DropboxImagesDatabasePrivate(DropboxImagesDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::Dropbox), SocialSyncInterface::dataType(SocialSyncInterface::Images), QLatin1String(DB_NAME), VERSION) { } DropboxImagesDatabasePrivate::~DropboxImagesDatabasePrivate() { } void DropboxImagesDatabasePrivate::clearCachedImages(QSqlQuery &query) { while (query.next()) { QString thumb = query.value(0).toString(); QString image = query.value(1).toString(); if (!thumb.isEmpty()) { QFile thumbFile (thumb); if (thumbFile.exists()) { thumbFile.remove(); } } if (!image.isEmpty()) { QFile imageFile (image); if (imageFile.exists()) { imageFile.remove(); } } } } QList DropboxImagesDatabasePrivate::queryImages(const QString &userId, const QString &albumId) { Q_Q(DropboxImagesDatabase); QList data; if (!userId.isEmpty() && !albumId.isEmpty()) { qWarning() << Q_FUNC_INFO << "Cannot select images in both an album and for an user"; return data; } QString queryString = QLatin1String("SELECT images.imageId, images.albumId, "\ "images.userId, images.createdTime, "\ "images.updatedTime, images.imageName, images.width, "\ "images.height, images.thumbnailUrl, images.imageUrl, "\ "images.thumbnailFile, images.imageFile, "\ "accounts.accountId, images.accessToken "\ "FROM images "\ "INNER JOIN accounts "\ "ON accounts.userId = images.userId%1 "\ "ORDER BY images.updatedTime %2"); if (!userId.isEmpty()) { queryString = queryString.arg(QLatin1String(" WHERE images.userId = :userId"), QLatin1String("DESC")); } else if (!albumId.isEmpty()){ queryString = queryString.arg(QLatin1String(" WHERE images.albumId = :albumId"), QString()); } else { queryString = queryString.arg(QString(), QLatin1String("DESC")); } QSqlQuery query = q->prepare(queryString); if (!userId.isEmpty()) { query.bindValue(":userId", userId); } if (!albumId.isEmpty()) { query.bindValue(":albumId", albumId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all albums:" << query.lastError().text(); return data; } while (query.next()) { data.append(DropboxImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), #else QDateTime::fromTime_t(query.value(3).toUInt()), QDateTime::fromTime_t(query.value(4).toUInt()), #endif query.value(5).toString(), query.value(6).toInt(), query.value(7).toInt(), query.value(8).toString(), query.value(9).toString(), query.value(10).toString(), query.value(11).toString(), query.value(12).toInt(), query.value(13).toString())); } return data; } bool operator==(const DropboxUser::ConstPtr &user1, const DropboxUser::ConstPtr &user2) { return user1->userId() == user2->userId(); } bool operator==(const DropboxAlbum::ConstPtr &album1, const DropboxAlbum::ConstPtr &album2) { return album1->albumId() == album2->albumId() && album1->userId() == album2->userId(); } bool operator==(const DropboxImage::ConstPtr &image1, const DropboxImage::ConstPtr &image2) { return image1->imageId() == image2->imageId() && image1->albumId() == image2->albumId() && image1->userId() == image2->userId(); } // Note // // Insertion operations needs to use write(), while Delete // operations are automatically using transactions and // don't need write(). DropboxImagesDatabase::DropboxImagesDatabase() : AbstractSocialCacheDatabase(*(new DropboxImagesDatabasePrivate(this))) { } DropboxImagesDatabase::~DropboxImagesDatabase() { wait(); } bool DropboxImagesDatabase::syncAccount(int accountId, const QString &userId) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.syncAccounts.insert(accountId, userId); return true; } void DropboxImagesDatabase::purgeAccount(int accountId) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.purgeAccounts.append(accountId); } // Returns the user but do not return a count // if you want a count, better consider users DropboxUser::ConstPtr DropboxImagesDatabase::user(const QString &userId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT userId, updatedTime, userName " "FROM users WHERE userId = :userId")); query.bindValue(":userId", userId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from users table:" << query.lastError(); return DropboxUser::Ptr(); } // If we have the user, we have a result, otherwise we won't have the user if (!query.next()) { return DropboxUser::Ptr(); } DropboxUser::ConstPtr user = DropboxUser::create( query.value(0).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(1).toUInt()), #else QDateTime::fromTime_t(query.value(1).toUInt()), #endif query.value(2).toString()); query.finish(); return user; } void DropboxImagesDatabase::addUser(const QString &userId, const QDateTime &updatedTime, const QString &userName) { Q_D(DropboxImagesDatabase); DropboxUser::Ptr user = DropboxUser::create(userId, updatedTime, userName); QMutexLocker locker(&d->mutex); d->queue.insertUsers.insert(userId, user); } void DropboxImagesDatabase::removeUser(const QString &userId) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeUsers.append(userId); } QList DropboxImagesDatabasePrivate::queryUsers() const { QList data; QSqlQuery query = q_func()->prepare(QStringLiteral( "SELECT users.userId, users.updatedTime, users.userName, " "SUM(albums.imageCount) as count " "FROM users " "LEFT JOIN albums ON albums.userId = users.userId " "GROUP BY users.userId " "ORDER BY users.userId")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all users:" << query.lastError().text(); return data; } while (query.next()) { data.append(DropboxUser::create(query.value(0).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(1).toUInt()), #else QDateTime::fromTime_t(query.value(1).toUInt()), #endif query.value(2).toString(), query.value(3).toInt())); } return data; } QStringList DropboxImagesDatabase::allAlbumIds(bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT albumId " "FROM albums " "ORDER BY updatedTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch all albums" << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } QMap DropboxImagesDatabase::accounts(bool *ok) const { if (ok) { *ok = false; } QMap result; QSqlQuery query = prepare(QStringLiteral( "SELECT accountId, userId " "FROM accounts ")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch account mappings" << query.lastError().text(); return result; } while (query.next()) { result[query.value(0).toInt()] = query.value(1).toString(); } if (ok) { *ok = true; } return result; } DropboxAlbum::ConstPtr DropboxImagesDatabase::album(const QString &albumId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT albumId, userId, createdTime, updatedTime, albumName, " "imageCount, hash " "FROM albums WHERE albumId = :albumId")); query.bindValue(":albumId", albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from albums table:" << query.lastError(); return DropboxAlbum::Ptr(); } // If we have the album, we have a result, otherwise we won't have the album if (!query.next()) { return DropboxAlbum::Ptr(); } DropboxAlbum::ConstPtr album = DropboxAlbum::create(query.value(0).toString(), query.value(1).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(2).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), #else QDateTime::fromTime_t(query.value(2).toUInt()), QDateTime::fromTime_t(query.value(3).toUInt()), #endif query.value(4).toString(), query.value(5).toInt(), query.value(6).toString()); query.finish(); return album; } void DropboxImagesDatabase::addAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash) { Q_D(DropboxImagesDatabase); DropboxAlbum::Ptr album = DropboxAlbum::create(albumId, userId, createdTime, updatedTime, albumName, imageCount, hash); QMutexLocker locker(&d->mutex); d->queue.insertAlbums.insert(albumId, album); } void DropboxImagesDatabase::removeAlbum(const QString &albumId) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums.append(albumId); } void DropboxImagesDatabase::removeAlbums(const QStringList &albumIds) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums += albumIds; } QList DropboxImagesDatabasePrivate::queryAlbums(const QString &userId) const { QList data; QString queryString = QLatin1String("SELECT albumId, userId, createdTime, updatedTime, "\ "albumName, imageCount, hash "\ "FROM albums%1 ORDER BY updatedTime DESC"); if (!userId.isEmpty()) { queryString = queryString.arg(QLatin1String(" WHERE userId = :userId")); } else { queryString = queryString.arg(QString()); } QSqlQuery query = q_func()->prepare(queryString); if (!userId.isEmpty()) { query.bindValue(":userId", userId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all albums:" << query.lastError().text(); return data; } while (query.next()) { data.append(DropboxAlbum::create(query.value(0).toString(), query.value(1).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(2).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), #else QDateTime::fromTime_t(query.value(2).toUInt()), QDateTime::fromTime_t(query.value(3).toUInt()), #endif query.value(4).toString(), query.value(5).toInt(), query.value(6).toString())); } return data; } QStringList DropboxImagesDatabase::allImageIds(bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT imageId " "FROM images " "ORDER BY updatedTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch all images" << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } QStringList DropboxImagesDatabase::imageIds(const QString &albumId, bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT imageId " "FROM images WHERE albumId = :albumId")); query.bindValue(":albumId", albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch images for album" << albumId << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } DropboxImage::ConstPtr DropboxImagesDatabase::image(const QString &imageId) const { QSqlQuery query = prepare( "SELECT imageId, albumId, userId, createdTime, updatedTime, imageName, " "width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, accessToken " "FROM images WHERE imageId = :imageId"); query.bindValue(":imageId", imageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from albums table:" << query.lastError(); return DropboxImage::Ptr(); } // If we have the image, we have a result, otherwise we won't have the image if (!query.next()) { return DropboxImage::Ptr(); } return DropboxImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), #else QDateTime::fromTime_t(query.value(3).toUInt()), QDateTime::fromTime_t(query.value(4).toUInt()), #endif query.value(5).toString(), query.value(6).toInt(), query.value(7).toInt(), query.value(8).toString(), query.value(9).toString(), query.value(10).toString(), query.value(11).toString(),-1, query.value(12).toString()); } void DropboxImagesDatabase::removeImage(const QString &imageId) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages.append(imageId); } void DropboxImagesDatabase::removeImages(const QStringList &imageIds) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages += imageIds; } void DropboxImagesDatabase::addImage(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &accessToken) { Q_D(DropboxImagesDatabase); DropboxImage::Ptr image = DropboxImage::create(imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, QString(), QString(),-1,accessToken); QMutexLocker locker(&d->mutex); d->queue.insertImages.insert(imageId, image); } void DropboxImagesDatabase::updateImageThumbnail(const QString &imageId, const QString &thumbnailFile) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateThumbnailFiles.insert(imageId, thumbnailFile); } void DropboxImagesDatabase::updateImageFile(const QString &imageId, const QString &imageFile) { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateImageFiles.insert(imageId, imageFile); } void DropboxImagesDatabase::commit() { executeWrite(); } QList DropboxImagesDatabase::users() const { return d_func()->result.users; } QList DropboxImagesDatabase::images() const { return d_func()->result.images; } QList DropboxImagesDatabase::albums() const { return d_func()->result.albums; } void DropboxImagesDatabase::queryUsers() { Q_D(DropboxImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = DropboxImagesDatabasePrivate::Users; } executeRead(); } void DropboxImagesDatabase::queryAlbums(const QString &userId) { Q_D(DropboxImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = DropboxImagesDatabasePrivate::Albums; d->query.id = userId; } executeRead(); } void DropboxImagesDatabase::queryUserImages(const QString &userId) { Q_D(DropboxImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = DropboxImagesDatabasePrivate::UserImages; d->query.id = userId; } executeRead(); } void DropboxImagesDatabase::queryAlbumImages(const QString &albumId) { Q_D(DropboxImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = DropboxImagesDatabasePrivate::AlbumImages; d->query.id = albumId; } executeRead(); } bool DropboxImagesDatabase::read() { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); switch (d->query.type) { case DropboxImagesDatabasePrivate::Users: { locker.unlock(); QList users = d->queryUsers(); locker.relock(); d->query.users = users; return true; } case DropboxImagesDatabasePrivate::Albums: { const QString userId = d->query.id; locker.unlock(); QList albums = d->queryAlbums(userId); locker.relock(); d->query.albums = albums; return true; } case DropboxImagesDatabasePrivate::UserImages: case DropboxImagesDatabasePrivate::AlbumImages: { const QString userId = d->query.type == DropboxImagesDatabasePrivate::UserImages ? d->query.id : QString(); const QString albumId = d->query.type == DropboxImagesDatabasePrivate::AlbumImages ? d->query.id : QString(); locker.unlock(); QList images = d->queryImages(userId, albumId); locker.relock(); d->query.images = images; return true; } default: return false; } } void DropboxImagesDatabase::readFinished() { Q_D(DropboxImagesDatabase); { QMutexLocker locker(&d->mutex); d->result.users = d->query.users; d->result.albums = d->query.albums; d->result.images = d->query.images; d->query.users.clear(); d->query.albums.clear(); d->query.images.clear(); } emit queryFinished(); } bool DropboxImagesDatabase::write() { Q_D(DropboxImagesDatabase); QMutexLocker locker(&d->mutex); qWarning() << "Queued users being saved:" << d->queue.insertUsers.count(); qWarning() << "Queued albums being saved:" << d->queue.insertAlbums.count(); qWarning() << "Queued images being saved:" << d->queue.insertImages.count(); qWarning() << "Queued thumbnail files being updated:" << d->queue.updateThumbnailFiles.count(); qWarning() << "Queued image files being updated:" << d->queue.updateImageFiles.count(); const QList purgeAccounts = d->queue.purgeAccounts; QStringList removeUsers = d->queue.removeUsers; const QStringList removeAlbums = d->queue.removeAlbums; const QStringList removeImages = d->queue.removeImages; const QMap insertUsers = d->queue.insertUsers; const QMap insertAlbums = d->queue.insertAlbums; const QMap insertImages = d->queue.insertImages; const QMap syncAccounts = d->queue.syncAccounts; const QMap updateThumbnailFiles = d->queue.updateThumbnailFiles; const QMap updateImageFiles = d->queue.updateImageFiles; d->queue.purgeAccounts.clear(); d->queue.removeUsers.clear(); d->queue.removeAlbums.clear(); d->queue.removeImages.clear(); d->queue.insertUsers.clear(); d->queue.insertAlbums.clear(); d->queue.insertImages.clear(); d->queue.syncAccounts.clear(); d->queue.updateThumbnailFiles.clear(); d->queue.updateImageFiles.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!purgeAccounts.isEmpty()) { query = prepare(QStringLiteral( "SELECT userId " "FROM accounts " "WHERE accountId = :accountId")); Q_FOREACH (int accountId, purgeAccounts) { query.bindValue(QStringLiteral(":accountId"), accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec account id selection query:" << query.lastError().text(); success = false; } else while (query.next()) { removeUsers.append(query.value(0).toString()); } } } if (!removeUsers.isEmpty()) { QVariantList userIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE userId = :userId")); Q_FOREACH (const QString &userId, removeUsers) { userIds.append(userId); query.bindValue(QStringLiteral(":userId"), userId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM accounts " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM users " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); } if (!removeAlbums.isEmpty()) { QVariantList albumIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE albumId = :albumId")); Q_FOREACH (const QString &albumId, removeAlbums) { albumIds.append(albumId); query.bindValue(QStringLiteral(":albumId"), albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE albumId = :albumId")); query.bindValue(QStringLiteral(":albumId"), albumIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE albumId = :albumId")); query.bindValue(QStringLiteral(":albumId"), albumIds); executeBatchSocialCacheQuery(query); } if (!removeImages.isEmpty()) { QVariantList imageIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE imageId = :imageId")); Q_FOREACH (const QString &imageId, removeImages) { imageIds.append(imageId); query.bindValue(QStringLiteral(":imageId"), imageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM images " "WHERE imageId = :imageId")); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } if (!insertUsers.isEmpty()) { QVariantList userIds; QVariantList updatedTimes; QVariantList usernames; Q_FOREACH (const DropboxUser::ConstPtr &user, insertUsers) { userIds.append(user->userId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) updatedTimes.append(user->updatedTime().toSecsSinceEpoch()); #else updatedTimes.append(user->updatedTime().toTime_t()); #endif usernames.append(user->userName()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO users (" " userId, updatedTime, userName) " "VALUES (" " :userId, :updatedTime, :userName);")); query.bindValue(QStringLiteral(":userId"), userIds); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":userName"), usernames); executeBatchSocialCacheQuery(query); } if (!insertAlbums.isEmpty()) { QVariantList albumIds, userIds; QVariantList createdTimes, updatedTimes; QVariantList albumNames; QVariantList imageCounts; QVariantList hashes; Q_FOREACH (const DropboxAlbum::ConstPtr &album, insertAlbums) { albumIds.append(album->albumId()); userIds.append(album->userId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(album->createdTime().toSecsSinceEpoch()); updatedTimes.append(album->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(album->createdTime().toTime_t()); updatedTimes.append(album->updatedTime().toTime_t()); #endif albumNames.append(album->albumName()); imageCounts.append(album->imageCount()); hashes.append(album->hash()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO albums(" " albumId, userId, createdTime, updatedTime, albumName, imageCount, hash) " "VALUES (" " :albumId, :userId, :createdTime, :updatedTime, :albumName, :imageCount, :hash)")); query.bindValue(QStringLiteral(":albumId"), albumIds); query.bindValue(QStringLiteral(":userId"), userIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":albumName"), albumNames); query.bindValue(QStringLiteral(":imageCount"), imageCounts); query.bindValue(QStringLiteral(":hash"), hashes); executeBatchSocialCacheQuery(query); } if (!insertImages.isEmpty()) { QVariantList imageIds, albumIds, userIds; QVariantList createdTimes, updatedTimes; QVariantList imageNames; QVariantList widths, heights; QVariantList thumbnailUrls, imageUrls; QVariantList thumbnailFiles, imageFiles; QVariantList accessTokens; Q_FOREACH (const DropboxImage::ConstPtr &image, insertImages) { imageIds.append(image->imageId()); albumIds.append(image->albumId()); userIds.append(image->userId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(image->createdTime().toSecsSinceEpoch()); updatedTimes.append(image->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(image->createdTime().toTime_t()); updatedTimes.append(image->updatedTime().toTime_t()); #endif imageNames.append(image->imageName()); widths.append(image->width()); heights.append(image->height()); thumbnailUrls.append(image->thumbnailUrl()); imageUrls.append(image->imageUrl()); thumbnailFiles.append(image->thumbnailFile()); imageFiles.append(image->imageFile()); accessTokens.append(image->accessToken()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO images (" " imageId, albumId, userId, createdTime, updatedTime, imageName," " width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, accessToken) " "VALUES (" " :imageId, :albumId, :userId, :createdTime, :updatedTime, :imageName," " :width, :height, :thumbnailUrl, :imageUrl, :thumbnailFile, :imageFile, :accessToken)")); query.bindValue(QStringLiteral(":imageId"), imageIds); query.bindValue(QStringLiteral(":albumId"), albumIds); query.bindValue(QStringLiteral(":userId"), userIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":imageName"), imageNames); query.bindValue(QStringLiteral(":width"), widths); query.bindValue(QStringLiteral(":height"), heights); query.bindValue(QStringLiteral(":thumbnailUrl"), thumbnailUrls); query.bindValue(QStringLiteral(":imageUrl"), imageUrls); query.bindValue(QStringLiteral(":thumbnailFile"), thumbnailFiles); query.bindValue(QStringLiteral(":imageFile"), imageFiles); query.bindValue(QStringLiteral(":accessToken"), accessTokens); executeBatchSocialCacheQuery(query); } if (!syncAccounts.isEmpty()) { QVariantList accountIds; QVariantList userIds; for (QMap::const_iterator it = syncAccounts.begin(); it != syncAccounts.end(); ++it) { accountIds.append(it.key()); userIds.append(it.value()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO accounts (" " accountId, userId) " "VALUES(" " :accountId, :userId)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); } if (!updateThumbnailFiles.isEmpty()) { QVariantList imageIds; QVariantList thumbnailFiles; for (QMap::const_iterator it = updateThumbnailFiles.begin(); it != updateThumbnailFiles.end(); ++it) { imageIds.append(it.key()); thumbnailFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE images " "SET thumbnailFile = :thumbnailFile " "WHERE imageId = :imageId")); query.bindValue(QStringLiteral(":thumbnailFile"), thumbnailFiles); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } if (!updateImageFiles.isEmpty()) { QVariantList imageIds; QVariantList imageFiles; for (QMap::const_iterator it = updateImageFiles.begin(); it != updateImageFiles.end(); ++it) { imageIds.append(it.key()); imageFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE images " "SET imageFile = :imageFile " "WHERE imageId = :imageId")); query.bindValue(QStringLiteral(":imageFile"), imageFiles); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } return success; } bool DropboxImagesDatabase::createTables(QSqlDatabase database) const { // create the Dropbox image db tables // images = imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, // thumbnailUrl, imageUrl, thumbnailFile, imageFile // albums = albumId, userId, createdTime, updatedTime, albumName, imageCount, hash // users = userId, updatedTime, userName, thumbnailUrl, imageUrl, thumbnailFile, imageFile QSqlQuery query(database); query.prepare( "CREATE TABLE IF NOT EXISTS images (" "imageId TEXT UNIQUE PRIMARY KEY," "albumId TEXT," "userId TEXT," "createdTime INTEGER," "updatedTime INTEGER," "imageName TEXT," "width INTEGER," "height INTEGER," "thumbnailUrl TEXT," "imageUrl TEXT," "thumbnailFile TEXT," "imageFile TEXT," "accessToken TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create images table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS albums (" "albumId TEXT UNIQUE PRIMARY KEY," "userId TEXT," "createdTime INTEGER," "updatedTime INTEGER," "albumName TEXT," "imageCount INTEGER," "hash TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create albums table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS users (" "userId TEXT UNIQUE PRIMARY KEY," "updatedTime INTEGER," "userName TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create users table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS accounts (" "accountId INTEGER UNIQUE PRIMARY KEY," "userId TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create accounts table:" << query.lastError().text(); return false; } return true; } bool DropboxImagesDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS images"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete images table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS albums"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete albums table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS users"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete users table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS accounts"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete accounts table:" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/dropboximagesdatabase.h000066400000000000000000000165751462333522700225220ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DROPBOXIMAGESDATABASE_H #define DROPBOXIMAGESDATABASE_H #include "abstractsocialcachedatabase_p.h" #include #include #include class DropboxUserPrivate; class DropboxUser { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~DropboxUser(); static DropboxUser::Ptr create(const QString &userId, const QDateTime &updatedTime, const QString &userName, int count = -1); QString userId() const; QDateTime updatedTime() const; QString userName() const; int count() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(DropboxUser) explicit DropboxUser(const QString &userId, const QDateTime &updatedTime, const QString &userName, int count = -1); }; class DropboxAlbumPrivate; class DropboxAlbum { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~DropboxAlbum(); static DropboxAlbum::Ptr create(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash); QString albumId() const; QString userId() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString albumName() const; QString hash() const; int imageCount() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(DropboxAlbum) explicit DropboxAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash); }; class DropboxImagePrivate; class DropboxImage { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~DropboxImage(); static DropboxImage::Ptr create(const QString & imageId, const QString & albumId, const QString & userId, const QDateTime & createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & thumbnailFile, const QString & imageFile, int account = -1, const QString & accessToken = QString()); QString imageId() const; QString albumId() const; QString userId() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString imageName() const; int width() const; int height() const; QString thumbnailUrl() const; QString imageUrl() const; QString thumbnailFile() const; QString imageFile() const; int account() const; QString accessToken() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(DropboxImage) explicit DropboxImage(const QString & imageId, const QString & albumId, const QString & userId, const QDateTime & createdTime, const QDateTime & updatedTime, const QString & imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & thumbnailFile, const QString & imageFile, int account = -1, const QString & accessToken = QString()); }; bool operator==(const DropboxUser::ConstPtr &user1, const DropboxUser::ConstPtr &user2); bool operator==(const DropboxAlbum::ConstPtr &album1, const DropboxAlbum::ConstPtr &album2); bool operator==(const DropboxImage::ConstPtr &image1, const DropboxImage::ConstPtr &image2); class DropboxImagesDatabasePrivate; class DropboxImagesDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit DropboxImagesDatabase(); ~DropboxImagesDatabase(); // Account manipulation bool syncAccount(int accountId, const QString &userId); void purgeAccount(int accountId); QMap accounts(bool *ok = 0) const; // User cache manipulation DropboxUser::ConstPtr user(const QString &userId) const; void addUser(const QString &userId, const QDateTime &updatedTime, const QString &userName); void removeUser(const QString &userId); // Album cache manipulation QStringList allAlbumIds(bool *ok = 0) const; DropboxAlbum::ConstPtr album(const QString &albumId) const; void addAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount, const QString &hash); void removeAlbum(const QString &albumId); void removeAlbums(const QStringList &albumIds); // Images cache manipulation QStringList allImageIds(bool *ok = 0) const; QStringList imageIds(const QString &albumId, bool *ok = 0) const; DropboxImage::ConstPtr image(const QString &imageId) const; void addImage(const QString & imageId, const QString & albumId, const QString & userId, const QDateTime & createdTime, const QDateTime & updatedTime, const QString & imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & accessToken); void updateImageThumbnail(const QString &imageId, const QString &thumbnailFile); void updateImageFile(const QString &imageId, const QString &imageFile); void removeImage(const QString &imageId); void removeImages(const QStringList &imageIds); void commit(); QList users() const; QList images() const; QList albums() const; void queryUsers(); void queryAlbums(const QString &userId = QString()); void queryUserImages(const QString &userId = QString()); void queryAlbumImages(const QString &albumId); Q_SIGNALS: void queryFinished(); protected: bool read(); void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(DropboxImagesDatabase) }; #endif // DROPBOXIMAGESDATABASE_H libsocialcache-0.2.1/src/lib/facebookcontactsdatabase.cpp000066400000000000000000000371421462333522700235130ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebookcontactsdatabase.h" #include "abstractsocialcachedatabase_p.h" #include "socialsyncinterface.h" #include #include #include #include #include static const char *DB_NAME = "facebook.db"; static const int VERSION = 3; //static const char *PICTURE_FILE_KEY = "pictureFile"; //static const char *COVER_FILE_KEY = "coverFile"; struct FacebookContactPrivate { explicit FacebookContactPrivate(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl, const QString &pictureFile, const QString &coverFile); QString fbFriendId; int accountId; QString pictureUrl; QString coverUrl; QString pictureFile; QString coverFile; }; FacebookContactPrivate::FacebookContactPrivate(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl, const QString &pictureFile, const QString &coverFile) : fbFriendId(fbFriendId), accountId(accountId), pictureUrl(pictureUrl) , coverUrl(coverUrl), pictureFile(pictureFile), coverFile(coverFile) { } FacebookContact::FacebookContact(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl, const QString &pictureFile, const QString &coverFile) : d_ptr(new FacebookContactPrivate(fbFriendId, accountId, pictureUrl, coverUrl, pictureFile, coverFile)) { } FacebookContact::~FacebookContact() { } FacebookContact::Ptr FacebookContact::create(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl, const QString &pictureFile, const QString &coverFile) { return FacebookContact::Ptr(new FacebookContact(fbFriendId, accountId, pictureUrl, coverUrl, pictureFile, coverFile)); } QString FacebookContact::fbFriendId() const { Q_D(const FacebookContact); return d->fbFriendId; } int FacebookContact::accountId() const { Q_D(const FacebookContact); return d->accountId; } QString FacebookContact::pictureUrl() const { Q_D(const FacebookContact); return d->pictureUrl; } QString FacebookContact::coverUrl() const { Q_D(const FacebookContact); return d->coverUrl; } QString FacebookContact::pictureFile() const { Q_D(const FacebookContact); return d->pictureFile; } QString FacebookContact::coverFile() const { Q_D(const FacebookContact); return d->coverFile; } class FacebookContactsDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: explicit FacebookContactsDatabasePrivate(FacebookContactsDatabase *q); void clearCachedImages(QSqlQuery &query); struct { QList removeAccounts; QStringList removeContacts; QList insertContacts; QMap updatePictures; QMap updateCovers; } queue; }; FacebookContactsDatabasePrivate::FacebookContactsDatabasePrivate(FacebookContactsDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::Facebook), SocialSyncInterface::dataType(SocialSyncInterface::Contacts), QLatin1String(DB_NAME), VERSION) { } void FacebookContactsDatabasePrivate::clearCachedImages(QSqlQuery &query) { while (query.next()) { QString picture = query.value(0).toString(); QString cover = query.value(1).toString(); if (!picture.isEmpty()) { QFile pictureFile (picture); if (pictureFile.exists()) { pictureFile.remove(); } } if (!cover.isEmpty()) { QFile coverFile (cover); if (coverFile.exists()) { coverFile.remove(); } } } } FacebookContactsDatabase::FacebookContactsDatabase() : AbstractSocialCacheDatabase(*(new FacebookContactsDatabasePrivate(this))) { } FacebookContactsDatabase::~FacebookContactsDatabase() { wait(); } bool FacebookContactsDatabase::removeContacts(int accountId) { Q_D(FacebookContactsDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAccounts.append(accountId); return true; } bool FacebookContactsDatabase::removeContacts(const QStringList &fbFriendIds) { Q_D(FacebookContactsDatabase); QMutexLocker locker(&d->mutex); d->queue.removeContacts += fbFriendIds; return true; } FacebookContact::ConstPtr FacebookContactsDatabase::contact(const QString &fbFriendId, int accountId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT fbFriendId, accountId, pictureUrl, coverUrl, "\ "pictureFile, coverFile "\ "FROM friends "\ "WHERE fbFriendId = :fbFriendId "\ "AND accountId = :accountId")); query.bindValue(":fbFriendId", fbFriendId); query.bindValue(":accountId", accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query contacts" << query.lastError().text(); return FacebookContact::ConstPtr(); } if (!query.next()) { return FacebookContact::ConstPtr(); } FacebookContact::ConstPtr contact = FacebookContact::create( query.value(0).toString(), query.value(1).toInt(), query.value(2).toString(), query.value(3).toString(), query.value(4).toString(), query.value(5).toString()); query.finish(); return contact; } QList FacebookContactsDatabase::contacts(int accountId) const { QList data; QSqlQuery query = prepare(QStringLiteral( "SELECT fbFriendId, accountId, pictureUrl, coverUrl, "\ "pictureFile, coverFile "\ "FROM friends "\ "WHERE accountId = :accountId")); query.bindValue(":accountId", accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query contacts" << query.lastError().text(); return data; } while (query.next()) { data.append(FacebookContact::create(query.value(0).toString(), query.value(1).toInt(), query.value(2).toString(), query.value(3).toString(), query.value(4).toString(), query.value(5).toString())); } return data; } QStringList FacebookContactsDatabase::contactIds(int accountId) const { QStringList data; QSqlQuery query = prepare(QStringLiteral( "SELECT fbFriendId FROM friends "\ "WHERE accountId = :accountId")); query.bindValue(":accountId", accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query contacts" << query.lastError().text(); return data; } while (query.next()) { data.append(query.value(0).toString()); } return data; } void FacebookContactsDatabase::addSyncedContact(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl) { Q_D(FacebookContactsDatabase); QMutexLocker locker(&d->mutex); d->queue.insertContacts.append(FacebookContact::create(fbFriendId, accountId, pictureUrl, coverUrl, QString(), QString())); } void FacebookContactsDatabase::updatePictureFile(const QString &fbFriendId, const QString &pictureFile) { Q_D(FacebookContactsDatabase); QMutexLocker locker(&d->mutex); d->queue.updatePictures.insert(fbFriendId, pictureFile); } void FacebookContactsDatabase::updateCoverFile(const QString &fbFriendId, const QString &coverFile) { Q_D(FacebookContactsDatabase); QMutexLocker locker(&d->mutex); d->queue.updateCovers.insert(fbFriendId, coverFile); } void FacebookContactsDatabase::commit() { executeWrite(); } bool FacebookContactsDatabase::write() { Q_D(FacebookContactsDatabase); QMutexLocker locker(&d->mutex); const QList removeAccounts = d->queue.removeAccounts; const QStringList removeContacts = d->queue.removeContacts; const QList insertContacts = d->queue.insertContacts; const QMap updatePictures = d->queue.updatePictures; const QMap updateCovers = d->queue.updateCovers; d->queue.removeAccounts.clear(); d->queue.removeContacts.clear(); d->queue.insertContacts.clear(); d->queue.updatePictures.clear(); d->queue.updateCovers.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!removeAccounts.isEmpty()) { QVariantList accountIds; query = prepare(QStringLiteral( "SELECT pictureFile, coverFile " "FROM friends " "WHERE accountId = :accountId")); Q_FOREACH (int accountId, removeAccounts) { accountIds.append(accountId); query.bindValue(QStringLiteral(":accountId"), accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached contacts selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM friends " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!removeContacts.isEmpty()) { QVariantList friendIds; query = prepare(QStringLiteral( "SELECT pictureFile, coverFile " "FROM friends " "WHERE fbFriendId = :fbFriendId")); Q_FOREACH (const QString &friendId, removeContacts) { friendIds.append(friendId); query.bindValue(QStringLiteral(":fbFriendId"), friendId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached contacts selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM friends " "WHERE fbFriendId = :fbFriendId")); query.bindValue(QStringLiteral(":fbFriendId"), friendIds); executeBatchSocialCacheQuery(query); } if (!insertContacts.isEmpty()) { QVariantList friendIds, accountIds; QVariantList pictureUrls, coverUrls; QVariantList pictureFiles, coverFiles; Q_FOREACH (const FacebookContact::ConstPtr &contact, insertContacts) { friendIds.append(contact->fbFriendId()); accountIds.append(contact->accountId()); pictureUrls.append(contact->pictureUrl()); coverUrls.append(contact->coverUrl()); pictureFiles.append(contact->pictureFile()); coverFiles.append(contact->coverFile()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO friends (" " fbFriendId, accountId, pictureUrl, coverUrl, pictureFile, coverUrl) " "VALUES (" " :fbFriendId, :accountId, :pictureUrl, :coverUrl, :pictureFile, :coverUrl)")); query.bindValue(QStringLiteral(":fbFriendId"), friendIds); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":pictureUrl"), pictureUrls); query.bindValue(QStringLiteral(":coverUrl"), coverUrls); query.bindValue(QStringLiteral(":pictureFile"), pictureFiles); query.bindValue(QStringLiteral(":coverFile"), coverFiles); executeBatchSocialCacheQuery(query); } if (!updatePictures.isEmpty()) { QVariantList friendIds; QVariantList pictureFiles; for (QMap::const_iterator it = updatePictures.begin(); it != updatePictures.end(); ++it) { friendIds.append(it.key()); pictureFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE friends " "SET pictureFile = :pictureFile " "WHERE fbFriendId = :fbFriendId")); query.bindValue(QStringLiteral(":pictureFile"), pictureFiles); query.bindValue(QStringLiteral(":fbFriendId"), friendIds); executeBatchSocialCacheQuery(query); } if (!updateCovers.isEmpty()) { QVariantList friendIds; QVariantList coverFiles; for (QMap::const_iterator it = updateCovers.begin(); it != updateCovers.end(); ++it) { friendIds.append(it.key()); coverFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE friends " "SET coverFile = :coverFile " "WHERE fbFriendId = :fbFriendId")); query.bindValue(QStringLiteral(":coverFile"), coverFiles); query.bindValue(QStringLiteral(":fbFriendId"), friendIds); executeBatchSocialCacheQuery(query); } return success; } bool FacebookContactsDatabase::createTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare( "CREATE TABLE IF NOT EXISTS friends (" "fbFriendId TEXT," "accountId INTEGER," "pictureUrl TEXT," "coverUrl TEXT," "pictureFile TEXT," "coverFile TEXT," "CONSTRAINT id PRIMARY KEY (fbFriendId, accountId))"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create friends table:" << query.lastError().text(); return false; } return true; } bool FacebookContactsDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS friends"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to delete friends table:" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/facebookcontactsdatabase.h000066400000000000000000000057521462333522700231620ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKCONTACTSDATABASE_H #define FACEBOOKCONTACTSDATABASE_H #include "abstractsocialcachedatabase.h" #include class FacebookContactPrivate; class FacebookContact { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~FacebookContact(); static FacebookContact::Ptr create(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl, const QString &pictureFile, const QString &coverFile); QString fbFriendId() const; int accountId() const; QString pictureUrl() const; QString coverUrl() const; QString pictureFile() const; QString coverFile() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(FacebookContact) explicit FacebookContact(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl, const QString &pictureFile, const QString &coverFile); }; class FacebookContactsDatabasePrivate; class FacebookContactsDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit FacebookContactsDatabase(); ~FacebookContactsDatabase(); bool removeContacts(int accountId); bool removeContacts(const QStringList &fbFriendIds); FacebookContact::ConstPtr contact(const QString &fbFriendId, int accountId) const; QList contacts(int accountId) const; QStringList contactIds(int accountId) const; void addSyncedContact(const QString &fbFriendId, int accountId, const QString &pictureUrl, const QString &coverUrl); void updatePictureFile(const QString &fbFriendId, const QString &pictureFile); void updateCoverFile(const QString &fbFriendId, const QString &coverFile); void commit(); protected: bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(FacebookContactsDatabase) }; #endif // FACEBOOKCONTACTSDATABASE_H libsocialcache-0.2.1/src/lib/facebookimagesdatabase.cpp000066400000000000000000001374701462333522700231470ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebookimagesdatabase.h" #include "abstractsocialcachedatabase.h" #include "socialsyncinterface.h" #include #include #include #include static const char *DB_NAME = "facebook.db"; static const int VERSION = 3; struct FacebookUserPrivate { explicit FacebookUserPrivate(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName, int count = -1); QString fbUserId; QDateTime updatedTime; QString userName; int count; }; FacebookUserPrivate::FacebookUserPrivate(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName, int count) : fbUserId(fbUserId), updatedTime(updatedTime), userName(userName), count(count) { } FacebookUser::FacebookUser(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName, int count) : d_ptr(new FacebookUserPrivate(fbUserId, updatedTime, userName, count)) { } FacebookUser::~FacebookUser() { } FacebookUser::Ptr FacebookUser::create(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName, int count) { return FacebookUser::Ptr(new FacebookUser(fbUserId, updatedTime, userName, count)); } QString FacebookUser::fbUserId() const { Q_D(const FacebookUser); return d->fbUserId; } QDateTime FacebookUser::updatedTime() const { Q_D(const FacebookUser); return d->updatedTime; } QString FacebookUser::userName() const { Q_D(const FacebookUser); return d->userName; } int FacebookUser::count() const { Q_D(const FacebookUser); return d->count; } struct FacebookAlbumPrivate { explicit FacebookAlbumPrivate(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); QString fbAlbumId; QString fbUserId; QDateTime createdTime; QDateTime updatedTime; QString albumName; int imageCount; }; FacebookAlbumPrivate::FacebookAlbumPrivate(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) : fbAlbumId(fbAlbumId), fbUserId(fbUserId), createdTime(createdTime) , updatedTime(updatedTime), albumName(albumName), imageCount(imageCount) { } FacebookAlbum::FacebookAlbum(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) : d_ptr(new FacebookAlbumPrivate(fbAlbumId, fbUserId, createdTime, updatedTime, albumName, imageCount)) { } FacebookAlbum::~FacebookAlbum() { } FacebookAlbum::Ptr FacebookAlbum::create(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) { return FacebookAlbum::Ptr(new FacebookAlbum(fbAlbumId, fbUserId, createdTime, updatedTime, albumName, imageCount)); } QString FacebookAlbum::fbAlbumId() const { Q_D(const FacebookAlbum); return d->fbAlbumId; } QString FacebookAlbum::fbUserId() const { Q_D(const FacebookAlbum); return d->fbUserId; } QDateTime FacebookAlbum::createdTime() const { Q_D(const FacebookAlbum); return d->createdTime; } QDateTime FacebookAlbum::updatedTime() const { Q_D(const FacebookAlbum); return d->updatedTime; } QString FacebookAlbum::albumName() const { Q_D(const FacebookAlbum); return d->albumName; } int FacebookAlbum::imageCount() const { Q_D(const FacebookAlbum); return d->imageCount; } struct FacebookImagePrivate { explicit FacebookImagePrivate(const QString &fbImageId, const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account = -1); QString fbImageId; QString fbAlbumId; QString fbUserId; QDateTime createdTime; QDateTime updatedTime; QString imageName; int width; int height; QString thumbnailUrl; QString imageUrl; QString thumbnailFile; QString imageFile; int account; }; FacebookImagePrivate::FacebookImagePrivate(const QString &fbImageId, const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account) : fbImageId(fbImageId), fbAlbumId(fbAlbumId), fbUserId(fbUserId) , createdTime(createdTime), updatedTime(updatedTime), imageName(imageName) , width(width), height(height), thumbnailUrl(thumbnailUrl) , imageUrl(imageUrl), thumbnailFile(thumbnailFile), imageFile(imageFile), account(account) { } FacebookImage::FacebookImage(const QString &fbImageId, const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account) : d_ptr(new FacebookImagePrivate(fbImageId, fbAlbumId, fbUserId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, account)) { } FacebookImage::~FacebookImage() { } FacebookImage::Ptr FacebookImage::create(const QString &fbImageId, const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, int account) { return FacebookImage::Ptr(new FacebookImage(fbImageId, fbAlbumId, fbUserId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, account)); } QString FacebookImage::fbImageId() const { Q_D(const FacebookImage); return d->fbImageId; } QString FacebookImage::fbAlbumId() const { Q_D(const FacebookImage); return d->fbAlbumId; } QString FacebookImage::fbUserId() const { Q_D(const FacebookImage); return d->fbUserId; } QDateTime FacebookImage::createdTime() const { Q_D(const FacebookImage); return d->createdTime; } QDateTime FacebookImage::updatedTime() const { Q_D(const FacebookImage); return d->updatedTime; } QString FacebookImage::imageName() const { Q_D(const FacebookImage); return d->imageName; } int FacebookImage::width() const { Q_D(const FacebookImage); return d->width; } int FacebookImage::height() const { Q_D(const FacebookImage); return d->height; } QString FacebookImage::thumbnailUrl() const { Q_D(const FacebookImage); return d->thumbnailUrl; } QString FacebookImage::imageUrl() const { Q_D(const FacebookImage); return d->imageUrl; } QString FacebookImage::thumbnailFile() const { Q_D(const FacebookImage); return d->thumbnailFile; } QString FacebookImage::imageFile() const { Q_D(const FacebookImage); return d->imageFile; } int FacebookImage::account() const { Q_D(const FacebookImage); return d->account; } class FacebookImagesDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: enum QueryType { Users, Albums, UserImages, AlbumImages }; explicit FacebookImagesDatabasePrivate(FacebookImagesDatabase *q); ~FacebookImagesDatabasePrivate(); private: Q_DECLARE_PUBLIC(FacebookImagesDatabase) static void clearCachedImages(QSqlQuery &query); QList queryUsers() const; QList queryAlbums(const QString &fbUserId) const; QList queryImages(const QString &fbUserId, const QString &fbAlbumId); struct { QList purgeAccounts; QStringList removeUsers; QStringList removeAlbums; QStringList removeImages; QMap insertUsers; QMap insertAlbums; QMap insertImages; QMap syncAccounts; QMap updateThumbnailFiles; QMap updateImageFiles; } queue; struct { QueryType type; QString id; QList users; QList albums; QList images; } query; struct { QList users; QList albums; QList images; } result; }; FacebookImagesDatabasePrivate::FacebookImagesDatabasePrivate(FacebookImagesDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::Facebook), SocialSyncInterface::dataType(SocialSyncInterface::Images), QLatin1String(DB_NAME), VERSION) { } FacebookImagesDatabasePrivate::~FacebookImagesDatabasePrivate() { } void FacebookImagesDatabasePrivate::clearCachedImages(QSqlQuery &query) { while (query.next()) { QString thumb = query.value(0).toString(); QString image = query.value(1).toString(); if (!thumb.isEmpty()) { QFile thumbFile (thumb); if (thumbFile.exists()) { thumbFile.remove(); } } if (!image.isEmpty()) { QFile imageFile (image); if (imageFile.exists()) { imageFile.remove(); } } } } QList FacebookImagesDatabasePrivate::queryImages(const QString &fbUserId, const QString &fbAlbumId) { Q_Q(FacebookImagesDatabase); QList data; if (!fbUserId.isEmpty() && !fbAlbumId.isEmpty()) { qWarning() << Q_FUNC_INFO << "Cannot select images in both an album and for an user"; return data; } QString queryString = QLatin1String("SELECT images.fbImageId, images.fbAlbumId, "\ "images.fbUserId, images.createdTime, "\ "images.updatedTime, images.imageName, images.width, "\ "images.height, images.thumbnailUrl, images.imageUrl, "\ "images.thumbnailFile, images.imageFile, "\ "accounts.accountId "\ "FROM images "\ "INNER JOIN accounts "\ "ON accounts.fbUserId = images.fbUserId%1 "\ "ORDER BY images.updatedTime %2"); if (!fbUserId.isEmpty()) { queryString = queryString.arg(QLatin1String(" WHERE images.fbUserId = :fbUserId"), QLatin1String("DESC")); } else if (!fbAlbumId.isEmpty()){ queryString = queryString.arg(QLatin1String(" WHERE images.fbAlbumId = :fbAlbumId"), QString()); } else { queryString = queryString.arg(QString(), QLatin1String("DESC")); } QSqlQuery query = q->prepare(queryString); if (!fbUserId.isEmpty()) { query.bindValue(":fbUserId", fbUserId); } if (!fbAlbumId.isEmpty()) { query.bindValue(":fbAlbumId", fbAlbumId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all albums:" << query.lastError().text(); return data; } while (query.next()) { data.append(FacebookImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), #else QDateTime::fromTime_t(query.value(3).toUInt()), QDateTime::fromTime_t(query.value(4).toUInt()), #endif query.value(5).toString(), query.value(6).toInt(), query.value(7).toInt(), query.value(8).toString(), query.value(9).toString(), query.value(10).toString(), query.value(11).toString(), query.value(12).toInt())); } return data; } bool operator==(const FacebookUser::ConstPtr &user1, const FacebookUser::ConstPtr &user2) { return user1->fbUserId() == user2->fbUserId(); } bool operator==(const FacebookAlbum::ConstPtr &album1, const FacebookAlbum::ConstPtr &album2) { return album1->fbAlbumId() == album2->fbAlbumId() && album1->fbUserId() == album2->fbUserId(); } bool operator==(const FacebookImage::ConstPtr &image1, const FacebookImage::ConstPtr &image2) { return image1->fbImageId() == image2->fbImageId() && image1->fbAlbumId() == image2->fbAlbumId() && image1->fbUserId() == image2->fbUserId(); } // Note // // Insertion operations needs to use write(), while Delete // operations are automatically using transactions and // don't need write(). FacebookImagesDatabase::FacebookImagesDatabase() : AbstractSocialCacheDatabase(*(new FacebookImagesDatabasePrivate(this))) { } FacebookImagesDatabase::~FacebookImagesDatabase() { wait(); } bool FacebookImagesDatabase::syncAccount(int accountId, const QString &fbUserId) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.syncAccounts.insert(accountId, fbUserId); return true; } void FacebookImagesDatabase::purgeAccount(int accountId) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.purgeAccounts.append(accountId); } // Returns the user but do not return a count // if you want a count, better consider users FacebookUser::ConstPtr FacebookImagesDatabase::user(const QString &fbUserId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT fbUserId, updatedTime, userName " "FROM users WHERE fbUserId = :fbUserId")); query.bindValue(":fbUserId", fbUserId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from users table:" << query.lastError(); return FacebookUser::Ptr(); } // If we have the user, we have a result, otherwise we won't have the user if (!query.next()) { return FacebookUser::Ptr(); } FacebookUser::ConstPtr user = FacebookUser::create( query.value(0).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(1).toUInt()), #else QDateTime::fromTime_t(query.value(1).toUInt()), #endif query.value(2).toString()); query.finish(); return user; } void FacebookImagesDatabase::addUser(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName) { Q_D(FacebookImagesDatabase); FacebookUser::Ptr user = FacebookUser::create(fbUserId, updatedTime, userName); QMutexLocker locker(&d->mutex); d->queue.insertUsers.insert(fbUserId, user); } void FacebookImagesDatabase::removeUser(const QString &fbUserId) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeUsers.append(fbUserId); } QList FacebookImagesDatabasePrivate::queryUsers() const { QList data; QSqlQuery query = q_func()->prepare(QStringLiteral( "SELECT users.fbUserId, users.updatedTime, users.userName, " "SUM(albums.imageCount) as count " "FROM users " "LEFT JOIN albums ON albums.fbUserId = users.fbUserId " "GROUP BY users.fbUserId " "ORDER BY users.fbUserId")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all users:" << query.lastError().text(); return data; } while (query.next()) { data.append(FacebookUser::create(query.value(0).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(1).toUInt()), #else QDateTime::fromTime_t(query.value(1).toUInt()), #endif query.value(2).toString(), query.value(3).toInt())); } return data; } QStringList FacebookImagesDatabase::allAlbumIds(bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT fbAlbumId " "FROM albums " "ORDER BY updatedTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch all albums" << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } QMap FacebookImagesDatabase::accounts(bool *ok) const { if (ok) { *ok = false; } QMap result; QSqlQuery query = prepare(QStringLiteral( "SELECT accountId, fbUserId " "FROM accounts ")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch account mappings" << query.lastError().text(); return result; } while (query.next()) { result[query.value(0).toInt()] = query.value(1).toString(); } if (ok) { *ok = true; } return result; } FacebookAlbum::ConstPtr FacebookImagesDatabase::album(const QString &fbAlbumId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT fbAlbumId, fbUserId, createdTime, updatedTime, albumName, " "imageCount " "FROM albums WHERE fbAlbumId = :fbAlbumId")); query.bindValue(":fbAlbumId", fbAlbumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from albums table:" << query.lastError(); return FacebookAlbum::Ptr(); } // If we have the album, we have a result, otherwise we won't have the album if (!query.next()) { return FacebookAlbum::Ptr(); } FacebookAlbum::ConstPtr album = FacebookAlbum::create(query.value(0).toString(), query.value(1).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(2).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), #else QDateTime::fromTime_t(query.value(2).toUInt()), QDateTime::fromTime_t(query.value(3).toUInt()), #endif query.value(4).toString(), query.value(5).toInt()); query.finish(); return album; } void FacebookImagesDatabase::addAlbum(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) { Q_D(FacebookImagesDatabase); FacebookAlbum::Ptr album = FacebookAlbum::create(fbAlbumId, fbUserId, createdTime, updatedTime, albumName, imageCount); QMutexLocker locker(&d->mutex); d->queue.insertAlbums.insert(fbAlbumId, album); } void FacebookImagesDatabase::removeAlbum(const QString &fbAlbumId) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums.append(fbAlbumId); } void FacebookImagesDatabase::removeAlbums(const QStringList &fbAlbumIds) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums += fbAlbumIds; } QList FacebookImagesDatabasePrivate::queryAlbums(const QString &fbUserId) const { QList data; QString queryString = QLatin1String("SELECT fbAlbumId, fbUserId, createdTime, updatedTime, "\ "albumName, imageCount "\ "FROM albums%1 ORDER BY updatedTime DESC"); if (!fbUserId.isEmpty()) { queryString = queryString.arg(QLatin1String(" WHERE fbUserId = :fbUserId")); } else { queryString = queryString.arg(QString()); } QSqlQuery query = q_func()->prepare(queryString); if (!fbUserId.isEmpty()) { query.bindValue(":fbUserId", fbUserId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all albums:" << query.lastError().text(); return data; } while (query.next()) { data.append(FacebookAlbum::create(query.value(0).toString(), query.value(1).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(2).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), #else QDateTime::fromTime_t(query.value(2).toUInt()), QDateTime::fromTime_t(query.value(3).toUInt()), #endif query.value(4).toString(), query.value(5).toInt())); } return data; } QStringList FacebookImagesDatabase::allImageIds(bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT fbImageId " "FROM images " "ORDER BY updatedTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch all images" << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } QStringList FacebookImagesDatabase::imageIds(const QString &fbAlbumId, bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT fbImageId " "FROM images WHERE fbAlbumId = :fbAlbumId")); query.bindValue(":fbAlbumId", fbAlbumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch images for album" << fbAlbumId << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } FacebookImage::ConstPtr FacebookImagesDatabase::image(const QString &fbImageId) const { QSqlQuery query = prepare( "SELECT fbImageId, fbAlbumId, fbUserId, createdTime, updatedTime, imageName, " "width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile " "FROM images WHERE fbImageId = :fbImageId"); query.bindValue(":fbImageId", fbImageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from albums table:" << query.lastError(); return FacebookImage::Ptr(); } // If we have the image, we have a result, otherwise we won't have the image if (!query.next()) { return FacebookImage::Ptr(); } return FacebookImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), #else QDateTime::fromTime_t(query.value(3).toUInt()), QDateTime::fromTime_t(query.value(4).toUInt()), #endif query.value(5).toString(), query.value(6).toInt(), query.value(7).toInt(), query.value(8).toString(), query.value(9).toString(), query.value(10).toString(), query.value(11).toString()); } void FacebookImagesDatabase::removeImage(const QString &fbImageId) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages.append(fbImageId); } void FacebookImagesDatabase::removeImages(const QStringList &fbImageIds) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages += fbImageIds; } void FacebookImagesDatabase::addImage(const QString &fbImageId, const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl) { Q_D(FacebookImagesDatabase); FacebookImage::Ptr image = FacebookImage::create(fbImageId, fbAlbumId, fbUserId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, QString(), QString()); QMutexLocker locker(&d->mutex); d->queue.insertImages.insert(fbImageId, image); } void FacebookImagesDatabase::updateImageThumbnail(const QString &fbImageId, const QString &thumbnailFile) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateThumbnailFiles.insert(fbImageId, thumbnailFile); } void FacebookImagesDatabase::updateImageFile(const QString &fbImageId, const QString &imageFile) { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateImageFiles.insert(fbImageId, imageFile); } void FacebookImagesDatabase::commit() { executeWrite(); } QList FacebookImagesDatabase::users() const { return d_func()->result.users; } QList FacebookImagesDatabase::images() const { return d_func()->result.images; } QList FacebookImagesDatabase::albums() const { return d_func()->result.albums; } void FacebookImagesDatabase::queryUsers() { Q_D(FacebookImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = FacebookImagesDatabasePrivate::Users; } executeRead(); } void FacebookImagesDatabase::queryAlbums(const QString &userId) { Q_D(FacebookImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = FacebookImagesDatabasePrivate::Albums; d->query.id = userId; } executeRead(); } void FacebookImagesDatabase::queryUserImages(const QString &userId) { Q_D(FacebookImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = FacebookImagesDatabasePrivate::UserImages; d->query.id = userId; } executeRead(); } void FacebookImagesDatabase::queryAlbumImages(const QString &albumId) { Q_D(FacebookImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = FacebookImagesDatabasePrivate::AlbumImages; d->query.id = albumId; } executeRead(); } bool FacebookImagesDatabase::read() { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); switch (d->query.type) { case FacebookImagesDatabasePrivate::Users: { locker.unlock(); QList users = d->queryUsers(); locker.relock(); d->query.users = users; return true; } case FacebookImagesDatabasePrivate::Albums: { const QString userId = d->query.id; locker.unlock(); QList albums = d->queryAlbums(userId); locker.relock(); d->query.albums = albums; return true; } case FacebookImagesDatabasePrivate::UserImages: case FacebookImagesDatabasePrivate::AlbumImages: { const QString userId = d->query.type == FacebookImagesDatabasePrivate::UserImages ? d->query.id : QString(); const QString albumId = d->query.type == FacebookImagesDatabasePrivate::AlbumImages ? d->query.id : QString(); locker.unlock(); QList images = d->queryImages(userId, albumId); locker.relock(); d->query.images = images; return true; } default: return false; } } void FacebookImagesDatabase::readFinished() { Q_D(FacebookImagesDatabase); { QMutexLocker locker(&d->mutex); d->result.users = d->query.users; d->result.albums = d->query.albums; d->result.images = d->query.images; d->query.users.clear(); d->query.albums.clear(); d->query.images.clear(); } emit queryFinished(); } bool FacebookImagesDatabase::write() { Q_D(FacebookImagesDatabase); QMutexLocker locker(&d->mutex); qWarning() << "Queued users being saved:" << d->queue.insertUsers.count(); qWarning() << "Queued albums being saved:" << d->queue.insertAlbums.count(); qWarning() << "Queued images being saved:" << d->queue.insertImages.count(); qWarning() << "Queued thumbnail files being updated:" << d->queue.updateThumbnailFiles.count(); qWarning() << "Queued image files being updated:" << d->queue.updateImageFiles.count(); const QList purgeAccounts = d->queue.purgeAccounts; QStringList removeUsers = d->queue.removeUsers; const QStringList removeAlbums = d->queue.removeAlbums; const QStringList removeImages = d->queue.removeImages; const QMap insertUsers = d->queue.insertUsers; const QMap insertAlbums = d->queue.insertAlbums; const QMap insertImages = d->queue.insertImages; const QMap syncAccounts = d->queue.syncAccounts; const QMap updateThumbnailFiles = d->queue.updateThumbnailFiles; const QMap updateImageFiles = d->queue.updateImageFiles; d->queue.purgeAccounts.clear(); d->queue.removeUsers.clear(); d->queue.removeAlbums.clear(); d->queue.removeImages.clear(); d->queue.insertUsers.clear(); d->queue.insertAlbums.clear(); d->queue.insertImages.clear(); d->queue.syncAccounts.clear(); d->queue.updateThumbnailFiles.clear(); d->queue.updateImageFiles.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!purgeAccounts.isEmpty()) { query = prepare(QStringLiteral( "SELECT fbUserId " "FROM accounts " "WHERE accountId = :accountId")); Q_FOREACH (int accountId, purgeAccounts) { query.bindValue(QStringLiteral(":accountId"), accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec account id selection query:" << query.lastError().text(); success = false; } else while (query.next()) { removeUsers.append(query.value(0).toString()); } } } if (!removeUsers.isEmpty()) { QVariantList userIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE fbUserId = :fbUserId")); Q_FOREACH (const QString &userId, removeUsers) { userIds.append(userId); query.bindValue(QStringLiteral(":fbUserId"), userId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM accounts " "WHERE fbUserId = :fbUserId")); query.bindValue(QStringLiteral(":fbUserId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM users " "WHERE fbUserId = :fbUserId")); query.bindValue(QStringLiteral(":fbUserId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE fbUserId = :fbUserId")); query.bindValue(QStringLiteral(":fbUserId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE fbUserId = :fbUserId")); query.bindValue(QStringLiteral(":fbUserId"), userIds); executeBatchSocialCacheQuery(query); } if (!removeAlbums.isEmpty()) { QVariantList albumIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE fbAlbumId = :fbAlbumId")); Q_FOREACH (const QString &albumId, removeAlbums) { albumIds.append(albumId); query.bindValue(QStringLiteral(":fbAlbumId"), albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE fbAlbumId = :fbAlbumId")); query.bindValue(QStringLiteral(":fbAlbumId"), albumIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE fbAlbumId = :fbAlbumId")); query.bindValue(QStringLiteral(":fbAlbumId"), albumIds); executeBatchSocialCacheQuery(query); } if (!removeImages.isEmpty()) { QVariantList imageIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE fbImageId = :fbImageId")); Q_FOREACH (const QString &imageId, removeImages) { imageIds.append(imageId); query.bindValue(QStringLiteral(":fbImageId"), imageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM images " "WHERE fbImageId = :fbImageId")); query.bindValue(QStringLiteral(":fbImageId"), imageIds); executeBatchSocialCacheQuery(query); } if (!insertUsers.isEmpty()) { QVariantList userIds; QVariantList updatedTimes; QVariantList usernames; Q_FOREACH (const FacebookUser::ConstPtr &user, insertUsers) { userIds.append(user->fbUserId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) updatedTimes.append(user->updatedTime().toSecsSinceEpoch()); #else updatedTimes.append(user->updatedTime().toTime_t()); #endif usernames.append(user->userName()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO users (" " fbUserId, updatedTime, userName) " "VALUES (" " :fbUserId, :updatedTime, :userName);")); query.bindValue(QStringLiteral(":fbUserId"), userIds); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":userName"), usernames); executeBatchSocialCacheQuery(query); } if (!insertAlbums.isEmpty()) { QVariantList albumIds, userIds; QVariantList createdTimes, updatedTimes; QVariantList albumNames; QVariantList imageCounts; Q_FOREACH (const FacebookAlbum::ConstPtr &album, insertAlbums) { albumIds.append(album->fbAlbumId()); userIds.append(album->fbUserId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(album->createdTime().toSecsSinceEpoch()); updatedTimes.append(album->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(album->createdTime().toTime_t()); updatedTimes.append(album->updatedTime().toTime_t()); #endif albumNames.append(album->albumName()); imageCounts.append(album->imageCount()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO albums(" " fbAlbumId, fbUserId, createdTime, updatedTime, albumName, imageCount) " "VALUES (" " :fbAlbumId, :fbUserId, :createdTime, :updatedTime, :albumName, :imageCount)")); query.bindValue(QStringLiteral(":fbAlbumId"), albumIds); query.bindValue(QStringLiteral(":fbUserId"), userIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":albumName"), albumNames); query.bindValue(QStringLiteral(":imageCount"), imageCounts); executeBatchSocialCacheQuery(query); } if (!insertImages.isEmpty()) { QVariantList imageIds, albumIds, userIds; QVariantList createdTimes, updatedTimes; QVariantList imageNames; QVariantList widths, heights; QVariantList thumbnailUrls, imageUrls; QVariantList thumbnailFiles, imageFiles; Q_FOREACH (const FacebookImage::ConstPtr &image, insertImages) { imageIds.append(image->fbImageId()); albumIds.append(image->fbAlbumId()); userIds.append(image->fbUserId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(image->createdTime().toSecsSinceEpoch()); updatedTimes.append(image->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(image->createdTime().toTime_t()); updatedTimes.append(image->updatedTime().toTime_t()); #endif imageNames.append(image->imageName()); widths.append(image->width()); heights.append(image->height()); thumbnailUrls.append(image->thumbnailUrl()); imageUrls.append(image->imageUrl()); thumbnailFiles.append(image->thumbnailFile()); imageFiles.append(image->imageFile()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO images (" " fbImageId, fbAlbumId, fbUserId, createdTime, updatedTime, imageName," " width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile) " "VALUES (" " :fbImageId, :fbAlbumId, :fbUserId, :createdTime, :updatedTime, :imageName," " :width, :height, :thumbnailUrl, :imageUrl, :thumbnailFile, :imageFile)")); query.bindValue(QStringLiteral(":fbImageId"), imageIds); query.bindValue(QStringLiteral(":fbAlbumId"), albumIds); query.bindValue(QStringLiteral(":fbUserId"), userIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":imageName"), imageNames); query.bindValue(QStringLiteral(":width"), widths); query.bindValue(QStringLiteral(":height"), heights); query.bindValue(QStringLiteral(":thumbnailUrl"), thumbnailUrls); query.bindValue(QStringLiteral(":imageUrl"), imageUrls); query.bindValue(QStringLiteral(":thumbnailFile"), thumbnailFiles); query.bindValue(QStringLiteral(":imageFile"), imageFiles); executeBatchSocialCacheQuery(query); } if (!syncAccounts.isEmpty()) { QVariantList accountIds; QVariantList userIds; for (QMap::const_iterator it = syncAccounts.begin(); it != syncAccounts.end(); ++it) { accountIds.append(it.key()); userIds.append(it.value()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO accounts (" " accountId, fbUserId) " "VALUES(" " :accountId, :fbUserId)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":fbUserId"), userIds); executeBatchSocialCacheQuery(query); } if (!updateThumbnailFiles.isEmpty()) { QVariantList imageIds; QVariantList thumbnailFiles; for (QMap::const_iterator it = updateThumbnailFiles.begin(); it != updateThumbnailFiles.end(); ++it) { imageIds.append(it.key()); thumbnailFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE images " "SET thumbnailFile = :thumbnailFile " "WHERE fbImageId = :fbImageId")); query.bindValue(QStringLiteral(":thumbnailFile"), thumbnailFiles); query.bindValue(QStringLiteral(":fbImageId"), imageIds); executeBatchSocialCacheQuery(query); } if (!updateImageFiles.isEmpty()) { QVariantList imageIds; QVariantList imageFiles; for (QMap::const_iterator it = updateImageFiles.begin(); it != updateImageFiles.end(); ++it) { imageIds.append(it.key()); imageFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE images " "SET imageFile = :imageFile " "WHERE fbImageId = :fbImageId")); query.bindValue(QStringLiteral(":imageFile"), imageFiles); query.bindValue(QStringLiteral(":fbImageId"), imageIds); executeBatchSocialCacheQuery(query); } return success; } bool FacebookImagesDatabase::createTables(QSqlDatabase database) const { // create the facebook image db tables // images = fbImageId, fbAlbumId, fbUserId, createdTime, updatedTime, imageName, width, height, // thumbnailUrl, imageUrl, thumbnailFile, imageFile // albums = fbAlbumId, fbUserId, createdTime, updatedTime, albumName, imageCount // users = fbUserId, updatedTime, userName, thumbnailUrl, imageUrl, thumbnailFile, imageFile QSqlQuery query(database); query.prepare( "CREATE TABLE IF NOT EXISTS images (" "fbImageId TEXT UNIQUE PRIMARY KEY," "fbAlbumId TEXT," "fbUserId TEXT," "createdTime INTEGER," "updatedTime INTEGER," "imageName TEXT," "width INTEGER," "height INTEGER," "thumbnailUrl TEXT," "imageUrl TEXT," "thumbnailFile TEXT," "imageFile TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create images table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS albums (" "fbAlbumId TEXT UNIQUE PRIMARY KEY," "fbUserId TEXT," "createdTime INTEGER," "updatedTime INTEGER," "albumName TEXT," "imageCount INTEGER)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create albums table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS users (" "fbUserId TEXT UNIQUE PRIMARY KEY," "updatedTime INTEGER," "userName TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create users table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS accounts (" "accountId INTEGER UNIQUE PRIMARY KEY," "fbUserId TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create accounts table:" << query.lastError().text(); return false; } return true; } bool FacebookImagesDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS images"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete images table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS albums"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete albums table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS users"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete users table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS accounts"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete accounts table:" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/facebookimagesdatabase.h000066400000000000000000000163021462333522700226020ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKIMAGESDATABASE_H #define FACEBOOKIMAGESDATABASE_H #include "abstractsocialcachedatabase_p.h" #include #include #include class FacebookUserPrivate; class FacebookUser { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~FacebookUser(); static FacebookUser::Ptr create(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName, int count = -1); QString fbUserId() const; QDateTime updatedTime() const; QString userName() const; int count() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(FacebookUser) explicit FacebookUser(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName, int count = -1); }; class FacebookAlbumPrivate; class FacebookAlbum { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~FacebookAlbum(); static FacebookAlbum::Ptr create(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); QString fbAlbumId() const; QString fbUserId() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString albumName() const; int imageCount() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(FacebookAlbum) explicit FacebookAlbum(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); }; class FacebookImagePrivate; class FacebookImage { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~FacebookImage(); static FacebookImage::Ptr create(const QString & fbImageId, const QString & fbAlbumId, const QString & fbUserId, const QDateTime & createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & thumbnailFile, const QString & imageFile, int account = -1); QString fbImageId() const; QString fbAlbumId() const; QString fbUserId() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString imageName() const; int width() const; int height() const; QString thumbnailUrl() const; QString imageUrl() const; QString thumbnailFile() const; QString imageFile() const; int account() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(FacebookImage) explicit FacebookImage(const QString & fbImageId, const QString & fbAlbumId, const QString & fbUserId, const QDateTime & createdTime, const QDateTime & updatedTime, const QString & imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & thumbnailFile, const QString & imageFile, int account = -1); }; bool operator==(const FacebookUser::ConstPtr &user1, const FacebookUser::ConstPtr &user2); bool operator==(const FacebookAlbum::ConstPtr &album1, const FacebookAlbum::ConstPtr &album2); bool operator==(const FacebookImage::ConstPtr &image1, const FacebookImage::ConstPtr &image2); class FacebookImagesDatabasePrivate; class FacebookImagesDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit FacebookImagesDatabase(); ~FacebookImagesDatabase(); // Account manipulation bool syncAccount(int accountId, const QString &fbUserId); void purgeAccount(int accountId); QMap accounts(bool *ok = 0) const; // User cache manipulation FacebookUser::ConstPtr user(const QString &fbUserId) const; void addUser(const QString &fbUserId, const QDateTime &updatedTime, const QString &userName); void removeUser(const QString &fbUserId); // Album cache manipulation QStringList allAlbumIds(bool *ok = 0) const; FacebookAlbum::ConstPtr album(const QString &fbAlbumId) const; void addAlbum(const QString &fbAlbumId, const QString &fbUserId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); void removeAlbum(const QString &fbAlbumId); void removeAlbums(const QStringList &fbAlbumIds); // Images cache manipulation QStringList allImageIds(bool *ok = 0) const; QStringList imageIds(const QString &fbAlbumId, bool *ok = 0) const; FacebookImage::ConstPtr image(const QString &fbImageId) const; void addImage(const QString & fbImageId, const QString & fbAlbumId, const QString & fbUserId, const QDateTime & createdTime, const QDateTime & updatedTime, const QString & imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl); void updateImageThumbnail(const QString &fbImageId, const QString &thumbnailFile); void updateImageFile(const QString &fbImageId, const QString &imageFile); void removeImage(const QString &fbImageId); void removeImages(const QStringList &fbImageIds); void commit(); QList users() const; QList images() const; QList albums() const; void queryUsers(); void queryAlbums(const QString &userId = QString()); void queryUserImages(const QString &userId = QString()); void queryAlbumImages(const QString &albumId); Q_SIGNALS: void queryFinished(); protected: bool read(); void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(FacebookImagesDatabase) }; #endif // FACEBOOKIMAGESDATABASE_H libsocialcache-0.2.1/src/lib/facebooknotificationsdatabase.cpp000066400000000000000000000500411462333522700245370ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebooknotificationsdatabase.h" #include "abstractsocialcachedatabase_p.h" #include "socialsyncinterface.h" #include #include static const char *DB_NAME = "facebookNotifications.db"; static const int VERSION = 1; struct FacebookNotificationPrivate { explicit FacebookNotificationPrivate(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId); QString m_facebookId; QString m_from; QString m_to; QDateTime m_createdTime; QDateTime m_updatedTime; QString m_title; QString m_link; QString m_application; QString m_object; bool m_unread; int m_accountId; QString m_clientId; }; FacebookNotificationPrivate::FacebookNotificationPrivate(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId) : m_facebookId(facebookId) , m_from(from) , m_to(to) , m_createdTime(createdTime) , m_updatedTime(updatedTime) , m_title(title) , m_link(link) , m_application(application) , m_object(object) , m_unread(unread) , m_accountId(accountId) , m_clientId(clientId) { } FacebookNotification::FacebookNotification(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId) : d_ptr(new FacebookNotificationPrivate(facebookId, from, to, createdTime, updatedTime, title, link, application, object, unread, accountId, clientId)) { } FacebookNotification::Ptr FacebookNotification::create(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId) { return FacebookNotification::Ptr(new FacebookNotification(facebookId, from, to, createdTime, updatedTime, title, link, application, object, unread, accountId, clientId)); } FacebookNotification::~FacebookNotification() { } QString FacebookNotification::facebookId() const { Q_D(const FacebookNotification); return d->m_facebookId; } QString FacebookNotification::from() const { Q_D(const FacebookNotification); return d->m_from; } QString FacebookNotification::to() const { Q_D(const FacebookNotification); return d->m_to; } QDateTime FacebookNotification::createdTime() const { Q_D(const FacebookNotification); return d->m_createdTime; } QDateTime FacebookNotification::updatedTime() const { Q_D(const FacebookNotification); return d->m_updatedTime; } QString FacebookNotification::title() const { Q_D(const FacebookNotification); return d->m_title; } QString FacebookNotification::link() const { Q_D(const FacebookNotification); return d->m_link; } QString FacebookNotification::application() const { Q_D(const FacebookNotification); return d->m_application; } QString FacebookNotification::object() const { Q_D(const FacebookNotification); return d->m_object; } bool FacebookNotification::unread() const { Q_D(const FacebookNotification); return d->m_unread; } int FacebookNotification::accountId() const { Q_D(const FacebookNotification); return d->m_accountId; } QString FacebookNotification::clientId() const { Q_D(const FacebookNotification); return d->m_clientId; } class FacebookNotificationsDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: explicit FacebookNotificationsDatabasePrivate(FacebookNotificationsDatabase *q); QMap > insertNotifications; QList removeNotificationsFromAccounts; QVariantList accountIdFilter; QStringList removeNotifications; int purgeTimeLimit; struct { QMap > insertNotifications; QList removeNotificationsFromAccounts; QStringList removeNotifications; bool removeAll; } queue; }; FacebookNotificationsDatabasePrivate::FacebookNotificationsDatabasePrivate(FacebookNotificationsDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::Facebook), SocialSyncInterface::dataType(SocialSyncInterface::Notifications), QLatin1String(DB_NAME), VERSION) , purgeTimeLimit(0) { queue.removeAll = false; } FacebookNotificationsDatabase::FacebookNotificationsDatabase() : AbstractSocialCacheDatabase(*(new FacebookNotificationsDatabasePrivate(this))) { } FacebookNotificationsDatabase::~FacebookNotificationsDatabase() { wait(); } QVariantList FacebookNotificationsDatabase::accountIdFilter() const { Q_D(const FacebookNotificationsDatabase); return d->accountIdFilter; } void FacebookNotificationsDatabase::setAccountIdFilter(const QVariantList &accountIds) { Q_D(FacebookNotificationsDatabase); if (d->accountIdFilter != accountIds) { d->accountIdFilter = accountIds; emit accountIdFilterChanged(); } } void FacebookNotificationsDatabase::addFacebookNotification(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId) { Q_D(FacebookNotificationsDatabase); d->insertNotifications[accountId].append(FacebookNotification::create(facebookId, from, to, createdTime, updatedTime, title, link, application, object, unread, accountId, clientId)); } void FacebookNotificationsDatabase::removeAllNotifications() { Q_D(FacebookNotificationsDatabase); { QMutexLocker locker(&d->mutex); d->queue.insertNotifications.clear(); d->queue.removeNotificationsFromAccounts.clear(); d->queue.removeNotifications.clear(); d->queue.removeAll = true; } executeWrite(); } void FacebookNotificationsDatabase::removeNotifications(int accountId) { Q_D(FacebookNotificationsDatabase); QMutexLocker locker(&d->mutex); if (!d->queue.removeNotificationsFromAccounts.contains(accountId)) { d->queue.removeNotificationsFromAccounts.append(accountId); } d->queue.insertNotifications.remove(accountId); } void FacebookNotificationsDatabase::removeNotification(const QString ¬ificationId) { Q_D(FacebookNotificationsDatabase); QMutexLocker locker(&d->mutex); removeNotificationFromQueues(notificationId); } void FacebookNotificationsDatabase::removeNotifications(QStringList notificationIds) { Q_D(FacebookNotificationsDatabase); QMutexLocker locker(&d->mutex); Q_FOREACH(const QString notifId, notificationIds) { removeNotificationFromQueues(notifId); } } void FacebookNotificationsDatabase::purgeOldNotifications(int limitInDays) { Q_D(FacebookNotificationsDatabase); d->purgeTimeLimit = limitInDays; } void FacebookNotificationsDatabase::removeNotificationFromQueues(const QString ¬ificationId) { Q_D(FacebookNotificationsDatabase); if (!d->queue.removeNotifications.contains(notificationId)) { d->queue.removeNotifications.append(notificationId); Q_FOREACH(int accountId, d->insertNotifications.keys()) { for (int i = 0; i < d->insertNotifications[accountId].count(); ++i) { if (d->insertNotifications[accountId][i]->facebookId() == notificationId) { d->insertNotifications[accountId].removeAt(i); i--; } } } } } void FacebookNotificationsDatabase::sync() { Q_D(FacebookNotificationsDatabase); { QMutexLocker locker(&d->mutex); Q_FOREACH(int accountId, d->insertNotifications.keys()) { d->queue.insertNotifications.insert(accountId, d->insertNotifications.take(accountId)); } while (d->removeNotificationsFromAccounts.count()) { d->queue.removeNotificationsFromAccounts.append(d->removeNotificationsFromAccounts.takeFirst()); } while (d->removeNotifications.count()) { d->queue.removeNotifications.append(d->removeNotifications.takeFirst()); } } executeWrite(); } QList FacebookNotificationsDatabase::notifications() { Q_D(FacebookNotificationsDatabase); QList data; QString queryString = QStringLiteral( "SELECT facebookId, accountId, fromStr, toStr, createdTime, updatedTime, title, link, application," \ "objectStr, unread, clientId FROM notifications"); if (!d->accountIdFilter.isEmpty()) { QStringList accountIds; for (int i=0; iaccountIdFilter.count(); i++) { if (d->accountIdFilter[i].type() == QVariant::Int) { accountIds << d->accountIdFilter[i].toString(); } } if (accountIds.count()) { queryString += " WHERE accountId IN (" + accountIds.join(',') + ')'; } } queryString += QStringLiteral(" ORDER BY updatedTime DESC"); QSqlQuery query = prepare(queryString); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query events" << query.lastError().text(); return data; } while (query.next()) { data.append(FacebookNotification::create(query.value(0).toString(), // facebookId query.value(2).toString(), // from query.value(3).toString(), // to #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(4).toInt()), // createdTime QDateTime::fromSecsSinceEpoch(query.value(5).toInt()), // updatedTime #else QDateTime::fromTime_t(query.value(4).toInt()), // createdTime QDateTime::fromTime_t(query.value(5).toInt()), // updatedTime #endif query.value(6).toString(), // title query.value(7).toString(), // link query.value(8).toString(), // application query.value(9).toString(), // object query.value(10).toBool(), // unread query.value(1).toInt(), // accountId query.value(11).toString())); // clientId } return data; } void FacebookNotificationsDatabase::readFinished() { emit notificationsChanged(); } bool FacebookNotificationsDatabase::write() { Q_D(FacebookNotificationsDatabase); QMutexLocker locker(&d->mutex); const QMap > insertNotifications = d->queue.insertNotifications; const QList removeNotificationsFromAccounts = d->queue.removeNotificationsFromAccounts; QStringList removeNotifications = d->queue.removeNotifications; bool removeAll = d->queue.removeAll; d->queue.insertNotifications.clear(); d->queue.removeNotificationsFromAccounts.clear(); d->queue.removeNotifications.clear(); d->queue.removeAll = false; locker.unlock(); bool success = true; QSqlQuery query; if (removeAll) { QVariantList accountIds; accountIds.append(-1); query = prepare(QStringLiteral("DELETE FROM notifications WHERE accountId > :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!removeNotificationsFromAccounts.isEmpty()) { QVariantList accountIds; Q_FOREACH (const int accountId, removeNotificationsFromAccounts) { accountIds.append(accountId); } query = prepare(QStringLiteral("DELETE FROM notifications WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!removeNotifications.isEmpty()) { QVariantList notifIds; Q_FOREACH (const QString notifId, removeNotifications) { notifIds.append(notifId); } query = prepare(QStringLiteral("DELETE FROM notifications WHERE facebookId = :facebookId")); query.bindValue(QStringLiteral(":facebookId"), notifIds); executeBatchSocialCacheQuery(query); } if (!insertNotifications.isEmpty()) { QVariantList facebookIds; QVariantList accountIds; QVariantList fromStrings; QVariantList toStrings; QVariantList createdTimes; QVariantList updatedTimes; QVariantList titles; QVariantList links; QVariantList applications; QVariantList unreads; QVariantList objects; QVariantList clientIds; Q_FOREACH (const QList ¬ifications, insertNotifications) { Q_FOREACH (const FacebookNotification::ConstPtr ¬ification, notifications) { facebookIds.append(notification->facebookId()); accountIds.append(notification->accountId()); fromStrings.append(notification->from()); toStrings.append(notification->to()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(notification->createdTime().toSecsSinceEpoch()); updatedTimes.append(notification->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(notification->createdTime().toTime_t()); updatedTimes.append(notification->updatedTime().toTime_t()); #endif titles.append(notification->title()); links.append(notification->link()); applications.append(notification->application()); objects.append(notification->object()); unreads.append((notification->unread())); clientIds.append(notification->clientId()); } } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO notifications (" " facebookId, accountId, fromStr, toStr, createdTime, updatedTime, title, link, application, objectStr, unread, clientId) " "VALUES(" " :facebookId, :accountId, :fromStr, :toStr, :createdTime, :updatedTime, :title, :link, :application, :objectStr, :unread, :clientId)")); query.bindValue(QStringLiteral(":facebookId"), facebookIds); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":fromStr"), fromStrings); query.bindValue(QStringLiteral(":toStr"), toStrings); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":title"), titles); query.bindValue(QStringLiteral(":link"), links); query.bindValue(QStringLiteral(":application"), applications); query.bindValue(QStringLiteral(":objectStr"), objects); query.bindValue(QStringLiteral(":unread"), unreads); query.bindValue(QStringLiteral(":clientId"), clientIds); executeBatchSocialCacheQuery(query); } if (d->purgeTimeLimit > 0) { QVariantList limits; // purge notifications older than expirationTime in days #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) const quint32 limit = QDateTime::currentDateTime().toSecsSinceEpoch() - d->purgeTimeLimit * 24 * 60 * 60; #else const quint32 limit = QDateTime::currentDateTime().toTime_t() - d->purgeTimeLimit * 24 * 60 * 60; #endif limits.append(limit); query = prepare(QStringLiteral("DELETE FROM notifications WHERE updatedTime < :timeLimit")); query.bindValue(QStringLiteral(":timeLimit"), limits); executeBatchSocialCacheQuery(query); d->purgeTimeLimit = 0; } return success; } bool FacebookNotificationsDatabase::createTables(QSqlDatabase database) const { QSqlQuery query(database); // create the Facebook notification db tables // notifications = facebookId, accountId, from, to, createdTime, updatedTime, title, link, application, objectStr, unread, clientId query.prepare("CREATE TABLE IF NOT EXISTS notifications (" "facebookId TEXT UNIQUE PRIMARY KEY," "accountId INTEGER," "fromStr TEXT," "toStr TEXT," "createdTime INTEGER," "updatedTime INTEGER," "title TEXT," "link TEXT," "application TEXT," "objectStr TEXT," "unread INTEGER," "clientId TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create notifications table: " << query.lastError().text(); return false; } return true; } bool FacebookNotificationsDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); if (!query.exec(QStringLiteral("DROP TABLE IF EXISTS notifications"))) { qWarning() << Q_FUNC_INFO << "Unable to delete notifications table: " << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/facebooknotificationsdatabase.h000066400000000000000000000103521462333522700242050ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKNOTIFICATIONSDATABASE_H #define FACEBOOKNOTIFICATIONSDATABASE_H #include "abstractsocialcachedatabase.h" #include #include #include class FacebookNotificationPrivate; class FacebookNotification { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~FacebookNotification(); static FacebookNotification::Ptr create(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId); QString facebookId() const; QString from() const; QString to() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString title() const; QString link() const; QString application() const; QString object() const; bool unread() const; int accountId() const; QString clientId() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(FacebookNotification) explicit FacebookNotification(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId); }; class FacebookNotificationsDatabasePrivate; class FacebookNotificationsDatabase: public AbstractSocialCacheDatabase { Q_OBJECT Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged) public: explicit FacebookNotificationsDatabase(); ~FacebookNotificationsDatabase(); QVariantList accountIdFilter() const; void setAccountIdFilter(const QVariantList &accountIds); void addFacebookNotification(const QString &facebookId, const QString &from, const QString &to, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &title, const QString &link, const QString &application, const QString &object, bool unread, int accountId, const QString &clientId); void removeAllNotifications(); void removeNotifications(int accountId); void removeNotification(const QString ¬ificationId); void removeNotifications(QStringList notificationIds); void purgeOldNotifications(int limitInDays); void sync(); QList notifications(); signals: void notificationsChanged(); void accountIdFilterChanged(); protected: void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: void removeNotificationFromQueues(const QString ¬ificationId); private: Q_DECLARE_PRIVATE(FacebookNotificationsDatabase) }; #endif // FACEBOOKNOTIFICATIONSDATABASE_H libsocialcache-0.2.1/src/lib/facebookpostsdatabase.cpp000066400000000000000000000104411462333522700230360ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebookpostsdatabase.h" #include "socialsyncinterface.h" #include static const char *DB_NAME = "facebook.db"; static const char *ATTACHMENT_NAME_KEY = "post_attachment_name"; static const char *ATTACHMENT_CAPTION_KEY = "post_attachment_caption"; static const char *ATTACHMENT_DESCRIPTION_KEY = "post_attachment_description"; static const char *ATTACHMENT_URL_KEY = "post_attachment_url"; static const char *CLIENT_ID_KEY = "client_id"; static const char *ALLOW_LIKE_KEY = "allow_like"; static const char *ALLOW_COMMENT_KEY = "allow_comment"; FacebookPostsDatabase::FacebookPostsDatabase() : AbstractSocialPostCacheDatabase( SocialSyncInterface::socialNetwork(SocialSyncInterface::Facebook), QLatin1String(DB_NAME)) { } FacebookPostsDatabase::~FacebookPostsDatabase() { } void FacebookPostsDatabase::addFacebookPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QString &icon, const QList > &images, const QString &attachmentName, const QString &attachmentCaption, const QString &attachmentDescription, const QString &attachmentUrl, bool allowLike, bool allowComment, const QString &clientId, int account) { QVariantMap extra; extra.insert(ATTACHMENT_NAME_KEY, attachmentName); extra.insert(ATTACHMENT_CAPTION_KEY, attachmentCaption); extra.insert(ATTACHMENT_DESCRIPTION_KEY, attachmentDescription); extra.insert(ALLOW_LIKE_KEY, allowLike); extra.insert(ALLOW_COMMENT_KEY, allowComment); extra.insert(ATTACHMENT_URL_KEY, attachmentUrl); extra.insert(CLIENT_ID_KEY, clientId); addPost(identifier, name, body, timestamp, icon, images, extra, account); } QString FacebookPostsDatabase::attachmentName(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(ATTACHMENT_NAME_KEY).toString(); } QString FacebookPostsDatabase::attachmentCaption(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(ATTACHMENT_CAPTION_KEY).toString(); } QString FacebookPostsDatabase::attachmentDescription(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(ATTACHMENT_DESCRIPTION_KEY).toString(); } QString FacebookPostsDatabase::attachmentUrl(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(ATTACHMENT_URL_KEY).toString(); } bool FacebookPostsDatabase::allowLike(const SocialPost::ConstPtr &post) { if (post.isNull()) { return false; } return post->extra().value(ALLOW_LIKE_KEY).toBool(); } bool FacebookPostsDatabase::allowComment(const SocialPost::ConstPtr &post) { if (post.isNull()) { return false; } return post->extra().value(ALLOW_COMMENT_KEY).toBool(); } QString FacebookPostsDatabase::clientId(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(CLIENT_ID_KEY).toString(); } libsocialcache-0.2.1/src/lib/facebookpostsdatabase.h000066400000000000000000000042421462333522700225050ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKPOSTSDATABASE_H #define FACEBOOKPOSTSDATABASE_H #include "abstractsocialpostcachedatabase.h" class FacebookPostsDatabase: public AbstractSocialPostCacheDatabase { Q_OBJECT public: explicit FacebookPostsDatabase(); ~FacebookPostsDatabase(); void addFacebookPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QString &icon, const QList > &images, const QString &attachmentName, const QString &attachmentCaption, const QString &attachmentDescription, const QString &attachmentUrl, bool allowLike, bool allowComment, const QString &clientId, int account); static QString attachmentName(const SocialPost::ConstPtr &post); static QString attachmentCaption(const SocialPost::ConstPtr &post); static QString attachmentDescription(const SocialPost::ConstPtr &post); static QString attachmentUrl(const SocialPost::ConstPtr &post); static bool allowLike(const SocialPost::ConstPtr &post); static bool allowComment(const SocialPost::ConstPtr &post); static QString clientId(const SocialPost::ConstPtr &post); }; #endif // FACEBOOKPOSTSDATABASE_H libsocialcache-0.2.1/src/lib/lib.pro000066400000000000000000000034161462333522700172770ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = lib CONFIG += qt create_prl no_install_prl create_pc link_pkgconfig QT += sql isEmpty(PREFIX) { PREFIX=/usr } TARGET = socialcache target.path = $$[QT_INSTALL_LIBS] HEADERS = \ semaphore_p.h \ socialsyncinterface.h \ abstractimagedownloader.h \ abstractimagedownloader_p.h \ abstractsocialcachedatabase.h \ abstractsocialcachedatabase_p.h \ abstractsocialpostcachedatabase.h \ socialnetworksyncdatabase.h \ facebookimagesdatabase.h \ facebookcontactsdatabase.h \ facebooknotificationsdatabase.h \ facebookpostsdatabase.h \ twitterpostsdatabase.h \ twitternotificationsdatabase.h \ socialimagesdatabase.h \ onedriveimagesdatabase.h \ dropboximagesdatabase.h \ vkpostsdatabase.h \ vknotificationsdatabase.h \ vkimagesdatabase.h SOURCES = \ semaphore_p.cpp \ socialsyncinterface.cpp \ abstractimagedownloader.cpp \ abstractsocialcachedatabase.cpp \ abstractsocialpostcachedatabase.cpp \ socialnetworksyncdatabase.cpp \ facebookimagesdatabase.cpp \ facebookcontactsdatabase.cpp \ facebooknotificationsdatabase.cpp \ facebookpostsdatabase.cpp \ twitterpostsdatabase.cpp \ twitternotificationsdatabase.cpp \ socialimagesdatabase.cpp \ onedriveimagesdatabase.cpp \ dropboximagesdatabase.cpp \ vkpostsdatabase.cpp \ vknotificationsdatabase.cpp \ vkimagesdatabase.cpp headers.files = $$HEADERS headers.path = $$PREFIX/include/socialcache QMAKE_PKGCONFIG_NAME = lib$$TARGET QMAKE_PKGCONFIG_DESCRIPTION = Social cache development files QMAKE_PKGCONFIG_LIBDIR = $$target.path QMAKE_PKGCONFIG_INCDIR = $$headers.path QMAKE_PKGCONFIG_DESTDIR = pkgconfig QMAKE_PKGCONFIG_VERSION = $$VERSION INSTALLS += target headers libsocialcache-0.2.1/src/lib/onedriveimagesdatabase.cpp000066400000000000000000001421611462333522700232020ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "onedriveimagesdatabase.h" #include "abstractsocialcachedatabase.h" #include "socialsyncinterface.h" #include #include #include #include static const char *DB_NAME = "onedrive.db"; static const int VERSION = 3; struct OneDriveUserPrivate { explicit OneDriveUserPrivate(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId, int count = -1); QString userId; QDateTime updatedTime; QString userName; int accountId; int count; }; OneDriveUserPrivate::OneDriveUserPrivate(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId, int count) : userId(userId), updatedTime(updatedTime), userName(userName), accountId(accountId), count(count) { } OneDriveUser::OneDriveUser(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId, int count) : d_ptr(new OneDriveUserPrivate(userId, updatedTime, userName, accountId, count)) { } OneDriveUser::~OneDriveUser() { } OneDriveUser::Ptr OneDriveUser::create(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId, int count) { return OneDriveUser::Ptr(new OneDriveUser(userId, updatedTime, userName, accountId, count)); } QString OneDriveUser::userId() const { Q_D(const OneDriveUser); return d->userId; } QDateTime OneDriveUser::updatedTime() const { Q_D(const OneDriveUser); return d->updatedTime; } QString OneDriveUser::userName() const { Q_D(const OneDriveUser); return d->userName; } int OneDriveUser::accountId() const { Q_D(const OneDriveUser); return d->accountId; } int OneDriveUser::count() const { Q_D(const OneDriveUser); return d->count; } struct OneDriveAlbumPrivate { explicit OneDriveAlbumPrivate(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); QString albumId; QString userId; QDateTime createdTime; QDateTime updatedTime; QString albumName; int imageCount; }; OneDriveAlbumPrivate::OneDriveAlbumPrivate(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) : albumId(albumId), userId(userId), createdTime(createdTime) , updatedTime(updatedTime), albumName(albumName), imageCount(imageCount) { } OneDriveAlbum::OneDriveAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) : d_ptr(new OneDriveAlbumPrivate(albumId, userId, createdTime, updatedTime, albumName, imageCount)) { } OneDriveAlbum::~OneDriveAlbum() { } OneDriveAlbum::Ptr OneDriveAlbum::create(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) { return OneDriveAlbum::Ptr(new OneDriveAlbum(albumId, userId, createdTime, updatedTime, albumName, imageCount)); } QString OneDriveAlbum::albumId() const { Q_D(const OneDriveAlbum); return d->albumId; } QString OneDriveAlbum::userId() const { Q_D(const OneDriveAlbum); return d->userId; } QDateTime OneDriveAlbum::createdTime() const { Q_D(const OneDriveAlbum); return d->createdTime; } QDateTime OneDriveAlbum::updatedTime() const { Q_D(const OneDriveAlbum); return d->updatedTime; } QString OneDriveAlbum::albumName() const { Q_D(const OneDriveAlbum); return d->albumName; } int OneDriveAlbum::imageCount() const { Q_D(const OneDriveAlbum); return d->imageCount; } struct OneDriveImagePrivate { explicit OneDriveImagePrivate(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, const QString &description, int accountId); QString imageId; QString albumId; QString userId; QDateTime createdTime; QDateTime updatedTime; QString imageName; int width; int height; QString thumbnailUrl; QString imageUrl; QString thumbnailFile; QString imageFile; QString description; int accountId; }; OneDriveImagePrivate::OneDriveImagePrivate(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, const QString & description, int accountId) : imageId(imageId), albumId(albumId), userId(userId) , createdTime(createdTime), updatedTime(updatedTime), imageName(imageName) , width(width), height(height), thumbnailUrl(thumbnailUrl) , imageUrl(imageUrl), thumbnailFile(thumbnailFile), imageFile(imageFile) , description(description), accountId(accountId) { } OneDriveImage::OneDriveImage(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, const QString &description, int accountId) : d_ptr(new OneDriveImagePrivate(imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, description, accountId)) { } OneDriveImage::~OneDriveImage() { } OneDriveImage::Ptr OneDriveImage::create(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &thumbnailFile, const QString &imageFile, const QString &description, int accountId) { return OneDriveImage::Ptr(new OneDriveImage(imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, description, accountId)); } QString OneDriveImage::imageId() const { Q_D(const OneDriveImage); return d->imageId; } QString OneDriveImage::albumId() const { Q_D(const OneDriveImage); return d->albumId; } QString OneDriveImage::userId() const { Q_D(const OneDriveImage); return d->userId; } QDateTime OneDriveImage::createdTime() const { Q_D(const OneDriveImage); return d->createdTime; } QDateTime OneDriveImage::updatedTime() const { Q_D(const OneDriveImage); return d->updatedTime; } QString OneDriveImage::imageName() const { Q_D(const OneDriveImage); return d->imageName; } int OneDriveImage::width() const { Q_D(const OneDriveImage); return d->width; } int OneDriveImage::height() const { Q_D(const OneDriveImage); return d->height; } QString OneDriveImage::thumbnailUrl() const { Q_D(const OneDriveImage); return d->thumbnailUrl; } QString OneDriveImage::imageUrl() const { Q_D(const OneDriveImage); return d->imageUrl; } QString OneDriveImage::thumbnailFile() const { Q_D(const OneDriveImage); return d->thumbnailFile; } QString OneDriveImage::imageFile() const { Q_D(const OneDriveImage); return d->imageFile; } QString OneDriveImage::description() const { Q_D(const OneDriveImage); return d->description; } int OneDriveImage::accountId() const { Q_D(const OneDriveImage); return d->accountId; } class OneDriveImagesDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: enum QueryType { Users, Albums, UserImages, AlbumImages }; explicit OneDriveImagesDatabasePrivate(OneDriveImagesDatabase *q); ~OneDriveImagesDatabasePrivate(); private: Q_DECLARE_PUBLIC(OneDriveImagesDatabase) static void clearCachedImages(QSqlQuery &query); QList queryUsers() const; QList queryAlbums(const QString &userId) const; QList queryImages(const QString &userId, const QString &albumId); struct { QList purgeAccounts; QStringList removeUsers; QStringList removeAlbums; QStringList removeImages; QMap insertUsers; QMap insertAlbums; QMap insertImages; QMap syncAccounts; QMap updateThumbnailFiles; QMap updateImageFiles; } queue; struct { QueryType type; QString id; QList users; QList albums; QList images; } query; struct { QList users; QList albums; QList images; } result; }; OneDriveImagesDatabasePrivate::OneDriveImagesDatabasePrivate(OneDriveImagesDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::OneDrive), SocialSyncInterface::dataType(SocialSyncInterface::Images), QLatin1String(DB_NAME), VERSION) { } OneDriveImagesDatabasePrivate::~OneDriveImagesDatabasePrivate() { } void OneDriveImagesDatabasePrivate::clearCachedImages(QSqlQuery &query) { while (query.next()) { QString thumb = query.value(0).toString(); QString image = query.value(1).toString(); if (!thumb.isEmpty()) { QFile thumbFile (thumb); if (thumbFile.exists()) { thumbFile.remove(); } } if (!image.isEmpty()) { QFile imageFile (image); if (imageFile.exists()) { imageFile.remove(); } } } } QList OneDriveImagesDatabasePrivate::queryImages(const QString &userId, const QString &albumId) { Q_Q(OneDriveImagesDatabase); QList data; if (!userId.isEmpty() && !albumId.isEmpty()) { qWarning() << Q_FUNC_INFO << "Cannot select images in both an album and for an user"; return data; } QString queryString = QLatin1String("SELECT images.imageId, images.albumId, "\ "images.userId, images.createdTime, "\ "images.updatedTime, images.imageName, images.width, "\ "images.height, images.thumbnailUrl, images.imageUrl, "\ "images.thumbnailFile, images.imageFile, images.description, images.accountId,"\ "accounts.accountId "\ "FROM images "\ "INNER JOIN accounts "\ "ON accounts.userId = images.userId%1 "\ "ORDER BY images.updatedTime %2"); if (!userId.isEmpty()) { queryString = queryString.arg(QLatin1String(" WHERE images.userId = :userId"), QLatin1String("DESC")); } else if (!albumId.isEmpty()){ queryString = queryString.arg(QLatin1String(" WHERE images.albumId = :albumId"), QLatin1String("DESC")); } else { queryString = queryString.arg(QString(), QLatin1String("DESC")); } QSqlQuery query = q->prepare(queryString); if (!userId.isEmpty()) { query.bindValue(":userId", userId); } if (!albumId.isEmpty()) { query.bindValue(":albumId", albumId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all albums:" << query.lastError().text(); return data; } while (query.next()) { data.append(OneDriveImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), #else QDateTime::fromTime_t(query.value(3).toUInt()), QDateTime::fromTime_t(query.value(4).toUInt()), #endif query.value(5).toString(), query.value(6).toInt(), query.value(7).toInt(), query.value(8).toString(), query.value(9).toString(), query.value(10).toString(), query.value(11).toString(), query.value(12).toString(), query.value(13).toInt())); } return data; } bool operator==(const OneDriveUser::ConstPtr &user1, const OneDriveUser::ConstPtr &user2) { return user1->userId() == user2->userId(); } bool operator==(const OneDriveAlbum::ConstPtr &album1, const OneDriveAlbum::ConstPtr &album2) { return album1->albumId() == album2->albumId() && album1->userId() == album2->userId(); } bool operator==(const OneDriveImage::ConstPtr &image1, const OneDriveImage::ConstPtr &image2) { return image1->imageId() == image2->imageId() && image1->albumId() == image2->albumId() && image1->userId() == image2->userId(); } // Note // // Insertion operations needs to use write(), while Delete // operations are automatically using transactions and // don't need write(). OneDriveImagesDatabase::OneDriveImagesDatabase() : AbstractSocialCacheDatabase(*(new OneDriveImagesDatabasePrivate(this))) { } OneDriveImagesDatabase::~OneDriveImagesDatabase() { wait(); } bool OneDriveImagesDatabase::syncAccount(int accountId, const QString &userId) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.syncAccounts.insert(accountId, userId); return true; } void OneDriveImagesDatabase::purgeAccount(int accountId) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.purgeAccounts.append(accountId); } // Returns the user but do not return a count // if you want a count, better consider users OneDriveUser::ConstPtr OneDriveImagesDatabase::user(const QString &userId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT userId, updatedTime, userName, accountId " "FROM users WHERE userId = :userId")); query.bindValue(":userId", userId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from users table:" << query.lastError(); return OneDriveUser::Ptr(); } // If we have the user, we have a result, otherwise we won't have the user if (!query.next()) { return OneDriveUser::Ptr(); } OneDriveUser::ConstPtr user = OneDriveUser::create( query.value(0).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(1).toUInt()), #else QDateTime::fromTime_t(query.value(1).toUInt()), #endif query.value(2).toString(), query.value(3).toInt()); query.finish(); return user; } void OneDriveImagesDatabase::addUser(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId) { Q_D(OneDriveImagesDatabase); OneDriveUser::Ptr user = OneDriveUser::create(userId, updatedTime, userName, accountId); QMutexLocker locker(&d->mutex); d->queue.insertUsers.insert(userId, user); } void OneDriveImagesDatabase::removeUser(const QString &userId) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeUsers.append(userId); } QList OneDriveImagesDatabasePrivate::queryUsers() const { QList data; QSqlQuery query = q_func()->prepare(QStringLiteral( "SELECT users.userId, users.updatedTime, users.userName, users.accountId," "SUM(albums.imageCount) as count " "FROM users " "LEFT JOIN albums ON albums.userId = users.userId " "GROUP BY users.userId " "ORDER BY users.userId")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all users:" << query.lastError().text(); return data; } while (query.next()) { data.append(OneDriveUser::create(query.value(0).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(1).toUInt()), #else QDateTime::fromTime_t(query.value(1).toUInt()), #endif query.value(2).toString(), query.value(3).toInt(), query.value(4).toInt())); } return data; } QStringList OneDriveImagesDatabase::allAlbumIds(bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT albumId " "FROM albums " "ORDER BY updatedTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch all albums" << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } QMap OneDriveImagesDatabase::accounts(bool *ok) const { if (ok) { *ok = false; } QMap result; QSqlQuery query = prepare(QStringLiteral( "SELECT accountId, userId " "FROM accounts ")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch account mappings" << query.lastError().text(); return result; } while (query.next()) { result[query.value(0).toInt()] = query.value(1).toString(); } if (ok) { *ok = true; } return result; } OneDriveAlbum::ConstPtr OneDriveImagesDatabase::album(const QString &albumId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT albumId, userId, createdTime, updatedTime, albumName, " "imageCount " "FROM albums WHERE albumId = :albumId")); query.bindValue(":albumId", albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from albums table:" << query.lastError(); return OneDriveAlbum::Ptr(); } // If we have the album, we have a result, otherwise we won't have the album if (!query.next()) { return OneDriveAlbum::Ptr(); } OneDriveAlbum::ConstPtr album = OneDriveAlbum::create(query.value(0).toString(), query.value(1).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(2).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), #else QDateTime::fromTime_t(query.value(2).toUInt()), QDateTime::fromTime_t(query.value(3).toUInt()), #endif query.value(4).toString(), query.value(5).toInt()); query.finish(); return album; } void OneDriveImagesDatabase::addAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount) { Q_D(OneDriveImagesDatabase); OneDriveAlbum::Ptr album = OneDriveAlbum::create(albumId, userId, createdTime, updatedTime, albumName, imageCount); QMutexLocker locker(&d->mutex); d->queue.insertAlbums.insert(albumId, album); } void OneDriveImagesDatabase::removeAlbum(const QString &albumId) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums.append(albumId); } void OneDriveImagesDatabase::removeAlbums(const QStringList &albumIds) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums += albumIds; } QList OneDriveImagesDatabasePrivate::queryAlbums(const QString &userId) const { QList data; QString queryString = QLatin1String("SELECT albumId, userId, createdTime, updatedTime, "\ "albumName, imageCount "\ "FROM albums%1 ORDER BY updatedTime DESC"); if (!userId.isEmpty()) { queryString = queryString.arg(QLatin1String(" WHERE userId = :userId")); } else { queryString = queryString.arg(QString()); } QSqlQuery query = q_func()->prepare(queryString); if (!userId.isEmpty()) { query.bindValue(":userId", userId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query all albums:" << query.lastError().text(); return data; } while (query.next()) { data.append(OneDriveAlbum::create(query.value(0).toString(), query.value(1).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(2).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), #else QDateTime::fromTime_t(query.value(2).toUInt()), QDateTime::fromTime_t(query.value(3).toUInt()), #endif query.value(4).toString(), query.value(5).toInt())); } return data; } QStringList OneDriveImagesDatabase::allImageIds(bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT imageId " "FROM images " "ORDER BY updatedTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch all images" << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } QStringList OneDriveImagesDatabase::imageIds(const QString &albumId, bool *ok) const { if (ok) { *ok = false; } QStringList ids; QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT imageId " "FROM images WHERE albumId = :albumId")); query.bindValue(":albumId", albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to fetch images for album" << albumId << query.lastError().text(); return ids; } while (query.next()) { ids.append(query.value(0).toString()); } if (ok) { *ok = true; } return ids; } OneDriveImage::ConstPtr OneDriveImagesDatabase::image(const QString &imageId) const { QSqlQuery query = prepare( "SELECT imageId, albumId, userId, createdTime, updatedTime, imageName, " "width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, description, accountId " "FROM images WHERE imageId = :imageId"); query.bindValue(":imageId", imageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from albums table:" << query.lastError(); return OneDriveImage::Ptr(); } // If we have the image, we have a result, otherwise we won't have the image if (!query.next()) { return OneDriveImage::Ptr(); } return OneDriveImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), #else QDateTime::fromTime_t(query.value(3).toUInt()), QDateTime::fromTime_t(query.value(4).toUInt()), #endif query.value(5).toString(), query.value(6).toInt(), query.value(7).toInt(), query.value(8).toString(), query.value(9).toString(), query.value(10).toString(), query.value(11).toString(), query.value(12).toString(), query.value(13).toInt()); } void OneDriveImagesDatabase::removeImage(const QString &imageId) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages.append(imageId); } void OneDriveImagesDatabase::removeImages(const QStringList &imageIds) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages += imageIds; } void OneDriveImagesDatabase::addImage(const QString &imageId, const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString &thumbnailUrl, const QString &imageUrl, const QString &description, int accountId) { Q_D(OneDriveImagesDatabase); OneDriveImage::Ptr image = OneDriveImage::create(imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, thumbnailUrl, imageUrl, QString(), QString(), description, accountId); QMutexLocker locker(&d->mutex); d->queue.insertImages.insert(imageId, image); } void OneDriveImagesDatabase::updateImageThumbnail(const QString &imageId, const QString &thumbnailFile) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateThumbnailFiles.insert(imageId, thumbnailFile); } void OneDriveImagesDatabase::updateImageFile(const QString &imageId, const QString &imageFile) { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateImageFiles.insert(imageId, imageFile); } void OneDriveImagesDatabase::commit() { executeWrite(); } QList OneDriveImagesDatabase::users() const { return d_func()->result.users; } QList OneDriveImagesDatabase::images() const { return d_func()->result.images; } QList OneDriveImagesDatabase::albums() const { return d_func()->result.albums; } void OneDriveImagesDatabase::queryUsers() { Q_D(OneDriveImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = OneDriveImagesDatabasePrivate::Users; } executeRead(); } void OneDriveImagesDatabase::queryAlbums(const QString &userId) { Q_D(OneDriveImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = OneDriveImagesDatabasePrivate::Albums; d->query.id = userId; } executeRead(); } void OneDriveImagesDatabase::queryUserImages(const QString &userId) { Q_D(OneDriveImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = OneDriveImagesDatabasePrivate::UserImages; d->query.id = userId; } executeRead(); } void OneDriveImagesDatabase::queryAlbumImages(const QString &albumId) { Q_D(OneDriveImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = OneDriveImagesDatabasePrivate::AlbumImages; d->query.id = albumId; } executeRead(); } bool OneDriveImagesDatabase::read() { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); switch (d->query.type) { case OneDriveImagesDatabasePrivate::Users: { locker.unlock(); QList users = d->queryUsers(); locker.relock(); d->query.users = users; return true; } case OneDriveImagesDatabasePrivate::Albums: { const QString userId = d->query.id; locker.unlock(); QList albums = d->queryAlbums(userId); locker.relock(); d->query.albums = albums; return true; } case OneDriveImagesDatabasePrivate::UserImages: case OneDriveImagesDatabasePrivate::AlbumImages: { const QString userId = d->query.type == OneDriveImagesDatabasePrivate::UserImages ? d->query.id : QString(); const QString albumId = d->query.type == OneDriveImagesDatabasePrivate::AlbumImages ? d->query.id : QString(); locker.unlock(); QList images = d->queryImages(userId, albumId); locker.relock(); d->query.images = images; return true; } default: return false; } } void OneDriveImagesDatabase::readFinished() { Q_D(OneDriveImagesDatabase); { QMutexLocker locker(&d->mutex); d->result.users = d->query.users; d->result.albums = d->query.albums; d->result.images = d->query.images; d->query.users.clear(); d->query.albums.clear(); d->query.images.clear(); } emit queryFinished(); } bool OneDriveImagesDatabase::write() { Q_D(OneDriveImagesDatabase); QMutexLocker locker(&d->mutex); qWarning() << "Queued users being saved:" << d->queue.insertUsers.count(); qWarning() << "Queued albums being saved:" << d->queue.insertAlbums.count(); qWarning() << "Queued images being saved:" << d->queue.insertImages.count(); qWarning() << "Queued thumbnail files being updated:" << d->queue.updateThumbnailFiles.count(); qWarning() << "Queued image files being updated:" << d->queue.updateImageFiles.count(); const QList purgeAccounts = d->queue.purgeAccounts; QStringList removeUsers = d->queue.removeUsers; const QStringList removeAlbums = d->queue.removeAlbums; const QStringList removeImages = d->queue.removeImages; const QMap insertUsers = d->queue.insertUsers; const QMap insertAlbums = d->queue.insertAlbums; const QMap insertImages = d->queue.insertImages; const QMap syncAccounts = d->queue.syncAccounts; const QMap updateThumbnailFiles = d->queue.updateThumbnailFiles; const QMap updateImageFiles = d->queue.updateImageFiles; d->queue.purgeAccounts.clear(); d->queue.removeUsers.clear(); d->queue.removeAlbums.clear(); d->queue.removeImages.clear(); d->queue.insertUsers.clear(); d->queue.insertAlbums.clear(); d->queue.insertImages.clear(); d->queue.syncAccounts.clear(); d->queue.updateThumbnailFiles.clear(); d->queue.updateImageFiles.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!purgeAccounts.isEmpty()) { query = prepare(QStringLiteral( "SELECT userId " "FROM accounts " "WHERE accountId = :accountId")); Q_FOREACH (int accountId, purgeAccounts) { query.bindValue(QStringLiteral(":accountId"), accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec account id selection query:" << query.lastError().text(); success = false; } else while (query.next()) { removeUsers.append(query.value(0).toString()); } } } if (!removeUsers.isEmpty()) { QVariantList userIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE userId = :userId")); Q_FOREACH (const QString &userId, removeUsers) { userIds.append(userId); query.bindValue(QStringLiteral(":userId"), userId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM accounts " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM users " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE userId = :userId")); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); } if (!removeAlbums.isEmpty()) { QVariantList albumIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE albumId = :albumId")); Q_FOREACH (const QString &albumId, removeAlbums) { albumIds.append(albumId); query.bindValue(QStringLiteral(":albumId"), albumId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE albumId = :albumId")); query.bindValue(QStringLiteral(":albumId"), albumIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE albumId = :albumId")); query.bindValue(QStringLiteral(":albumId"), albumIds); executeBatchSocialCacheQuery(query); } if (!removeImages.isEmpty()) { QVariantList imageIds; query = prepare(QStringLiteral( "SELECT thumbnailFile, imageFile " "FROM images " "WHERE imageId = :imageId")); Q_FOREACH (const QString &imageId, removeImages) { imageIds.append(imageId); query.bindValue(QStringLiteral(":imageId"), imageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query:" << query.lastError().text(); } else { d->clearCachedImages(query); } } query = prepare(QStringLiteral( "DELETE FROM images " "WHERE imageId = :imageId")); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } if (!insertUsers.isEmpty()) { QVariantList userIds; QVariantList updatedTimes; QVariantList usernames; QVariantList accountIds; Q_FOREACH (const OneDriveUser::ConstPtr &user, insertUsers) { userIds.append(user->userId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) updatedTimes.append(user->updatedTime().toSecsSinceEpoch()); #else updatedTimes.append(user->updatedTime().toTime_t()); #endif usernames.append(user->userName()); accountIds.append(user->accountId()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO users (" " userId, updatedTime, userName, accountId) " "VALUES (" " :userId, :updatedTime, :userName, :accountId);")); query.bindValue(QStringLiteral(":userId"), userIds); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":userName"), usernames); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!insertAlbums.isEmpty()) { QVariantList albumIds, userIds; QVariantList createdTimes, updatedTimes; QVariantList albumNames; QVariantList imageCounts; Q_FOREACH (const OneDriveAlbum::ConstPtr &album, insertAlbums) { albumIds.append(album->albumId()); userIds.append(album->userId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(album->createdTime().toSecsSinceEpoch()); updatedTimes.append(album->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(album->createdTime().toTime_t()); updatedTimes.append(album->updatedTime().toTime_t()); #endif albumNames.append(album->albumName()); imageCounts.append(album->imageCount()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO albums(" " albumId, userId, createdTime, updatedTime, albumName, imageCount) " "VALUES (" " :albumId, :userId, :createdTime, :updatedTime, :albumName, :imageCount)")); query.bindValue(QStringLiteral(":albumId"), albumIds); query.bindValue(QStringLiteral(":userId"), userIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":albumName"), albumNames); query.bindValue(QStringLiteral(":imageCount"), imageCounts); executeBatchSocialCacheQuery(query); } if (!insertImages.isEmpty()) { QVariantList imageIds, albumIds, userIds; QVariantList createdTimes, updatedTimes; QVariantList imageNames; QVariantList widths, heights; QVariantList thumbnailUrls, imageUrls; QVariantList thumbnailFiles, imageFiles; QVariantList descriptions, accountIds; Q_FOREACH (const OneDriveImage::ConstPtr &image, insertImages) { imageIds.append(image->imageId()); albumIds.append(image->albumId()); userIds.append(image->userId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(image->createdTime().toSecsSinceEpoch()); updatedTimes.append(image->updatedTime().toSecsSinceEpoch()); #else createdTimes.append(image->createdTime().toTime_t()); updatedTimes.append(image->updatedTime().toTime_t()); #endif imageNames.append(image->imageName()); widths.append(image->width()); heights.append(image->height()); thumbnailUrls.append(image->thumbnailUrl()); imageUrls.append(image->imageUrl()); thumbnailFiles.append(image->thumbnailFile()); imageFiles.append(image->imageFile()); descriptions.append(image->description()); accountIds.append(image->accountId()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO images (" " imageId, albumId, userId, createdTime, updatedTime, imageName," " width, height, thumbnailUrl, imageUrl, thumbnailFile, imageFile, description, accountId) " "VALUES (" " :imageId, :albumId, :userId, :createdTime, :updatedTime, :imageName," " :width, :height, :thumbnailUrl, :imageUrl, :thumbnailFile, :imageFile, :description, :accountId)")); query.bindValue(QStringLiteral(":imageId"), imageIds); query.bindValue(QStringLiteral(":albumId"), albumIds); query.bindValue(QStringLiteral(":userId"), userIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":updatedTime"), updatedTimes); query.bindValue(QStringLiteral(":imageName"), imageNames); query.bindValue(QStringLiteral(":width"), widths); query.bindValue(QStringLiteral(":height"), heights); query.bindValue(QStringLiteral(":thumbnailUrl"), thumbnailUrls); query.bindValue(QStringLiteral(":imageUrl"), imageUrls); query.bindValue(QStringLiteral(":thumbnailFile"), thumbnailFiles); query.bindValue(QStringLiteral(":imageFile"), imageFiles); query.bindValue(QStringLiteral(":description"), descriptions); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!syncAccounts.isEmpty()) { QVariantList accountIds; QVariantList userIds; for (QMap::const_iterator it = syncAccounts.begin(); it != syncAccounts.end(); ++it) { accountIds.append(it.key()); userIds.append(it.value()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO accounts (" " accountId, userId) " "VALUES(" " :accountId, :userId)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":userId"), userIds); executeBatchSocialCacheQuery(query); } if (!updateThumbnailFiles.isEmpty()) { QVariantList imageIds; QVariantList thumbnailFiles; for (QMap::const_iterator it = updateThumbnailFiles.begin(); it != updateThumbnailFiles.end(); ++it) { imageIds.append(it.key()); thumbnailFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE images " "SET thumbnailFile = :thumbnailFile " "WHERE imageId = :imageId")); query.bindValue(QStringLiteral(":thumbnailFile"), thumbnailFiles); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } if (!updateImageFiles.isEmpty()) { QVariantList imageIds; QVariantList imageFiles; for (QMap::const_iterator it = updateImageFiles.begin(); it != updateImageFiles.end(); ++it) { imageIds.append(it.key()); imageFiles.append(it.value()); } query = prepare(QStringLiteral( "UPDATE images " "SET imageFile = :imageFile " "WHERE imageId = :imageId")); query.bindValue(QStringLiteral(":imageFile"), imageFiles); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } return success; } bool OneDriveImagesDatabase::createTables(QSqlDatabase database) const { // create the onedrive image db tables // images = imageId, albumId, userId, createdTime, updatedTime, imageName, width, height, // thumbnailUrl, imageUrl, thumbnailFile, imageFile // albums = albumId, userId, createdTime, updatedTime, albumName, imageCount // users = userId, updatedTime, userName, thumbnailUrl, imageUrl, thumbnailFile, imageFile QSqlQuery query(database); query.prepare( "CREATE TABLE IF NOT EXISTS images (" "imageId TEXT UNIQUE PRIMARY KEY," "albumId TEXT," "userId TEXT," "createdTime INTEGER," "updatedTime INTEGER," "imageName TEXT," "width INTEGER," "height INTEGER," "thumbnailUrl TEXT," "imageUrl TEXT," "thumbnailFile TEXT," "imageFile TEXT," "description TEXT," "accountId INTEGER)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create images table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS albums (" "albumId TEXT UNIQUE PRIMARY KEY," "userId TEXT," "createdTime INTEGER," "updatedTime INTEGER," "albumName TEXT," "imageCount INTEGER)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create albums table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS users (" "userId TEXT UNIQUE PRIMARY KEY," "updatedTime INTEGER," "userName TEXT," "accountId INTEGER)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create users table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS accounts (" "accountId INTEGER UNIQUE PRIMARY KEY," "userId TEXT)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create accounts table:" << query.lastError().text(); return false; } return true; } bool OneDriveImagesDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS images"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete images table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS albums"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete albums table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS users"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete users table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS accounts"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete accounts table:" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/onedriveimagesdatabase.h000066400000000000000000000166411462333522700226520ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ONEDRIVEIMAGESDATABASE_H #define ONEDRIVEIMAGESDATABASE_H #include "abstractsocialcachedatabase_p.h" #include #include #include class OneDriveUserPrivate; class OneDriveUser { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~OneDriveUser(); static OneDriveUser::Ptr create(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId, int count = -1); QString userId() const; QDateTime updatedTime() const; QString userName() const; int accountId() const; int count() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(OneDriveUser) explicit OneDriveUser(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId, int count = -1); }; class OneDriveAlbumPrivate; class OneDriveAlbum { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~OneDriveAlbum(); static OneDriveAlbum::Ptr create(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); QString albumId() const; QString userId() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString albumName() const; int imageCount() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(OneDriveAlbum) explicit OneDriveAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); }; class OneDriveImagePrivate; class OneDriveImage { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~OneDriveImage(); static OneDriveImage::Ptr create(const QString & imageId, const QString & albumId, const QString & userId, const QDateTime & createdTime, const QDateTime &updatedTime, const QString &imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & thumbnailFile, const QString & imageFile, const QString & description, int accountId); QString imageId() const; QString albumId() const; QString userId() const; QDateTime createdTime() const; QDateTime updatedTime() const; QString imageName() const; int width() const; int height() const; QString thumbnailUrl() const; QString imageUrl() const; QString thumbnailFile() const; QString imageFile() const; QString description() const; int accountId() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(OneDriveImage) explicit OneDriveImage(const QString & imageId, const QString & albumId, const QString & userId, const QDateTime & createdTime, const QDateTime & updatedTime, const QString & imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & thumbnailFile, const QString & imageFile, const QString & description, int accountId); }; bool operator==(const OneDriveUser::ConstPtr &user1, const OneDriveUser::ConstPtr &user2); bool operator==(const OneDriveAlbum::ConstPtr &album1, const OneDriveAlbum::ConstPtr &album2); bool operator==(const OneDriveImage::ConstPtr &image1, const OneDriveImage::ConstPtr &image2); class OneDriveImagesDatabasePrivate; class OneDriveImagesDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit OneDriveImagesDatabase(); ~OneDriveImagesDatabase(); // Account manipulation bool syncAccount(int accountId, const QString &userId); void purgeAccount(int accountId); QMap accounts(bool *ok = 0) const; // User cache manipulation OneDriveUser::ConstPtr user(const QString &userId) const; void addUser(const QString &userId, const QDateTime &updatedTime, const QString &userName, int accountId); void removeUser(const QString &userId); // Album cache manipulation QStringList allAlbumIds(bool *ok = 0) const; OneDriveAlbum::ConstPtr album(const QString &albumId) const; void addAlbum(const QString &albumId, const QString &userId, const QDateTime &createdTime, const QDateTime &updatedTime, const QString &albumName, int imageCount); void removeAlbum(const QString &albumId); void removeAlbums(const QStringList &albumIds); // Images cache manipulation QStringList allImageIds(bool *ok = 0) const; QStringList imageIds(const QString &albumId, bool *ok = 0) const; OneDriveImage::ConstPtr image(const QString &imageId) const; void addImage(const QString & imageId, const QString & albumId, const QString & userId, const QDateTime & createdTime, const QDateTime & updatedTime, const QString & imageName, int width, int height, const QString & thumbnailUrl, const QString & imageUrl, const QString & description, int accountId); void updateImageThumbnail(const QString &imageId, const QString &thumbnailFile); void updateImageFile(const QString &imageId, const QString &imageFile); void removeImage(const QString &imageId); void removeImages(const QStringList &imageIds); void commit(); QList users() const; QList images() const; QList albums() const; void queryUsers(); void queryAlbums(const QString &userId = QString()); void queryUserImages(const QString &userId = QString()); void queryAlbumImages(const QString &albumId); Q_SIGNALS: void queryFinished(); protected: bool read(); void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(OneDriveImagesDatabase) }; #endif // ONEDRIVEIMAGESDATABASE_H libsocialcache-0.2.1/src/lib/semaphore_p.cpp000066400000000000000000000107701462333522700210160ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Matthew Vogt * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include "semaphore_p.h" #include #include #include #include #include #include #include namespace { // Defined as required for ::semun union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; } Semaphore::Semaphore(const char *id, int initial) : m_identifier(id) , m_initialValue(-1) , m_id(-1) { key_t key = ::ftok(m_identifier, 0); m_id = ::semget(key, 1, 0); if (m_id == -1) { if (errno != ENOENT) { error("Unable to get semaphore", errno); } else { // The semaphore does not currently exist m_id = ::semget(key, 1, IPC_CREAT | IPC_EXCL | S_IRWXU); if (m_id == -1) { if (errno == EEXIST) { // Someone else won the race to create the semaphore - retry get m_id = ::semget(key, 1, 0); } if (m_id == -1) { error("Unable to create semaphore", errno); } } else { // Set the initial value union semun arg = { 0 }; arg.val = initial; int status = ::semctl(m_id, 0, SETVAL, arg); if (status == -1) { m_id = -1; error("Unable to initialize semaphore", errno); } else { m_initialValue = initial; } } } } } Semaphore::~Semaphore() { } bool Semaphore::decrement() { if (m_id == -1) return false; struct sembuf op; op.sem_num = 0; op.sem_op = -1; op.sem_flg = SEM_UNDO; if (::semop(m_id, &op, 1) == 0) return true; error("Unable to decrement semaphore", errno); return false; } bool Semaphore::increment() { if (m_id == -1) return false; struct sembuf op; op.sem_num = 0; op.sem_op = 1; op.sem_flg = SEM_UNDO; if (::semop(m_id, &op, 1) == 0) return true; error("Unable to increment semaphore", errno); return false; } int Semaphore::value() const { if (m_id == -1) return -1; return ::semctl(m_id, 0, GETVAL, 0); } void Semaphore::error(const char *msg, int error) { qWarning() << QString("%1 %2: %3 (%4)").arg(msg).arg(m_identifier).arg(::strerror(error)).arg(error); } ProcessMutex::ProcessMutex(const QString &path) : m_semaphore(path.toLatin1(), 1) { } bool ProcessMutex::lock() { if (!m_semaphore.decrement()) { return false; } if (m_mutex.tryLock(40)) { return true; } return false; } bool ProcessMutex::unlock() { m_mutex.unlock(); bool couldRelease = m_semaphore.increment(); if (!couldRelease) { return false; } return true; } libsocialcache-0.2.1/src/lib/semaphore_p.h000066400000000000000000000043061462333522700204610ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Matthew Vogt * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #ifndef SEMAPHORE_P_H #define SEMAPHORE_P_H #include #include class Semaphore { public: Semaphore(const char *identifier, int initial); ~Semaphore(); bool decrement(); bool increment(); int value() const; private: void error(const char *msg, int error); const char *m_identifier; int m_initialValue; int m_id; }; class ProcessMutex { Semaphore m_semaphore; QMutex m_mutex; public: ProcessMutex(const QString &path); bool lock(); bool unlock(); }; #endif // SEMAPHORE_P_H libsocialcache-0.2.1/src/lib/socialimagesdatabase.cpp000066400000000000000000000500531462333522700226370ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "socialimagesdatabase.h" #include "abstractsocialcachedatabase.h" #include "socialsyncinterface.h" #include #include #include #include static const char *DB_NAME = "socialimagecache.db"; static const int VERSION = 4; struct SocialImagePrivate { explicit SocialImagePrivate(int accountId, const QString &imageUrl, const QString &imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId); int accountId; QString imageUrl; QString imageFile; QDateTime createdTime; QDateTime expires; QString imageId; }; SocialImagePrivate::SocialImagePrivate(int accountId, const QString &imageUrl, const QString &imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId) : accountId(accountId) , imageUrl(imageUrl) , imageFile(imageFile) , createdTime(createdTime) , expires(expires) , imageId(imageId) { } SocialImage::SocialImage(int accountId, const QString &imageUrl, const QString &imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId) : d_ptr(new SocialImagePrivate(accountId, imageUrl, imageFile, createdTime, expires, imageId)) { } SocialImage::~SocialImage() { } SocialImage::Ptr SocialImage::create(int accountId, const QString & imageUrl, const QString & imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId) { return SocialImage::Ptr(new SocialImage(accountId, imageUrl, imageFile, createdTime, expires, imageId)); } int SocialImage::accountId() const { Q_D(const SocialImage); return d->accountId; } QString SocialImage::imageUrl() const { Q_D(const SocialImage); return d->imageUrl; } QString SocialImage::imageFile() const { Q_D(const SocialImage); return d->imageFile; } QDateTime SocialImage::createdTime() const { Q_D(const SocialImage); return d->createdTime; } QDateTime SocialImage::expires() const { Q_D(const SocialImage); return d->expires; } QString SocialImage::imageId() const { Q_D(const SocialImage); return d->imageId; } class SocialImagesDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: explicit SocialImagesDatabasePrivate(SocialImagesDatabase *q); ~SocialImagesDatabasePrivate(); private: Q_DECLARE_PUBLIC(SocialImagesDatabase) static void clearCachedImages(QSqlQuery &query); QList queryImages(int accountId, const QDateTime &olderThan); QList queryExpired(int accountId); struct { QList purgeAccounts; QStringList removeImages; QMap insertImages; } queue; struct { bool queryExpired; int accountId; QDateTime olderThan; QList images; } query; struct { QList images; } result; QSqlQuery m_singleImageQuery; }; SocialImagesDatabasePrivate::SocialImagesDatabasePrivate(SocialImagesDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::Facebook), SocialSyncInterface::dataType(SocialSyncInterface::Images), QLatin1String(DB_NAME), VERSION) { } SocialImagesDatabasePrivate::~SocialImagesDatabasePrivate() { } void SocialImagesDatabasePrivate::clearCachedImages(QSqlQuery &query) { while (query.next()) { QString image = query.value(0).toString(); if (!image.isEmpty()) { QFile imageFile(image); if (imageFile.exists()) { imageFile.remove(); } } } } QList SocialImagesDatabasePrivate::queryImages(int accountId, const QDateTime &olderThan) { Q_Q(SocialImagesDatabase); QList data; QString queryString = QLatin1String("SELECT accountId, " "imageUrl, imageFile, createdTime, expires, imageId " "FROM images " "WHERE accountId = :accountId"); if (olderThan.isValid()) { queryString.append(" AND createdTime < :createdTime"); } QSqlQuery query = q->prepare(queryString); query.bindValue(":accountId", accountId); if (olderThan.isValid()) { #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) query.bindValue(":createdTime", olderThan.toSecsSinceEpoch()); #else query.bindValue(":createdTime", olderThan.toTime_t()); #endif } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query images:" << query.lastError().text(); return data; } while (query.next()) { data.append(SocialImage::create(query.value(0).toInt(), // accountId query.value(1).toString(), // imageUrl query.value(2).toString(), // imageFile #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), // createdTime QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), // expires #else QDateTime::fromTime_t(query.value(3).toUInt()), // createdTime QDateTime::fromTime_t(query.value(4).toUInt()), // expires #endif query.value(5).toString())); // imageId } return data; } QList SocialImagesDatabasePrivate::queryExpired(int accountId) { Q_Q(SocialImagesDatabase); QList data; #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) int currentTime = QDateTime::currentDateTime().toSecsSinceEpoch(); #else int currentTime = QDateTime::currentDateTime().toTime_t(); #endif QString queryString = QLatin1String("SELECT accountId, " "imageUrl, imageFile, createdTime, expires, imageId " "FROM images " "WHERE accountId = :accountId AND expires < :currentTime"); QSqlQuery query = q->prepare(queryString); query.bindValue(":accountId", accountId); query.bindValue(":currentTime", currentTime); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query images:" << query.lastError().text(); return data; } while (query.next()) { data.append(SocialImage::create(query.value(0).toInt(), // accountId query.value(1).toString(), // imageUrl query.value(2).toString(), // imageFile #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), // createdTime QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), // expires #else QDateTime::fromTime_t(query.value(3).toUInt()), // createdTime QDateTime::fromTime_t(query.value(4).toUInt()), // expires #endif query.value(5).toString())); // imageId } return data; } bool operator==(const SocialImage::ConstPtr &image1, const SocialImage::ConstPtr &image2) { return image1->accountId() == image2->accountId() && image1->imageUrl() == image2->imageUrl(); } SocialImagesDatabase::SocialImagesDatabase() : AbstractSocialCacheDatabase(*(new SocialImagesDatabasePrivate(this))) { } SocialImagesDatabase::~SocialImagesDatabase() { wait(); } void SocialImagesDatabase::purgeAccount(int accountId) { Q_D(SocialImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.purgeAccounts.append(accountId); } SocialImage::ConstPtr SocialImagesDatabase::image(const QString &imageUrl) const { Q_D(const SocialImagesDatabase); if (d->queue.insertImages.contains(imageUrl)) { return d->queue.insertImages.value(imageUrl); } QSqlQuery query = prepare( "SELECT accountId, " "imageUrl, imageFile, createdTime, expires, imageId " "FROM images WHERE imageUrl = :imageUrl"); query.bindValue(":imageUrl", imageUrl); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from images table:" << query.lastError(); return SocialImage::Ptr(); } // If we have the image, we have a result, otherwise we won't have the image if (!query.next()) { return SocialImage::Ptr(); } return SocialImage::create(query.value(0).toInt(), // accountId query.value(1).toString(), // imageUrl query.value(2).toString(), // imageFile #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), // createdTime QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), // expires #else QDateTime::fromTime_t(query.value(3).toUInt()), // createdTime QDateTime::fromTime_t(query.value(4).toUInt()), // expires #endif query.value(5).toString()); // imageId } SocialImage::ConstPtr SocialImagesDatabase::imageById(const QString &imageId) const { Q_D(const SocialImagesDatabase); QMap::const_iterator i = d->queue.insertImages.constBegin(); while (i != d->queue.insertImages.constEnd()) { if (i.value()->imageId() == imageId) { return i.value(); } ++i; } QSqlQuery query = prepare( "SELECT accountId, " "imageUrl, imageFile, createdTime, expires, imageId " "FROM images WHERE imageId = :imageId"); query.bindValue(":imageId", imageId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Error reading from images table:" << query.lastError(); return SocialImage::Ptr(); } // If we have the image, we have a result, otherwise we won't have the image if (!query.next()) { return SocialImage::Ptr(); } return SocialImage::create(query.value(0).toInt(), // accountId query.value(1).toString(), // imageUrl query.value(2).toString(), // imageFile #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(3).toUInt()), // createdTime QDateTime::fromSecsSinceEpoch(query.value(4).toUInt()), // expires #else QDateTime::fromTime_t(query.value(3).toUInt()), // createdTime QDateTime::fromTime_t(query.value(4).toUInt()), // expires #endif query.value(5).toString()); // imageId } void SocialImagesDatabase::removeImage(const QString &imageUrl) { Q_D(SocialImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.insertImages.remove(imageUrl); d->queue.removeImages.append(imageUrl); } void SocialImagesDatabase::removeImages(QList images) { Q_D(SocialImagesDatabase); QMutexLocker locker(&d->mutex); foreach (SocialImage::ConstPtr image, images) { d->queue.insertImages.remove(image->imageUrl()); d->queue.removeImages.append(image->imageUrl()); } } void SocialImagesDatabase::addImage(int accountId, const QString &imageUrl, const QString &imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId) { Q_D(SocialImagesDatabase); SocialImage::Ptr image = SocialImage::create(accountId, imageUrl, imageFile, createdTime, expires, imageId); QMutexLocker locker(&d->mutex); d->queue.removeImages.removeAll(imageUrl); d->queue.insertImages.insert(imageUrl, image); } void SocialImagesDatabase::commit() { executeWrite(); } QList SocialImagesDatabase::images() const { return d_func()->result.images; } void SocialImagesDatabase::queryImages(int accountId, const QDateTime &olderThan) { Q_D(SocialImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.queryExpired = false; d->query.accountId = accountId; d->query.olderThan = olderThan; } executeRead(); } void SocialImagesDatabase::queryExpired(int accountId) { Q_D(SocialImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.accountId = accountId; d->query.queryExpired = true; } executeRead(); } bool SocialImagesDatabase::read() { Q_D(SocialImagesDatabase); QMutexLocker locker(&d->mutex); if (d->query.queryExpired) { d->query.images = d->queryExpired(d->query.accountId); } else { d->query.images = d->queryImages(d->query.accountId, d->query.olderThan); } return true; } void SocialImagesDatabase::readFinished() { Q_D(SocialImagesDatabase); { QMutexLocker locker(&d->mutex); d->result.images = d->query.images; d->query.images.clear(); } emit queryFinished(); } bool SocialImagesDatabase::write() { Q_D(SocialImagesDatabase); QMutexLocker locker(&d->mutex); qWarning() << "Queued images being saved:" << d->queue.insertImages.count(); const QList purgeAccounts = d->queue.purgeAccounts; const QStringList removeImages = d->queue.removeImages; QList insertImages; QMap::const_iterator i = d->queue.insertImages.constBegin(); while (i != d->queue.insertImages.constEnd()) { insertImages.append(i.value()); ++i; } d->queue.purgeAccounts.clear(); d->queue.removeImages.clear(); d->queue.insertImages.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!purgeAccounts.isEmpty()) { QVariantList accountIds; Q_FOREACH (int accountId, purgeAccounts) { accountIds.append(accountId); } query = prepare(QStringLiteral( "SELECT imageFile FROM images " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec images selection query:" << query.lastError().text(); success = false; } else { d->clearCachedImages(query); } query = prepare(QStringLiteral( "DELETE FROM images " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!removeImages.isEmpty()) { QVariantList imageUrls; foreach (const QString image, removeImages) { imageUrls.append(image); } query = prepare(QStringLiteral( "DELETE FROM images " "WHERE imageUrl = :imageUrl")); query.bindValue(QStringLiteral(":imageUrl"), imageUrls); executeBatchSocialCacheQuery(query); } if (!insertImages.isEmpty()) { QVariantList accountIds; QVariantList imageUrls, imageFiles; QVariantList createdTimes, expireTimes; QVariantList imageIds; Q_FOREACH (const SocialImage::ConstPtr &image, insertImages) { accountIds.append(image->accountId()); imageUrls.append(image->imageUrl()); imageFiles.append(image->imageFile()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(image->createdTime().toSecsSinceEpoch()); expireTimes.append(image->expires().toSecsSinceEpoch()); #else createdTimes.append(image->createdTime().toTime_t()); expireTimes.append(image->expires().toTime_t()); #endif imageIds.append(image->imageId()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO images (" " accountId, imageUrl, imageFile, createdTime, expires, imageId) " "VALUES (" " :accountId, :imageUrl, :imageFile, :createdTime, :expires, :imageId)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":imageUrl"), imageUrls); query.bindValue(QStringLiteral(":imageFile"), imageFiles); query.bindValue(QStringLiteral(":createdTime"), createdTimes); query.bindValue(QStringLiteral(":expires"), expireTimes); query.bindValue(QStringLiteral(":imageId"), imageIds); executeBatchSocialCacheQuery(query); } return success; } bool SocialImagesDatabase::createTables(QSqlDatabase database) const { // create the db table QSqlQuery query(database); query.prepare("CREATE TABLE IF NOT EXISTS images (" "accountId INTEGER," "imageUrl TEXT," "imageFile TEXT," "createdTime INTEGER," "expires INTEGER," "imageId STRING)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create images table:" << query.lastError().text(); return false; } return true; } bool SocialImagesDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS images"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete images table:" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/socialimagesdatabase.h000066400000000000000000000066451462333522700223140ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SOCIALIMAGESDATABASE_H #define SOCIALIMAGESDATABASE_H #include "abstractsocialcachedatabase_p.h" #include #include #include class SocialImagePrivate; class SocialImage { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~SocialImage(); static SocialImage::Ptr create(int accountId, const QString &imageUrl, const QString &imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId); int accountId() const; QString imageUrl() const; QString imageFile() const; QDateTime createdTime() const; QDateTime expires() const; QString imageId() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(SocialImage) explicit SocialImage(int accountId, const QString & imageUrl, const QString & imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString &imageId); }; bool operator==(const SocialImage::ConstPtr &image1, const SocialImage::ConstPtr &image2); class SocialImagesDatabasePrivate; class SocialImagesDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit SocialImagesDatabase(); ~SocialImagesDatabase(); void purgeAccount(int accountId); SocialImage::ConstPtr image(const QString &imageUrl) const; SocialImage::ConstPtr imageById(const QString &imageId) const; void addImage(int accountId, const QString & imageUrl, const QString & imageFile, const QDateTime &createdTime, const QDateTime &expires, const QString & imageId = QString()); void removeImage(const QString &imageUrl); void removeImages(QList images); void queryImages(int accountId, const QDateTime &olderThan = QDateTime()); void queryExpired(int accountId); QList images() const; void commit(); Q_SIGNALS: void queryFinished(); protected: bool read(); void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(SocialImagesDatabase) }; #endif // SOCIALIMAGESDATABASE_H libsocialcache-0.2.1/src/lib/socialnetworksyncdatabase.cpp000066400000000000000000000162461462333522700237660ustar00rootroot00000000000000/* * Copyright (C) 2013 Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "socialnetworksyncdatabase.h" #include "abstractsocialcachedatabase_p.h" #include #include #include #include #include #include static const char *SERVICE_NAME = "Sync"; static const char *DATA_TYPE = "Sync"; static const char *DB_NAME = "sociald-sync.db"; static const int VERSION = 3; struct SocialNetworkSyncData { QString serviceName; QString dataType; int accountId; QDateTime timestamp; }; class SocialNetworkSyncDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: explicit SocialNetworkSyncDatabasePrivate(SocialNetworkSyncDatabase *q); virtual ~SocialNetworkSyncDatabasePrivate(); QList queuedData; }; SocialNetworkSyncDatabasePrivate::SocialNetworkSyncDatabasePrivate(SocialNetworkSyncDatabase *q) : AbstractSocialCacheDatabasePrivate( q, QLatin1String(SERVICE_NAME), QLatin1String(DATA_TYPE), QLatin1String(DB_NAME), VERSION) { } SocialNetworkSyncDatabasePrivate::~SocialNetworkSyncDatabasePrivate() { qDeleteAll(queuedData); } SocialNetworkSyncDatabase::SocialNetworkSyncDatabase() : AbstractSocialCacheDatabase(*(new SocialNetworkSyncDatabasePrivate(this))) { } SocialNetworkSyncDatabase::~SocialNetworkSyncDatabase() { wait(); } QList SocialNetworkSyncDatabase::syncedAccounts(const QString &serviceName, const QString &dataType) const { QSqlQuery query = prepare(QStringLiteral( "SELECT DISTINCT accountId FROM syncTimestamps "\ "WHERE serviceName = :serviceName "\ "AND dataType = :dataType")); query.bindValue(":serviceName", serviceName); query.bindValue(":dataType", dataType); bool success = query.exec(); if (!success) { qWarning() << "Failed to query synced accounts" << query.lastError().text(); return QList(); } QList accounts; while (query.next()) { accounts.append(query.value(0).toInt()); } return accounts; } QDateTime SocialNetworkSyncDatabase::lastSyncTimestamp(const QString &serviceName, const QString &dataType, int accountId) const { QSqlQuery query = prepare(QStringLiteral( "SELECT syncTimestamp FROM syncTimestamps "\ "WHERE serviceName = :serviceName "\ "AND accountId = :accountId "\ "AND dataType = :dataType ORDER BY syncTimestamp DESC LIMIT 1")); query.bindValue(":serviceName", serviceName); query.bindValue(":accountId", accountId); query.bindValue(":dataType", dataType); bool success = query.exec(); if (!success) { qWarning() << "Failed to query last synced timestamp" << query.lastError().text(); return QDateTime(); } if (!query.next()) { return QDateTime(); } #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime dateTime = QDateTime::fromSecsSinceEpoch(query.value(0).toUInt()); #else QDateTime dateTime = QDateTime::fromTime_t(query.value(0).toUInt()); #endif return dateTime; } void SocialNetworkSyncDatabase::addSyncTimestamp(const QString &serviceName, const QString &dataType, int accountId, const QDateTime ×tamp) { Q_D(SocialNetworkSyncDatabase); SocialNetworkSyncData *data = new SocialNetworkSyncData; data->serviceName = serviceName; data->dataType = dataType; data->accountId = accountId; data->timestamp = timestamp; QMutexLocker locker(&d->mutex); d->queuedData.append(data); } void SocialNetworkSyncDatabase::commit() { executeWrite(); } bool SocialNetworkSyncDatabase::write() { Q_D(SocialNetworkSyncDatabase); QMutexLocker locker(&d->mutex); const QList insertData = d->queuedData; d->queuedData.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!insertData.isEmpty()) { QVariantList serviceNames; QVariantList dataTypes; QVariantList accountIds; QVariantList timestamps; Q_FOREACH (SocialNetworkSyncData *data, insertData) { serviceNames.append(data->serviceName); dataTypes.append(data->dataType); accountIds.append(data->accountId); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) timestamps.append(data->timestamp.toSecsSinceEpoch()); #else timestamps.append(data->timestamp.toTime_t()); #endif delete data; } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO syncTimestamps (" " accountId, serviceName, dataType, syncTimestamp) " "VALUES (" " :accountId, :serviceName, :dataType, :syncTimestamp)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":serviceName"), serviceNames); query.bindValue(QStringLiteral(":dataType"), dataTypes); query.bindValue(QStringLiteral(":syncTimestamp"), timestamps); executeBatchSocialCacheQuery(query); } return success; } bool SocialNetworkSyncDatabase::createTables(QSqlDatabase database) const { QSqlQuery query (database); // Create the sociald db tables // syncTimestamps = id, accountId, service, dataType, syncTimestamp query.prepare("CREATE TABLE IF NOT EXISTS syncTimestamps ("\ "accountId TEXT, "\ "serviceName TEXT, "\ "dataType TEXT, "\ "syncTimestamp INTEGER, "\ "CONSTRAINT id PRIMARY KEY (accountId, serviceName, dataType))"); if (!query.exec()) { qWarning() << "Unable to create syncTimestamps table" << query.lastError().text(); return false; } return true; } bool SocialNetworkSyncDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS syncTimestamps"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to delete syncTimestamps table" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/socialnetworksyncdatabase.h000066400000000000000000000034301462333522700234220ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SOCIALNETWORKSYNCDATABASE_H #define SOCIALNETWORKSYNCDATABASE_H #include "abstractsocialcachedatabase.h" #include class SocialNetworkSyncDatabasePrivate; class SocialNetworkSyncDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit SocialNetworkSyncDatabase(); ~SocialNetworkSyncDatabase(); QList syncedAccounts(const QString &serviceName, const QString &dataType) const; QDateTime lastSyncTimestamp(const QString &serviceName, const QString &dataType, int accountId) const; void addSyncTimestamp(const QString &serviceName, const QString &dataType, int accountId, const QDateTime ×tamp); void commit(); protected: bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(SocialNetworkSyncDatabase) }; #endif // SOCIALNETWORKSYNCDATABASE_H libsocialcache-0.2.1/src/lib/socialsyncinterface.cpp000066400000000000000000000027061462333522700225440ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "socialsyncinterface.h" #include #include QString SocialSyncInterface::socialNetwork(SocialNetwork sn) { QMetaEnum metaEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SocialNetwork")); return metaEnum.valueToKey(sn); } QString SocialSyncInterface::dataType(DataType t) { QMetaEnum metaEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("DataType")); return metaEnum.valueToKey(t); } QString SocialSyncInterface::profileName(SocialNetwork sn, DataType t) { return QString("%1.%2").arg(socialNetwork(sn).toLower(), dataType(t)); } libsocialcache-0.2.1/src/lib/socialsyncinterface.h000066400000000000000000000031471462333522700222110ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SOCIALSYNCINTERFACE_H #define SOCIALSYNCINTERFACE_H #include class SocialSyncInterface : public QObject { Q_OBJECT Q_ENUMS(SocialNetwork) Q_ENUMS(DataType) public: enum SocialNetwork { InvalidSocialNetwork, Facebook, Twitter, Google, VK, Diaspora, CalDAV, OneDrive, Dropbox }; enum DataType { InvalidDataType, Contacts, Calendars, Notifications, Images, Videos, Posts, Messages, Emails }; Q_INVOKABLE static QString socialNetwork(SocialNetwork sn); Q_INVOKABLE static QString dataType(DataType t); static QString profileName(SocialNetwork sn, DataType t); }; #endif // SOCIALSYNCINTERFACE_H libsocialcache-0.2.1/src/lib/twitternotificationsdatabase.cpp000066400000000000000000000220431462333522700244710ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "twitternotificationsdatabase.h" #include "abstractsocialcachedatabase_p.h" #include "socialsyncinterface.h" #include #include static const char *DB_NAME = "twitterNotifications.db"; static const int VERSION = 1; class TwitterNotificationsDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: explicit TwitterNotificationsDatabasePrivate(TwitterNotificationsDatabase *q); // in the future, we may support more notification information. // for now, we just support retweet and follower information. QMap > setRetweetedTweetCounts; QMap > setFollowerIds; struct { QMap > setRetweetedTweetCounts; QMap > setFollowerIds; } queue; }; TwitterNotificationsDatabasePrivate::TwitterNotificationsDatabasePrivate(TwitterNotificationsDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::Twitter), SocialSyncInterface::dataType(SocialSyncInterface::Notifications), QLatin1String(DB_NAME), VERSION) { } TwitterNotificationsDatabase::TwitterNotificationsDatabase() : AbstractSocialCacheDatabase(*(new TwitterNotificationsDatabasePrivate(this))) { } TwitterNotificationsDatabase::~TwitterNotificationsDatabase() { wait(); } void TwitterNotificationsDatabase::setFollowerIds(int accountId, const QSet &followerIds) { Q_D(TwitterNotificationsDatabase); QMutexLocker locker(&d->mutex); d->setFollowerIds[accountId] = followerIds; } void TwitterNotificationsDatabase::setRetweetedTweetCounts(int accountId, const QHash &tweetCounts) { Q_D(TwitterNotificationsDatabase); QMutexLocker locker(&d->mutex); d->setRetweetedTweetCounts[accountId] = tweetCounts; } void TwitterNotificationsDatabase::sync() { Q_D(TwitterNotificationsDatabase); { // queue changes to write later in write() QMutexLocker locker(&d->mutex); if (d->setFollowerIds.size()) { d->queue.setFollowerIds = d->setFollowerIds; d->setFollowerIds.clear(); } if (d->setRetweetedTweetCounts.size()) { d->queue.setRetweetedTweetCounts = d->setRetweetedTweetCounts; d->setRetweetedTweetCounts.clear(); } } executeWrite(); } QSet TwitterNotificationsDatabase::followerIds(int accountId) { QSqlQuery query; query = prepare(QStringLiteral( "SELECT followerId " "FROM followerIds " "WHERE accountId = :accountId")); query.bindValue(":accountId", accountId); QSet retn; if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query follower ids" << query.lastError().text(); return retn; } while (query.next()) { retn.insert(query.value(0).toString()); } return retn; } QHash TwitterNotificationsDatabase::retweetedTweetCounts(int accountId) { QSqlQuery query; query = prepare(QStringLiteral( "SELECT retweetedTweetId, retweetsCount " "FROM retweetedTweets " "WHERE accountId = :accountId")); query.bindValue(":accountId", accountId); QHash retn; if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query retweeted tweet counts:" << query.lastError().text(); return retn; } while (query.next()) { retn.insert(query.value(0).toString(), query.value(1).toInt()); } return retn; } void TwitterNotificationsDatabase::readFinished() { // emit followersChanged(); } bool TwitterNotificationsDatabase::write() { Q_D(TwitterNotificationsDatabase); // write changes queued during sync() QMutexLocker locker(&d->mutex); QMap > setFollowerIds = d->queue.setFollowerIds; d->queue.setFollowerIds.clear(); QMap > setRetweetedTweetCounts = d->queue.setRetweetedTweetCounts; d->queue.setRetweetedTweetCounts.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!setFollowerIds.isEmpty()) { Q_FOREACH (int accountId, setFollowerIds.keys()) { // first step: delete all data associated with the account. query = prepare(QStringLiteral("DELETE FROM followerIds WHERE accountId = :accountId")); query.bindValue(":accountId", accountId); executeSocialCacheQuery(query); // second step: insert the data set. QVariantList followerIdsForQuery; QVariantList accountIdsForQuery; QSet followerIdsSet = setFollowerIds[accountId]; #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QList followerIdsList(followerIdsSet.begin(), followerIdsSet.end()); Q_FOREACH (const QString &fid, followerIdsList) { #else Q_FOREACH (const QString &fid, followerIdsSet.toList()) { #endif followerIdsForQuery.append(fid); accountIdsForQuery.append(accountId); } query = prepare(QStringLiteral("INSERT INTO followerIds VALUES (:accountId, :followerId)")); query.bindValue(":accountId", accountIdsForQuery); query.bindValue(":followerId", followerIdsForQuery); executeBatchSocialCacheQuery(query); } } if (!setRetweetedTweetCounts.isEmpty()) { Q_FOREACH (int accountId, setRetweetedTweetCounts.keys()) { // first step: delete all data associated with the account. query = prepare(QStringLiteral("DELETE FROM retweetedTweets WHERE accountId = :accountId")); query.bindValue(":accountId", accountId); executeSocialCacheQuery(query); // second step: insert the data set. QVariantList retweetedTweetIdsForQuery; QVariantList retweetCountsForQuery; QVariantList accountIdsForQuery; QList retweetedTweetIds = setRetweetedTweetCounts[accountId].keys(); Q_FOREACH (const QString &tweetId, retweetedTweetIds) { retweetedTweetIdsForQuery.append(tweetId); retweetCountsForQuery.append(setRetweetedTweetCounts[accountId][tweetId]); accountIdsForQuery.append(accountId); } query = prepare(QStringLiteral("INSERT INTO retweetedTweets VALUES (:accountId, :retweetedTweetId, :retweetCount)")); query.bindValue(":accountId", accountIdsForQuery); query.bindValue(":retweetedTweetId", retweetedTweetIdsForQuery); query.bindValue(":retweetCount", retweetCountsForQuery); executeBatchSocialCacheQuery(query); } } return success; } bool TwitterNotificationsDatabase::createTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("CREATE TABLE IF NOT EXISTS followerIds (" "accountId INTEGER NOT NULL," "followerId TEXT NOT NULL," "PRIMARY KEY (accountId, followerId))"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create followerIds table: " << query.lastError().text(); return false; } query.prepare("CREATE TABLE IF NOT EXISTS retweetedTweets (" "accountId INTEGER NOT NULL," "retweetedTweetId TEXT NOT NULL," "retweetsCount INTEGER NOT NULL," "PRIMARY KEY (accountId, retweetedTweetId))"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create followerIds table: " << query.lastError().text(); return false; } return true; } bool TwitterNotificationsDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); if (!query.exec(QStringLiteral("DROP TABLE IF EXISTS followerIds"))) { qWarning() << Q_FUNC_INFO << "Unable to delete followerIds table: " << query.lastError().text(); return false; } if (!query.exec(QStringLiteral("DROP TABLE IF EXISTS retweetedTweets"))) { qWarning() << Q_FUNC_INFO << "Unable to delete retweetedTweets table: " << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/twitternotificationsdatabase.h000066400000000000000000000034411462333522700241370ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TWITTERNOTIFICATIONSDATABASE_H #define TWITTERNOTIFICATIONSDATABASE_H #include "abstractsocialcachedatabase.h" #include #include #include #include class TwitterNotificationsDatabasePrivate; class TwitterNotificationsDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit TwitterNotificationsDatabase(); ~TwitterNotificationsDatabase(); QHash retweetedTweetCounts(int accountId); QSet followerIds(int accountId); void setRetweetedTweetCounts(int accountId, const QHash &retweetCounts); void setFollowerIds(int accountId, const QSet &followerIds); void sync(); protected: void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(TwitterNotificationsDatabase) }; #endif // TWITTERNOTIFICATIONSDATABASE_H libsocialcache-0.2.1/src/lib/twitterpostsdatabase.cpp000066400000000000000000000060711462333522700227730ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "twitterpostsdatabase.h" #include "socialsyncinterface.h" #include static const char *DB_NAME = "twitter.db"; static const char *SCREEN_NAME_KEY = "screen_name"; static const char *RETWEETER_KEY = "retweeter"; static const char *CONSUMER_KEY_KEY = "consumer_key"; static const char *CONSUMER_SECRET_KEY = "consumer_secret"; TwitterPostsDatabase::TwitterPostsDatabase() : AbstractSocialPostCacheDatabase( SocialSyncInterface::socialNetwork(SocialSyncInterface::Twitter), QLatin1String(DB_NAME)) { } TwitterPostsDatabase::~TwitterPostsDatabase() { } void TwitterPostsDatabase::addTwitterPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QString &icon, const QList > &images, const QString &screenName, const QString &retweeter, const QString &consumerKey, const QString &consumerSecret, int account) { QVariantMap extra; extra.insert(SCREEN_NAME_KEY, screenName); extra.insert(RETWEETER_KEY, retweeter); extra.insert(CONSUMER_KEY_KEY, consumerKey); extra.insert(CONSUMER_SECRET_KEY, consumerSecret); addPost(identifier, name, body, timestamp, icon, images, extra, account); } QString TwitterPostsDatabase::screenName(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(SCREEN_NAME_KEY).toString(); } QString TwitterPostsDatabase::retweeter(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(RETWEETER_KEY).toString(); } QString TwitterPostsDatabase::consumerKey(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(CONSUMER_KEY_KEY).toString(); } QString TwitterPostsDatabase::consumerSecret(const SocialPost::ConstPtr &post) { if (post.isNull()) { return QString(); } return post->extra().value(CONSUMER_SECRET_KEY).toString(); } libsocialcache-0.2.1/src/lib/twitterpostsdatabase.h000066400000000000000000000035311462333522700224360ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TWITTERPOSTSDATABASE_H #define TWITTERPOSTSDATABASE_H #include "abstractsocialpostcachedatabase.h" class TwitterPostsDatabase: public AbstractSocialPostCacheDatabase { Q_OBJECT public: explicit TwitterPostsDatabase(); ~TwitterPostsDatabase(); void addTwitterPost(const QString &identifier, const QString &name, const QString &body, const QDateTime ×tamp, const QString &icon, const QList > &images, const QString &screenName, const QString &retweeter, const QString &consumerKey, const QString &consumerSecret, int account); static QString screenName(const SocialPost::ConstPtr &post); static QString retweeter(const SocialPost::ConstPtr &post); static QString consumerKey(const SocialPost::ConstPtr &post); static QString consumerSecret(const SocialPost::ConstPtr &post); }; #endif // TWITTERPOSTSDATABASE_H libsocialcache-0.2.1/src/lib/vkimagesdatabase.cpp000066400000000000000000001366631462333522700220210ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vkimagesdatabase.h" #include "abstractsocialcachedatabase.h" #include "socialsyncinterface.h" #include #include #include #include static const char *DB_NAME = "vk.db"; static const int VERSION = 1; struct VKUserPrivate { explicit VKUserPrivate(const QString &id, const QString &first_name, const QString &last_name, const QString &photo_src, const QString &photo_file, int accountId); QString id; // user id QString first_name; QString last_name; QString photo_src; QString photo_file; int accountId; int photos_count; // transient, not database-stored value! Not used in comparisons etc. }; VKUserPrivate::VKUserPrivate(const QString &id, const QString &first_name, const QString &last_name, const QString &photo_src, const QString &photo_file, int accountId) : id(id), first_name(first_name), last_name(last_name), photo_src(photo_src), photo_file(photo_file), accountId(accountId), photos_count(0) {} VKUser::VKUser(const QString &id, const QString &first_name, const QString &last_name, const QString &photo_src, const QString &photo_file, int accountId) : d_ptr(new VKUserPrivate(id, first_name, last_name, photo_src, photo_file, accountId)) { } VKUser::~VKUser() { } VKUser::Ptr VKUser::create(const QString &id, const QString &first_name, const QString &last_name, const QString &photo_src, const QString &photo_file, int accountId) { return VKUser::Ptr(new VKUser(id, first_name, last_name, photo_src, photo_file, accountId)); } QString VKUser::id() const { Q_D(const VKUser); return d->id; } QString VKUser::firstName() const { Q_D(const VKUser); return d->first_name; } QString VKUser::lastName() const { Q_D(const VKUser); return d->last_name; } QString VKUser::photoSrc() const { Q_D(const VKUser); return d->photo_src; } QString VKUser::photoFile() const { Q_D(const VKUser); return d->photo_file; } int VKUser::accountId() const { Q_D(const VKUser); return d->accountId; } int VKUser::photosCount() const { Q_D(const VKUser); return d->photos_count; } void VKUser::setPhotosCount(int photosCount) { Q_D(VKUser); d->photos_count = photosCount; } bool VKUser::operator==(const VKUser &other) const { Q_D(const VKUser); return d->id == other.d_ptr->id && d->first_name == other.d_ptr->first_name && d->last_name == other.d_ptr->last_name && d->photo_src == other.d_ptr->photo_src && d->photo_file == other.d_ptr->photo_file && d->accountId == other.d_ptr->accountId; } struct VKAlbumPrivate { explicit VKAlbumPrivate(const QString &id, const QString &owner_id, const QString &title, const QString &description, const QString &thumb_src, const QString &thumb_file, int size, int created, int updated, int accountId); QString id; // album id QString owner_id; // user id QString title; QString description; QString thumb_src; QString thumb_file; int size; int created; int updated; int accountId; }; VKAlbumPrivate::VKAlbumPrivate(const QString &id, const QString &owner_id, const QString &title, const QString &description, const QString &thumb_src, const QString &thumb_file, int size, int created, int updated, int accountId) : id(id), owner_id(owner_id), title(title), description(description), thumb_src(thumb_src), thumb_file(thumb_file), size(size), created(created), updated(updated), accountId(accountId) {} VKAlbum::VKAlbum(const QString &id, const QString &owner_id, const QString &title, const QString &description, const QString &thumb_src, const QString &thumb_file, int size, int created, int updated, int accountId) : d_ptr(new VKAlbumPrivate(id, owner_id, title, description, thumb_src, thumb_file, size, created, updated, accountId)) { } VKAlbum::Ptr VKAlbum::create(const QString &id, const QString &owner_id, const QString &title, const QString &description, const QString &thumb_src, const QString &thumb_file, int size, int created, int updated, int accountId) { return VKAlbum::Ptr(new VKAlbum(id, owner_id, title, description, thumb_src, thumb_file, size, created, updated, accountId)); } VKAlbum::~VKAlbum() { } QString VKAlbum::id() const { Q_D(const VKAlbum); return d->id; } QString VKAlbum::ownerId() const { Q_D(const VKAlbum); return d->owner_id; } QString VKAlbum::title() const { Q_D(const VKAlbum); return d->title; } QString VKAlbum::description() const { Q_D(const VKAlbum); return d->description; } QString VKAlbum::thumbSrc() const { Q_D(const VKAlbum); return d->thumb_src; } QString VKAlbum::thumbFile() const { Q_D(const VKAlbum); return d->thumb_file; } int VKAlbum::size() const { Q_D(const VKAlbum); return d->size; } int VKAlbum::created() const { Q_D(const VKAlbum); return d->created; } int VKAlbum::updated() const { Q_D(const VKAlbum); return d->updated; } int VKAlbum::accountId() const { Q_D(const VKAlbum); return d->accountId; } bool VKAlbum::operator==(const VKAlbum &other) const { Q_D(const VKAlbum); return d->id == other.d_ptr->id && d->owner_id == other.d_ptr->owner_id && d->title == other.d_ptr->title && d->description == other.d_ptr->description && d->thumb_src == other.d_ptr->thumb_src && d->thumb_file == other.d_ptr->thumb_file && d->size == other.d_ptr->size && d->created == other.d_ptr->created && d->updated == other.d_ptr->updated && d->accountId == other.d_ptr->accountId; } struct VKImagePrivate { VKImagePrivate(const QString &id, const QString &album_id, const QString &owner_id, const QString &text, const QString &thumb_src, const QString &photo_src, const QString &thumb_file, const QString &photo_file, int width, int height, int date, int accountId); QString id; // photo id QString album_id; // album id QString owner_id; // user id QString text; QString thumb_src; // remote url QString photo_src; // remote url QString thumb_file; // local file QString photo_file; // local file int width; int height; int date; int accountId; }; VKImagePrivate::VKImagePrivate(const QString &id, const QString &album_id, const QString &owner_id, const QString &text, const QString &thumb_src, const QString &photo_src, const QString &thumb_file, const QString &photo_file, int width, int height, int date, int accountId) : id(id), album_id(album_id), owner_id(owner_id), text(text), thumb_src(thumb_src), photo_src(photo_src), thumb_file(thumb_file), photo_file(photo_file), width(width), height(height), date(date), accountId(accountId) {} VKImage::VKImage(const QString &id, const QString &album_id, const QString &owner_id, const QString &text, const QString &thumb_src, const QString &photo_src, const QString &thumb_file, const QString &photo_file, int width, int height, int date, int accountId) : d_ptr(new VKImagePrivate(id, album_id, owner_id, text, thumb_src, photo_src, thumb_file, photo_file, width, height, date, accountId)) { } VKImage::Ptr VKImage::create(const QString &id, const QString &album_id, const QString &owner_id, const QString &text, const QString &thumb_src, const QString &photo_src, const QString &thumb_file, const QString &photo_file, int width, int height, int date, int accountId) { return VKImage::Ptr(new VKImage(id, album_id, owner_id, text, thumb_src, photo_src, thumb_file, photo_file, width, height, date, accountId)); } VKImage::~VKImage() { } QString VKImage::id() const { Q_D(const VKImage); return d->id; } QString VKImage::albumId() const { Q_D(const VKImage); return d->album_id; } QString VKImage::ownerId() const { Q_D(const VKImage); return d->owner_id; } QString VKImage::text() const { Q_D(const VKImage); return d->text; } QString VKImage::thumbSrc() const { Q_D(const VKImage); return d->thumb_src; } QString VKImage::photoSrc() const { Q_D(const VKImage); return d->photo_src; } QString VKImage::thumbFile() const { Q_D(const VKImage); return d->thumb_file; } QString VKImage::photoFile() const { Q_D(const VKImage); return d->photo_file; } int VKImage::width() const { Q_D(const VKImage); return d->width; } int VKImage::height() const { Q_D(const VKImage); return d->height; } int VKImage::date() const { Q_D(const VKImage); return d->date; } int VKImage::accountId() const { Q_D(const VKImage); return d->accountId; } bool VKImage::operator==(const VKImage &other) const { Q_D(const VKImage); return d->id == other.d_ptr->id && d->album_id == other.d_ptr->album_id && d->owner_id == other.d_ptr->owner_id && d->text == other.d_ptr->text && d->thumb_src == other.d_ptr->thumb_src && d->photo_src == other.d_ptr->photo_src && d->thumb_file == other.d_ptr->thumb_file && d->photo_file == other.d_ptr->photo_file; } class VKImagesDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: enum QueryType { Users, Albums, Images }; explicit VKImagesDatabasePrivate(VKImagesDatabase *q); ~VKImagesDatabasePrivate(); private: Q_DECLARE_PUBLIC(VKImagesDatabase) static void clearCachedImages(QSqlQuery &query); QList queryUsers(int accountId) const; QList queryAlbums(int accountId, const QString &vkUserId, const QString &vkAlbumId) const; QList queryImages(int accountId, const QString &vkUserId, const QString &vkAlbumId, const QString &vkImageId) const; struct { QList purgeAccounts; QList removeUsers; QList removeAlbums; QList removeImages; QList insertUsers; QList insertAlbums; QList insertImages; QList > updateThumbnailFiles; QList > updateImageFiles; } queue; struct { QueryType type; int accountId; QString ownerId; QString albumId; QList users; QList albums; QList images; } query; struct { QList users; QList albums; QList images; } result; }; VKImagesDatabasePrivate::VKImagesDatabasePrivate(VKImagesDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::VK), SocialSyncInterface::dataType(SocialSyncInterface::Images), QLatin1String(DB_NAME), VERSION) { } VKImagesDatabasePrivate::~VKImagesDatabasePrivate() { } void VKImagesDatabasePrivate::clearCachedImages(QSqlQuery &query) { while (query.next()) { QString thumb = query.value(0).toString(); QString image = query.value(1).toString(); if (!thumb.isEmpty()) { QFile thumbFile (thumb); if (thumbFile.exists()) { thumbFile.remove(); } } if (!image.isEmpty()) { QFile imageFile (image); if (imageFile.exists()) { imageFile.remove(); } } } } QList VKImagesDatabasePrivate::queryUsers(int accountId) const { QList retn; QString queryString = QStringLiteral( "SELECT accountId, vkUserId, first_name, last_name, photo_src, photo_file " "FROM users "); if (accountId != 0) { queryString.append(QStringLiteral("WHERE accountId = :accountId ")); } queryString.append(QStringLiteral("ORDER BY first_name ASC")); QSqlQuery query = q_func()->prepare(queryString); if (accountId != 0) { query.bindValue(QStringLiteral(":accountId"), accountId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query users:" << query.lastError().text(); return retn; } while (query.next()) { VKUser::Ptr user = VKUser::create(query.value(1).toString(), query.value(2).toString(), query.value(3).toString(), query.value(4).toString(), query.value(5).toString(), query.value(0).toInt()); // now determine the (transient) photos count for the user. QString countQueryString = QStringLiteral( "SELECT count(*) FROM images WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId;"); QSqlQuery countQuery = q_func()->prepare(countQueryString); countQuery.bindValue(QStringLiteral(":accountId"), user->accountId()); countQuery.bindValue(QStringLiteral(":vkOwnerId"), user->id()); if (!countQuery.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query user photos count:" << query.lastError().text(); return retn; } if (countQuery.next()) { user->setPhotosCount(query.value(0).toInt()); } retn.append(user); } query.finish(); return retn; } QList VKImagesDatabasePrivate::queryAlbums(int accountId, const QString &vkOwnerId, const QString &vkAlbumId) const { QList retn; QString queryString = QStringLiteral("SELECT accountId," " vkOwnerId," " vkAlbumId," " title," " description," " thumb_src," " thumb_file," " size," " created," " updated " "FROM albums "); if (accountId != 0) { queryString.append(QStringLiteral("WHERE accountId = :accountId ")); if (!vkOwnerId.isEmpty()) { queryString.append(QStringLiteral("AND vkOwnerId = :vkOwnerId ")); if (!vkAlbumId.isEmpty()) { queryString.append(QStringLiteral("AND vkAlbumId = :vkAlbumId ")); } } } else if (!vkAlbumId.isEmpty()) { queryString.append(QStringLiteral("WHERE vkAlbumId = :vkAlbumId ")); } queryString.append(QStringLiteral("ORDER BY vkOwnerId DESC, created ASC")); QSqlQuery query = q_func()->prepare(queryString); if (accountId != 0) { query.bindValue(":accountId", accountId); if (!vkOwnerId.isEmpty()) { query.bindValue(":vkOwnerId", vkOwnerId); if (!vkAlbumId.isEmpty()) { query.bindValue(":vkAlbumId", vkAlbumId); } } } else if (!vkAlbumId.isEmpty()) { query.bindValue(":vkAlbumId", vkAlbumId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query albums:" << query.lastError().text(); return retn; } while (query.next()) { retn.append(VKAlbum::create(query.value(2).toString(), query.value(1).toString(), query.value(3).toString(), query.value(4).toString(), query.value(5).toString(), query.value(6).toString(), query.value(7).toInt(), query.value(8).toInt(), query.value(9).toInt(), query.value(0).toInt())); } query.finish(); return retn; } QList VKImagesDatabasePrivate::queryImages(int accountId, const QString &vkOwnerId, const QString &vkAlbumId, const QString &vkImageId) const { QList retn; QString queryString = QLatin1String("SELECT vkImageId," " vkAlbumId," " vkOwnerId," " text," " thumb_src," " photo_src," " thumb_file," " photo_file," " width," " height," " date," " accountId " "FROM images "); if (accountId > 0) { queryString.append(QLatin1String("WHERE accountId = :accountId ")); if (!vkOwnerId.isEmpty()) { queryString.append(QLatin1String("AND vkOwnerId = :vkOwnerId ")); if (!vkAlbumId.isEmpty()) { queryString.append(QLatin1String("AND vkAlbumId = :vkAlbumId ")); if (!vkImageId.isEmpty()) { queryString.append(QLatin1String("AND vkImageId = :vkImageId ")); } } } } else if (!vkImageId.isEmpty()) { queryString.append(QLatin1String("WHERE vkImageId = :vkImageId ")); } queryString.append(QLatin1String("ORDER BY date ASC")); QSqlQuery query = q_func()->prepare(queryString); if (accountId > 0) { query.bindValue(":accountId", accountId); if (!vkOwnerId.isEmpty()) { query.bindValue(":vkOwnerId", vkOwnerId); if (!vkAlbumId.isEmpty()) { query.bindValue(":vkAlbumId", vkAlbumId); if (!vkImageId.isEmpty()) { query.bindValue(":vkImageId", vkImageId); } } } } else if (!vkImageId.isEmpty()) { query.bindValue(":vkImageId", vkImageId); } if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query images:" << query.lastError().text(); return retn; } while (query.next()) { retn.append(VKImage::create(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), query.value(3).toString(), query.value(4).toString(), query.value(5).toString(), query.value(6).toString(), query.value(7).toString(), query.value(8).toInt(), query.value(9).toInt(), query.value(10).toInt(), query.value(11).toInt())); } query.finish(); return retn; } //--------------------------------------------------------------------- VKImagesDatabase::VKImagesDatabase() : AbstractSocialCacheDatabase(*(new VKImagesDatabasePrivate(this))) { } VKImagesDatabase::~VKImagesDatabase() { wait(); } void VKImagesDatabase::addUser(const VKUser::ConstPtr &vkUser) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.insertUsers.append(vkUser); } void VKImagesDatabase::removeUser(const VKUser::ConstPtr &vkUser) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeUsers.append(vkUser); } void VKImagesDatabase::addAlbum(const VKAlbum::ConstPtr &vkAlbum) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.insertAlbums.append(vkAlbum); } void VKImagesDatabase::addAlbums(const QList &vkAlbums) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.insertAlbums.append(vkAlbums); } void VKImagesDatabase::removeAlbum(const VKAlbum::ConstPtr &vkAlbum) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums.append(vkAlbum); } void VKImagesDatabase::removeAlbums(const QList &vkAlbums) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeAlbums += vkAlbums; } void VKImagesDatabase::addImage(const VKImage::ConstPtr &vkImage) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.insertImages.append(vkImage); } void VKImagesDatabase::addImages(const QList &vkImages) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.insertImages.append(vkImages); } void VKImagesDatabase::updateImageThumbnail(const VKImage::ConstPtr &vkImage, const QString &thumb_file) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateThumbnailFiles.append(qMakePair(vkImage, thumb_file)); } void VKImagesDatabase::updateImageFile(const VKImage::ConstPtr &vkImage, const QString &photo_file) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.updateImageFiles.append(qMakePair(vkImage, photo_file)); } void VKImagesDatabase::removeImage(const VKImage::ConstPtr &vkImage) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages.append(vkImage); } void VKImagesDatabase::removeImages(const QList &vkImages) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.removeImages += vkImages; } void VKImagesDatabase::purgeAccount(int accountId) { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); d->queue.purgeAccounts.append(accountId); } void VKImagesDatabase::commit() { executeWrite(); } VKUser::ConstPtr VKImagesDatabase::user(int accountId) const { Q_D(const VKImagesDatabase); QList users = d->queryUsers(accountId); if (users.size() == 0) { qWarning() << Q_FUNC_INFO << "No user in database for account:" << accountId; return VKUser::Ptr(); } else if (users.size() > 1) { qWarning() << Q_FUNC_INFO << "Multiple users in database for account:" << accountId; // shouldn't happen, but return the first one anyway. } return users[0]; } VKAlbum::ConstPtr VKImagesDatabase::album(int accountId, const QString &vkUserId, const QString &vkAlbumId) const { Q_D(const VKImagesDatabase); QList albums = d->queryAlbums(accountId, vkUserId, vkAlbumId); if (albums.size() == 0) { qWarning() << Q_FUNC_INFO << "No album in database for account:" << accountId << "user:" << vkUserId << "album:" << vkAlbumId; return VKAlbum::Ptr(); } else if (albums.size() > 1) { qWarning() << Q_FUNC_INFO << "Multiple albums in database for account:" << accountId << "user:" << vkUserId << "album:" << vkAlbumId; // shouldn't happen, but return the first one anyway. } return albums[0]; } VKAlbum::ConstPtr VKImagesDatabase::album(const QString &vkAlbumId) const { Q_D(const VKImagesDatabase); QList albums = d->queryAlbums(0, QString(), vkAlbumId); if (albums.size() == 0) { qWarning() << Q_FUNC_INFO << "No album in database for: " << vkAlbumId; return VKAlbum::Ptr(); } else if (albums.size() > 1) { qWarning() << Q_FUNC_INFO << "Multiple albums in database for: " << vkAlbumId; // shouldn't happen, but return the first one anyway. } return albums[0]; } VKImage::ConstPtr VKImagesDatabase::image(int accountId, const QString &vkUserId, const QString &vkAlbumId, const QString &vkImageId) const { Q_D(const VKImagesDatabase); QList images = d->queryImages(accountId, vkUserId, vkAlbumId, vkImageId); if (images.size() == 0) { qWarning() << Q_FUNC_INFO << "No image in database for account:" << accountId << "user:" << vkUserId << "album:" << vkAlbumId << "image:" << vkImageId; return VKImage::Ptr(); } else if (images.size() > 1) { qWarning() << Q_FUNC_INFO << "Multiple images in database for account:" << accountId << "user:" << vkUserId << "album:" << vkAlbumId << "image:" << vkImageId; // shouldn't happen, but return the first one anyway. } return images[0]; } VKImage::ConstPtr VKImagesDatabase::image(const QString &vkImageId) const { Q_D(const VKImagesDatabase); QList images = d->queryImages(0, QString(), QString(), vkImageId); if (images.size() == 0) { qWarning() << Q_FUNC_INFO << "No VK image found for:" << vkImageId; return VKImage::Ptr(); } else if (images.size() > 1) { qWarning() << Q_FUNC_INFO << "Multiple images in database for vkImageId: " << vkImageId; // shouldn't happen, but return the first one anyway. } return images[0]; } QList VKImagesDatabase::albums(int accountId, const QString &vkUserId) const { Q_D(const VKImagesDatabase); return d->queryAlbums(accountId, vkUserId, QString()); } QList VKImagesDatabase::images(int accountId, const QString &vkUserId, const QString &vkAlbumId) const { Q_D(const VKImagesDatabase); return d->queryImages(accountId, vkUserId, vkAlbumId, QString()); } QList VKImagesDatabase::users() const { return d_func()->result.users; } QList VKImagesDatabase::images() const { return d_func()->result.images; } QList VKImagesDatabase::albums() const { return d_func()->result.albums; } void VKImagesDatabase::queryUsers() { Q_D(VKImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = VKImagesDatabasePrivate::Users; d->query.accountId = 0; d->query.ownerId = QString(); d->query.albumId = QString(); } executeRead(); } void VKImagesDatabase::queryAlbums(int accountId, const QString &vkUserId) { Q_D(VKImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = VKImagesDatabasePrivate::Albums; d->query.accountId = accountId; d->query.ownerId = vkUserId; d->query.albumId = QString(); } executeRead(); } void VKImagesDatabase::queryUserImages(int accountId, const QString &vkUserId) { Q_D(VKImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = VKImagesDatabasePrivate::Images; d->query.accountId = accountId; d->query.ownerId = vkUserId; d->query.albumId = QString(); } executeRead(); } void VKImagesDatabase::queryAlbumImages(int accountId, const QString &vkUserId, const QString &vkAlbumId) { Q_D(VKImagesDatabase); { QMutexLocker locker(&d->mutex); d->query.type = VKImagesDatabasePrivate::Images; d->query.accountId = accountId; d->query.ownerId = vkUserId; d->query.albumId = vkAlbumId; } executeRead(); } bool VKImagesDatabase::read() { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); int queryAccountId = d->query.accountId; QString queryOwnerId = d->query.ownerId; QString queryAlbumId = d->query.albumId; switch (d->query.type) { case VKImagesDatabasePrivate::Users: { locker.unlock(); QList users = d->queryUsers(queryAccountId); locker.relock(); d->query.users = users; return true; } case VKImagesDatabasePrivate::Albums: { locker.unlock(); QList albums = d->queryAlbums(queryAccountId, queryOwnerId, queryAlbumId); locker.relock(); d->query.albums = albums; return true; } case VKImagesDatabasePrivate::Images: { locker.unlock(); QList images = d->queryImages(queryAccountId, queryOwnerId, queryAlbumId, QString()); locker.relock(); d->query.images = images; return true; } default: return false; } } void VKImagesDatabase::readFinished() { Q_D(VKImagesDatabase); { QMutexLocker locker(&d->mutex); d->result.users = d->query.users; d->result.albums = d->query.albums; d->result.images = d->query.images; d->query.users.clear(); d->query.albums.clear(); d->query.images.clear(); } emit queryFinished(); } bool VKImagesDatabase::write() { Q_D(VKImagesDatabase); QMutexLocker locker(&d->mutex); qWarning() << "Number of queued users being saved:" << d->queue.insertUsers.count(); qWarning() << "Number of queued albums being saved:" << d->queue.insertAlbums.count(); qWarning() << "Number of queued images being saved:" << d->queue.insertImages.count(); qWarning() << "Number of queued thumbnail files being updated:" << d->queue.updateThumbnailFiles.count(); qWarning() << "Number of queued image files being updated:" << d->queue.updateImageFiles.count(); const QList purgeAccounts = d->queue.purgeAccounts; const QList removeUsers = d->queue.removeUsers; const QList removeAlbums = d->queue.removeAlbums; const QList removeImages = d->queue.removeImages; const QList insertUsers = d->queue.insertUsers; const QList insertAlbums = d->queue.insertAlbums; const QList insertImages = d->queue.insertImages; const QList > updateThumbnailFiles = d->queue.updateThumbnailFiles; const QList > updateImageFiles = d->queue.updateImageFiles; d->queue.purgeAccounts.clear(); d->queue.removeUsers.clear(); d->queue.removeAlbums.clear(); d->queue.removeImages.clear(); d->queue.insertUsers.clear(); d->queue.insertAlbums.clear(); d->queue.insertImages.clear(); d->queue.updateThumbnailFiles.clear(); d->queue.updateImageFiles.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!purgeAccounts.isEmpty()) { // delete image files and build bindable accountIds list QVariantList accountIds; Q_FOREACH (int accountId, purgeAccounts) { accountIds << accountId; query = prepare(QStringLiteral( "SELECT DISTINCT thumb_file, photo_file " "FROM images " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountId); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query while purging accounts:" << query.lastError().text(); } else { d->clearCachedImages(query); } } // delete the data from the database. query = prepare(QStringLiteral( "DELETE FROM users " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } Q_FOREACH (const VKUser::ConstPtr &user, removeUsers) { // delete image files query = prepare(QStringLiteral( "SELECT DISTINCT thumb_file, photo_file " "FROM images " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId")); query.bindValue(QStringLiteral(":accountId"), user->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), user->id()); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query while removing albums:" << query.lastError().text(); } else { d->clearCachedImages(query); } // delete the data from the database. query = prepare(QStringLiteral( "DELETE FROM users " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId")); query.bindValue(QStringLiteral(":accountId"), user->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), user->id()); executeSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId")); query.bindValue(QStringLiteral(":accountId"), user->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), user->id()); executeSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId")); query.bindValue(QStringLiteral(":accountId"), user->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), user->id()); executeSocialCacheQuery(query); } Q_FOREACH (const VKAlbum::ConstPtr &album, removeAlbums) { // delete image files query = prepare(QStringLiteral( "SELECT DISTINCT thumb_file, photo_file " "FROM images " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId")); query.bindValue(QStringLiteral(":accountId"), album->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), album->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), album->id()); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query while removing albums:" << query.lastError().text(); } else { d->clearCachedImages(query); } // delete the data from the database. query = prepare(QStringLiteral( "DELETE FROM albums " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId")); query.bindValue(QStringLiteral(":accountId"), album->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), album->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), album->id()); executeSocialCacheQuery(query); query = prepare(QStringLiteral( "DELETE FROM images " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId")); query.bindValue(QStringLiteral(":accountId"), album->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), album->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), album->id()); executeSocialCacheQuery(query); } Q_FOREACH (const VKImage::ConstPtr &image, removeImages) { // delete image files query = prepare(QStringLiteral( "SELECT DISTINCT thumb_file, photo_file " "FROM images " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId AND vkImageId = :vkImageId")); query.bindValue(QStringLiteral(":accountId"), image->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), image->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), image->albumId()); query.bindValue(QStringLiteral(":vkImageId"), image->id()); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to exec cached images selection query while removing images:" << query.lastError().text(); } else { d->clearCachedImages(query); } // delete the data from the database. query = prepare(QStringLiteral( "DELETE FROM images " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId AND vkImageId = :vkImageId")); query.bindValue(QStringLiteral(":accountId"), image->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), image->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), image->albumId()); query.bindValue(QStringLiteral(":vkImageId"), image->id()); executeSocialCacheQuery(query); } if (!insertUsers.isEmpty()) { QVariantList userIds, firstNames, lastNames, photoSrcs, photoFiles, accountIds; Q_FOREACH (const VKUser::ConstPtr &user, insertUsers) { userIds.append(user->id()); firstNames.append(user->firstName()); lastNames.append(user->lastName()); photoSrcs.append(user->photoSrc()); photoFiles.append(user->photoFile()); accountIds.append(user->accountId()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO users (" " accountId, vkUserId, first_name, last_name, photo_src, photo_file) " "VALUES (" " :accountId, :vkUserId, :first_name, :last_name, :photo_src, :photo_file);")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":vkUserId"), userIds); query.bindValue(QStringLiteral(":first_name"), firstNames); query.bindValue(QStringLiteral(":last_name"), lastNames); query.bindValue(QStringLiteral(":photo_src"), photoSrcs); query.bindValue(QStringLiteral(":photo_file"), photoFiles); executeBatchSocialCacheQuery(query); } if (!insertAlbums.isEmpty()) { QVariantList accountIds, vkOwnerIds, vkAlbumIds, titles, descriptions, thumbSrcs, sizes, createds, updateds, thumbFiles; Q_FOREACH (const VKAlbum::ConstPtr &album, insertAlbums) { accountIds.append(album->accountId()); vkOwnerIds.append(album->ownerId()); vkAlbumIds.append(album->id()); titles.append(album->title()); descriptions.append(album->description()); thumbSrcs.append(album->thumbSrc()); sizes.append(album->size()); createds.append(album->created()); updateds.append(album->updated()); thumbFiles.append(album->thumbFile()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO albums(" " accountId, vkOwnerId, vkAlbumId, title, description, thumb_src, size, created, updated, thumb_file) " "VALUES (" " :accountId, :vkOwnerId, :vkAlbumId, :title, :description, :thumb_src, :size, :created, :updated, :thumb_file)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":vkOwnerId"), vkOwnerIds); query.bindValue(QStringLiteral(":vkAlbumId"), vkAlbumIds); query.bindValue(QStringLiteral(":title"), titles); query.bindValue(QStringLiteral(":description"), descriptions); query.bindValue(QStringLiteral(":thumb_src"), thumbSrcs); query.bindValue(QStringLiteral(":size"), sizes); query.bindValue(QStringLiteral(":created"), createds); query.bindValue(QStringLiteral(":updated"), updateds); query.bindValue(QStringLiteral(":thumb_file"), thumbFiles); executeBatchSocialCacheQuery(query); } if (!insertImages.isEmpty()) { QVariantList accountIds, vkOwnerIds, vkAlbumIds, vkImageIds, texts, thumbSrcs, photoSrcs, widths, heights, dates, thumbFiles, photoFiles; Q_FOREACH (const VKImage::ConstPtr &image, insertImages) { accountIds.append(image->accountId()); vkOwnerIds.append(image->ownerId()); vkAlbumIds.append(image->albumId()); vkImageIds.append(image->id()); texts.append(image->text()); thumbSrcs.append(image->thumbSrc()); photoSrcs.append(image->photoSrc()); widths.append(image->width()); heights.append(image->height()); dates.append(image->date()); thumbFiles.append(image->thumbFile()); photoFiles.append(image->photoFile()); } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO images (" " accountId, vkOwnerId, vkAlbumId, vkImageId, text, thumb_src, photo_src, width, height, date, thumb_file, photo_file) " "VALUES (" " :accountId, :vkOwnerId, :vkAlbumId, :vkImageId, :text, :thumb_src, :photo_src, :width, :height, :date, :thumb_file, :photo_file);")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":vkOwnerId"), vkOwnerIds); query.bindValue(QStringLiteral(":vkAlbumId"), vkAlbumIds); query.bindValue(QStringLiteral(":vkImageId"), vkImageIds); query.bindValue(QStringLiteral(":text"), texts); query.bindValue(QStringLiteral(":thumb_src"), thumbSrcs); query.bindValue(QStringLiteral(":photo_src"), photoSrcs); query.bindValue(QStringLiteral(":width"), widths); query.bindValue(QStringLiteral(":height"), heights); query.bindValue(QStringLiteral(":date"), dates); query.bindValue(QStringLiteral(":thumb_file"), thumbFiles); query.bindValue(QStringLiteral(":photo_file"), photoFiles); executeBatchSocialCacheQuery(query); } for (int i = 0; i < updateThumbnailFiles.size(); ++i) { query = prepare(QStringLiteral( "UPDATE images " "SET thumb_file = :thumb_file " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId AND vkImageId = :vkImageId")); query.bindValue(QStringLiteral(":thumb_file"), updateThumbnailFiles[i].second); query.bindValue(QStringLiteral(":accountId"), updateThumbnailFiles[i].first->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), updateThumbnailFiles[i].first->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), updateThumbnailFiles[i].first->albumId()); query.bindValue(QStringLiteral(":vkImageId"), updateThumbnailFiles[i].first->id()); executeSocialCacheQuery(query); } for (int i = 0; i < updateImageFiles.size(); ++i) { query = prepare(QStringLiteral( "UPDATE images " "SET photo_file = :photo_file " "WHERE accountId = :accountId AND vkOwnerId = :vkOwnerId AND vkAlbumId = :vkAlbumId AND vkImageId = :vkImageId")); query.bindValue(QStringLiteral(":photo_file"), updateImageFiles[i].second); query.bindValue(QStringLiteral(":accountId"), updateImageFiles[i].first->accountId()); query.bindValue(QStringLiteral(":vkOwnerId"), updateImageFiles[i].first->ownerId()); query.bindValue(QStringLiteral(":vkAlbumId"), updateImageFiles[i].first->albumId()); query.bindValue(QStringLiteral(":vkImageId"), updateImageFiles[i].first->id()); executeSocialCacheQuery(query); } return success; } bool VKImagesDatabase::createTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare( "CREATE TABLE IF NOT EXISTS images (" "accountId INTEGER NOT NULL," "vkOwnerId TEXT NOT NULL," "vkAlbumId TEXT NOT NULL," "vkImageId TEXT NOT NULL," "text TEXT," "thumb_src TEXT," "photo_src TEXT," "width INTEGER," "height INTEGER," "date INTEGER," "thumb_file TEXT," "photo_file TEXT," "PRIMARY KEY (accountId, vkOwnerId, vkAlbumId, vkImageId) )"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create images table:" << query.lastError().text(); return false; } query.prepare( "CREATE TABLE IF NOT EXISTS albums (" "accountId INTEGER NOT NULL," "vkOwnerId TEXT NOT NULL," "vkAlbumId TEXT NOT NULL," "title TEXT," "description TEXT," "thumb_src TEXT," "size INTEGER," "created INTEGER," "updated INTEGER," "thumb_file TEXT," "PRIMARY KEY (accountId, vkOwnerId, vkAlbumId) )"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create albums table:" << query.lastError().text(); return false; } struct VKUser { QString id; // user id QString first_name; QString last_name; QString photo_src; int accountId; }; query.prepare( "CREATE TABLE IF NOT EXISTS users (" "accountId INTEGER NOT NULL," "vkUserId TEXT NOT NULL," "first_name TEXT," "last_name TEXT," "photo_src TEXT," "photo_file TEXT," "PRIMARY KEY (accountId, vkUserId) )"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create users table:" << query.lastError().text(); return false; } return true; } bool VKImagesDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS images"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete images table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS albums"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete albums table:" << query.lastError().text(); return false; } query.prepare("DROP TABLE IF EXISTS users"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to delete users table:" << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/vkimagesdatabase.h000066400000000000000000000167251462333522700214620ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKIMAGESDATABASE_H #define VKIMAGESDATABASE_H #include "abstractsocialcachedatabase_p.h" #include #include #include class VKUserPrivate; class VKUser { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~VKUser(); static VKUser::Ptr create(const QString &id, const QString &first_name, const QString &last_name, const QString &photo_src, const QString &photo_file, int accountId); QString id() const; QString firstName() const; QString lastName() const; QString photoSrc() const; QString photoFile() const; int accountId() const; int photosCount() const; void setPhotosCount(int photosCount); bool operator==(const VKUser &other) const; bool operator!=(const VKUser &other) const { return !(*this == other); } protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(VKUser) explicit VKUser(const QString &id, const QString &first_name, const QString &last_name, const QString &photo_src, const QString &photo_file, int accountId); }; class VKAlbumPrivate; class VKAlbum { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; static VKAlbum::Ptr create(const QString &id, const QString &owner_id, const QString &title, const QString &description, const QString &thumb_src, const QString &thumb_file, int size, int created, int updated, int accountId); virtual ~VKAlbum(); QString id() const; // album id QString ownerId() const; // user id QString title() const; QString description() const; QString thumbSrc() const; QString thumbFile() const; int size() const; int created() const; int updated() const; int accountId() const; bool operator==(const VKAlbum &other) const; bool operator!=(const VKAlbum &other) const { return !(*this == other); } protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(VKAlbum) explicit VKAlbum(const QString &id, const QString &owner_id, const QString &title, const QString &description, const QString &thumb_src, const QString &thumb_file, int size, int created, int updated, int accountId); }; class VKImagePrivate; class VKImage { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; static VKImage::Ptr create(const QString &id, const QString &album_id, const QString &owner_id, const QString &text, const QString &thumb_src, const QString &photo_src, const QString &thumb_file, const QString &photo_file, int width, int height, int date, int accountId); virtual ~VKImage(); QString id() const; // photo id QString albumId() const; // album id QString ownerId() const; // user id QString text() const; QString thumbSrc() const; // remote url QString photoSrc() const; // remote url QString thumbFile() const; // local file QString photoFile() const; // local file int width() const; int height() const; int date() const; int accountId() const; bool operator==(const VKImage &other) const; bool operator!=(const VKImage &other) const { return !(*this == other); } protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(VKImage) explicit VKImage(const QString &id, const QString &album_id, const QString &owner_id, const QString &text, const QString &thumb_src, const QString &photo_src, const QString &thumb_file, const QString &photo_file, int width, int height, int date, int accountId); }; class VKImagesDatabasePrivate; class VKImagesDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit VKImagesDatabase(); ~VKImagesDatabase(); // User data manipulation, not applied to db until commit() void addUser(const VKUser::ConstPtr &vkUser); void removeUser(const VKUser::ConstPtr &vkUser); // Album data manipulation, not applied to db until commit() void addAlbum(const VKAlbum::ConstPtr &vkAlbum); void addAlbums(const QList &vkAlbums); void removeAlbum(const VKAlbum::ConstPtr &vkAlbum); void removeAlbums(const QList &vkAlbums); // Images data manipulation, not applied to db until commit() void addImage(const VKImage::ConstPtr &vkImage); void addImages(const QList &vkImages); void updateImageThumbnail(const VKImage::ConstPtr &vkImage, const QString &thumb_file); void updateImageFile(const VKImage::ConstPtr &vkImage, const QString &photo_file); void removeImage(const VKImage::ConstPtr &vkImage); void removeImages(const QList &vkImages); // Purge all data associated with a given account, not applied to db until commit() void purgeAccount(int accountId); // commit changes in memory to disk. void commit(); // methods to perform synchronous queries. For use by sync adapters only! VKUser::ConstPtr user(int accountId) const; VKAlbum::ConstPtr album(int accountId, const QString &vkUserId, const QString &vkAlbumId) const; VKAlbum::ConstPtr album(const QString &vkAlbumId) const; VKImage::ConstPtr image(int accountId, const QString &vkUserId, const QString &vkAlbumId, const QString &vkImageId) const; VKImage::ConstPtr image(const QString &vkImageId) const; QList albums(int accountId, const QString &vkUserId) const; QList images(int accountId, const QString &vkUserId, const QString &vkAlbumId) const; // methods to perform asynchronous queries and retrieve the results of those queries. void queryUsers(); QList users() const; void queryAlbums(int accountId = 0, const QString &vkUserId = QString()); QList albums() const; void queryUserImages(int accountId = 0, const QString &vkUserId = QString()); void queryAlbumImages(int accountId, const QString &vkUserId, const QString &vkAlbumId); QList images() const; Q_SIGNALS: void queryFinished(); protected: bool read(); void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(VKImagesDatabase) }; #endif // VKIMAGESDATABASE_H libsocialcache-0.2.1/src/lib/vknotificationsdatabase.cpp000066400000000000000000000327721462333522700234210ustar00rootroot00000000000000/* * Copyright (C) 2014 Jolla Ltd. * Contact: Bea Lam * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vknotificationsdatabase.h" #include "abstractsocialcachedatabase_p.h" #include "socialsyncinterface.h" #include #include static const char *DB_NAME = "vkNotifications.db"; static const int VERSION = 1; struct VKNotificationPrivate { explicit VKNotificationPrivate(const QString &identifier, int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime); QString m_id; int m_accountId; QString m_type; QString m_fromId; QString m_fromName; QString m_fromIcon; QString m_toId; QDateTime m_createdTime; }; VKNotificationPrivate::VKNotificationPrivate(const QString &identifier, int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime) : m_id(identifier) , m_accountId(accountId) , m_type(type) , m_fromId(fromId) , m_fromName(fromName) , m_fromIcon(fromIcon) , m_toId(toId) , m_createdTime(createdTime) { } VKNotification::VKNotification(const QString &identifier, int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime) : d_ptr(new VKNotificationPrivate(identifier, accountId, type, fromId, fromName, fromIcon, toId, createdTime)) { } VKNotification::Ptr VKNotification::create(const QString &identifier, int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime) { return VKNotification::Ptr(new VKNotification(identifier, accountId, type, fromId, fromName, fromIcon, toId, createdTime)); } VKNotification::~VKNotification() { } QString VKNotification::identifier() const { Q_D(const VKNotification); return d->m_id; } QString VKNotification::type() const { Q_D(const VKNotification); return d->m_type; } QString VKNotification::fromId() const { Q_D(const VKNotification); return d->m_fromId; } QString VKNotification::fromName() const { Q_D(const VKNotification); return d->m_fromName; } QString VKNotification::fromIcon() const { Q_D(const VKNotification); return d->m_fromIcon; } QString VKNotification::toId() const { Q_D(const VKNotification); return d->m_toId; } QDateTime VKNotification::createdTime() const { Q_D(const VKNotification); return d->m_createdTime; } int VKNotification::accountId() const { Q_D(const VKNotification); return d->m_accountId; } class VKNotificationsDatabasePrivate: public AbstractSocialCacheDatabasePrivate { public: explicit VKNotificationsDatabasePrivate(VKNotificationsDatabase *q); QMap > insertNotifications; QList removeNotificationsFromAccounts; QStringList removeNotifications; struct { QMap > insertNotifications; QList removeNotificationsFromAccounts; QStringList removeNotifications; } queue; }; VKNotificationsDatabasePrivate::VKNotificationsDatabasePrivate(VKNotificationsDatabase *q) : AbstractSocialCacheDatabasePrivate( q, SocialSyncInterface::socialNetwork(SocialSyncInterface::VK), SocialSyncInterface::dataType(SocialSyncInterface::Notifications), QLatin1String(DB_NAME), VERSION) { } VKNotificationsDatabase::VKNotificationsDatabase() : AbstractSocialCacheDatabase(*(new VKNotificationsDatabasePrivate(this))) { } VKNotificationsDatabase::~VKNotificationsDatabase() { wait(); } void VKNotificationsDatabase::addVKNotification(int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime) { Q_D(VKNotificationsDatabase); d->insertNotifications[accountId].append(VKNotification::create(QString(), accountId, type, fromId, fromName, fromIcon, toId, createdTime)); } void VKNotificationsDatabase::removeNotifications(int accountId) { Q_D(VKNotificationsDatabase); QMutexLocker locker(&d->mutex); if (!d->queue.removeNotificationsFromAccounts.contains(accountId)) { d->queue.removeNotificationsFromAccounts.append(accountId); } d->queue.insertNotifications.remove(accountId); } void VKNotificationsDatabase::removeNotification(const QString ¬ificationId) { Q_D(VKNotificationsDatabase); QMutexLocker locker(&d->mutex); if (!d->queue.removeNotifications.contains(notificationId)) { d->queue.removeNotifications.append(notificationId); } } void VKNotificationsDatabase::removeNotifications(const QStringList ¬ificationIds) { Q_D(VKNotificationsDatabase); QMutexLocker locker(&d->mutex); Q_FOREACH(const QString notifId, notificationIds) { removeNotification(notifId); } } void VKNotificationsDatabase::sync() { Q_D(VKNotificationsDatabase); { QMutexLocker locker(&d->mutex); Q_FOREACH(int accountId, d->insertNotifications.keys()) { d->queue.insertNotifications.insert(accountId, d->insertNotifications.take(accountId)); } while (d->removeNotificationsFromAccounts.count()) { d->queue.removeNotificationsFromAccounts.append(d->removeNotificationsFromAccounts.takeFirst()); } while (d->removeNotifications.count()) { d->queue.removeNotifications.append(d->removeNotifications.takeFirst()); } } executeWrite(); } QList VKNotificationsDatabase::notifications() { QList data; QSqlQuery query; query = prepare(QStringLiteral( "SELECT identifier, accountId, type, fromId, fromName, fromIcon, toId, createdTime " \ "FROM notifications ORDER BY createdTime DESC")); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to query events" << query.lastError().text(); return data; } while (query.next()) { data.append(VKNotification::create(QString::number(query.value(0).toInt()), // id query.value(1).toInt(), // accountId query.value(2).toString(), // type query.value(3).toString(), // fromId query.value(4).toString(), // fromName query.value(5).toString(), // fromIcon query.value(6).toString(), // toId #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QDateTime::fromSecsSinceEpoch(query.value(7).toInt()))); // createdTime #else QDateTime::fromTime_t(query.value(7).toInt()))); // createdTime #endif } return data; } void VKNotificationsDatabase::readFinished() { emit notificationsChanged(); } bool VKNotificationsDatabase::write() { Q_D(VKNotificationsDatabase); QMutexLocker locker(&d->mutex); const QMap > insertNotifications = d->queue.insertNotifications; const QList removeNotificationsFromAccounts = d->queue.removeNotificationsFromAccounts; QStringList removeNotifications = d->queue.removeNotifications; d->queue.insertNotifications.clear(); d->queue.removeNotificationsFromAccounts.clear(); d->queue.removeNotifications.clear(); locker.unlock(); bool success = true; QSqlQuery query; if (!removeNotificationsFromAccounts.isEmpty()) { QVariantList accountIds; Q_FOREACH (const int accountId, removeNotificationsFromAccounts) { accountIds.append(accountId); } query = prepare(QStringLiteral("DELETE FROM notifications WHERE accountId = :accountId")); query.bindValue(QStringLiteral(":accountId"), accountIds); executeBatchSocialCacheQuery(query); } if (!removeNotifications.isEmpty()) { QVariantList notifIds; Q_FOREACH (const QString notifId, removeNotifications) { notifIds.append(notifId.toInt()); } query = prepare(QStringLiteral("DELETE FROM notifications WHERE identifier = :identifier")); query.bindValue(QStringLiteral(":identifier"), notifIds); executeBatchSocialCacheQuery(query); } if (!insertNotifications.isEmpty()) { QVariantList accountIds; QVariantList types; QVariantList fromIds; QVariantList fromNames; QVariantList fromIcons; QVariantList toIds; QVariantList createdTimes; Q_FOREACH (const QList ¬ifications, insertNotifications) { Q_FOREACH (const VKNotification::ConstPtr ¬ification, notifications) { accountIds.append(notification->accountId()); fromIds.append(notification->fromId()); fromNames.append(notification->fromName()); fromIcons.append(notification->fromIcon()); toIds.append(notification->toId()); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) createdTimes.append(notification->createdTime().toSecsSinceEpoch()); #else createdTimes.append(notification->createdTime().toTime_t()); #endif types.append(notification->type()); } } query = prepare(QStringLiteral( "INSERT OR REPLACE INTO notifications (" " accountId, type, fromId, fromName, fromIcon, toId, createdTime) " "VALUES(" " :accountId, :type, :fromId, :fromName, :fromIcon, :toId, :createdTime)")); query.bindValue(QStringLiteral(":accountId"), accountIds); query.bindValue(QStringLiteral(":type"), types); query.bindValue(QStringLiteral(":fromId"), fromIds); query.bindValue(QStringLiteral(":fromName"), fromNames); query.bindValue(QStringLiteral(":fromIcon"), fromIcons); query.bindValue(QStringLiteral(":toId"), toIds); query.bindValue(QStringLiteral(":createdTime"), createdTimes); executeBatchSocialCacheQuery(query); } return success; } bool VKNotificationsDatabase::createTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("CREATE TABLE IF NOT EXISTS notifications ("\ "identifier INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT,"\ "accountId INTEGER,"\ "type TEXT,"\ "fromId TEXT,"\ "fromName TEXT,"\ "fromIcon TEXT,"\ "toId TEXT,"\ "createdTime INTEGER)"); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Unable to create notifications table: " << query.lastError().text(); return false; } return true; } bool VKNotificationsDatabase::dropTables(QSqlDatabase database) const { QSqlQuery query(database); if (!query.exec(QStringLiteral("DROP TABLE IF EXISTS notifications"))) { qWarning() << Q_FUNC_INFO << "Unable to delete notifications table: " << query.lastError().text(); return false; } return true; } libsocialcache-0.2.1/src/lib/vknotificationsdatabase.h000066400000000000000000000067541462333522700230670ustar00rootroot00000000000000/* * Copyright (C) 2014 Jolla Ltd. * Contact: Bea Lam * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKNOTIFICATIONSDATABASE_H #define VKNOTIFICATIONSDATABASE_H #include "abstractsocialcachedatabase.h" #include #include #include class VKNotificationPrivate; class VKNotification { public: typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; virtual ~VKNotification(); static VKNotification::Ptr create(const QString &identifier, int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime); QString identifier() const; QString type() const; QString fromId() const; QString fromName() const; QString fromIcon() const; QString toId() const; QDateTime createdTime() const; int accountId() const; protected: QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(VKNotification) explicit VKNotification(const QString &identifier, int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime); }; class VKNotificationsDatabasePrivate; class VKNotificationsDatabase: public AbstractSocialCacheDatabase { Q_OBJECT public: explicit VKNotificationsDatabase(); ~VKNotificationsDatabase(); void addVKNotification(int accountId, const QString &type, const QString &fromId, const QString &fromName, const QString &fromIcon, const QString &toId, const QDateTime &createdTime); void removeNotifications(int accountId); void removeNotification(const QString ¬ificationId); void removeNotifications(const QStringList ¬ificationIds); void sync(); QList notifications(); signals: void notificationsChanged(); protected: void readFinished(); bool write(); bool createTables(QSqlDatabase database) const; bool dropTables(QSqlDatabase database) const; private: Q_DECLARE_PRIVATE(VKNotificationsDatabase) }; #endif // VKNOTIFICATIONSDATABASE_H libsocialcache-0.2.1/src/lib/vkpostsdatabase.cpp000066400000000000000000000304761462333522700217170ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Bea Lam * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vkpostsdatabase.h" #include "socialsyncinterface.h" #include static const char *DB_NAME = "vk.db"; static const char *COMMENT_COUNT_KEY = "comment_count"; static const char *COMMENT_ALLOW_KEY = "allow_comment"; static const char *LIKE_COUNT_KEY = "like_count"; static const char *LIKE_BY_USER = "like_by_user"; static const char *LIKE_ALLOW_KEY = "allow_like"; static const char *LIKE_ALLOW_PUBLISH_KEY = "allow_like_publish"; static const char *REPOST_COUNT_KEY = "repost_count"; static const char *REPOST_BY_USER_KEY = "repost_by_user"; static const char *POST_SOURCE_TYPE_KEY = "post_type"; static const char *POST_SOURCE_DATA_KEY = "post_data"; static const char *GEO_PLACE_ID_KEY = "geo_place_id"; static const char *GEO_TITLE_KEY = "geo_title"; static const char *GEO_TYPE_KEY = "geo_type"; static const char *GEO_COUNTRY_ID_KEY = "geo_country_id"; static const char *GEO_CITY_ID_KEY = "geo_city_id"; static const char *GEO_ADDRESS_KEY = "geo_address"; static const char *GEO_SHOWMAP_KEY = "geo_showmap"; static const char *COPIED_POST_CREATED_TIME_KEY = "copied_post_created_time"; static const char *COPIED_POST_TYPE_KEY = "copied_post_type"; static const char *COPIED_POST_OWNER_ID_KEY = "copied_post_owner_id"; static const char *COPIED_POST_OWNER_NAME_KEY = "copied_post_owner_name"; static const char *COPIED_POST_OWNER_AVATAR_KEY = "copied_post_owner_avatar"; static const char *COPIED_POST_POST_ID_KEY = "copied_post_post_id"; static const char *COPIED_POST_TEXT_KEY = "copied_post_text"; static const char *COPIED_POST_PHOTO_KEY = "copied_post_photo"; static const char *COPIED_POST_VIDEO_KEY = "copied_post_video"; static const char *COPIED_POST_LINK_KEY = "copied_post_link"; static const char *POST_FROM_ID_KEY = "post_from_id"; static const char *POST_TO_ID_KEY = "post_to_id"; static const char *POST_REPLY_OWNER_ID_KEY = "post_reply_owner_id"; static const char *POST_REPLY_POST_ID_KEY = "post_reply_post_id"; static const char *POST_FRIENDS_ONLY_KEY = "post_friends_only"; static const char *POST_SIGNER_ID_KEY = "post_signer_id"; static const char *POST_LINK_KEY = "post_link_key"; VKPostsDatabase::Comments::Comments() : count(0), userCanComment(false) {} VKPostsDatabase::Comments::~Comments() {} VKPostsDatabase::Comments::Comments(const VKPostsDatabase::Comments &other) { operator=(other); } VKPostsDatabase::Comments& VKPostsDatabase::Comments::operator=(const VKPostsDatabase::Comments &other) { if (&other == this) { return *this; } count = other.count; userCanComment = other.userCanComment; return *this; } VKPostsDatabase::Likes::Likes() : count(0), userLikes(false), userCanLike(false), userCanPublish(false) {} VKPostsDatabase::Likes::~Likes() {} VKPostsDatabase::Likes::Likes(const VKPostsDatabase::Likes &other) { operator=(other); } VKPostsDatabase::Likes& VKPostsDatabase::Likes::operator=(const VKPostsDatabase::Likes &other) { if (&other == this) { return *this; } count = other.count; userLikes = other.userLikes; userCanLike = other.userCanLike; userCanPublish = other.userCanPublish; return *this; } VKPostsDatabase::Reposts::Reposts() : count(0), userReposted(false) {} VKPostsDatabase::Reposts::~Reposts() {} VKPostsDatabase::Reposts::Reposts(const VKPostsDatabase::Reposts &other) { operator=(other); } VKPostsDatabase::Reposts& VKPostsDatabase::Reposts::operator=(const VKPostsDatabase::Reposts &other) { if (&other == this) { return *this; } count = other.count; userReposted = other.userReposted; return *this; } VKPostsDatabase::PostSource::PostSource() {} VKPostsDatabase::PostSource::~PostSource() {} VKPostsDatabase::PostSource::PostSource(const VKPostsDatabase::PostSource &other) { operator=(other); } VKPostsDatabase::PostSource& VKPostsDatabase::PostSource::operator=(const VKPostsDatabase::PostSource &other) { if (&other == this) { return *this; } type = other.type; data = other.data; return *this; } VKPostsDatabase::GeoLocation::GeoLocation() : placeId(0), countryId(0), cityId(0), showMap(false) {} VKPostsDatabase::GeoLocation::~GeoLocation() {} VKPostsDatabase::GeoLocation::GeoLocation(const VKPostsDatabase::GeoLocation &other) { operator=(other); } VKPostsDatabase::GeoLocation& VKPostsDatabase::GeoLocation::operator=(const VKPostsDatabase::GeoLocation &other) { if (&other == this) { return *this; } placeId = other.placeId; title = other.title; type = other.type; countryId = other.countryId; cityId = other.cityId; address = other.address; showMap = other.showMap; return *this; } VKPostsDatabase::CopyPost::CopyPost() : ownerId(0), postId(0) {} VKPostsDatabase::CopyPost::~CopyPost() {} VKPostsDatabase::CopyPost::CopyPost(const VKPostsDatabase::CopyPost &other) { operator=(other); } VKPostsDatabase::CopyPost& VKPostsDatabase::CopyPost::operator=(const VKPostsDatabase::CopyPost &other) { if (&other == this) { return *this; } createdTime = other.createdTime; type = other.type; ownerId = other.ownerId; ownerName = other.ownerName; ownerAvatar = other.ownerAvatar; postId = other.postId; text = other.text; photo = other.photo; video = other.video; link = other.link; return *this; } VKPostsDatabase::Post::Post() : fromId(0), toId(0), replyOwnerId(0), replyPostId(0), signerId(0), friendsOnly(0) {} VKPostsDatabase::Post::~Post() {} VKPostsDatabase::Post::Post(const VKPostsDatabase::Post &other) { operator=(other); } VKPostsDatabase::Post& VKPostsDatabase::Post::operator=(const VKPostsDatabase::Post &other) { if (&other == this) { return *this; } comments = other.comments; likes = other.likes; reposts = other.reposts; geo = other.geo; copyPost = other.copyPost; fromId = other.fromId; toId = other.toId; postType = other.postType; replyOwnerId = other.replyOwnerId; replyPostId = other.replyPostId; signerId = other.signerId; friendsOnly = other.friendsOnly; link = other.link; return *this; } VKPostsDatabase::VKPostsDatabase() : AbstractSocialPostCacheDatabase( SocialSyncInterface::socialNetwork(SocialSyncInterface::VK), QLatin1String(DB_NAME)) { } VKPostsDatabase::~VKPostsDatabase() { } void VKPostsDatabase::addVKPost(const QString &identifier, const QDateTime &createdTime, const QString &body, const Post &post, const QList > &images, const QString &personName, const QString &personIcon, int accountId) { QVariantMap extra; extra.insert(POST_FROM_ID_KEY, post.fromId); extra.insert(POST_TO_ID_KEY, post.toId); extra.insert(POST_REPLY_OWNER_ID_KEY, post.replyOwnerId); extra.insert(POST_REPLY_POST_ID_KEY, post.replyPostId); extra.insert(POST_FRIENDS_ONLY_KEY, post.friendsOnly); extra.insert(POST_SIGNER_ID_KEY, post.signerId); extra.insert(POST_LINK_KEY, post.link); extra.insert(COMMENT_COUNT_KEY, post.comments.count); extra.insert(COMMENT_ALLOW_KEY, post.comments.userCanComment); extra.insert(LIKE_COUNT_KEY, post.likes.count); extra.insert(LIKE_BY_USER, post.likes.userLikes); extra.insert(LIKE_ALLOW_KEY, post.likes.userCanLike); extra.insert(LIKE_ALLOW_PUBLISH_KEY, post.likes.userCanPublish); extra.insert(REPOST_COUNT_KEY, post.reposts.count); extra.insert(REPOST_BY_USER_KEY, post.reposts.userReposted); extra.insert(POST_SOURCE_TYPE_KEY, post.postSource.type); extra.insert(POST_SOURCE_DATA_KEY, post.postSource.data); extra.insert(GEO_PLACE_ID_KEY, post.geo.placeId); extra.insert(GEO_TITLE_KEY, post.geo.title); extra.insert(GEO_TYPE_KEY, post.geo.type); extra.insert(GEO_COUNTRY_ID_KEY, post.geo.countryId); extra.insert(GEO_CITY_ID_KEY, post.geo.cityId); extra.insert(GEO_ADDRESS_KEY, post.geo.address); extra.insert(GEO_SHOWMAP_KEY, post.geo.showMap); extra.insert(COPIED_POST_CREATED_TIME_KEY, post.copyPost.createdTime); extra.insert(COPIED_POST_TYPE_KEY, post.copyPost.type); extra.insert(COPIED_POST_OWNER_ID_KEY, post.copyPost.ownerId); extra.insert(COPIED_POST_OWNER_NAME_KEY, post.copyPost.ownerName); extra.insert(COPIED_POST_OWNER_AVATAR_KEY, post.copyPost.ownerAvatar); extra.insert(COPIED_POST_POST_ID_KEY, post.copyPost.postId); extra.insert(COPIED_POST_TEXT_KEY, post.copyPost.text); extra.insert(COPIED_POST_PHOTO_KEY, post.copyPost.photo); extra.insert(COPIED_POST_VIDEO_KEY, post.copyPost.video); extra.insert(COPIED_POST_LINK_KEY, post.copyPost.link); addPost(identifier, personName, body, createdTime, personIcon, images, extra, accountId); } VKPostsDatabase::Post::Ptr VKPostsDatabase::Post::create(const SocialPost::ConstPtr &socialPost) { VKPostsDatabase::Post *vkPost = new VKPostsDatabase::Post; const QVariantMap &extra = socialPost->extra(); vkPost->fromId = extra.value(POST_FROM_ID_KEY).toInt(); vkPost->toId = extra.value(POST_TO_ID_KEY).toInt(); vkPost->replyOwnerId = extra.value(POST_REPLY_OWNER_ID_KEY).toInt(); vkPost->replyPostId = extra.value(POST_REPLY_POST_ID_KEY).toInt(); vkPost->friendsOnly = extra.value(POST_FRIENDS_ONLY_KEY).toBool(); vkPost->signerId = extra.value(POST_SIGNER_ID_KEY).toInt(); vkPost->link = extra.value(POST_LINK_KEY).toString(); vkPost->comments.count = extra.value(COMMENT_COUNT_KEY).toInt(); vkPost->comments.userCanComment = extra.value(COMMENT_ALLOW_KEY).toBool(); vkPost->likes.count = extra.value(LIKE_COUNT_KEY).toInt(); vkPost->likes.userLikes = extra.value(LIKE_BY_USER).toBool(); vkPost->likes.userCanLike = extra.value(LIKE_ALLOW_KEY).toBool(); vkPost->likes.userCanPublish = extra.value(LIKE_ALLOW_PUBLISH_KEY).toBool(); vkPost->reposts.count = extra.value(REPOST_COUNT_KEY).toInt(); vkPost->reposts.userReposted = extra.value(REPOST_BY_USER_KEY).toBool(); vkPost->postSource.type = extra.value(POST_SOURCE_TYPE_KEY).toString(); vkPost->postSource.data = extra.value(POST_SOURCE_DATA_KEY).toString(); vkPost->geo.placeId = extra.value(GEO_PLACE_ID_KEY).toInt(); vkPost->geo.title = extra.value(GEO_TITLE_KEY).toString(); vkPost->geo.type = extra.value(GEO_TYPE_KEY).toString(); vkPost->geo.countryId = extra.value(GEO_COUNTRY_ID_KEY).toInt(); vkPost->geo.cityId = extra.value(GEO_CITY_ID_KEY).toInt(); vkPost->geo.address = extra.value(GEO_ADDRESS_KEY).toString(); vkPost->geo.showMap = extra.value(GEO_SHOWMAP_KEY).toBool(); vkPost->copyPost.createdTime = extra.value(COPIED_POST_CREATED_TIME_KEY).toDateTime(); vkPost->copyPost.type = extra.value(COPIED_POST_TYPE_KEY).toString(); vkPost->copyPost.ownerId = extra.value(COPIED_POST_OWNER_ID_KEY).toInt(); vkPost->copyPost.ownerName = extra.value(COPIED_POST_OWNER_NAME_KEY).toString(); vkPost->copyPost.ownerAvatar = extra.value(COPIED_POST_OWNER_AVATAR_KEY).toString(); vkPost->copyPost.postId = extra.value(COPIED_POST_POST_ID_KEY).toInt(); vkPost->copyPost.text = extra.value(COPIED_POST_TEXT_KEY).toInt(); vkPost->copyPost.photo = extra.value(COPIED_POST_PHOTO_KEY).toString(); vkPost->copyPost.video = extra.value(COPIED_POST_VIDEO_KEY).toString(); vkPost->copyPost.link = extra.value(COPIED_POST_LINK_KEY).toString(); return VKPostsDatabase::Post::Ptr(vkPost); } libsocialcache-0.2.1/src/lib/vkpostsdatabase.h000066400000000000000000000076111462333522700213570ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Bea Lam * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKPOSTSDATABASE_H #define VKPOSTSDATABASE_H #include "abstractsocialpostcachedatabase.h" class VKPostsDatabase: public AbstractSocialPostCacheDatabase { Q_OBJECT public: explicit VKPostsDatabase(); ~VKPostsDatabase(); class Comments { public: Comments(const Comments &other); Comments &operator=(const Comments &other); Comments(); ~Comments(); int count; bool userCanComment; }; class Likes { public: Likes(); ~Likes(); Likes(const Likes &other); Likes &operator=(const Likes &other); int count; bool userLikes; bool userCanLike; bool userCanPublish; }; // has data if this post has been reposted elsewhere class Reposts { public: Reposts(); ~Reposts(); Reposts(const Reposts &other); Reposts &operator=(const Reposts &other); int count; bool userReposted; }; class PostSource { public: PostSource(); ~PostSource(); PostSource(const PostSource &other); PostSource &operator=(const PostSource &other); QString type; QString data; }; class GeoLocation { public: GeoLocation(); ~GeoLocation(); GeoLocation(const GeoLocation &other); GeoLocation &operator=(const GeoLocation &other); int placeId; QString title; QString type; int countryId; int cityId; QString address; bool showMap; }; // has data if this post itself is a repost class CopyPost { public: CopyPost(); ~CopyPost(); CopyPost(const CopyPost &other); CopyPost &operator=(const CopyPost &other); QDateTime createdTime; QString type; int ownerId; QString ownerName; QString ownerAvatar; int postId; QString text; QString photo; QString video; QString link; }; class Post { public: Post(); ~Post(); Post(const Post &other); Post &operator=(const Post &other); Comments comments; Likes likes; Reposts reposts; PostSource postSource; GeoLocation geo; CopyPost copyPost; int fromId; int toId; QString postType; int replyOwnerId; int replyPostId; int signerId; bool friendsOnly; QString link; typedef QSharedPointer Ptr; typedef QSharedPointer ConstPtr; static Post::Ptr create(const SocialPost::ConstPtr &socialPost); }; void addVKPost(const QString &identifier, const QDateTime &createdTime, const QString &body, const Post &post, const QList > &images, const QString &personName, const QString &personIcon, int accountId); }; #endif // VKPOSTSDATABASE_H libsocialcache-0.2.1/src/qml/000077500000000000000000000000001462333522700160265ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/abstractsocialcachemodel.cpp000066400000000000000000000122651462333522700235430ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "abstractsocialcachemodel.h" #include "abstractsocialcachemodel_p.h" #include #include #include template <> bool compareIdentity( const SocialCacheModelRow &item, const SocialCacheModelRow &reference) { return item.value(0) == reference.value(0); } template <> int updateRange( AbstractSocialCacheModelPrivate *d, int index, int count, const SocialCacheModelData &source, int sourceIndex) { d->updateRange(index, count, source, sourceIndex); return count; } AbstractSocialCacheModelPrivate::AbstractSocialCacheModelPrivate(AbstractSocialCacheModel *q) : q_ptr(q) { } AbstractSocialCacheModelPrivate::~AbstractSocialCacheModelPrivate() { } void AbstractSocialCacheModelPrivate::clearData() { Q_Q(AbstractSocialCacheModel); if (m_data.count() > 0) { q->beginRemoveRows(QModelIndex(), 0, m_data.count() - 1); m_data.clear(); q->endRemoveRows(); emit q->countChanged(); } } void AbstractSocialCacheModelPrivate::updateData(const SocialCacheModelData &data) { Q_Q(AbstractSocialCacheModel); q->updateData(data); } void AbstractSocialCacheModelPrivate::updateRow(int row, const SocialCacheModelRow &data) { Q_Q(AbstractSocialCacheModel); q->updateRow(row, data); } void AbstractSocialCacheModelPrivate::insertRange( int index, int count, const SocialCacheModelData &source, int sourceIndex) { Q_Q(AbstractSocialCacheModel); if (count > 0 && index >= 0) { q->beginInsertRows(QModelIndex(), index, index + count - 1); m_data = m_data.mid(0, index) + source.mid(sourceIndex, count) + m_data.mid(index); q->endInsertRows(); emit q->countChanged(); } } void AbstractSocialCacheModelPrivate::removeRange(int index, int count) { Q_Q(AbstractSocialCacheModel); if (count > 0 && index >= 0) { q->beginRemoveRows(QModelIndex(), index, index + count - 1); m_data = m_data.mid(0, index) + m_data.mid(index + count); q->endRemoveRows(); emit q->countChanged(); } } void AbstractSocialCacheModelPrivate::updateRange( int index, int count, const SocialCacheModelData &source, int sourceIndex) { Q_Q(AbstractSocialCacheModel); for (int i = 0; i < count; ++i) { m_data[index + i] = source[sourceIndex + i]; } emit q->dataChanged(q->createIndex(index, 0), q->createIndex(index + count - 1, 0)); } AbstractSocialCacheModel::AbstractSocialCacheModel(AbstractSocialCacheModelPrivate &dd, QObject *parent) : QAbstractListModel(parent), d_ptr(&dd) { } AbstractSocialCacheModel::~AbstractSocialCacheModel() { } int AbstractSocialCacheModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) Q_D(const AbstractSocialCacheModel); return d->m_data.count(); } QVariant AbstractSocialCacheModel::data(const QModelIndex &index, int role) const { int row = index.row(); return getField(row, role); } QVariant AbstractSocialCacheModel::getField(int row, int role) const { Q_D(const AbstractSocialCacheModel); if (row < 0 || row >= d->m_data.count()) { return QVariant(); } return d->m_data.at(row).value(role); } QString AbstractSocialCacheModel::nodeIdentifier() const { Q_D(const AbstractSocialCacheModel); return d->nodeIdentifier; } void AbstractSocialCacheModel::setNodeIdentifier(const QString &nodeIdentifier) { Q_D(AbstractSocialCacheModel); if (d->nodeIdentifier != nodeIdentifier) { d->nodeIdentifier = nodeIdentifier; emit nodeIdentifierChanged(); d->nodeIdentifierChanged(); } } int AbstractSocialCacheModel::count() const { return rowCount(); } void AbstractSocialCacheModel::updateData(const SocialCacheModelData &data) { Q_D(AbstractSocialCacheModel); const int count = d->m_data.count(); synchronizeList(d, d->m_data, data); if (d->m_data.count() != count) { emit countChanged(); } emit modelUpdated(); } void AbstractSocialCacheModel::updateRow(int row, const SocialCacheModelRow &data) { Q_D(AbstractSocialCacheModel); foreach (int key, data.keys()) { d->m_data[row].insert(key, data.value(key)); } emit dataChanged(index(row), index(row)); } libsocialcache-0.2.1/src/qml/abstractsocialcachemodel.h000066400000000000000000000045321462333522700232060ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ABSTRACTSOCIALCACHEMODEL_H #define ABSTRACTSOCIALCACHEMODEL_H #include typedef QMap SocialCacheModelRow; typedef QList SocialCacheModelData; class AbstractSocialCacheModelPrivate; class AbstractSocialCacheModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString nodeIdentifier READ nodeIdentifier WRITE setNodeIdentifier NOTIFY nodeIdentifierChanged) Q_PROPERTY(int count READ count NOTIFY countChanged) public: virtual ~AbstractSocialCacheModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; Q_INVOKABLE QVariant getField(int row, int role) const; // properties QString nodeIdentifier() const; void setNodeIdentifier(const QString &nodeIdentifier); int count() const; public Q_SLOTS: virtual void refresh() = 0; Q_SIGNALS: void nodeIdentifierChanged(); void countChanged(); void modelUpdated(); protected: // Methods used to update the model in the C++ side void updateData(const SocialCacheModelData &data); void updateRow(int row, const SocialCacheModelRow &data); explicit AbstractSocialCacheModel(AbstractSocialCacheModelPrivate &dd, QObject *parent = 0); QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(AbstractSocialCacheModel) }; Q_DECLARE_METATYPE(SocialCacheModelRow) Q_DECLARE_METATYPE(SocialCacheModelData) #endif // ABSTRACTSOCIALCACHEMODEL_H libsocialcache-0.2.1/src/qml/abstractsocialcachemodel_p.h000066400000000000000000000034011462333522700235170ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ABSTRACTSOCIALCACHEMODEL_P_H #define ABSTRACTSOCIALCACHEMODEL_P_H #include "abstractsocialcachemodel.h" #include class AbstractSocialCacheModelPrivate { public: virtual ~AbstractSocialCacheModelPrivate(); QString nodeIdentifier; void insertRange(int index, int count, const SocialCacheModelData &source, int sourceIndex); void updateRange(int index, int count, const SocialCacheModelData &source, int sourceIndex); void removeRange(int index, int count); void clearData(); void updateData(const SocialCacheModelData &data); void updateRow(int row, const SocialCacheModelRow &data); QList > m_data; protected: explicit AbstractSocialCacheModelPrivate(AbstractSocialCacheModel *q); virtual void nodeIdentifierChanged() {} AbstractSocialCacheModel * const q_ptr; private: Q_DECLARE_PUBLIC(AbstractSocialCacheModel) }; #endif // ABSTRACTSOCIALCACHEMODEL_P_H libsocialcache-0.2.1/src/qml/dropbox/000077500000000000000000000000001462333522700175035ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/dropbox/dropboximagecachemodel.cpp000066400000000000000000000343021462333522700246760ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dropboximagecachemodel.h" #include "abstractsocialcachemodel_p.h" #include "dropboximagesdatabase.h" #include "dropboximagedownloader_p.h" #include "dropboximagedownloaderconstants_p.h" #include #include #include // Note: // // When querying photos, the nodeIdentifier should be either // - nothing: query all photos // - user-USER_ID: query all photos for the given user // - album-ALBUM_ID: query all photos for the given album // // When querying albums, the nodeIdentifier should be either // - nothing: query all albums for all users // - USER_ID: query all albums for the given user static const char *PHOTO_USER_PREFIX = "user-"; static const char *PHOTO_ALBUM_PREFIX = "album-"; static const char *URL_KEY = "url"; static const char *ROW_KEY = "row"; static const char *MODEL_KEY = "model"; static const char *ACCESSTOKEN = "accessToken"; class DropboxImageCacheModelPrivate : public AbstractSocialCacheModelPrivate { public: DropboxImageCacheModelPrivate(DropboxImageCacheModel *q); void queue( int row, DropboxImageDownloader::ImageType imageType, const QString &identifier, const QString &url, const QString &accessToken); DropboxImageDownloader *downloader; DropboxImagesDatabase database; DropboxImageCacheModel::ModelDataType type; }; DropboxImageCacheModelPrivate::DropboxImageCacheModelPrivate(DropboxImageCacheModel *q) : AbstractSocialCacheModelPrivate(q), downloader(0), type(DropboxImageCacheModel::Images) { } void DropboxImageCacheModelPrivate::queue( int row, DropboxImageDownloader::ImageType imageType, const QString &identifier, const QString &url, const QString &accessToken) { DropboxImageCacheModel *modelPtr = qobject_cast(q_ptr); if (downloader) { QVariantMap metadata; metadata.insert(QLatin1String(TYPE_KEY), imageType); metadata.insert(QLatin1String(IDENTIFIER_KEY), identifier); metadata.insert(QLatin1String(URL_KEY), url); metadata.insert(QLatin1String(ROW_KEY), row); metadata.insert(QLatin1String(MODEL_KEY), QVariant::fromValue((void*)modelPtr)); metadata.insert(QLatin1String(ACCESSTOKEN), accessToken); // no use to download if there is no accessToken if (accessToken.length()) { downloader->queue(url, metadata); } else { qWarning() << Q_FUNC_INFO << "fail accesstoken is missing" << url; } } } DropboxImageCacheModel::DropboxImageCacheModel(QObject *parent) : AbstractSocialCacheModel(*(new DropboxImageCacheModelPrivate(this)), parent) { Q_D(const DropboxImageCacheModel); connect(&d->database, &DropboxImagesDatabase::queryFinished, this, &DropboxImageCacheModel::queryFinished); } DropboxImageCacheModel::~DropboxImageCacheModel() { Q_D(DropboxImageCacheModel); if (d->downloader) { d->downloader->removeModelFromHash(this); } } QHash DropboxImageCacheModel::roleNames() const { QHash roleNames; roleNames.insert(DropboxId, "id"); roleNames.insert(Thumbnail, "thumbnail"); roleNames.insert(Image, "image"); roleNames.insert(Title, "title"); roleNames.insert(DateTaken, "dateTaken"); roleNames.insert(Width, "photoWidth"); roleNames.insert(Height, "photoHeight"); roleNames.insert(Count, "dataCount"); roleNames.insert(MimeType, "mimeType"); roleNames.insert(AccountId, "accountId"); roleNames.insert(UserId, "userId"); roleNames.insert(AccessToken, "accessToken"); return roleNames; } DropboxImageCacheModel::ModelDataType DropboxImageCacheModel::type() const { Q_D(const DropboxImageCacheModel); return d->type; } void DropboxImageCacheModel::setType(DropboxImageCacheModel::ModelDataType type) { Q_D(DropboxImageCacheModel); if (d->type != type) { d->type = type; emit typeChanged(); } } DropboxImageDownloader * DropboxImageCacheModel::downloader() const { Q_D(const DropboxImageCacheModel); return d->downloader; } void DropboxImageCacheModel::setDownloader(DropboxImageDownloader *downloader) { Q_D(DropboxImageCacheModel); if (d->downloader != downloader) { if (d->downloader) { // Disconnect worker object disconnect(d->downloader); d->downloader->removeModelFromHash(this); } d->downloader = downloader; d->downloader->addModelToHash(this); emit downloaderChanged(); } } void DropboxImageCacheModel::removeImage(const QString &imageUrl) { Q_D(DropboxImageCacheModel); int row = -1; for (int i = 0; i < count(); ++i) { QString dbId = data(index(i), DropboxImageCacheModel::Image).toString(); if (dbId == imageUrl) { row = i; break; } } if (row >= 0) { QString imageId = data(index(row), DropboxImageCacheModel::DropboxId).toString(); beginRemoveRows(QModelIndex(), row, row); d->m_data.removeAt(row); endRemoveRows(); // Update album image count DropboxImage::ConstPtr image = d->database.image(imageId); if (image) { DropboxAlbum::ConstPtr album = d->database.album(image->albumId()); if (album) { d->database.addAlbum(album->albumId(), album->userId(), album->createdTime(), album->updatedTime(), album->albumName(), album->imageCount()-1, album->hash()); } } d->database.removeImage(imageId); d->database.commit(); } } QVariant DropboxImageCacheModel::data(const QModelIndex &index, int role) const { Q_D(const DropboxImageCacheModel); int row = index.row(); if (row < 0 || row >= d->m_data.count()) { return QVariant(); } return d->m_data.at(row).value(role); } void DropboxImageCacheModel::loadImages() { refresh(); } void DropboxImageCacheModel::refresh() { Q_D(DropboxImageCacheModel); const QString userPrefix = QLatin1String(PHOTO_USER_PREFIX); const QString albumPrefix = QLatin1String(PHOTO_ALBUM_PREFIX); switch (d->type) { case DropboxImageCacheModel::Users: d->database.queryUsers(); break; case DropboxImageCacheModel::Albums: d->database.queryAlbums(d->nodeIdentifier); break; case DropboxImageCacheModel::Images: if (d->nodeIdentifier.startsWith(userPrefix)) { d->database.queryUserImages(d->nodeIdentifier.mid(userPrefix.size())); } else if (d->nodeIdentifier.startsWith(albumPrefix)) { d->database.queryAlbumImages(d->nodeIdentifier.mid(albumPrefix.size())); } else { d->database.queryUserImages(); } break; default: break; } } // NOTE: this is now called directly by DropboxImageDownloader // rather than connected to the imageDownloaded signal, for // performance reasons. void DropboxImageCacheModel::imageDownloaded( const QString &, const QString &path, const QVariantMap &imageData) { Q_D(DropboxImageCacheModel); if (path.isEmpty()) { // empty path signifies an error, which we don't handle here at the moment. // Return, otherwise dataChanged signal would cause UI to read back // related value, which, being empty, would trigger another download request, // potentially causing never ending loop. return; } int row = imageData.value(ROW_KEY).toInt(); if (row < 0 || row >= d->m_data.count()) { qWarning() << Q_FUNC_INFO << "Invalid row:" << row << "max row:" << d->m_data.count(); return; } int type = imageData.value(TYPE_KEY).toInt(); switch (type) { case DropboxImageDownloader::ThumbnailImage: d->m_data[row].insert(DropboxImageCacheModel::Thumbnail, path); break; default: qWarning() << Q_FUNC_INFO << "invalid downloader type: " << type; break; } emit dataChanged(index(row), index(row)); } void DropboxImageCacheModel::queryFinished() { Q_D(DropboxImageCacheModel); QList thumbQueue; SocialCacheModelData data; switch (d->type) { case Users: { QList usersData = d->database.users(); int count = 0; Q_FOREACH (const DropboxUser::ConstPtr &userData, usersData) { QMap userMap; userMap.insert(DropboxImageCacheModel::DropboxId, userData->userId()); userMap.insert(DropboxImageCacheModel::Title, userData->userName()); userMap.insert(DropboxImageCacheModel::Count, userData->count()); count += userData->count(); data.append(userMap); } if (data.count() > 1) { QMap userMap; userMap.insert(DropboxImageCacheModel::DropboxId, QString()); userMap.insert(DropboxImageCacheModel::Thumbnail, QString()); //: Label for the "show all users from all Dropbox accounts" option //% "All" userMap.insert(DropboxImageCacheModel::Title, qtTrId("nemo_socialcache_dropbox_images_model-all-users")); userMap.insert(DropboxImageCacheModel::Count, count); data.prepend(userMap); } break; } case Albums: { QList albumsData = d->database.albums(); int count = 0; Q_FOREACH (const DropboxAlbum::ConstPtr &albumData, albumsData) { QMap albumMap; albumMap.insert(DropboxImageCacheModel::DropboxId, albumData->albumId()); albumMap.insert(DropboxImageCacheModel::Title, albumData->albumName()); albumMap.insert(DropboxImageCacheModel::Count, albumData->imageCount()); albumMap.insert(DropboxImageCacheModel::UserId, albumData->userId()); count += albumData->imageCount(); data.append(albumMap); } if (data.count() > 1) { QMap albumMap; albumMap.insert(DropboxImageCacheModel::DropboxId, QString()); // albumMap.insert(DropboxImageCacheModel::Icon, QString()); //: Label for the "show all photos from all albums by this user" option //% "All" albumMap.insert(DropboxImageCacheModel::Title, qtTrId("nemo_socialcache_dropbox_images_model-all-albums")); albumMap.insert(DropboxImageCacheModel::Count, count); if (d->nodeIdentifier.isEmpty()) { albumMap.insert(DropboxImageCacheModel::UserId, QString()); } else { albumMap.insert(DropboxImageCacheModel::UserId, data.first().value(DropboxImageCacheModel::UserId)); } data.prepend(albumMap); } break; } case Images: { QList imagesData = d->database.images(); for (int i = 0; i < imagesData.count(); i ++) { const DropboxImage::ConstPtr & imageData = imagesData.at(i); QMap imageMap; imageMap.insert(DropboxImageCacheModel::DropboxId, imageData->imageId()); if (imageData->thumbnailFile().isEmpty()) { QVariantMap thumbQueueData; thumbQueueData.insert("row", QVariant::fromValue(i)); thumbQueueData.insert("imageType", QVariant::fromValue(DropboxImageDownloader::ThumbnailImage)); thumbQueueData.insert("identifier", imageData->imageId()); thumbQueueData.insert("url", imageData->thumbnailUrl()); thumbQueueData.insert("accessToken", imageData->accessToken()); thumbQueue.append(thumbQueueData); } // note: we don't queue the image file until the user explicitly opens that in fullscreen. imageMap.insert(DropboxImageCacheModel::Thumbnail, imageData->thumbnailFile()); imageMap.insert(DropboxImageCacheModel::Image, imageData->imageUrl()); imageMap.insert(DropboxImageCacheModel::Title, imageData->imageName()); imageMap.insert(DropboxImageCacheModel::DateTaken, imageData->createdTime()); imageMap.insert(DropboxImageCacheModel::Width, imageData->width()); imageMap.insert(DropboxImageCacheModel::Height, imageData->height()); imageMap.insert(DropboxImageCacheModel::MimeType, QLatin1String("image/jpeg")); imageMap.insert(DropboxImageCacheModel::AccountId, imageData->account()); imageMap.insert(DropboxImageCacheModel::UserId, imageData->userId()); imageMap.insert(DropboxImageCacheModel::AccessToken, imageData->accessToken()); data.append(imageMap); } break; } default: return; } updateData(data); // now download the queued thumbnails. Q_FOREACH (const QVariantMap &thumbQueueData, thumbQueue) { if (thumbQueueData["accessToken"].toString().length()) { d->queue(thumbQueueData["row"].toInt(), static_cast(thumbQueueData["imageType"].toInt()), thumbQueueData["identifier"].toString(), thumbQueueData["url"].toString(), thumbQueueData["accessToken"].toString()); } else { qWarning() << "Error: cannot queue without accessToken"; } } } libsocialcache-0.2.1/src/qml/dropbox/dropboximagecachemodel.h000066400000000000000000000052651462333522700243510ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DROPBOXIMAGECACHEMODEL_H #define DROPBOXIMAGECACHEMODEL_H #include "abstractsocialcachemodel.h" #include "dropboximagedownloader.h" class DropboxImageCacheModelPrivate; class DropboxImageCacheModel: public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(DropboxImageCacheModel::ModelDataType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(DropboxImageDownloader * downloader READ downloader WRITE setDownloader NOTIFY downloaderChanged) Q_ENUMS(DropboxGalleryRole) Q_ENUMS(ModelDataType) public: enum DropboxGalleryRole { DropboxId = 0, Thumbnail, Image, Title, DateTaken, Width, Height, Count, MimeType, AccountId, UserId, AccessToken }; enum ModelDataType { None = 0, // used for resetting/refreshing the model. Users, Albums, Images }; explicit DropboxImageCacheModel(QObject *parent = 0); ~DropboxImageCacheModel(); QHash roleNames() const; // properties DropboxImageCacheModel::ModelDataType type() const; void setType(DropboxImageCacheModel::ModelDataType type); DropboxImageDownloader *downloader() const; void setDownloader(DropboxImageDownloader *downloader); Q_INVOKABLE void removeImage(const QString &imageUrl); // from AbstractListModel QVariant data(const QModelIndex &index, int role) const; public Q_SLOTS: void loadImages(); void refresh(); Q_SIGNALS: void typeChanged(); void downloaderChanged(); private Q_SLOTS: void queryFinished(); void imageDownloaded(const QString &url, const QString &path, const QVariantMap &imageData); private: Q_DECLARE_PRIVATE(DropboxImageCacheModel) friend class DropboxImageDownloader; }; #endif // DROPBOXIMAGECACHEMODEL_H libsocialcache-0.2.1/src/qml/dropbox/dropboximagedownloader.cpp000066400000000000000000000101471462333522700247510ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dropboximagedownloader.h" #include "dropboximagedownloader_p.h" #include "dropboximagedownloaderconstants_p.h" #include "dropboximagecachemodel.h" #include #include #include static const char *MODEL_KEY = "model"; DropboxImageDownloaderPrivate::DropboxImageDownloaderPrivate(DropboxImageDownloader *q) : AbstractImageDownloaderPrivate(q) { } DropboxImageDownloaderPrivate::~DropboxImageDownloaderPrivate() { } DropboxImageDownloader::DropboxImageDownloader(QObject *parent) : AbstractImageDownloader(*new DropboxImageDownloaderPrivate(this), parent) { connect(this, &AbstractImageDownloader::imageDownloaded, this, &DropboxImageDownloader::invokeSpecificModelCallback); } DropboxImageDownloader::~DropboxImageDownloader() { } void DropboxImageDownloader::addModelToHash(DropboxImageCacheModel *model) { Q_D(DropboxImageDownloader); d->m_connectedModels.insert(model); } void DropboxImageDownloader::removeModelFromHash(DropboxImageCacheModel *model) { Q_D(DropboxImageDownloader); d->m_connectedModels.remove(model); } /* * A DropboxImageDownloader can be connected to multiple models. * Instead of connecting the imageDownloaded signal directly to the * model, we connect it to this slot, which retrieves the target model * from the metadata map and invokes its callback directly. * This avoids a possibly large number of signal connections + invocations. */ void DropboxImageDownloader::invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata) { Q_D(DropboxImageDownloader); DropboxImageCacheModel *model = static_cast(metadata.value(MODEL_KEY).value()); // check to see if the model was destroyed in the meantime. // If not, we can directly invoke the callback. if (d->m_connectedModels.contains(model)) { model->imageDownloaded(url, path, metadata); } } QString DropboxImageDownloader::outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const { Q_UNUSED(url); // We create the identifier by appending the type to the real identifier QString identifier = data.value(QLatin1String(IDENTIFIER_KEY)).toString(); if (identifier.isEmpty()) { return QString(); } QString typeString = data.value(QLatin1String(TYPE_KEY)).toString(); if (typeString.isEmpty()) { return QString(); } identifier.append(typeString); return makeOutputFile(SocialSyncInterface::Dropbox, SocialSyncInterface::Images, identifier, mimeType); } void DropboxImageDownloader::dbQueueImage(const QString &url, const QVariantMap &data, const QString &file) { Q_D(DropboxImageDownloader); Q_UNUSED(url); QString identifier = data.value(QLatin1String(IDENTIFIER_KEY)).toString(); if (identifier.isEmpty()) { return; } int type = data.value(QLatin1String(TYPE_KEY)).toInt(); switch (type) { case ThumbnailImage: d->database.updateImageThumbnail(identifier, file); break; } } void DropboxImageDownloader::dbWrite() { Q_D(DropboxImageDownloader); d->database.commit(); } libsocialcache-0.2.1/src/qml/dropbox/dropboximagedownloader.h000066400000000000000000000036751462333522700244260ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DROPBOXIMAGEDOWNLOADER_H #define DROPBOXIMAGEDOWNLOADER_H #include #include "abstractimagedownloader.h" class DropboxImageCacheModel; class DropboxImageDownloaderWorkerObject; class DropboxImageDownloaderPrivate; class DropboxImageDownloader : public AbstractImageDownloader { Q_OBJECT public: enum ImageType { ThumbnailImage, FullImage }; explicit DropboxImageDownloader(QObject *parent = 0); virtual ~DropboxImageDownloader(); // tracking object lifetime of models connected to this downloader. void addModelToHash(DropboxImageCacheModel *model); void removeModelFromHash(DropboxImageCacheModel *model); protected: QString outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const override; void dbQueueImage(const QString &url, const QVariantMap &data, const QString &file); void dbWrite(); private Q_SLOTS: void invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata); private: Q_DECLARE_PRIVATE(DropboxImageDownloader) }; #endif // DROPBOXIMAGEDOWNLOADER_H libsocialcache-0.2.1/src/qml/dropbox/dropboximagedownloader_p.h000066400000000000000000000030531462333522700247330ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Jonni Rainisto * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DROPBOXIMAGEDOWNLOADER_P_H #define DROPBOXIMAGEDOWNLOADER_P_H #include #include #include #include #include #include "abstractimagedownloader_p.h" #include "dropboximagedownloader.h" #include "dropboximagesdatabase.h" class DropboxImageCacheModel; class DropboxImageDownloaderPrivate: public AbstractImageDownloaderPrivate { public: explicit DropboxImageDownloaderPrivate(DropboxImageDownloader *q); virtual ~DropboxImageDownloaderPrivate(); DropboxImagesDatabase database; QSet m_connectedModels; private: Q_DECLARE_PUBLIC(DropboxImageDownloader) }; #endif // DROPBOXIMAGEDOWNLOADER_P_H libsocialcache-0.2.1/src/qml/dropbox/dropboximagedownloaderconstants_p.h000066400000000000000000000020551462333522700266710ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DROPBOXIMAGEDOWNLOADERCONSTANTS_P_H #define DROPBOXIMAGEDOWNLOADERCONSTANTS_P_H static const char *IDENTIFIER_KEY = "identifier"; static const char *TYPE_KEY = "type"; #endif // DROPBOXIMAGEDOWNLOADERCONSTANTS_P_H libsocialcache-0.2.1/src/qml/facebook/000077500000000000000000000000001462333522700175775ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/facebook/facebookimagecachemodel.cpp000066400000000000000000000323561462333522700250750ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebookimagecachemodel.h" #include "abstractsocialcachemodel_p.h" #include "facebookimagesdatabase.h" #include "facebookimagedownloader_p.h" #include "facebookimagedownloaderconstants_p.h" #include #include #include // Note: // // When querying photos, the nodeIdentifier should be either // - nothing: query all photos // - user-USER_ID: query all photos for the given user // - album-ALBUM_ID: query all photos for the given album // // When querying albums, the nodeIdentifier should be either // - nothing: query all albums for all users // - USER_ID: query all albums for the given user static const char *PHOTO_USER_PREFIX = "user-"; static const char *PHOTO_ALBUM_PREFIX = "album-"; static const char *URL_KEY = "url"; static const char *ROW_KEY = "row"; static const char *MODEL_KEY = "model"; #define SOCIALCACHE_FACEBOOK_IMAGE_DIR PRIVILEGED_DATA_DIR + QLatin1String("/Images/") class FacebookImageCacheModelPrivate : public AbstractSocialCacheModelPrivate { public: FacebookImageCacheModelPrivate(FacebookImageCacheModel *q); void queue( int row, FacebookImageDownloader::ImageType imageType, const QString &identifier, const QString &url); FacebookImageDownloader *downloader; FacebookImagesDatabase database; FacebookImageCacheModel::ModelDataType type; }; FacebookImageCacheModelPrivate::FacebookImageCacheModelPrivate(FacebookImageCacheModel *q) : AbstractSocialCacheModelPrivate(q), downloader(0), type(FacebookImageCacheModel::Images) { } void FacebookImageCacheModelPrivate::queue( int row, FacebookImageDownloader::ImageType imageType, const QString &identifier, const QString &url) { FacebookImageCacheModel *modelPtr = qobject_cast(q_ptr); if (downloader) { QVariantMap metadata; metadata.insert(QLatin1String(TYPE_KEY), imageType); metadata.insert(QLatin1String(IDENTIFIER_KEY), identifier); metadata.insert(QLatin1String(URL_KEY), url); metadata.insert(QLatin1String(ROW_KEY), row); metadata.insert(QLatin1String(MODEL_KEY), QVariant::fromValue((void*)modelPtr)); downloader->queue(url, metadata); } } FacebookImageCacheModel::FacebookImageCacheModel(QObject *parent) : AbstractSocialCacheModel(*(new FacebookImageCacheModelPrivate(this)), parent) { Q_D(const FacebookImageCacheModel); connect(&d->database, &FacebookImagesDatabase::queryFinished, this, &FacebookImageCacheModel::queryFinished); } FacebookImageCacheModel::~FacebookImageCacheModel() { Q_D(FacebookImageCacheModel); if (d->downloader) { d->downloader->removeModelFromHash(this); } } QHash FacebookImageCacheModel::roleNames() const { QHash roleNames; roleNames.insert(FacebookId, "facebookId"); roleNames.insert(Thumbnail, "thumbnail"); roleNames.insert(Image, "image"); roleNames.insert(Title, "title"); roleNames.insert(DateTaken, "dateTaken"); roleNames.insert(Width, "photoWidth"); roleNames.insert(Height, "photoHeight"); roleNames.insert(Count, "dataCount"); roleNames.insert(MimeType, "mimeType"); roleNames.insert(AccountId, "accountId"); roleNames.insert(UserId, "userId"); return roleNames; } FacebookImageCacheModel::ModelDataType FacebookImageCacheModel::type() const { Q_D(const FacebookImageCacheModel); return d->type; } void FacebookImageCacheModel::setType(FacebookImageCacheModel::ModelDataType type) { Q_D(FacebookImageCacheModel); if (d->type != type) { d->type = type; emit typeChanged(); } } FacebookImageDownloader * FacebookImageCacheModel::downloader() const { Q_D(const FacebookImageCacheModel); return d->downloader; } void FacebookImageCacheModel::setDownloader(FacebookImageDownloader *downloader) { Q_D(FacebookImageCacheModel); if (d->downloader != downloader) { if (d->downloader) { // Disconnect worker object disconnect(d->downloader); d->downloader->removeModelFromHash(this); } d->downloader = downloader; d->downloader->addModelToHash(this); emit downloaderChanged(); } } QVariant FacebookImageCacheModel::data(const QModelIndex &index, int role) const { Q_D(const FacebookImageCacheModel); int row = index.row(); if (row < 0 || row >= d->m_data.count()) { return QVariant(); } if (role == FacebookImageCacheModel::Image) { if (d->m_data.at(row).value(role).toString().isEmpty()) { // haven't downloaded the image yet. Download it. if (d->database.images().size() > row) { FacebookImage::ConstPtr imageData = d->database.images().at(row); FacebookImageCacheModelPrivate *nonconstD = const_cast(d); nonconstD->queue(row, FacebookImageDownloader::FullImage, imageData->fbImageId(), imageData->imageUrl()); } } } return d->m_data.at(row).value(role); } void FacebookImageCacheModel::loadImages() { refresh(); } void FacebookImageCacheModel::refresh() { Q_D(FacebookImageCacheModel); const QString userPrefix = QLatin1String(PHOTO_USER_PREFIX); const QString albumPrefix = QLatin1String(PHOTO_ALBUM_PREFIX); switch (d->type) { case FacebookImageCacheModel::Users: d->database.queryUsers(); break; case FacebookImageCacheModel::Albums: d->database.queryAlbums(d->nodeIdentifier); break; case FacebookImageCacheModel::Images: if (d->nodeIdentifier.startsWith(userPrefix)) { d->database.queryUserImages(d->nodeIdentifier.mid(userPrefix.size())); } else if (d->nodeIdentifier.startsWith(albumPrefix)) { d->database.queryAlbumImages(d->nodeIdentifier.mid(albumPrefix.size())); } else { d->database.queryUserImages(); } break; default: break; } } // NOTE: this is now called directly by FacebookImageDownloader // rather than connected to the imageDownloaded signal, for // performance reasons. void FacebookImageCacheModel::imageDownloaded( const QString &, const QString &path, const QVariantMap &imageData) { Q_D(FacebookImageCacheModel); if (path.isEmpty()) { // empty path signifies an error, which we don't handle here at the moment. // Return, otherwise dataChanged signal would cause UI to read back // related value, which, being empty, would trigger another download request, // potentially causing never ending loop. return; } int row = imageData.value(ROW_KEY).toInt(); if (row < 0 || row >= d->m_data.count()) { qWarning() << Q_FUNC_INFO << "Invalid row:" << row << "max row:" << d->m_data.count(); return; } int type = imageData.value(TYPE_KEY).toInt(); switch (type) { case FacebookImageDownloader::ThumbnailImage: d->m_data[row].insert(FacebookImageCacheModel::Thumbnail, path); break; case FacebookImageDownloader::FullImage: d->m_data[row].insert(FacebookImageCacheModel::Image, path); break; default: qWarning() << Q_FUNC_INFO << "invalid downloader type: " << type; break; } emit dataChanged(index(row), index(row)); } void FacebookImageCacheModel::queryFinished() { Q_D(FacebookImageCacheModel); QList thumbQueue; SocialCacheModelData data; switch (d->type) { case Users: { QList usersData = d->database.users(); int count = 0; Q_FOREACH (const FacebookUser::ConstPtr &userData, usersData) { QMap userMap; userMap.insert(FacebookImageCacheModel::FacebookId, userData->fbUserId()); userMap.insert(FacebookImageCacheModel::Title, userData->userName()); userMap.insert(FacebookImageCacheModel::Count, userData->count()); count += userData->count(); data.append(userMap); } if (data.count() > 1) { QMap userMap; userMap.insert(FacebookImageCacheModel::FacebookId, QString()); userMap.insert(FacebookImageCacheModel::Thumbnail, QString()); //: Label for the "show all users from all Facebook accounts" option //% "All" userMap.insert(FacebookImageCacheModel::Title, qtTrId("nemo_socialcache_facebook_images_model-all-users")); userMap.insert(FacebookImageCacheModel::Count, count); data.prepend(userMap); } break; } case Albums: { QList albumsData = d->database.albums(); int count = 0; Q_FOREACH (const FacebookAlbum::ConstPtr &albumData, albumsData) { QMap albumMap; albumMap.insert(FacebookImageCacheModel::FacebookId, albumData->fbAlbumId()); albumMap.insert(FacebookImageCacheModel::Title, albumData->albumName()); albumMap.insert(FacebookImageCacheModel::Count, albumData->imageCount()); albumMap.insert(FacebookImageCacheModel::UserId, albumData->fbUserId()); count += albumData->imageCount(); data.append(albumMap); } if (data.count() > 1) { QMap albumMap; albumMap.insert(FacebookImageCacheModel::FacebookId, QString()); // albumMap.insert(FacebookImageCacheModel::Icon, QString()); //: Label for the "show all photos from all albums by this user" option //% "All" albumMap.insert(FacebookImageCacheModel::Title, qtTrId("nemo_socialcache_facebook_images_model-all-albums")); albumMap.insert(FacebookImageCacheModel::Count, count); if (d->nodeIdentifier.isEmpty()) { albumMap.insert(FacebookImageCacheModel::UserId, QString()); } else { albumMap.insert(FacebookImageCacheModel::UserId, data.first().value(FacebookImageCacheModel::UserId)); } data.prepend(albumMap); } break; } case Images: { QList imagesData = d->database.images(); for (int i = 0; i < imagesData.count(); i ++) { const FacebookImage::ConstPtr & imageData = imagesData.at(i); QMap imageMap; imageMap.insert(FacebookImageCacheModel::FacebookId, imageData->fbImageId()); if (imageData->thumbnailFile().isEmpty()) { QVariantMap thumbQueueData; thumbQueueData.insert("row", QVariant::fromValue(i)); thumbQueueData.insert("imageType", QVariant::fromValue(FacebookImageDownloader::ThumbnailImage)); thumbQueueData.insert("identifier", imageData->fbImageId()); thumbQueueData.insert("url", imageData->thumbnailUrl()); thumbQueue.append(thumbQueueData); } // note: we don't queue the image file until the user explicitly opens that in fullscreen. imageMap.insert(FacebookImageCacheModel::Thumbnail, imageData->thumbnailFile()); imageMap.insert(FacebookImageCacheModel::Image, imageData->imageFile()); imageMap.insert(FacebookImageCacheModel::Title, imageData->imageName()); imageMap.insert(FacebookImageCacheModel::DateTaken, imageData->createdTime()); imageMap.insert(FacebookImageCacheModel::Width, imageData->width()); imageMap.insert(FacebookImageCacheModel::Height, imageData->height()); imageMap.insert(FacebookImageCacheModel::MimeType, QLatin1String("image/jpeg")); imageMap.insert(FacebookImageCacheModel::AccountId, imageData->account()); imageMap.insert(FacebookImageCacheModel::UserId, imageData->fbUserId()); data.append(imageMap); } break; } default: return; } updateData(data); // now download the queued thumbnails. Q_FOREACH (const QVariantMap &thumbQueueData, thumbQueue) { d->queue(thumbQueueData["row"].toInt(), static_cast(thumbQueueData["imageType"].toInt()), thumbQueueData["identifier"].toString(), thumbQueueData["url"].toString()); } } libsocialcache-0.2.1/src/qml/facebook/facebookimagecachemodel.h000066400000000000000000000051551462333522700245370ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKIMAGECACHEMODEL_H #define FACEBOOKIMAGECACHEMODEL_H #include "abstractsocialcachemodel.h" #include "facebookimagedownloader.h" class FacebookImageCacheModelPrivate; class FacebookImageCacheModel: public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(FacebookImageCacheModel::ModelDataType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(FacebookImageDownloader * downloader READ downloader WRITE setDownloader NOTIFY downloaderChanged) Q_ENUMS(FacebookGalleryRole) Q_ENUMS(ModelDataType) public: enum FacebookGalleryRole { FacebookId = 0, Thumbnail, Image, Title, DateTaken, Width, Height, Count, MimeType, AccountId, UserId }; enum ModelDataType { None = 0, // used for resetting/refreshing the model. Users, Albums, Images }; explicit FacebookImageCacheModel(QObject *parent = 0); ~FacebookImageCacheModel(); QHash roleNames() const; // properties FacebookImageCacheModel::ModelDataType type() const; void setType(FacebookImageCacheModel::ModelDataType type); FacebookImageDownloader *downloader() const; void setDownloader(FacebookImageDownloader *downloader); // from AbstractListModel QVariant data(const QModelIndex &index, int role) const; public Q_SLOTS: void loadImages(); void refresh(); Q_SIGNALS: void typeChanged(); void downloaderChanged(); private Q_SLOTS: void queryFinished(); void imageDownloaded(const QString &url, const QString &path, const QVariantMap &imageData); private: Q_DECLARE_PRIVATE(FacebookImageCacheModel) friend class FacebookImageDownloader; }; #endif // FACEBOOKIMAGECACHEMODEL_H libsocialcache-0.2.1/src/qml/facebook/facebookimagedownloader.cpp000066400000000000000000000104151462333522700251370ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebookimagedownloader.h" #include "facebookimagedownloader_p.h" #include "facebookimagedownloaderconstants_p.h" #include "facebookimagecachemodel.h" #include #include #include static const char *MODEL_KEY = "model"; FacebookImageDownloaderPrivate::FacebookImageDownloaderPrivate(FacebookImageDownloader *q) : AbstractImageDownloaderPrivate(q) { } FacebookImageDownloaderPrivate::~FacebookImageDownloaderPrivate() { } FacebookImageDownloader::FacebookImageDownloader(QObject *parent) : AbstractImageDownloader(*new FacebookImageDownloaderPrivate(this), parent) { connect(this, &AbstractImageDownloader::imageDownloaded, this, &FacebookImageDownloader::invokeSpecificModelCallback); } FacebookImageDownloader::~FacebookImageDownloader() { } void FacebookImageDownloader::addModelToHash(FacebookImageCacheModel *model) { Q_D(FacebookImageDownloader); d->m_connectedModels.insert(model); } void FacebookImageDownloader::removeModelFromHash(FacebookImageCacheModel *model) { Q_D(FacebookImageDownloader); d->m_connectedModels.remove(model); } /* * A FacebookImageDownloader can be connected to multiple models. * Instead of connecting the imageDownloaded signal directly to the * model, we connect it to this slot, which retrieves the target model * from the metadata map and invokes its callback directly. * This avoids a possibly large number of signal connections + invocations. */ void FacebookImageDownloader::invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata) { Q_D(FacebookImageDownloader); FacebookImageCacheModel *model = static_cast(metadata.value(MODEL_KEY).value()); // check to see if the model was destroyed in the meantime. // If not, we can directly invoke the callback. if (d->m_connectedModels.contains(model)) { model->imageDownloaded(url, path, metadata); } } QString FacebookImageDownloader::outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const { Q_UNUSED(url); Q_UNUSED(mimeType); // TODO: use? // We create the identifier by appending the type to the real identifier QString identifier = data.value(QLatin1String(IDENTIFIER_KEY)).toString(); if (identifier.isEmpty()) { return QString(); } QString typeString = data.value(QLatin1String(TYPE_KEY)).toString(); if (typeString.isEmpty()) { return QString(); } identifier.append(typeString); return makeOutputFile(SocialSyncInterface::Facebook, SocialSyncInterface::Images, identifier, QString()); } void FacebookImageDownloader::dbQueueImage(const QString &url, const QVariantMap &data, const QString &file) { Q_D(FacebookImageDownloader); Q_UNUSED(url); QString identifier = data.value(QLatin1String(IDENTIFIER_KEY)).toString(); if (identifier.isEmpty()) { return; } int type = data.value(QLatin1String(TYPE_KEY)).toInt(); switch (type) { case ThumbnailImage: d->database.updateImageThumbnail(identifier, file); break; case FullImage: d->database.updateImageFile(identifier, file); break; } } void FacebookImageDownloader::dbWrite() { Q_D(FacebookImageDownloader); d->database.commit(); } libsocialcache-0.2.1/src/qml/facebook/facebookimagedownloader.h000066400000000000000000000036771462333522700246200ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKIMAGEDOWNLOADER_H #define FACEBOOKIMAGEDOWNLOADER_H #include #include "abstractimagedownloader.h" class FacebookImageCacheModel; class FacebookImageDownloaderWorkerObject; class FacebookImageDownloaderPrivate; class FacebookImageDownloader : public AbstractImageDownloader { Q_OBJECT public: enum ImageType { ThumbnailImage, FullImage }; explicit FacebookImageDownloader(QObject *parent = 0); virtual ~FacebookImageDownloader(); // tracking object lifetime of models connected to this downloader. void addModelToHash(FacebookImageCacheModel *model); void removeModelFromHash(FacebookImageCacheModel *model); protected: QString outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const override; void dbQueueImage(const QString &url, const QVariantMap &data, const QString &file); void dbWrite(); private Q_SLOTS: void invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata); private: Q_DECLARE_PRIVATE(FacebookImageDownloader) }; #endif // FACEBOOKIMAGEDOWNLOADER_H libsocialcache-0.2.1/src/qml/facebook/facebookimagedownloader_p.h000066400000000000000000000030561462333522700251260ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKIMAGEDOWNLOADER_P_H #define FACEBOOKIMAGEDOWNLOADER_P_H #include #include #include #include #include #include "abstractimagedownloader_p.h" #include "facebookimagedownloader.h" #include "facebookimagesdatabase.h" class FacebookImageCacheModel; class FacebookImageDownloaderPrivate: public AbstractImageDownloaderPrivate { public: explicit FacebookImageDownloaderPrivate(FacebookImageDownloader *q); virtual ~FacebookImageDownloaderPrivate(); FacebookImagesDatabase database; QSet m_connectedModels; private: Q_DECLARE_PUBLIC(FacebookImageDownloader) }; #endif // FACEBOOKIMAGEDOWNLOADER_P_H libsocialcache-0.2.1/src/qml/facebook/facebookimagedownloaderconstants_p.h000066400000000000000000000020461462333522700270610ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKIMAGEDOWNLOADERCONSTANTS_P_H #define FACEBOOKIMAGEDOWNLOADERCONSTANTS_P_H static const char *IDENTIFIER_KEY = "identifier"; static const char *TYPE_KEY = "type"; #endif // FACEBOOKIMAGEDOWNLOADERCONSTANTS_P_H libsocialcache-0.2.1/src/qml/facebook/facebooknotificationsmodel.cpp000066400000000000000000000114131462333522700256670ustar00rootroot00000000000000/* * Copyright (C) 2014 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebooknotificationsmodel.h" #include "abstractsocialcachemodel_p.h" #include "facebooknotificationsdatabase.h" class FacebookNotificationsModelPrivate : public AbstractSocialCacheModelPrivate { public: explicit FacebookNotificationsModelPrivate(FacebookNotificationsModel *q); FacebookNotificationsDatabase database; private: Q_DECLARE_PUBLIC(FacebookNotificationsModel) }; FacebookNotificationsModelPrivate::FacebookNotificationsModelPrivate(FacebookNotificationsModel *q) : AbstractSocialCacheModelPrivate(q) { } FacebookNotificationsModel::FacebookNotificationsModel(QObject *parent) : AbstractSocialCacheModel(*(new FacebookNotificationsModelPrivate(this)), parent) { Q_D(FacebookNotificationsModel); connect(&d->database, SIGNAL(notificationsChanged()), this, SLOT(notificationsChanged())); connect(&d->database, SIGNAL(accountIdFilterChanged()), this, SIGNAL(accountIdFilterChanged())); } QHash FacebookNotificationsModel::roleNames() const { QHash roleNames; roleNames.insert(NotificationId, "notificationId"); roleNames.insert(From, "from"); roleNames.insert(To, "to"); roleNames.insert(Timestamp, "timestamp"); roleNames.insert(Title, "title"); roleNames.insert(Link, "link"); roleNames.insert(AppId, "appId"); roleNames.insert(Object, "object"); roleNames.insert(Unread, "unread"); roleNames.insert(Accounts, "accounts"); roleNames.insert(ClientId, "clientId"); return roleNames; } QVariantList FacebookNotificationsModel::accountIdFilter() const { Q_D(const FacebookNotificationsModel); return d->database.accountIdFilter(); } void FacebookNotificationsModel::setAccountIdFilter(const QVariantList &accountIds) { Q_D(FacebookNotificationsModel); d->database.setAccountIdFilter(accountIds); } void FacebookNotificationsModel::refresh() { notificationsChanged(); } void FacebookNotificationsModel::remove(const QString ¬ificationId) { Q_D(FacebookNotificationsModel); for (int i=0; iremoveRange(i, 1); d->database.removeNotification(notificationId); d->database.sync(); break; } } } void FacebookNotificationsModel::clear() { Q_D(FacebookNotificationsModel); d->clearData(); d->database.removeAllNotifications(); } void FacebookNotificationsModel::notificationsChanged() { Q_D(FacebookNotificationsModel); SocialCacheModelData data; QList notificationsData = d->database.notifications(); Q_FOREACH (const FacebookNotification::ConstPtr ¬ification, notificationsData) { QMap eventMap; eventMap.insert(FacebookNotificationsModel::NotificationId, notification->facebookId()); eventMap.insert(FacebookNotificationsModel::From, notification->from()); eventMap.insert(FacebookNotificationsModel::To, notification->to()); eventMap.insert(FacebookNotificationsModel::Timestamp, notification->updatedTime()); eventMap.insert(FacebookNotificationsModel::Title, notification->title()); eventMap.insert(FacebookNotificationsModel::Link, notification->link()); eventMap.insert(FacebookNotificationsModel::AppId, notification->application()); eventMap.insert(FacebookNotificationsModel::Object, notification->object()); eventMap.insert(FacebookNotificationsModel::Unread, notification->unread()); eventMap.insert(FacebookNotificationsModel::Accounts, notification->accountId()); QVariantList accountsVariant; accountsVariant.append(notification->accountId()); eventMap.insert(FacebookNotificationsModel::Accounts, accountsVariant); eventMap.insert(FacebookNotificationsModel::ClientId, notification->clientId()); data.append(eventMap); } updateData(data); } libsocialcache-0.2.1/src/qml/facebook/facebooknotificationsmodel.h000066400000000000000000000037271462333522700253450ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKNOTIFICATIONSMODEL_H #define FACEBOOKNOTIFICATIONSMODEL_H #include "abstractsocialcachemodel.h" class FacebookNotificationsModelPrivate; class FacebookNotificationsModel : public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged) Q_ENUMS(FacebookNotificationsRole) public: enum FacebookNotificationsRole { NotificationId = 0, From, To, Timestamp, Title, Link, AppId, Object, Accounts, ClientId, Unread }; explicit FacebookNotificationsModel(QObject *parent = 0); QHash roleNames() const; QVariantList accountIdFilter() const; void setAccountIdFilter(const QVariantList &accountIds); void refresh(); Q_INVOKABLE void remove(const QString ¬ificationId); Q_INVOKABLE void clear(); signals: void accountIdFilterChanged(); private Q_SLOTS: void notificationsChanged(); private: Q_DECLARE_PRIVATE(FacebookNotificationsModel) }; #endif // FACEBOOKNOTIFICATIONSMODEL_H libsocialcache-0.2.1/src/qml/facebook/facebookpostsmodel.cpp000066400000000000000000000104041462333522700241650ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "facebookpostsmodel.h" #include "abstractsocialcachemodel_p.h" #include "facebookpostsdatabase.h" #include #include "postimagehelper_p.h" class FacebookPostsModelPrivate: public AbstractSocialCacheModelPrivate { public: explicit FacebookPostsModelPrivate(FacebookPostsModel *q); FacebookPostsDatabase database; private: Q_DECLARE_PUBLIC(FacebookPostsModel) }; FacebookPostsModelPrivate::FacebookPostsModelPrivate(FacebookPostsModel *q) : AbstractSocialCacheModelPrivate(q) { } FacebookPostsModel::FacebookPostsModel(QObject *parent) : AbstractSocialCacheModel(*(new FacebookPostsModelPrivate(this)), parent) { Q_D(FacebookPostsModel); connect(&d->database, &AbstractSocialPostCacheDatabase::postsChanged, this, &FacebookPostsModel::postsChanged); } QHash FacebookPostsModel::roleNames() const { QHash roleNames; roleNames.insert(FacebookId, "facebookId"); roleNames.insert(Name, "name"); roleNames.insert(Body, "body"); roleNames.insert(Timestamp, "timestamp"); roleNames.insert(Icon, "icon"); roleNames.insert(Images, "images"); roleNames.insert(AttachmentName, "attachmentName"); roleNames.insert(AttachmentCaption, "attachmentCaption"); roleNames.insert(AttachmentDescription, "attachmentDescription"); roleNames.insert(AttachmentUrl, "attachmentUrl"); roleNames.insert(AllowLike, "allowLike"); roleNames.insert(AllowComment, "allowComment"); roleNames.insert(ClientId, "clientId"); roleNames.insert(Accounts, "accounts"); return roleNames; } void FacebookPostsModel::refresh() { Q_D(FacebookPostsModel); d->database.refresh(); } void FacebookPostsModel::postsChanged() { Q_D(FacebookPostsModel); SocialCacheModelData data; QList postsData = d->database.posts(); Q_FOREACH (const SocialPost::ConstPtr &post, postsData) { QMap eventMap; eventMap.insert(FacebookPostsModel::FacebookId, post->identifier()); eventMap.insert(FacebookPostsModel::Name, post->name()); eventMap.insert(FacebookPostsModel::Body, post->body()); eventMap.insert(FacebookPostsModel::Timestamp, post->timestamp()); eventMap.insert(FacebookPostsModel::Icon, post->icon()); QVariantList images; Q_FOREACH (const SocialPostImage::ConstPtr &image, post->images()) { images.append(createImageData(image)); } eventMap.insert(FacebookPostsModel::Images, images); eventMap.insert(FacebookPostsModel::AttachmentName, d->database.attachmentName(post)); eventMap.insert(FacebookPostsModel::AttachmentCaption, d->database.attachmentCaption(post)); eventMap.insert(FacebookPostsModel::AttachmentDescription, d->database.attachmentDescription(post)); eventMap.insert(FacebookPostsModel::AttachmentUrl, d->database.attachmentUrl(post)); eventMap.insert(FacebookPostsModel::AllowLike, d->database.allowLike(post)); eventMap.insert(FacebookPostsModel::AllowComment, d->database.allowComment(post)); eventMap.insert(FacebookPostsModel::ClientId, d->database.clientId(post)); QVariantList accountsVariant; Q_FOREACH (int account, post->accounts()) { accountsVariant.append(account); } eventMap.insert(FacebookPostsModel::Accounts, accountsVariant); data.append(eventMap); } updateData(data); } libsocialcache-0.2.1/src/qml/facebook/facebookpostsmodel.h000066400000000000000000000031201462333522700236270ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACEBOOKPOSTSMODEL_H #define FACEBOOKPOSTSMODEL_H #include "abstractsocialcachemodel.h" class FacebookPostsModelPrivate; class FacebookPostsModel: public AbstractSocialCacheModel { Q_OBJECT public: enum FacebookPostsRole { FacebookId = 0, Name, Body, Timestamp, Icon, Images, AttachmentName, AttachmentCaption, AttachmentDescription, AttachmentUrl, AllowLike, AllowComment, ClientId, Accounts }; explicit FacebookPostsModel(QObject *parent = 0); QHash roleNames() const; void refresh(); private Q_SLOTS: void postsChanged(); private: Q_DECLARE_PRIVATE(FacebookPostsModel) }; #endif // FACEBOOKPOSTSMODEL_H libsocialcache-0.2.1/src/qml/generic/000077500000000000000000000000001462333522700174425ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/generic/socialimagedownloader.cpp000066400000000000000000000176151462333522700245140ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "socialimagedownloader.h" #include "socialimagedownloader_p.h" #include #include #include #include #include SocialImageDownloaderPrivate::SocialImageDownloaderPrivate(SocialImageDownloader *q) : AbstractImageDownloaderPrivate(q) { } SocialImageDownloaderPrivate::~SocialImageDownloaderPrivate() { if (m_commitTimer.isActive()) { m_db.commit(); } m_db.wait(); } SocialImageDownloader::SocialImageDownloader(QObject *parent) : AbstractImageDownloader(*new SocialImageDownloaderPrivate(this), parent) { Q_D(SocialImageDownloader); connect(this, &AbstractImageDownloader::imageDownloaded, this, &SocialImageDownloader::notifyImageCached); // Commit timer will wait 30 seconds for additional addImage database calls to avoid // unnecessary commits. d->m_commitTimer.setInterval(30000); d->m_commitTimer.setSingleShot(true); connect(&d->m_commitTimer, SIGNAL(timeout()), this, SLOT(commitTimerTimeout())); } SocialImageDownloader::~SocialImageDownloader() { } QString SocialImageDownloader::cached(const QString &imageId) { Q_D(SocialImageDownloader); QString recentById = d->m_recentItemsById.value(imageId); if (!recentById.isEmpty()) { return recentById; } SocialImage::ConstPtr image = d->m_db.imageById(imageId); if (image != 0) { d->m_recentItemsById.insert(imageId, image->imageFile()); return image->imageFile(); } return QString(); } void SocialImageDownloader::imageFile(const QString &imageUrl, int accountId, QObject *caller, int expiresInDays, const QString &imageId, const QString &accessToken ) { Q_D(SocialImageDownloader); if (imageUrl.isEmpty() || caller == 0) { return; } QMutexLocker locker(&d->m_mutex); if (!imageId.isEmpty()) { // check if an image with same id was cached recently QString recentById = d->m_recentItemsById.value(imageId); if (!recentById.isEmpty()) { QMetaObject::invokeMethod(caller, "imageCached", Q_ARG(QVariant, recentById)); return; } else { SocialImage::ConstPtr image = d->m_db.imageById(imageId); if (image != 0) { d->m_recentItemsById.insert(imageId, image->imageFile()); QMetaObject::invokeMethod(caller, "imageCached", Q_ARG(QVariant, image->imageFile())); return; } } } else { QString recent = d->m_recentItems.value(imageUrl); if (!recent.isEmpty()) { QMetaObject::invokeMethod(caller, "imageCached", Q_ARG(QVariant, recent)); return; } else { SocialImage::ConstPtr image = d->m_db.image(imageUrl); if (image != 0) { d->m_recentItems.insert(imageUrl, image->imageFile()); QMetaObject::invokeMethod(caller, "imageCached", Q_ARG(QVariant, image->imageFile())); return; } } } d->m_ongoingCalls.insert(imageUrl, QPointer(caller)); QVariantMap data; data.insert(QStringLiteral("accountId"), accountId); data.insert(QStringLiteral("expiresInDays"), expiresInDays); data.insert(QStringLiteral("imageId"), imageId); if (accessToken.length()) data.insert(QStringLiteral("accessToken"), accessToken); queue(imageUrl, data); return; } void SocialImageDownloader::removeFromRecentlyUsed(const QString &imageUrl) { Q_D(SocialImageDownloader); QMutexLocker locker(&d->m_mutex); d->m_recentItems.remove(imageUrl); } void SocialImageDownloader::removeFromRecentlyUsedById(const QString &imageId) { Q_D(SocialImageDownloader); QMutexLocker locker(&d->m_mutex); d->m_recentItemsById.remove(imageId); } void SocialImageDownloader::notifyImageCached(const QString &imageUrl, const QString &imageFile, const QVariantMap &metadata) { Q_D(SocialImageDownloader); QMutexLocker locker(&d->m_mutex); QList< QPointer > ongoingCalls = d->m_ongoingCalls.values(imageUrl); if (imageFile.isEmpty()) { qWarning() << "SocialImageDownloader: failed to download " << imageUrl; for (int i = 0; i < ongoingCalls.count(); ++i) { if (ongoingCalls.at(i) != 0) { QMetaObject::invokeMethod(ongoingCalls.at(i).data(), "downloadError"); } } d->m_ongoingCalls.remove(imageUrl); return; } d->m_recentItems.insert(imageUrl, imageFile); int accountId = metadata.value(QStringLiteral("accountId")).toInt(); int expiresInDays = metadata.value(QStringLiteral("expiresInDays")).toInt(); QString imageId = metadata.value(QStringLiteral("imageId")).toString(); if (!imageId.isEmpty()) { d->m_recentItemsById.insert(imageId, imageFile); } QDateTime currentTime(QDateTime::currentDateTime()); d->m_db.addImage(accountId, imageUrl, imageFile, currentTime, currentTime.addDays(expiresInDays), imageId); // We assume that there will consecutive addImage calls. Wait suitable // time before commiting. if (d->m_commitTimer.isActive()) { d->m_commitTimer.stop(); } d->m_commitTimer.start(); for (int i = 0; i < ongoingCalls.count(); ++i) { if (ongoingCalls.at(i) != 0) { QMetaObject::invokeMethod(ongoingCalls.at(i).data(), "imageCached", Q_ARG(QVariant, imageFile)); } } d->m_ongoingCalls.remove(imageUrl); } QString SocialImageDownloader::outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const { Q_UNUSED(data); if (url.isEmpty()) { return QString(); } QUrl name(url); QString ending; QStringList parts = name.fileName().split("."); if (parts.count() > 1) { ending = parts.last(); } if (ending.isEmpty()) { if (mimeType == QStringLiteral("image/png")) { ending = QStringLiteral("png"); } else { // assume jpg ending = "jpg"; } } QCryptographicHash hash (QCryptographicHash::Md5); hash.addData(url.toUtf8()); QByteArray hashedIdentifier = hash.result().toHex(); QString path = QStringLiteral("%1%2/%3/%4/%5.%6").arg(PRIVILEGED_DATA_DIR, SocialSyncInterface::dataType(SocialSyncInterface::Images), "cache", QChar(hashedIdentifier.at(0)), hashedIdentifier, ending); return path; } void SocialImageDownloader::commitTimerTimeout() { Q_D(SocialImageDownloader); d->m_db.commit(); } libsocialcache-0.2.1/src/qml/generic/socialimagedownloader.h000066400000000000000000000040171462333522700241510ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SOCIALIMAGEDOWNLOADER_H #define SOCIALIMAGEDOWNLOADER_H #include #include "abstractimagedownloader.h" class SocialImageCacheModel; class SocialImageDownloaderPrivate; class SocialImageDownloader : public AbstractImageDownloader { Q_OBJECT public: explicit SocialImageDownloader(QObject *parent = 0); virtual ~SocialImageDownloader(); Q_INVOKABLE QString cached(const QString &imageId); Q_INVOKABLE void imageFile(const QString &imageUrl, int accountId, QObject *caller, int expiresInDays = 30, const QString &imageId = QString(), const QString &accessToken = QString()); Q_INVOKABLE void removeFromRecentlyUsed(const QString &imageUrl); Q_INVOKABLE void removeFromRecentlyUsedById(const QString &imageId); protected: QString outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const override; private Q_SLOTS: void notifyImageCached(const QString &url, const QString &path, const QVariantMap &metadata); void commitTimerTimeout(); private: Q_DECLARE_PRIVATE(SocialImageDownloader) }; #endif // SOCIALIMAGEDOWNLOADER_H libsocialcache-0.2.1/src/qml/generic/socialimagedownloader_p.h000066400000000000000000000030321462333522700244640ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SOCIALIMAGEDOWNLOADER_P_H #define SOCIALIMAGEDOWNLOADER_P_H #include "abstractimagedownloader_p.h" #include "socialimagedownloader.h" #include "socialimagesdatabase.h" #include class SocialImageDownloaderPrivate : public AbstractImageDownloaderPrivate { public: explicit SocialImageDownloaderPrivate(SocialImageDownloader *q); virtual ~SocialImageDownloaderPrivate(); SocialImagesDatabase m_db; QTimer m_commitTimer; QMap m_recentItems; QMap m_recentItemsById; QMultiMap > m_ongoingCalls; QMutex m_mutex; private: Q_DECLARE_PUBLIC(SocialImageDownloader) }; #endif // SOCIALIMAGEDOWNLOADER_P_H libsocialcache-0.2.1/src/qml/keyproviderhelper.cpp000066400000000000000000000102251462333522700222750ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "keyproviderhelper.h" #include KeyProviderHelper::KeyProviderHelper(QObject *parent) : QObject(parent) , m_triedLoadingFacebook(false) , m_triedLoadingTwitter(false) , m_triedLoadingOneDrive(false) , m_triedLoadingDropbox(false) , m_triedLoadingVk(false) { } QString KeyProviderHelper::dropboxClientId() { if (!m_triedLoadingDropbox) { loadDropbox(); } return m_dropboxClientId; } void KeyProviderHelper::loadDropbox() { m_triedLoadingDropbox = true; char *cClientId = NULL; int cSuccess = SailfishKeyProvider_storedKey("dropbox", "dropbox-sync", "client_id", &cClientId); if (cSuccess != 0 || cClientId == NULL) { return; } m_dropboxClientId = QLatin1String(cClientId); free(cClientId); } QString KeyProviderHelper::facebookClientId() { if (!m_triedLoadingFacebook) { loadFacebook(); } return m_facebookClientId; } QString KeyProviderHelper::twitterConsumerKey() { if (!m_triedLoadingTwitter) { loadTwitter(); } return m_twitterConsumerKey; } QString KeyProviderHelper::twitterConsumerSecret() { if (!m_triedLoadingTwitter) { loadTwitter(); } return m_twitterConsumerSecret; } QString KeyProviderHelper::oneDriveClientId() { if (!m_triedLoadingOneDrive) { loadOneDrive(); } return m_oneDriveClientId; } QString KeyProviderHelper::vkClientId() { if (!m_triedLoadingVk) { loadVk(); } return m_vkClientId; } void KeyProviderHelper::loadFacebook() { m_triedLoadingFacebook = true; char *cClientId = NULL; int cSuccess = SailfishKeyProvider_storedKey("facebook", "facebook-sync", "client_id", &cClientId); if (cSuccess != 0 || cClientId == NULL) { return; } m_facebookClientId = QLatin1String(cClientId); free(cClientId); } void KeyProviderHelper::loadTwitter() { m_triedLoadingTwitter = true; char *cConsumerKey = NULL; char *cConsumerSecret = NULL; int ckSuccess = SailfishKeyProvider_storedKey("twitter", "twitter-sync", "consumer_key", &cConsumerKey); int csSuccess = SailfishKeyProvider_storedKey("twitter", "twitter-sync", "consumer_secret", &cConsumerSecret); if (ckSuccess != 0 || cConsumerKey == NULL || csSuccess != 0 || cConsumerSecret == NULL) { return; } m_twitterConsumerKey = QLatin1String(cConsumerKey); m_twitterConsumerSecret = QLatin1String(cConsumerSecret); free(cConsumerKey); free(cConsumerSecret); } void KeyProviderHelper::loadOneDrive() { m_triedLoadingOneDrive = true; char *cClientId = NULL; int cSuccess = SailfishKeyProvider_storedKey("onedrive", "onedrive-sync", "client_id", &cClientId); if (cSuccess != 0 || cClientId == NULL) { return; } m_oneDriveClientId = QLatin1String(cClientId); free(cClientId); } void KeyProviderHelper::loadVk() { m_triedLoadingVk = true; char *cClientId = NULL; int cSuccess = SailfishKeyProvider_storedKey("vk", "vk-sync", "client_id", &cClientId); if (cSuccess != 0 || cClientId == NULL) { return; } m_vkClientId = QLatin1String(cClientId); free(cClientId); } libsocialcache-0.2.1/src/qml/keyproviderhelper.h000066400000000000000000000041721462333522700217460ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KEYPROVIDERHELPER_H #define KEYPROVIDERHELPER_H #include class KeyProviderHelper : public QObject { Q_OBJECT Q_PROPERTY(QString facebookClientId READ facebookClientId CONSTANT) Q_PROPERTY(QString twitterConsumerKey READ twitterConsumerKey CONSTANT) Q_PROPERTY(QString twitterConsumerSecret READ twitterConsumerSecret CONSTANT) Q_PROPERTY(QString oneDriveClientId READ oneDriveClientId CONSTANT) Q_PROPERTY(QString dropboxClientId READ dropboxClientId CONSTANT) Q_PROPERTY(QString vkClientId READ vkClientId CONSTANT) public: explicit KeyProviderHelper(QObject *parent = 0); QString facebookClientId(); QString twitterConsumerKey(); QString twitterConsumerSecret(); QString oneDriveClientId(); QString dropboxClientId(); QString vkClientId(); private: void loadFacebook(); void loadTwitter(); void loadOneDrive(); void loadDropbox(); void loadVk(); bool m_triedLoadingFacebook; QString m_facebookClientId; bool m_triedLoadingTwitter; QString m_twitterConsumerKey; QString m_twitterConsumerSecret; bool m_triedLoadingOneDrive; QString m_oneDriveClientId; bool m_triedLoadingDropbox; QString m_dropboxClientId; bool m_triedLoadingVk; QString m_vkClientId; }; #endif // KEYPROVIDERHELPER_H libsocialcache-0.2.1/src/qml/onedrive/000077500000000000000000000000001462333522700176415ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/onedrive/onedriveimagecachemodel.cpp000066400000000000000000000326431462333522700252000ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "onedriveimagecachemodel.h" #include "abstractsocialcachemodel_p.h" #include "onedriveimagesdatabase.h" #include "onedriveimagedownloader_p.h" #include "onedriveimagedownloaderconstants_p.h" #include #include #include // Note: // // When querying photos, the nodeIdentifier should be either // - nothing: query all photos // - user-USER_ID: query all photos for the given user // - album-ALBUM_ID: query all photos for the given album // // When querying albums, the nodeIdentifier should be either // - nothing: query all albums for all users // - USER_ID: query all albums for the given user static const char *PHOTO_USER_PREFIX = "user-"; static const char *PHOTO_ALBUM_PREFIX = "album-"; class OneDriveImageCacheModelPrivate : public AbstractSocialCacheModelPrivate { public: OneDriveImageCacheModelPrivate(OneDriveImageCacheModel *q); OneDriveImageDownloader *downloader; OneDriveImagesDatabase database; OneDriveImageCacheModel::ModelDataType type; }; OneDriveImageCacheModelPrivate::OneDriveImageCacheModelPrivate(OneDriveImageCacheModel *q) : AbstractSocialCacheModelPrivate(q), downloader(0), type(OneDriveImageCacheModel::Images) { } OneDriveImageCacheModel::OneDriveImageCacheModel(QObject *parent) : AbstractSocialCacheModel(*(new OneDriveImageCacheModelPrivate(this)), parent) { Q_D(const OneDriveImageCacheModel); connect(&d->database, &OneDriveImagesDatabase::queryFinished, this, &OneDriveImageCacheModel::queryFinished); } OneDriveImageCacheModel::~OneDriveImageCacheModel() { Q_D(OneDriveImageCacheModel); if (d->downloader) { d->downloader->removeModelFromHash(this); } } QHash OneDriveImageCacheModel::roleNames() const { QHash roleNames; roleNames.insert(OneDriveId, "id"); roleNames.insert(AlbumId, "albumId"); roleNames.insert(UserId, "userId"); roleNames.insert(AccountId, "accountId"); roleNames.insert(Thumbnail, "thumbnail"); roleNames.insert(ThumbnailUrl, "thumbnailUrl"); roleNames.insert(Image, "image"); roleNames.insert(ImageUrl, "imageUrl"); roleNames.insert(Title, "title"); roleNames.insert(DateTaken, "dateTaken"); roleNames.insert(Width, "photoWidth"); roleNames.insert(Height, "photoHeight"); roleNames.insert(Count, "dataCount"); roleNames.insert(MimeType, "mimeType"); roleNames.insert(Description, "description"); return roleNames; } OneDriveImageCacheModel::ModelDataType OneDriveImageCacheModel::type() const { Q_D(const OneDriveImageCacheModel); return d->type; } void OneDriveImageCacheModel::setType(OneDriveImageCacheModel::ModelDataType type) { Q_D(OneDriveImageCacheModel); if (d->type != type) { d->type = type; emit typeChanged(); } } OneDriveImageDownloader * OneDriveImageCacheModel::downloader() const { Q_D(const OneDriveImageCacheModel); return d->downloader; } void OneDriveImageCacheModel::setDownloader(OneDriveImageDownloader *downloader) { Q_D(OneDriveImageCacheModel); if (d->downloader != downloader) { if (d->downloader) { // Disconnect worker object disconnect(d->downloader); d->downloader->removeModelFromHash(this); } d->downloader = downloader; d->downloader->addModelToHash(this); emit downloaderChanged(); } } void OneDriveImageCacheModel::removeImage(const QString &imageId) { Q_D(OneDriveImageCacheModel); int row = -1; for (int i = 0; i < count(); ++i) { QString dbId = data(index(i), OneDriveImageCacheModel::OneDriveId).toString(); if (dbId == imageId) { row = i; break; } } if (row >= 0) { beginRemoveRows(QModelIndex(), row, row); d->m_data.removeAt(row); endRemoveRows(); // Update album image count OneDriveImage::ConstPtr image = d->database.image(imageId); if (image) { OneDriveAlbum::ConstPtr album = d->database.album(image->albumId()); if (album) { d->database.addAlbum(album->albumId(), album->userId(), album->createdTime(), album->updatedTime(), album->albumName(), album->imageCount()-1); } } d->database.removeImage(imageId); d->database.commit(); } } QVariant OneDriveImageCacheModel::data(const QModelIndex &index, int role) const { Q_D(const OneDriveImageCacheModel); int row = index.row(); if (row < 0 || row >= d->m_data.count()) { return QVariant(); } const QVariant value = d->m_data.at(row).value(role); switch (role) { case Thumbnail: { const QString thumbnailUrl = d->m_data.at(row).value(ThumbnailUrl).toString(); if (value.toString().isEmpty() && !thumbnailUrl.isEmpty()) { QList missingThumbnails; QVariantList modelPtrList; modelPtrList.append(QVariant::fromValue((void*)this)); missingThumbnails.append(OneDriveImageDownloader::UncachedImage( thumbnailUrl, d->m_data.at(row).value(OneDriveId).toString(), d->m_data.at(row).value(AlbumId).toString(), d->m_data.at(row).value(AccountId).toInt(), modelPtrList)); d->downloader->cacheImages(missingThumbnails); } break; } case Image: { // this should never be hit. we should always use the "cache" database // which has "expiresIn" handling for automatically deleting cloud content // after a certain amount of time. break; } default: break; } return value; } void OneDriveImageCacheModel::loadImages() { refresh(); } void OneDriveImageCacheModel::refresh() { Q_D(OneDriveImageCacheModel); const QString userPrefix = QLatin1String(PHOTO_USER_PREFIX); const QString albumPrefix = QLatin1String(PHOTO_ALBUM_PREFIX); switch (d->type) { case OneDriveImageCacheModel::Users: d->database.queryUsers(); break; case OneDriveImageCacheModel::Albums: d->database.queryAlbums(d->nodeIdentifier); break; case OneDriveImageCacheModel::Images: if (d->nodeIdentifier.startsWith(userPrefix)) { d->database.queryUserImages(d->nodeIdentifier.mid(userPrefix.size())); } else if (d->nodeIdentifier.startsWith(albumPrefix)) { d->database.queryAlbumImages(d->nodeIdentifier.mid(albumPrefix.size())); } else { d->database.queryUserImages(); } break; default: break; } } // NOTE: this is now called directly by OneDriveImageDownloader // rather than connected to the imageDownloaded signal, for // performance reasons. void OneDriveImageCacheModel::imageDownloaded( const QString &, const QString &path, const QVariantMap &imageData) { Q_D(OneDriveImageCacheModel); if (path.isEmpty()) { // empty path signifies an error, which we don't handle here at the moment. // Return, otherwise dataChanged signal would cause UI to read back // related value, which, being empty, would trigger another download request, // potentially causing never ending loop. return; } int row = -1; QString id = imageData.value(IDENTIFIER_KEY).toString(); for (int i = 0; i < count(); ++i) { QString dbId = data(index(i), OneDriveImageCacheModel::OneDriveId).toString(); if (dbId == id) { row = i; break; } } if (row >= 0) { int type = imageData.value(TYPE_KEY).toInt(); switch (type) { case OneDriveImageDownloader::ThumbnailImage: d->m_data[row].insert(OneDriveImageCacheModel::Thumbnail, path); break; default: qWarning() << Q_FUNC_INFO << "invalid downloader type: " << type; break; } emit dataChanged(index(row), index(row)); } } void OneDriveImageCacheModel::queryFinished() { Q_D(OneDriveImageCacheModel); SocialCacheModelData data; switch (d->type) { case Users: { QList usersData = d->database.users(); int count = 0; Q_FOREACH (const OneDriveUser::ConstPtr &userData, usersData) { QMap userMap; userMap.insert(OneDriveImageCacheModel::OneDriveId, userData->userId()); userMap.insert(OneDriveImageCacheModel::Title, userData->userName()); userMap.insert(OneDriveImageCacheModel::Count, userData->count()); userMap.insert(OneDriveImageCacheModel::AccountId, userData->accountId()); count += userData->count(); data.append(userMap); } if (data.count() > 1) { QMap userMap; userMap.insert(OneDriveImageCacheModel::OneDriveId, QString()); userMap.insert(OneDriveImageCacheModel::Thumbnail, QString()); //: Label for the "show all users from all OneDrive accounts" option //% "All" userMap.insert(OneDriveImageCacheModel::Title, qtTrId("nemo_socialcache_onedrive_images_model-all-users")); userMap.insert(OneDriveImageCacheModel::AccountId, -1); userMap.insert(OneDriveImageCacheModel::Count, count); data.prepend(userMap); } break; } case Albums: { QList albumsData = d->database.albums(); int count = 0; Q_FOREACH (const OneDriveAlbum::ConstPtr &albumData, albumsData) { QMap albumMap; albumMap.insert(OneDriveImageCacheModel::OneDriveId, albumData->albumId()); albumMap.insert(OneDriveImageCacheModel::Title, albumData->albumName()); albumMap.insert(OneDriveImageCacheModel::Count, albumData->imageCount()); albumMap.insert(OneDriveImageCacheModel::UserId, albumData->userId()); count += albumData->imageCount(); data.append(albumMap); } if (data.count() > 1) { QMap albumMap; albumMap.insert(OneDriveImageCacheModel::OneDriveId, QString()); // albumMap.insert(OneDriveImageCacheModel::Icon, QString()); //: Label for the "show all photos from all albums by this user" option //% "All" albumMap.insert(OneDriveImageCacheModel::Title, qtTrId("nemo_socialcache_onedrive_images_model-all-albums")); albumMap.insert(OneDriveImageCacheModel::Count, count); if (d->nodeIdentifier.isEmpty()) { albumMap.insert(OneDriveImageCacheModel::UserId, QString()); } else { albumMap.insert(OneDriveImageCacheModel::UserId, data.first().value(OneDriveImageCacheModel::UserId)); } data.prepend(albumMap); } break; } case Images: { QList imagesData = d->database.images(); for (int i = 0; i < imagesData.count(); i ++) { const OneDriveImage::ConstPtr & imageData = imagesData.at(i); QMap imageMap; imageMap.insert(OneDriveImageCacheModel::OneDriveId, imageData->imageId()); imageMap.insert(OneDriveImageCacheModel::AlbumId, imageData->albumId()); imageMap.insert(OneDriveImageCacheModel::UserId, imageData->userId()); imageMap.insert(OneDriveImageCacheModel::AccountId, imageData->accountId()); imageMap.insert(OneDriveImageCacheModel::Thumbnail, imageData->thumbnailFile()); imageMap.insert(OneDriveImageCacheModel::ThumbnailUrl, imageData->thumbnailUrl()); imageMap.insert(OneDriveImageCacheModel::Image, imageData->imageFile()); imageMap.insert(OneDriveImageCacheModel::ImageUrl, imageData->imageUrl()); imageMap.insert(OneDriveImageCacheModel::Title, imageData->imageName()); imageMap.insert(OneDriveImageCacheModel::DateTaken, imageData->createdTime()); imageMap.insert(OneDriveImageCacheModel::Width, imageData->width()); imageMap.insert(OneDriveImageCacheModel::Height, imageData->height()); imageMap.insert(OneDriveImageCacheModel::Description, imageData->description()); data.append(imageMap); } break; } default: return; } updateData(data); } libsocialcache-0.2.1/src/qml/onedrive/onedriveimagecachemodel.h000066400000000000000000000054001462333522700246340ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ONEDRIVEIMAGECACHEMODEL_H #define ONEDRIVEIMAGECACHEMODEL_H #include "abstractsocialcachemodel.h" #include "onedriveimagedownloader.h" class OneDriveImageCacheModelPrivate; class OneDriveImageCacheModel: public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(OneDriveImageCacheModel::ModelDataType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(OneDriveImageDownloader * downloader READ downloader WRITE setDownloader NOTIFY downloaderChanged) Q_ENUMS(OneDriveGalleryRole) Q_ENUMS(ModelDataType) public: enum OneDriveGalleryRole { OneDriveId = 0, AlbumId, UserId, AccountId, Thumbnail, ThumbnailUrl, Image, ImageUrl, Title, DateTaken, Width, Height, Count, MimeType, Description }; enum ModelDataType { None = 0, // used for resetting/refreshing the model. Users, Albums, Images }; explicit OneDriveImageCacheModel(QObject *parent = 0); ~OneDriveImageCacheModel(); QHash roleNames() const; // properties OneDriveImageCacheModel::ModelDataType type() const; void setType(OneDriveImageCacheModel::ModelDataType type); OneDriveImageDownloader *downloader() const; void setDownloader(OneDriveImageDownloader *downloader); // from AbstractListModel QVariant data(const QModelIndex &index, int role) const; Q_INVOKABLE void removeImage(const QString &imageId); public Q_SLOTS: void loadImages(); void refresh(); Q_SIGNALS: void typeChanged(); void downloaderChanged(); private Q_SLOTS: void queryFinished(); void imageDownloaded(const QString &url, const QString &path, const QVariantMap &imageData); private: Q_DECLARE_PRIVATE(OneDriveImageCacheModel) friend class OneDriveImageDownloader; }; #endif // ONEDRIVEIMAGECACHEMODEL_H libsocialcache-0.2.1/src/qml/onedrive/onedriveimagedownloader.cpp000066400000000000000000000155221462333522700252470ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "onedriveimagedownloader.h" #include "onedriveimagedownloader_p.h" #include "onedriveimagedownloaderconstants_p.h" #include "onedriveimagecachemodel.h" #include #include #include #include #include #include static const char *MODEL_KEY = "model"; static const char *URL_KEY = "url"; // static const char *TYPE_PHOTO = "photo"; OneDriveImageDownloader::UncachedImage::UncachedImage() {} OneDriveImageDownloader::UncachedImage::UncachedImage(const QString &thumbnailUrl, const QString &imageId, const QString &albumId, int accountId, QVariantList connectedModels) : thumbnailUrl(thumbnailUrl) , imageId(imageId) , albumId(albumId) , accountId(accountId) , connectedModels(connectedModels) {} OneDriveImageDownloader::UncachedImage::UncachedImage(const UncachedImage &other) : thumbnailUrl(other.thumbnailUrl) , imageId(other.imageId) , albumId(other.albumId) , accountId(other.accountId) , connectedModels(other.connectedModels) {} OneDriveImageDownloaderPrivate::OneDriveImageDownloaderPrivate(OneDriveImageDownloader *q) : AbstractImageDownloaderPrivate(q) , m_optimalThumbnailSize(180) { } OneDriveImageDownloaderPrivate::~OneDriveImageDownloaderPrivate() { } OneDriveImageDownloader::OneDriveImageDownloader(QObject *parent) : AbstractImageDownloader(*new OneDriveImageDownloaderPrivate(this), parent) { connect(this, &AbstractImageDownloader::imageDownloaded, this, &OneDriveImageDownloader::invokeSpecificModelCallback); } OneDriveImageDownloader::~OneDriveImageDownloader() { } void OneDriveImageDownloader::addModelToHash(OneDriveImageCacheModel *model) { Q_D(OneDriveImageDownloader); d->m_connectedModels.insert(model); } void OneDriveImageDownloader::removeModelFromHash(OneDriveImageCacheModel *model) { Q_D(OneDriveImageDownloader); d->m_connectedModels.remove(model); } int OneDriveImageDownloader::optimalThumbnailSize() const { Q_D(const OneDriveImageDownloader); return d->m_optimalThumbnailSize; } void OneDriveImageDownloader::setOptimalThumbnailSize(int optimalThumbnailSize) { Q_D(OneDriveImageDownloader); if (d->m_optimalThumbnailSize != optimalThumbnailSize) { d->m_optimalThumbnailSize = optimalThumbnailSize; emit optimalThumbnailSizeChanged(); } } /* * A OneDriveImageDownloader can be connected to multiple models. * Instead of connecting the imageDownloaded signal directly to the * model, we connect it to this slot, which retrieves the target model * from the metadata map and invokes its callback directly. * This avoids a possibly large number of signal connections + invocations. */ void OneDriveImageDownloader::invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata) { Q_D(OneDriveImageDownloader); OneDriveImageCacheModel *model = static_cast(metadata.value(MODEL_KEY).value()); // check to see if the model was destroyed in the meantime. // If not, we can directly invoke the callback. if (d->m_connectedModels.contains(model)) { model->imageDownloaded(url, path, metadata); } } QString OneDriveImageDownloader::outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const { Q_UNUSED(url); Q_UNUSED(mimeType); // TODO: use? // We create the identifier by appending the type to the real identifier QString identifier = data.value(QLatin1String(IDENTIFIER_KEY)).toString(); if (identifier.isEmpty()) { return QString(); } QString typeString = data.value(QLatin1String(TYPE_KEY)).toString(); if (typeString.isEmpty()) { return QString(); } identifier.append(typeString); return makeOutputFile(SocialSyncInterface::OneDrive, SocialSyncInterface::Images, identifier, QString()); } void OneDriveImageDownloader::dbQueueImage(const QString &url, const QVariantMap &data, const QString &file) { Q_D(OneDriveImageDownloader); Q_UNUSED(url); QString identifier = data.value(QLatin1String(IDENTIFIER_KEY)).toString(); if (identifier.isEmpty()) { return; } int type = data.value(QLatin1String(TYPE_KEY)).toInt(); switch (type) { case ThumbnailImage: d->database.updateImageThumbnail(identifier, file); break; } } void OneDriveImageDownloader::dbWrite() { Q_D(OneDriveImageDownloader); d->database.commit(); } void OneDriveImageDownloader::cacheImages(QList images) { Q_D(OneDriveImageDownloader); Q_FOREACH (const UncachedImage& image, images) { // no need for access token for thumbnail images, else // emit accessTokenRequested(accountId); would be needed. Q_FOREACH (QVariant modelPtr, image.connectedModels) { QVariantMap metadata; metadata.insert(QLatin1String(TYPE_KEY), (int)OneDriveImageDownloader::ThumbnailImage); metadata.insert(QLatin1String(IDENTIFIER_KEY), image.imageId); metadata.insert(QLatin1String(URL_KEY), image.thumbnailUrl); metadata.insert(QLatin1String(MODEL_KEY), modelPtr); queue(image.thumbnailUrl, metadata); } } } void OneDriveImageDownloader::accessTokenRetrieved(const QString &accessToken, int accountId) { Q_D(OneDriveImageDownloader); // currently unused, but if we want to support full-size images in future... QMutexLocker locker(&d->m_cacheMutex); d->m_accessTokens.insert(accountId, accessToken); } void OneDriveImageDownloader::accessTokenFailed(int accountId) { Q_D(OneDriveImageDownloader); qWarning() << "Failed to retrieve access token for account:" << accountId; } libsocialcache-0.2.1/src/qml/onedrive/onedriveimagedownloader.h000066400000000000000000000072331462333522700247140ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ONEDRIVEIMAGEDOWNLOADER_H #define ONEDRIVEIMAGEDOWNLOADER_H #include #include #include #include #include "abstractimagedownloader.h" class OneDriveImageCacheModel; class OneDriveImageDownloaderWorkerObject; class OneDriveImageDownloaderPrivate; class OneDriveImageDownloader : public AbstractImageDownloader { Q_OBJECT Q_PROPERTY(int optimalThumbnailSize READ optimalThumbnailSize WRITE setOptimalThumbnailSize NOTIFY optimalThumbnailSizeChanged) public: enum ImageType { // We cache only thumnail images, full size images are cached by UI code via SocialImageCache // which purges them after given number of days has passed. ThumbnailImage }; struct UncachedImage { UncachedImage(); UncachedImage(const QString &thumbnailUrl, const QString &imageId, const QString &albumId, int accountId, QVariantList connectedModels); UncachedImage(const UncachedImage &other); QString thumbnailUrl; QString imageId; QString albumId; int accountId; QVariantList connectedModels; }; explicit OneDriveImageDownloader(QObject *parent = 0); virtual ~OneDriveImageDownloader(); // tracking object lifetime of models connected to this downloader. void addModelToHash(OneDriveImageCacheModel *model); void removeModelFromHash(OneDriveImageCacheModel *model); void cacheImages(QList images); int optimalThumbnailSize() const; void setOptimalThumbnailSize(int optimalThumbnailSize); Q_INVOKABLE void accessTokenRetrieved(const QString &accessToken, int accountId); Q_INVOKABLE void accessTokenFailed(int accountId); signals: void optimalThumbnailSizeChanged(); void accessTokenRequested(int accountId); protected: QString outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const override; void dbQueueImage(const QString &url, const QVariantMap &data, const QString &file); void dbWrite(); private: void requestImages(int accountId, const QString &accessToken, const QString &albumId, const QString &nextRound = QString()); struct ImageSource { ImageSource(int width, int height, const QString &sourceUrl) : width(width), height(height), sourceUrl(sourceUrl) {} bool operator<(const ImageSource &other) const { return this->width < other.width; } int width; int height; QString sourceUrl; }; private Q_SLOTS: void invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata); private: Q_DECLARE_PRIVATE(OneDriveImageDownloader) }; #endif // ONEDRIVEIMAGEDOWNLOADER_H libsocialcache-0.2.1/src/qml/onedrive/onedriveimagedownloader_p.h000066400000000000000000000033431462333522700252310ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ONEDRIVEIMAGEDOWNLOADER_P_H #define ONEDRIVEIMAGEDOWNLOADER_P_H #include #include #include #include #include #include "abstractimagedownloader_p.h" #include "onedriveimagedownloader.h" #include "onedriveimagesdatabase.h" class OneDriveImageCacheModel; class OneDriveImageDownloaderPrivate: public AbstractImageDownloaderPrivate { public: explicit OneDriveImageDownloaderPrivate(OneDriveImageDownloader *q); virtual ~OneDriveImageDownloaderPrivate(); OneDriveImagesDatabase database; QSet m_connectedModels; QMutex m_cacheMutex; QMap m_accessTokens; int m_optimalThumbnailSize; QMap > m_networkReplyTimeouts; private: Q_DECLARE_PUBLIC(OneDriveImageDownloader) }; #endif // ONEDRIVEIMAGEDOWNLOADER_P_H libsocialcache-0.2.1/src/qml/onedrive/onedriveimagedownloaderconstants_p.h000066400000000000000000000020601462333522700271610ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ONEDRIVEIMAGEDOWNLOADERCONSTANTS_P_H #define ONEDRIVEIMAGEDOWNLOADERCONSTANTS_P_H static const char *IDENTIFIER_KEY = "identifier"; static const char *TYPE_KEY = "type"; #endif // ONEDRIVEIMAGEDOWNLOADERCONSTANTS_P_H libsocialcache-0.2.1/src/qml/plugin.cpp000066400000000000000000000125431462333522700200350ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "facebook/facebookimagecachemodel.h" #include "facebook/facebookpostsmodel.h" #include "facebook/facebooknotificationsmodel.h" #include "twitter/twitterpostsmodel.h" #include "generic/socialimagedownloader.h" #include "onedrive/onedriveimagecachemodel.h" #include "dropbox/dropboximagecachemodel.h" #include "vk/vkpostsmodel.h" #include "vk/vkimagecachemodel.h" #ifndef NO_DEPS #include "synchelper.h" #include "keyproviderhelper.h" #endif // using custom translator so it gets properly removed from qApp when engine is deleted class AppTranslator: public QTranslator { Q_OBJECT public: AppTranslator(QObject *parent) : QTranslator(parent) { qApp->installTranslator(this); } virtual ~AppTranslator() { qApp->removeTranslator(this); } }; static QObject *facebookImageDownloader_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) FacebookImageDownloader *downloader = new FacebookImageDownloader(); return downloader; } static QObject *oneDriveImageDownloader_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) OneDriveImageDownloader *downloader = new OneDriveImageDownloader(); return downloader; } static QObject *dropboxImageDownloader_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) DropboxImageDownloader *downloader = new DropboxImageDownloader(); return downloader; } static QObject *vkImageDownloader_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) VKImageDownloader *downloader = new VKImageDownloader(); return downloader; } class JollaSocialPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.socialcache") public: void initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri) Q_ASSERT(QLatin1String(uri) == QLatin1String("org.nemomobile.socialcache")); AppTranslator *engineeringEnglish = new AppTranslator(engine); AppTranslator *translator = new AppTranslator(engine); engineeringEnglish->load("socialcache_eng_en", "/usr/share/translations"); translator->load(QLocale(), "socialcache", "-", "/usr/share/translations"); } virtual void registerTypes(const char *uri) { Q_ASSERT(QLatin1String(uri) == QLatin1String("org.nemomobile.socialcache")); qRegisterMetaType("SocialCacheModelRow"); qRegisterMetaType("SocialCacheModelData"); qmlRegisterType(uri, 1, 0, "FacebookImageCacheModel"); qmlRegisterType(uri, 1, 0, "FacebookPostsModel"); qmlRegisterType(uri, 1, 0, "FacebookNotificationsModel"); qmlRegisterSingletonType(uri, 1, 0, "FacebookImageDownloader", &facebookImageDownloader_provider); qmlRegisterType(uri, 1, 0, "TwitterPostsModel"); qmlRegisterType(uri, 1, 0, "SocialImageCache"); qmlRegisterType(uri, 1, 0, "OneDriveImageCacheModel"); qmlRegisterSingletonType(uri, 1, 0, "OneDriveImageDownloader", &oneDriveImageDownloader_provider); qmlRegisterType(uri, 1, 0, "DropboxImageCacheModel"); qmlRegisterSingletonType(uri, 1, 0, "DropboxImageDownloader", &dropboxImageDownloader_provider); qmlRegisterType(uri, 1, 0, "VKPostsModel"); qmlRegisterType(uri, 1, 0, "VKImageCacheModel"); qmlRegisterSingletonType(uri, 1, 0, "VKImageDownloader", &vkImageDownloader_provider); #ifndef NO_DEPS qmlRegisterUncreatableType(uri, 1, 0, "SocialSync", QLatin1String("Cannot create")); qmlRegisterType(uri, 1, 0, "SyncHelper"); qmlRegisterType(uri, 1, 0, "KeyProviderHelper"); #endif } }; #include "plugin.moc" libsocialcache-0.2.1/src/qml/plugins.qmltypes000066400000000000000000000355301462333522700213150ustar00rootroot00000000000000import QtQuick.tooling 1.2 // This file describes the plugin-supplied types contained in the library. // It is used for QML tooling purposes only. // // This file was auto-generated by: // 'qmlplugindump -nonrelocatable org.nemomobile.socialcache 1.0' Module { dependencies: ["QtQuick 2.0"] Component { name: "AbstractImageDownloader" prototype: "QObject" Signal { name: "imageDownloaded" Parameter { name: "url"; type: "string" } Parameter { name: "path"; type: "string" } Parameter { name: "metadata"; type: "QVariantMap" } } Method { name: "queue" Parameter { name: "url"; type: "string" } Parameter { name: "data"; type: "QVariantMap" } } } Component { name: "AbstractSocialCacheModel" prototype: "QAbstractListModel" Property { name: "nodeIdentifier"; type: "string" } Property { name: "count"; type: "int"; isReadonly: true } Signal { name: "modelUpdated" } Method { name: "refresh" } Method { name: "getField" type: "QVariant" Parameter { name: "row"; type: "int" } Parameter { name: "role"; type: "int" } } } Component { name: "DropboxImageCacheModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/DropboxImageCacheModel 1.0"] exportMetaObjectRevisions: [0] Enum { name: "DropboxGalleryRole" values: { "DropboxId": 0, "Thumbnail": 1, "Image": 2, "Title": 3, "DateTaken": 4, "Width": 5, "Height": 6, "Count": 7, "MimeType": 8, "AccountId": 9, "UserId": 10, "AccessToken": 11 } } Enum { name: "ModelDataType" values: { "None": 0, "Users": 1, "Albums": 2, "Images": 3 } } Property { name: "type"; type: "DropboxImageCacheModel::ModelDataType" } Property { name: "downloader"; type: "DropboxImageDownloader"; isPointer: true } Method { name: "loadImages" } Method { name: "refresh" } Method { name: "removeImage" Parameter { name: "imageUrl"; type: "string" } } } Component { name: "DropboxImageDownloader" prototype: "AbstractImageDownloader" exports: ["org.nemomobile.socialcache/DropboxImageDownloader 1.0"] isCreatable: false isSingleton: true exportMetaObjectRevisions: [0] } Component { name: "FacebookImageCacheModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/FacebookImageCacheModel 1.0"] exportMetaObjectRevisions: [0] Enum { name: "FacebookGalleryRole" values: { "FacebookId": 0, "Thumbnail": 1, "Image": 2, "Title": 3, "DateTaken": 4, "Width": 5, "Height": 6, "Count": 7, "MimeType": 8, "AccountId": 9, "UserId": 10 } } Enum { name: "ModelDataType" values: { "None": 0, "Users": 1, "Albums": 2, "Images": 3 } } Property { name: "type"; type: "FacebookImageCacheModel::ModelDataType" } Property { name: "downloader"; type: "FacebookImageDownloader"; isPointer: true } Method { name: "loadImages" } Method { name: "refresh" } } Component { name: "FacebookImageDownloader" prototype: "AbstractImageDownloader" exports: ["org.nemomobile.socialcache/FacebookImageDownloader 1.0"] isCreatable: false isSingleton: true exportMetaObjectRevisions: [0] } Component { name: "FacebookNotificationsModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/FacebookNotificationsModel 1.0"] exportMetaObjectRevisions: [0] Enum { name: "FacebookNotificationsRole" values: { "NotificationId": 0, "From": 1, "To": 2, "Timestamp": 3, "Title": 4, "Link": 5, "AppId": 6, "Object": 7, "Accounts": 8, "ClientId": 9, "Unread": 10 } } Property { name: "accountIdFilter"; type: "QVariantList" } Method { name: "remove" Parameter { name: "notificationId"; type: "string" } } Method { name: "clear" } } Component { name: "FacebookPostsModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/FacebookPostsModel 1.0"] exportMetaObjectRevisions: [0] } Component { name: "KeyProviderHelper" prototype: "QObject" exports: ["org.nemomobile.socialcache/KeyProviderHelper 1.0"] exportMetaObjectRevisions: [0] Property { name: "facebookClientId"; type: "string"; isReadonly: true } Property { name: "twitterConsumerKey"; type: "string"; isReadonly: true } Property { name: "twitterConsumerSecret"; type: "string"; isReadonly: true } Property { name: "oneDriveClientId"; type: "string"; isReadonly: true } Property { name: "dropboxClientId"; type: "string"; isReadonly: true } Property { name: "vkClientId"; type: "string"; isReadonly: true } } Component { name: "OneDriveImageCacheModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/OneDriveImageCacheModel 1.0"] exportMetaObjectRevisions: [0] Enum { name: "OneDriveGalleryRole" values: { "OneDriveId": 0, "AlbumId": 1, "UserId": 2, "AccountId": 3, "Thumbnail": 4, "ThumbnailUrl": 5, "Image": 6, "ImageUrl": 7, "Title": 8, "DateTaken": 9, "Width": 10, "Height": 11, "Count": 12, "MimeType": 13, "Description": 14 } } Enum { name: "ModelDataType" values: { "None": 0, "Users": 1, "Albums": 2, "Images": 3 } } Property { name: "type"; type: "OneDriveImageCacheModel::ModelDataType" } Property { name: "downloader"; type: "OneDriveImageDownloader"; isPointer: true } Method { name: "loadImages" } Method { name: "refresh" } Method { name: "removeImage" Parameter { name: "imageId"; type: "string" } } } Component { name: "OneDriveImageDownloader" prototype: "AbstractImageDownloader" exports: ["org.nemomobile.socialcache/OneDriveImageDownloader 1.0"] isCreatable: false isSingleton: true exportMetaObjectRevisions: [0] Property { name: "optimalThumbnailSize"; type: "int" } Signal { name: "accessTokenRequested" Parameter { name: "accountId"; type: "int" } } Method { name: "accessTokenRetrieved" Parameter { name: "accessToken"; type: "string" } Parameter { name: "accountId"; type: "int" } } Method { name: "accessTokenFailed" Parameter { name: "accountId"; type: "int" } } } Component { name: "SocialImageDownloader" prototype: "AbstractImageDownloader" exports: ["org.nemomobile.socialcache/SocialImageCache 1.0"] exportMetaObjectRevisions: [0] Method { name: "cached" type: "string" Parameter { name: "imageId"; type: "string" } } Method { name: "imageFile" Parameter { name: "imageUrl"; type: "string" } Parameter { name: "accountId"; type: "int" } Parameter { name: "caller"; type: "QObject"; isPointer: true } Parameter { name: "expiresInDays"; type: "int" } Parameter { name: "imageId"; type: "string" } Parameter { name: "accessToken"; type: "string" } } Method { name: "imageFile" Parameter { name: "imageUrl"; type: "string" } Parameter { name: "accountId"; type: "int" } Parameter { name: "caller"; type: "QObject"; isPointer: true } Parameter { name: "expiresInDays"; type: "int" } Parameter { name: "imageId"; type: "string" } } Method { name: "imageFile" Parameter { name: "imageUrl"; type: "string" } Parameter { name: "accountId"; type: "int" } Parameter { name: "caller"; type: "QObject"; isPointer: true } Parameter { name: "expiresInDays"; type: "int" } } Method { name: "imageFile" Parameter { name: "imageUrl"; type: "string" } Parameter { name: "accountId"; type: "int" } Parameter { name: "caller"; type: "QObject"; isPointer: true } } Method { name: "removeFromRecentlyUsed" Parameter { name: "imageUrl"; type: "string" } } Method { name: "removeFromRecentlyUsedById" Parameter { name: "imageId"; type: "string" } } } Component { name: "SocialSyncInterface" prototype: "QObject" exports: ["org.nemomobile.socialcache/SocialSync 1.0"] isCreatable: false exportMetaObjectRevisions: [0] Enum { name: "SocialNetwork" values: { "InvalidSocialNetwork": 0, "Facebook": 1, "Twitter": 2, "Google": 3, "VK": 4, "Diaspora": 5, "CalDAV": 6, "OneDrive": 7, "Dropbox": 8 } } Enum { name: "DataType" values: { "InvalidDataType": 0, "Contacts": 1, "Calendars": 2, "Notifications": 3, "Images": 4, "Videos": 5, "Posts": 6, "Messages": 7, "Emails": 8 } } Method { name: "socialNetwork" type: "string" Parameter { name: "sn"; type: "SocialNetwork" } } Method { name: "dataType" type: "string" Parameter { name: "t"; type: "DataType" } } } Component { name: "SyncHelper" prototype: "QObject" exports: ["org.nemomobile.socialcache/SyncHelper 1.0"] exportMetaObjectRevisions: [0] Property { name: "socialNetwork"; type: "SocialSyncInterface::SocialNetwork" } Property { name: "dataType"; type: "SocialSyncInterface::DataType" } Property { name: "syncProfiles"; type: "QStringList"; isReadonly: true } Property { name: "loading"; type: "bool"; isReadonly: true } Signal { name: "profileDeleted" } Method { name: "sync" } } Component { name: "TwitterPostsModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/TwitterPostsModel 1.0"] exportMetaObjectRevisions: [0] Property { name: "accountIdFilter"; type: "QVariantList" } } Component { name: "VKImageCacheModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/VKImageCacheModel 1.0"] exportMetaObjectRevisions: [0] Enum { name: "VKGalleryRole" values: { "PhotoId": 0, "AlbumId": 1, "UserId": 2, "AccountId": 3, "Text": 4, "Date": 5, "Width": 6, "Height": 7, "Thumbnail": 8, "Image": 9, "Count": 10, "MimeType": 11, "ImageSource": 12 } } Enum { name: "ModelDataType" values: { "None": 0, "Users": 1, "Albums": 2, "Images": 3 } } Property { name: "type"; type: "VKImageCacheModel::ModelDataType" } Property { name: "downloader"; type: "VKImageDownloader"; isPointer: true } Method { name: "loadImages" } Method { name: "refresh" } Method { name: "constructNodeIdentifier" type: "string" Parameter { name: "accountId"; type: "int" } Parameter { name: "user_id"; type: "string" } Parameter { name: "album_id"; type: "string" } Parameter { name: "photo_id"; type: "string" } } Method { name: "parseNodeIdentifier" type: "QVariantMap" Parameter { name: "nid"; type: "string" } } Method { name: "removeImage" Parameter { name: "imageId"; type: "string" } } } Component { name: "VKImageDownloader" prototype: "AbstractImageDownloader" exports: ["org.nemomobile.socialcache/VKImageDownloader 1.0"] isCreatable: false isSingleton: true exportMetaObjectRevisions: [0] } Component { name: "VKPostsModel" prototype: "AbstractSocialCacheModel" exports: ["org.nemomobile.socialcache/VKPostsModel 1.0"] exportMetaObjectRevisions: [0] Enum { name: "VKPostsRole" values: { "VkId": 0, "Name": 1, "Body": 2, "Timestamp": 3, "Icon": 4, "Images": 5, "Extra": 6, "Accounts": 7, "RepostType": 8, "RepostOwnerName": 9, "RepostOwnerAvatar": 10, "RepostText": 11, "RepostVideo": 12, "RepostLink": 13, "RepostTimestamp": 14, "RepostImages": 15, "Link": 16 } } Property { name: "accountIdFilter"; type: "QVariantList" } Method { name: "remove" Parameter { name: "postId"; type: "string" } } Method { name: "clear" } } } libsocialcache-0.2.1/src/qml/postimagehelper_p.h000066400000000000000000000030451462333522700217100ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef POSTIMAGEHELPER_P_H #define POSTIMAGEHELPER_P_H #include static const char *URL_KEY = "url"; static const char *TYPE_KEY = "type"; static const char *TYPE_PHOTO = "photo"; static const char *TYPE_VIDEO = "video"; inline static QVariantMap createImageData(const SocialPostImage::ConstPtr &image) { QVariantMap imageData; imageData.insert(QLatin1String(URL_KEY), image->url()); switch (image->type()) { case SocialPostImage::Video: imageData.insert(QLatin1String(TYPE_KEY), QLatin1String(TYPE_VIDEO)); break; default: imageData.insert(QLatin1String(TYPE_KEY), QLatin1String(TYPE_PHOTO)); break; } return imageData; } #endif // POSTIMAGEHELPER_P_H libsocialcache-0.2.1/src/qml/qml.pro000066400000000000000000000064641462333522700173530ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = lib TARGET = socialcacheqml TARGET = $$qtLibraryTarget($$TARGET) MODULENAME = org/nemomobile/socialcache TARGETPATH = $$[QT_INSTALL_QML]/$$MODULENAME LIBS += -L../lib -lsocialcache INCLUDEPATH += ../lib/ QT += gui qml sql network dbus CONFIG += plugin CONFIG(nodeps):{ DEFINES += NO_DEPS } else { CONFIG += link_pkgconfig PKGCONFIG += buteosyncfw$${QT_MAJOR_VERSION} libsailfishkeyprovider HEADERS += synchelper.h \ keyproviderhelper.h SOURCES += synchelper.cpp \ keyproviderhelper.cpp } HEADERS += \ abstractsocialcachemodel.h \ abstractsocialcachemodel_p.h \ postimagehelper_p.h \ synchronizelists_p.h \ facebook/facebookimagecachemodel.h \ facebook/facebookimagedownloader.h \ facebook/facebookimagedownloader_p.h \ facebook/facebookimagedownloaderconstants_p.h \ facebook/facebookpostsmodel.h \ facebook/facebooknotificationsmodel.h \ twitter/twitterpostsmodel.h \ generic/socialimagedownloader.h \ generic/socialimagedownloader_p.h \ onedrive/onedriveimagedownloader_p.h \ onedrive/onedriveimagedownloaderconstants_p.h \ onedrive/onedriveimagedownloader.h \ onedrive/onedriveimagecachemodel.h \ dropbox/dropboximagecachemodel.h \ dropbox/dropboximagedownloader.h \ dropbox/dropboximagedownloader_p.h \ dropbox/dropboximagedownloaderconstants_p.h \ vk/vkimagecachemodel.h \ vk/vkimagedownloader.h \ vk/vkimagedownloader_p.h \ vk/vkpostsmodel.h SOURCES += plugin.cpp \ abstractsocialcachemodel.cpp \ facebook/facebookimagecachemodel.cpp \ facebook/facebookimagedownloader.cpp \ facebook/facebookpostsmodel.cpp \ facebook/facebooknotificationsmodel.cpp \ twitter/twitterpostsmodel.cpp \ generic/socialimagedownloader.cpp \ onedrive/onedriveimagedownloader.cpp \ onedrive/onedriveimagecachemodel.cpp \ dropbox/dropboximagecachemodel.cpp \ dropbox/dropboximagedownloader.cpp \ vk/vkimagecachemodel.cpp \ vk/vkimagedownloader.cpp \ vk/vkpostsmodel.cpp OTHER_FILES += qmldir plugins.qmltypes import.files = qmldir plugins.qmltypes import.path = $$TARGETPATH target.path = $$TARGETPATH INSTALLS += target import qmltypes.commands = qmlplugindump -nonrelocatable org.nemomobile.socialcache 1.0 > $$PWD/plugins.qmltypes QMAKE_EXTRA_TARGETS += qmltypes # translations TS_FILE = $$OUT_PWD/socialcache.ts EE_QM = $$OUT_PWD/socialcache_eng_en.qm qtPrepareTool(LUPDATE, lupdate) ts.commands += $$LUPDATE $$PWD/.. -ts $$TS_FILE ts.CONFIG += no_check_exist no_link ts.output = $$TS_FILE ts.input = .. ts_install.files = $$TS_FILE ts_install.path = /usr/share/translations/source ts_install.CONFIG += no_check_exist qtPrepareTool(LRELEASE, lrelease) # should add -markuntranslated "-" when proper translations are in place (or for testing) engineering_english.commands += $${LRELEASE} -idbased $$TS_FILE -qm $$EE_QM engineering_english.CONFIG += no_check_exist no_link engineering_english.depends = ts engineering_english.input = $$TS_FILE engineering_english.output = $$EE_QM engineering_english_install.path = /usr/share/translations engineering_english_install.files = $$EE_QM engineering_english_install.CONFIG += no_check_exist QMAKE_EXTRA_TARGETS += ts engineering_english PRE_TARGETDEPS += ts engineering_english INSTALLS += ts_install engineering_english_install libsocialcache-0.2.1/src/qml/qmldir000066400000000000000000000001221462333522700172340ustar00rootroot00000000000000module org.nemomobile.socialcache plugin socialcacheqml typeinfo plugins.qmltypes libsocialcache-0.2.1/src/qml/synchelper.cpp000066400000000000000000000115631462333522700207140ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "synchelper.h" #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #include #else #include #include #endif SyncHelper::SyncHelper(QObject *parent) : QObject(parent), QQmlParserStatus(), m_socialNetwork(SocialSyncInterface::InvalidSocialNetwork) , m_dataType(SocialSyncInterface::InvalidDataType), m_complete(false), m_loading(false) { m_interface = new Buteo::SyncClientInterface(); connect(m_interface, &Buteo::SyncClientInterface::syncStatus, this, &SyncHelper::slotSyncStatus); connect(m_interface, &Buteo::SyncClientInterface::profileChanged, this, &SyncHelper::slotProfileChanged); } void SyncHelper::classBegin() { } void SyncHelper::componentComplete() { m_complete = true; refresh(); } SocialSyncInterface::SocialNetwork SyncHelper::socialNetwork() const { return m_socialNetwork; } void SyncHelper::setSocialNetwork(SocialSyncInterface::SocialNetwork socialNetwork) { if (m_socialNetwork != socialNetwork) { m_socialNetwork = socialNetwork; emit socialNetworkChanged(); refresh(); } } SocialSyncInterface::DataType SyncHelper::dataType() const { return m_dataType; } void SyncHelper::setDataType(SocialSyncInterface::DataType dataType) { if (m_dataType != dataType) { m_dataType = dataType; emit dataTypeChanged(); refresh(); } } QStringList SyncHelper::syncProfiles() const { return m_syncProfiles; } bool SyncHelper::loading() const { return m_loading; } void SyncHelper::setLoading(bool loading) { if (m_loading != loading) { m_loading = loading; emit loadingChanged(); } } void SyncHelper::sync() { m_interface->startSync(SocialSyncInterface::profileName(m_socialNetwork, m_dataType)); } void SyncHelper::slotSyncStatus(const QString &aProfileId, int aStatus, const QString &aMessage, int aStatusDetails) { Q_UNUSED(aMessage) Q_UNUSED(aStatusDetails) if (!profileIdMatches(aProfileId)) { return; } bool newLoading = (aStatus == Sync::SYNC_QUEUED || aStatus == Sync::SYNC_STARTED || aStatus == Sync::SYNC_PROGRESS); if (!newLoading) { m_activeSyncs.removeAll(aProfileId); setLoading(m_activeSyncs.count() > 0); } else if (!m_activeSyncs.contains(aProfileId)) { m_activeSyncs.append(aProfileId); setLoading(true); } } void SyncHelper::slotProfileChanged(QString aProfileId, int aChangeType, QString aChangedProfile) { Q_UNUSED(aChangedProfile); if (profileIdMatches(aProfileId)) { refreshSyncProfiles(); if (aChangeType == Buteo::ProfileManager::PROFILE_REMOVED) { emit profileDeleted(); } } } void SyncHelper::refresh() { refreshSyncProfiles(); checkCurrentRun(); } void SyncHelper::refreshSyncProfiles() { if (!m_complete) { return; } QStringList syncProfiles; Q_FOREACH (const QString &profileId, m_interface->syncProfilesByType("sync")) { if (profileIdMatches(profileId)) { syncProfiles << profileId; } } if (syncProfiles != m_syncProfiles) { m_syncProfiles = syncProfiles; emit syncProfilesChanged(); } } void SyncHelper::checkCurrentRun() { if (!m_complete) { return; } // Get the current running syncs to see if we are running QStringList runningSyncs = m_interface->getRunningSyncList(); foreach (const QString profile, runningSyncs) { slotSyncStatus(profile, Sync::SYNC_PROGRESS, "", 0); } } bool SyncHelper::profileIdMatches(const QString &profileId) const { // Per-account profile names follow the convention 'socialnetworkName.DataType-' // Generate a prefix and check if it matches to the profile name. QString profileNamePrefix = SocialSyncInterface::profileName(m_socialNetwork, m_dataType) + "-"; return profileId.startsWith(profileNamePrefix); } libsocialcache-0.2.1/src/qml/synchelper.h000066400000000000000000000056001462333522700203540ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SYNCHELPER_H #define SYNCHELPER_H #include #include #include "socialsyncinterface.h" #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #else #include #endif class SyncHelper : public QObject, public QQmlParserStatus { Q_OBJECT Q_PROPERTY(SocialSyncInterface::SocialNetwork socialNetwork READ socialNetwork WRITE setSocialNetwork NOTIFY socialNetworkChanged) Q_PROPERTY(SocialSyncInterface::DataType dataType READ dataType WRITE setDataType NOTIFY dataTypeChanged) Q_PROPERTY(QStringList syncProfiles READ syncProfiles NOTIFY syncProfilesChanged) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) public: explicit SyncHelper(QObject *parent = 0); void classBegin(); void componentComplete(); SocialSyncInterface::SocialNetwork socialNetwork() const; void setSocialNetwork(SocialSyncInterface::SocialNetwork socialNetwork); SocialSyncInterface::DataType dataType() const; void setDataType(SocialSyncInterface::DataType dataType); QStringList syncProfiles() const; bool loading() const; public Q_SLOTS: void sync(); Q_SIGNALS: void socialNetworkChanged(); void dataTypeChanged(); void syncProfilesChanged(); void loadingChanged(); void profileDeleted(); private slots: void slotSyncStatus(const QString &aProfileId, int aStatus, const QString &aMessage, int aStatusDetails); void slotProfileChanged(QString aProfileId,int aChangeType, QString aChangedProfile); private: void refresh(); void refreshSyncProfiles(); void checkCurrentRun(); void setLoading(bool loading); bool profileIdMatches(const QString &profileId) const; Buteo::SyncClientInterface *m_interface; SocialSyncInterface::SocialNetwork m_socialNetwork; SocialSyncInterface::DataType m_dataType; QStringList m_activeSyncs; QStringList m_syncProfiles; bool m_complete; bool m_loading; }; #endif // SYNCHELPER_H libsocialcache-0.2.1/src/qml/synchronizelists_p.h000066400000000000000000000176531462333522700221640ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Mobile * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #ifndef SYNCHRONIZELISTS_P_H #define SYNCHRONIZELISTS_P_H template bool compareIdentity(const T &item, const T &reference) { return item == reference; } template int insertRange(Agent *agent, int index, int count, const ReferenceList &source, int sourceIndex) { agent->insertRange(index, count, source, sourceIndex); return count; } template int removeRange(Agent *agent, int index, int count) { agent->removeRange(index, count); return 0; } template int updateRange(Agent *agent, int index, int count, const ReferenceList &source, int sourceIndex) { Q_UNUSED(agent); Q_UNUSED(index); Q_UNUSED(source); Q_UNUSED(sourceIndex); return count; } template class SynchronizeList { public: SynchronizeList( Agent *agent, const CacheList &cache, int &c, const ReferenceList &reference, int &r) : agent(agent), cache(cache), c(c), reference(reference), r(r) { int lastEqualC = c; int lastEqualR = r; for (; c < cache.count() && r < reference.count(); ++c, ++r) { if (compareIdentity(cache.at(c), reference.at(r))) { continue; } if (c > lastEqualC) { lastEqualC += updateRange(agent, lastEqualC, c - lastEqualC, reference, lastEqualR); c = lastEqualC; lastEqualR = r; } bool match = false; // Iterate through both the reference and cache lists in parallel looking for first // point of commonality, when that is found resolve the differences and continue // looking. int count = 1; for (; !match && c + count < cache.count() && r + count < reference.count(); ++count) { typename CacheList::const_reference cacheItem = cache.at(c + count); typename ReferenceList::const_reference referenceItem = reference.at(r + count); for (int i = 0; i <= count; ++i) { if (cacheMatch(i, count, referenceItem) || referenceMatch(i, count, cacheItem)) { match = true; break; } } } // Continue scanning the reference list if the cache has been exhausted. for (int re = r + count; !match && re < reference.count(); ++re) { typename ReferenceList::const_reference referenceItem = reference.at(re); for (int i = 0; i < count; ++i) { if (cacheMatch(i, re - r, referenceItem)) { match = true; break; } } } // Continue scanning the cache if the reference list has been exhausted. for (int ce = c + count; !match && ce < cache.count(); ++ce) { typename CacheList::const_reference cacheItem = cache.at(ce); for (int i = 0; i < count; ++i) { if (referenceMatch(i, ce - c, cacheItem)) { match = true; break; } } } if (!match) return; lastEqualC = c; lastEqualR = r; } if (c > lastEqualC) { updateRange(agent, lastEqualC, c - lastEqualC, reference, lastEqualR); } } private: // Tests if the cached contact id at i matches a referenceId. // If there is a match removes all items traversed in the cache since the previous match // and inserts any items in the reference set found to to not be in the cache. bool cacheMatch(int i, int count, typename ReferenceList::const_reference referenceItem) { if (compareIdentity(cache.at(c + i), referenceItem)) { if (i > 0) c += removeRange(agent, c, i); c += insertRange(agent, c, count, reference, r); r += count; return true; } else { return false; } } // Tests if the reference contact id at i matches a cacheId. // If there is a match inserts all items traversed in the reference set since the // previous match and removes any items from the cache that were not found in the // reference list. bool referenceMatch(int i, int count, typename ReferenceList::const_reference cacheItem) { if (compareIdentity(reference.at(r + i), cacheItem)) { c += removeRange(agent, c, count); if (i > 0) c += insertRange(agent, c, i, reference, r); r += i; return true; } else { return false; } } Agent * const agent; const CacheList &cache; int &c; const ReferenceList &reference; int &r; }; template void completeSynchronizeList( Agent *agent, const CacheList &cache, int &cacheIndex, const ReferenceList &reference, int &referenceIndex) { if (cacheIndex < cache.count()) { agent->removeRange(cacheIndex, cache.count() - cacheIndex); } if (referenceIndex < reference.count()) { agent->insertRange(cache.count(), reference.count() - referenceIndex, reference, referenceIndex); } cacheIndex = 0; referenceIndex = 0; } template void synchronizeList( Agent *agent, const CacheList &cache, int &cacheIndex, const ReferenceList &reference, int &referenceIndex) { SynchronizeList( agent, cache, cacheIndex, reference, referenceIndex); } template void synchronizeList(Agent *agent, const CacheList &cache, const ReferenceList &reference) { int cacheIndex = 0; int referenceIndex = 0; SynchronizeList( agent, cache, cacheIndex, reference, referenceIndex); completeSynchronizeList(agent, cache, cacheIndex, reference, referenceIndex); } #endif libsocialcache-0.2.1/src/qml/twitter/000077500000000000000000000000001462333522700175305ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/twitter/twitterpostsmodel.cpp000066400000000000000000000102001462333522700240410ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "twitterpostsmodel.h" #include "abstractsocialcachemodel_p.h" #include "twitterpostsdatabase.h" #include #include "postimagehelper_p.h" class TwitterPostsModelPrivate: public AbstractSocialCacheModelPrivate { public: explicit TwitterPostsModelPrivate(TwitterPostsModel *q); TwitterPostsDatabase database; private: Q_DECLARE_PUBLIC(TwitterPostsModel) }; TwitterPostsModelPrivate::TwitterPostsModelPrivate(TwitterPostsModel *q) : AbstractSocialCacheModelPrivate(q) { } TwitterPostsModel::TwitterPostsModel(QObject *parent) : AbstractSocialCacheModel(*(new TwitterPostsModelPrivate(this)), parent) { Q_D(TwitterPostsModel); connect(&d->database, &AbstractSocialPostCacheDatabase::postsChanged, this, &TwitterPostsModel::postsChanged); connect(&d->database, SIGNAL(accountIdFilterChanged()), this, SIGNAL(accountIdFilterChanged())); } QHash TwitterPostsModel::roleNames() const { QHash roleNames; roleNames.insert(TwitterId, "twitterId"); roleNames.insert(Name, "name"); roleNames.insert(ScreenName, "screenName"); roleNames.insert(Body, "body"); roleNames.insert(Timestamp, "timestamp"); roleNames.insert(Icon, "icon"); roleNames.insert(Images, "images"); roleNames.insert(Retweeter, "retweeter"); roleNames.insert(ConsumerKey, "consumerKey"); roleNames.insert(ConsumerSecret, "consumerSecret"); roleNames.insert(Accounts, "accounts"); return roleNames; } QVariantList TwitterPostsModel::accountIdFilter() const { Q_D(const TwitterPostsModel); return d->database.accountIdFilter(); } void TwitterPostsModel::setAccountIdFilter(const QVariantList &accountIds) { Q_D(TwitterPostsModel); d->database.setAccountIdFilter(accountIds); } void TwitterPostsModel::refresh() { Q_D(TwitterPostsModel); d->database.refresh(); } void TwitterPostsModel::postsChanged() { Q_D(TwitterPostsModel); SocialCacheModelData data; QList postsData = d->database.posts(); Q_FOREACH (const SocialPost::ConstPtr &post, postsData) { QMap eventMap; eventMap.insert(TwitterPostsModel::TwitterId, post->identifier()); eventMap.insert(TwitterPostsModel::Name, post->name()); eventMap.insert(TwitterPostsModel::Body, post->body()); eventMap.insert(TwitterPostsModel::Timestamp, post->timestamp()); eventMap.insert(TwitterPostsModel::Icon, post->icon()); QVariantList images; Q_FOREACH (const SocialPostImage::ConstPtr &image, post->images()) { images.append(createImageData(image)); } eventMap.insert(TwitterPostsModel::Images, images); eventMap.insert(TwitterPostsModel::ScreenName, d->database.screenName(post)); eventMap.insert(TwitterPostsModel::Retweeter, d->database.retweeter(post)); eventMap.insert(TwitterPostsModel::ConsumerKey, d->database.consumerKey(post)); eventMap.insert(TwitterPostsModel::ConsumerSecret, d->database.consumerSecret(post)); QVariantList accountsVariant; Q_FOREACH (int account, post->accounts()) { accountsVariant.append(account); } eventMap.insert(TwitterPostsModel::Accounts, accountsVariant); data.append(eventMap); } updateData(data); } libsocialcache-0.2.1/src/qml/twitter/twitterpostsmodel.h000066400000000000000000000034041462333522700235160ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * Contact: Lucien Xu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TWITTERPOSTSMODEL_H #define TWITTERPOSTSMODEL_H #include "abstractsocialcachemodel.h" class TwitterPostsModelPrivate; class TwitterPostsModel: public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged) public: enum TwitterPostsRole { TwitterId = 0, Name, ScreenName, Body, Timestamp, Icon, Images, Retweeter, ConsumerKey, ConsumerSecret, Accounts }; explicit TwitterPostsModel(QObject *parent = 0); QHash roleNames() const; QVariantList accountIdFilter() const; void setAccountIdFilter(const QVariantList &accountIds); void refresh(); signals: void accountIdFilterChanged(); private slots: void postsChanged(); private: Q_DECLARE_PRIVATE(TwitterPostsModel) }; #endif // TWITTERPOSTSMODEL_H libsocialcache-0.2.1/src/qml/vk/000077500000000000000000000000001462333522700164465ustar00rootroot00000000000000libsocialcache-0.2.1/src/qml/vk/vkimagecachemodel.cpp000066400000000000000000000364641462333522700226170ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vkimagecachemodel.h" #include "abstractsocialcachemodel_p.h" #include "vkimagesdatabase.h" #include "vkimagedownloader_p.h" #include #include #include static const char *URL_KEY = "url"; static const char *ROW_KEY = "row"; static const char *MODEL_KEY = "model"; static const char *TYPE_KEY = "type"; static const char *PHOTOID_KEY = "photo_id"; static const char *ALBUMID_KEY = "album_id"; static const char *OWNERID_KEY = "owner_id"; static const char *ACCOUNTID_KEY = "account_id"; #define SOCIALCACHE_VK_IMAGE_DIR PRIVILEGED_DATA_DIR + QLatin1String("/Images/") class VKImageCacheModelPrivate : public AbstractSocialCacheModelPrivate { public: VKImageCacheModelPrivate(VKImageCacheModel *q); void queue( int row, VKImageDownloader::ImageType imageType, int accountId, const QString &user_id, const QString &album_id, const QString &photo_id, const QString &url); VKImageDownloader *downloader; VKImagesDatabase database; VKImageCacheModel::ModelDataType type; }; VKImageCacheModelPrivate::VKImageCacheModelPrivate(VKImageCacheModel *q) : AbstractSocialCacheModelPrivate(q), downloader(0), type(VKImageCacheModel::Images) { } void VKImageCacheModelPrivate::queue( int row, VKImageDownloader::ImageType imageType, int accountId, const QString &user_id, const QString &album_id, const QString &photo_id, const QString &url) { VKImageCacheModel *modelPtr = qobject_cast(q_ptr); if (downloader) { QVariantMap metadata; metadata.insert(QLatin1String(ROW_KEY), row); metadata.insert(QLatin1String(TYPE_KEY), imageType); metadata.insert(QLatin1String(ACCOUNTID_KEY), accountId); metadata.insert(QLatin1String(OWNERID_KEY), user_id); metadata.insert(QLatin1String(ALBUMID_KEY), album_id); metadata.insert(QLatin1String(PHOTOID_KEY), photo_id); metadata.insert(QLatin1String(URL_KEY), url); metadata.insert(QLatin1String(MODEL_KEY), QVariant::fromValue((void*)modelPtr)); downloader->queue(url, metadata); } } VKImageCacheModel::VKImageCacheModel(QObject *parent) : AbstractSocialCacheModel(*(new VKImageCacheModelPrivate(this)), parent) { Q_D(const VKImageCacheModel); connect(&d->database, &VKImagesDatabase::queryFinished, this, &VKImageCacheModel::queryFinished); } VKImageCacheModel::~VKImageCacheModel() { Q_D(VKImageCacheModel); if (d->downloader) { d->downloader->removeModelFromHash(this); } } QHash VKImageCacheModel::roleNames() const { QHash roleNames; roleNames.insert(PhotoId, "photoId"); roleNames.insert(AlbumId, "albumId"); roleNames.insert(UserId, "userId"); roleNames.insert(AccountId, "accountId"); roleNames.insert(Text, "text"); roleNames.insert(Date, "date"); roleNames.insert(Width, "photoWidth"); roleNames.insert(Height, "photoHeight"); roleNames.insert(Thumbnail, "thumbnail"); roleNames.insert(Image, "image"); roleNames.insert(Count, "dataCount"); roleNames.insert(MimeType, "mimeType"); roleNames.insert(ImageSource, "imageSource"); return roleNames; } QString VKImageCacheModel::constructNodeIdentifier(int accountId, const QString &user_id, const QString &album_id, const QString &photo_id) { return QString(QLatin1String("%1:%2:%3:%4")).arg(accountId).arg(user_id).arg(album_id).arg(photo_id); } QVariantMap VKImageCacheModel::parseNodeIdentifier(const QString &nid) const { QStringList values = nid.split(':'); QVariantMap retn; retn.insert("accountId", values.value(0)); retn.insert("user_id", values.value(1)); retn.insert("album_id", values.value(2)); retn.insert("photo_id", values.value(3)); return retn; } VKImageCacheModel::ModelDataType VKImageCacheModel::type() const { Q_D(const VKImageCacheModel); return d->type; } void VKImageCacheModel::setType(VKImageCacheModel::ModelDataType type) { Q_D(VKImageCacheModel); if (d->type != type) { d->type = type; emit typeChanged(); } } VKImageDownloader * VKImageCacheModel::downloader() const { Q_D(const VKImageCacheModel); return d->downloader; } void VKImageCacheModel::setDownloader(VKImageDownloader *downloader) { Q_D(VKImageCacheModel); if (d->downloader != downloader) { if (d->downloader) { // Disconnect worker object disconnect(d->downloader); d->downloader->removeModelFromHash(this); } d->downloader = downloader; d->downloader->addModelToHash(this); emit downloaderChanged(); } } void VKImageCacheModel::removeImage(const QString &imageId) { Q_D(VKImageCacheModel); int row = -1; for (int i = 0; i < count(); ++i) { QString dbId = data(index(i), VKImageCacheModel::PhotoId).toString(); if (dbId == imageId) { row = i; break; } } if (row >= 0) { beginRemoveRows(QModelIndex(), row, row); d->m_data.removeAt(row); endRemoveRows(); // Update album image count VKImage::ConstPtr image = d->database.image(imageId); if (image) { VKAlbum::ConstPtr album = d->database.album(image->albumId()); if (album) { d->database.addAlbum(VKAlbum::create(album->id(), album->ownerId(), album->title(), album->description(), album->thumbSrc(), album->thumbFile(), album->size()-1, album->created(), album->updated(), album->accountId())); } d->database.removeImage(image); d->database.commit(); } } } QVariant VKImageCacheModel::data(const QModelIndex &index, int role) const { Q_D(const VKImageCacheModel); int row = index.row(); if (row < 0 || row >= d->m_data.count()) { return QVariant(); } return d->m_data.at(row).value(role); } void VKImageCacheModel::loadImages() { refresh(); } void VKImageCacheModel::refresh() { Q_D(VKImageCacheModel); QVariantMap parsedNodeIdentifier = parseNodeIdentifier(d->nodeIdentifier); switch (d->type) { case VKImageCacheModel::Users: d->database.queryUsers(); break; case VKImageCacheModel::Albums: d->database.queryAlbums(parsedNodeIdentifier.value("accountId").toInt(), parsedNodeIdentifier.value("user_id").toString()); break; case VKImageCacheModel::Images: if (parsedNodeIdentifier.value("user_id").toString().isEmpty()) { d->database.queryUserImages(); } else if (parsedNodeIdentifier.value("album_id").toString().isEmpty()) { d->database.queryUserImages(parsedNodeIdentifier.value("accountId").toInt(), parsedNodeIdentifier.value("user_id").toString()); } else { d->database.queryAlbumImages(parsedNodeIdentifier.value("accountId").toInt(), parsedNodeIdentifier.value("user_id").toString(), parsedNodeIdentifier.value("album_id").toString()); } break; default: break; } } // NOTE: this is now called directly by VKImageDownloader // rather than connected to the imageDownloaded signal, for // performance reasons. void VKImageCacheModel::imageDownloaded(const QString &url, const QString &path, const QVariantMap &imageData) { Q_UNUSED(url); Q_D(VKImageCacheModel); if (path.isEmpty()) { // empty path signifies an error, which we don't handle here at the moment. // Return, otherwise dataChanged signal would cause UI to read back // related value, which, being empty, would trigger another download request, // potentially causing never ending loop. return; } int row = imageData.value(ROW_KEY).toInt(); if (row < 0 || row >= d->m_data.count()) { qWarning() << Q_FUNC_INFO << "Invalid row:" << row << "max row:" << d->m_data.count(); return; } int type = imageData.value(TYPE_KEY).toInt(); switch (type) { case VKImageDownloader::ThumbnailImage: d->m_data[row].insert(VKImageCacheModel::Thumbnail, path); break; default: qWarning() << Q_FUNC_INFO << "invalid downloader type: " << type; break; } emit dataChanged(index(row), index(row)); } void VKImageCacheModel::queryFinished() { Q_D(VKImageCacheModel); QList thumbQueue; SocialCacheModelData data; switch (d->type) { case Users: { QList usersData = d->database.users(); for (int i = 0; i < usersData.count(); i++) { const VKUser::ConstPtr &userData(usersData[i]); QMap userMap; userMap.insert(VKImageCacheModel::UserId, userData->id()); userMap.insert(VKImageCacheModel::AccountId, userData->accountId()); userMap.insert(VKImageCacheModel::Text, userData->firstName() + ' ' + userData->lastName()); userMap.insert(VKImageCacheModel::Count, userData->photosCount()); data.append(userMap); } if (data.count() > 1) { QMap userMap; int count = 0; Q_FOREACH (const VKUser::ConstPtr &userData, usersData) { count += userData->photosCount(); } userMap.insert(VKImageCacheModel::UserId, QString()); userMap.insert(VKImageCacheModel::Thumbnail, QString()); //: Label for the "show all users from all VK accounts" option //% "All" userMap.insert(VKImageCacheModel::Text, qtTrId("nemo_socialcache_VK_images_model-all-users")); userMap.insert(VKImageCacheModel::Count, count); userMap.insert(VKImageCacheModel::AccountId, 0); data.prepend(userMap); } break; } case Albums: { QList albumsData = d->database.albums(); Q_FOREACH (const VKAlbum::ConstPtr &albumData, albumsData) { QMap albumMap; albumMap.insert(VKImageCacheModel::AlbumId, albumData->id()); albumMap.insert(VKImageCacheModel::Text, albumData->title()); albumMap.insert(VKImageCacheModel::Count, albumData->size()); albumMap.insert(VKImageCacheModel::UserId, albumData->ownerId()); albumMap.insert(VKImageCacheModel::AccountId, albumData->accountId()); data.append(albumMap); } if (data.count() > 1) { QVariantMap parsedNodeIdentifier = parseNodeIdentifier(d->nodeIdentifier); QMap albumMap; int count = 0; Q_FOREACH (const VKAlbum::ConstPtr &albumData, albumsData) { count += albumData->size(); } albumMap.insert(VKImageCacheModel::AlbumId, QString()); //: Label for the "show all photos from all albums by this user" option //% "All" albumMap.insert(VKImageCacheModel::Text, qtTrId("nemo_socialcache_VK_images_model-all-albums")); albumMap.insert(VKImageCacheModel::Count, count); albumMap.insert(VKImageCacheModel::UserId, parsedNodeIdentifier.value("user_id").toString()); albumMap.insert(VKImageCacheModel::AccountId, parsedNodeIdentifier.value("accountId").toInt()); data.prepend(albumMap); } break; } case Images: { QList imagesData = d->database.images(); for (int i = 0; i < imagesData.count(); i ++) { const VKImage::ConstPtr &imageData = imagesData.at(i); QMap imageMap; if (imageData->thumbFile().isEmpty()) { QVariantMap thumbQueueData; thumbQueueData.insert(QLatin1String(ROW_KEY), QVariant::fromValue(i)); thumbQueueData.insert(QLatin1String(TYPE_KEY), QVariant::fromValue(VKImageDownloader::ThumbnailImage)); thumbQueueData.insert(QLatin1String(ACCOUNTID_KEY), imageData->accountId()); thumbQueueData.insert(QLatin1String(OWNERID_KEY), imageData->ownerId()); thumbQueueData.insert(QLatin1String(ALBUMID_KEY), imageData->albumId()); thumbQueueData.insert(QLatin1String(PHOTOID_KEY), imageData->id()); thumbQueueData.insert(QLatin1String(URL_KEY), imageData->thumbSrc()); thumbQueue.append(thumbQueueData); } // note: we don't queue the image file until the user explicitly opens that in fullscreen. imageMap.insert(VKImageCacheModel::PhotoId, imageData->id()); imageMap.insert(VKImageCacheModel::AlbumId, imageData->albumId()); imageMap.insert(VKImageCacheModel::UserId, imageData->ownerId()); imageMap.insert(VKImageCacheModel::AccountId, imageData->accountId()); imageMap.insert(VKImageCacheModel::Thumbnail, imageData->thumbFile()); imageMap.insert(VKImageCacheModel::Image, imageData->photoFile()); imageMap.insert(VKImageCacheModel::Text, imageData->text()); imageMap.insert(VKImageCacheModel::Date, imageData->date()); imageMap.insert(VKImageCacheModel::Width, imageData->width()); imageMap.insert(VKImageCacheModel::Height, imageData->height()); imageMap.insert(VKImageCacheModel::MimeType, QLatin1String("image/jpeg")); imageMap.insert(VKImageCacheModel::ImageSource, imageData->photoSrc()); data.append(imageMap); } break; } default: return; } updateData(data); // now download the queued thumbnails. foreach (const QVariantMap &thumbQueueData, thumbQueue) { d->queue(thumbQueueData[QLatin1String(ROW_KEY)].toInt(), static_cast(thumbQueueData[QLatin1String("TYPE_KEY")].toInt()), thumbQueueData[QLatin1String(ACCOUNTID_KEY)].toInt(), thumbQueueData[QLatin1String(OWNERID_KEY)].toString(), thumbQueueData[QLatin1String(ALBUMID_KEY)].toString(), thumbQueueData[QLatin1String(PHOTOID_KEY)].toString(), thumbQueueData[QLatin1String(URL_KEY)].toString()); } } libsocialcache-0.2.1/src/qml/vk/vkimagecachemodel.h000066400000000000000000000057421462333522700222570ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKIMAGECACHEMODEL_H #define VKIMAGECACHEMODEL_H #include "abstractsocialcachemodel.h" #include "vkimagedownloader.h" class VKImageCacheModelPrivate; class VKImageCacheModel: public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(VKImageCacheModel::ModelDataType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(VKImageDownloader * downloader READ downloader WRITE setDownloader NOTIFY downloaderChanged) Q_ENUMS(VKGalleryRole) Q_ENUMS(ModelDataType) public: enum VKGalleryRole { PhotoId = 0, AlbumId, UserId, AccountId, Text, Date, Width, Height, Thumbnail, Image, Count, MimeType, ImageSource }; enum ModelDataType { None = 0, // used for resetting/refreshing the model. Users, Albums, Images }; explicit VKImageCacheModel(QObject *parent = 0); ~VKImageCacheModel(); QHash roleNames() const; // properties VKImageCacheModel::ModelDataType type() const; void setType(VKImageCacheModel::ModelDataType type); VKImageDownloader *downloader() const; void setDownloader(VKImageDownloader *downloader); // from AbstractListModel QVariant data(const QModelIndex &index, int role) const; // since VK doens't use globally-unique identifiers, we need to encode breadcrumb information into the node identifier. Q_INVOKABLE QString constructNodeIdentifier(int accountId, const QString &user_id, const QString &album_id, const QString &photo_id); // { "accountId"=int, "user_id"=string, "album_id"=string, "photo_id"=string } Q_INVOKABLE QVariantMap parseNodeIdentifier(const QString &nid) const; Q_INVOKABLE void removeImage(const QString &imageId); public Q_SLOTS: void loadImages(); void refresh(); Q_SIGNALS: void typeChanged(); void downloaderChanged(); private Q_SLOTS: void queryFinished(); void imageDownloaded(const QString &url, const QString &path, const QVariantMap &imageData); private: Q_DECLARE_PRIVATE(VKImageCacheModel) friend class VKImageDownloader; }; #endif // VKIMAGECACHEMODEL_H libsocialcache-0.2.1/src/qml/vk/vkimagedownloader.cpp000066400000000000000000000112741462333522700226610ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vkimagedownloader.h" #include "vkimagedownloader_p.h" #include "vkimagecachemodel.h" #include #include #include static const char *MODEL_KEY = "model"; static const char *TYPE_KEY = "type"; static const char *PHOTOID_KEY = "photo_id"; static const char *ALBUMID_KEY = "album_id"; static const char *OWNERID_KEY = "owner_id"; static const char *ACCOUNTID_KEY = "account_id"; VKImageDownloaderPrivate::VKImageDownloaderPrivate(VKImageDownloader *q) : AbstractImageDownloaderPrivate(q) { } VKImageDownloaderPrivate::~VKImageDownloaderPrivate() { } VKImageDownloader::VKImageDownloader(QObject *parent) : AbstractImageDownloader(*new VKImageDownloaderPrivate(this), parent) { connect(this, &AbstractImageDownloader::imageDownloaded, this, &VKImageDownloader::invokeSpecificModelCallback); } VKImageDownloader::~VKImageDownloader() { } void VKImageDownloader::addModelToHash(VKImageCacheModel *model) { Q_D(VKImageDownloader); d->m_connectedModels.insert(model); } void VKImageDownloader::removeModelFromHash(VKImageCacheModel *model) { Q_D(VKImageDownloader); d->m_connectedModels.remove(model); } /* * A VKImageDownloader can be connected to multiple models. * Instead of connecting the imageDownloaded signal directly to the * model, we connect it to this slot, which retrieves the target model * from the metadata map and invokes its callback directly. * This avoids a possibly large number of signal connections + invocations. */ void VKImageDownloader::invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata) { Q_D(VKImageDownloader); VKImageCacheModel *model = static_cast(metadata.value(MODEL_KEY).value()); // check to see if the model was destroyed in the meantime. // If not, we can directly invoke the callback. if (d->m_connectedModels.contains(model)) { model->imageDownloaded(url, path, metadata); } } QString VKImageDownloader::outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const { Q_UNUSED(url); Q_UNUSED(mimeType); QString identifier = QStringLiteral("%1-%2-%3-%4") .arg(data.value(QLatin1String(OWNERID_KEY)).toString()) .arg(data.value(QLatin1String(ALBUMID_KEY)).toString()) .arg(data.value(QLatin1String(PHOTOID_KEY)).toString()) .arg(data.value(QLatin1String(TYPE_KEY)).toString()); return makeOutputFile(SocialSyncInterface::VK, SocialSyncInterface::Images, identifier, QString()); } void VKImageDownloader::dbQueueImage(const QString &url, const QVariantMap &data, const QString &file) { Q_UNUSED(url); Q_D(VKImageDownloader); QString photo_id = data.value(QLatin1String(PHOTOID_KEY)).toString(); QString album_id = data.value(QLatin1String(ALBUMID_KEY)).toString(); QString owner_id = data.value(QLatin1String(OWNERID_KEY)).toString(); int accountId = data.value(QLatin1String(ACCOUNTID_KEY)).toInt(); if (photo_id.isEmpty() || album_id.isEmpty() || owner_id.isEmpty() || accountId == 0) { qWarning() << "invalid photo metadata specified for downloaded image:" << photo_id << album_id << owner_id << accountId; return; } VKImage::Ptr image = VKImage::create(photo_id, album_id, owner_id, QString(), QString(), QString(), QString(), QString(), 0, 0, 0, accountId); int type = data.value(QLatin1String(TYPE_KEY)).toInt(); switch (type) { case ThumbnailImage: d->database.updateImageThumbnail(image, file); break; } } void VKImageDownloader::dbWrite() { Q_D(VKImageDownloader); d->database.commit(); } libsocialcache-0.2.1/src/qml/vk/vkimagedownloader.h000066400000000000000000000035471462333522700223320ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKIMAGEDOWNLOADER_H #define VKIMAGEDOWNLOADER_H #include #include "abstractimagedownloader.h" class VKImageCacheModel; class VKImageDownloaderWorkerObject; class VKImageDownloaderPrivate; class VKImageDownloader : public AbstractImageDownloader { Q_OBJECT public: enum ImageType { ThumbnailImage }; explicit VKImageDownloader(QObject *parent = 0); virtual ~VKImageDownloader(); // tracking object lifetime of models connected to this downloader. void addModelToHash(VKImageCacheModel *model); void removeModelFromHash(VKImageCacheModel *model); protected: QString outputFile(const QString &url, const QVariantMap &data, const QString &mimeType) const override; void dbQueueImage(const QString &url, const QVariantMap &data, const QString &file); void dbWrite(); private Q_SLOTS: void invokeSpecificModelCallback(const QString &url, const QString &path, const QVariantMap &metadata); private: Q_DECLARE_PRIVATE(VKImageDownloader) }; #endif // VKIMAGEDOWNLOADER_H libsocialcache-0.2.1/src/qml/vk/vkimagedownloader_p.h000066400000000000000000000027441462333522700226470ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * Contact: Chris Adams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKIMAGEDOWNLOADER_P_H #define VKIMAGEDOWNLOADER_P_H #include #include #include #include #include #include "abstractimagedownloader_p.h" #include "vkimagedownloader.h" #include "vkimagesdatabase.h" class VKImageCacheModel; class VKImageDownloaderPrivate: public AbstractImageDownloaderPrivate { public: explicit VKImageDownloaderPrivate(VKImageDownloader *q); virtual ~VKImageDownloaderPrivate(); VKImagesDatabase database; QSet m_connectedModels; private: Q_DECLARE_PUBLIC(VKImageDownloader) }; #endif // VKIMAGEDOWNLOADER_P_H libsocialcache-0.2.1/src/qml/vk/vkpostsmodel.cpp000066400000000000000000000140761462333522700217140ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vkpostsmodel.h" #include "abstractsocialcachemodel_p.h" #include "vkpostsdatabase.h" #include #include "postimagehelper_p.h" static const char *POST_LINK_KEY = "post_link_key"; static const char *COPIED_POST_CREATED_TIME_KEY = "copied_post_created_time"; static const char *COPIED_POST_TYPE_KEY = "copied_post_type"; static const char *COPIED_POST_OWNER_NAME_KEY = "copied_post_owner_name"; static const char *COPIED_POST_OWNER_AVATAR_KEY = "copied_post_owner_avatar"; static const char *COPIED_POST_TEXT_KEY = "copied_post_text"; static const char *COPIED_POST_PHOTO_KEY = "copied_post_photo"; static const char *COPIED_POST_VIDEO_KEY = "copied_post_video"; static const char *COPIED_POST_LINK_KEY = "copied_post_link"; class VKPostsModelPrivate: public AbstractSocialCacheModelPrivate { public: explicit VKPostsModelPrivate(VKPostsModel *q); VKPostsDatabase database; private: Q_DECLARE_PUBLIC(VKPostsModel) }; VKPostsModelPrivate::VKPostsModelPrivate(VKPostsModel *q) : AbstractSocialCacheModelPrivate(q) { } VKPostsModel::VKPostsModel(QObject *parent) : AbstractSocialCacheModel(*(new VKPostsModelPrivate(this)), parent) { Q_D(VKPostsModel); connect(&d->database, &AbstractSocialPostCacheDatabase::postsChanged, this, &VKPostsModel::postsChanged); } QHash VKPostsModel::roleNames() const { QHash roleNames; roleNames.insert(VkId, "vkId"); roleNames.insert(Name, "name"); roleNames.insert(Body, "body"); roleNames.insert(Timestamp, "timestamp"); roleNames.insert(Icon, "icon"); roleNames.insert(Images, "images"); roleNames.insert(Accounts, "accounts"); roleNames.insert(RepostType, "repostType"); roleNames.insert(RepostOwnerName, "repostOwnerName"); roleNames.insert(RepostOwnerAvatar, "repostOwnerAvatar"); roleNames.insert(RepostText, "repostText"); roleNames.insert(RepostVideo, "repostVideo"); roleNames.insert(RepostLink, "repostLink"); roleNames.insert(RepostTimestamp, "repostTimestamp"); roleNames.insert(RepostImages, "repostImages"); roleNames.insert(Link, "link"); return roleNames; } QVariantList VKPostsModel::accountIdFilter() const { Q_D(const VKPostsModel); return d->database.accountIdFilter(); } void VKPostsModel::setAccountIdFilter(const QVariantList &accountIds) { Q_D(VKPostsModel); d->database.setAccountIdFilter(accountIds); } void VKPostsModel::refresh() { Q_D(VKPostsModel); d->database.refresh(); } void VKPostsModel::remove(const QString &postId) { Q_D(VKPostsModel); for (int i=0; iremoveRange(i, 1); d->database.removePost(postId); d->database.commit(); break; } } } void VKPostsModel::clear() { Q_D(VKPostsModel); d->clearData(); d->database.removeAll(); } void VKPostsModel::postsChanged() { Q_D(VKPostsModel); SocialCacheModelData data; QList postsData = d->database.posts(); Q_FOREACH (const SocialPost::ConstPtr &post, postsData) { QMap eventMap; eventMap.insert(VKPostsModel::VkId, post->identifier()); eventMap.insert(VKPostsModel::Name, post->name()); eventMap.insert(VKPostsModel::Body, post->body()); eventMap.insert(VKPostsModel::Timestamp, post->timestamp()); eventMap.insert(VKPostsModel::Icon, post->icon()); eventMap.insert(VKPostsModel::Link, post->extra().value(POST_LINK_KEY)); eventMap.insert(VKPostsModel::RepostOwnerName, post->extra().value(COPIED_POST_OWNER_NAME_KEY)); eventMap.insert(VKPostsModel::RepostOwnerAvatar, post->extra().value(COPIED_POST_OWNER_AVATAR_KEY)); eventMap.insert(VKPostsModel::RepostType, post->extra().value(COPIED_POST_TYPE_KEY)); eventMap.insert(VKPostsModel::RepostText, post->extra().value(COPIED_POST_TEXT_KEY)); eventMap.insert(VKPostsModel::RepostVideo, post->extra().value(COPIED_POST_VIDEO_KEY)); eventMap.insert(VKPostsModel::RepostLink, post->extra().value(COPIED_POST_LINK_KEY)); eventMap.insert(VKPostsModel::RepostTimestamp, post->extra().value(COPIED_POST_CREATED_TIME_KEY)); QVariantList repostImages; QStringList imageUrls = post->extra().value(COPIED_POST_PHOTO_KEY).toString().split(QStringLiteral(",")); Q_FOREACH (const QString url, imageUrls) { if (!url.isEmpty()) { SocialPostImage::Ptr repostImage = SocialPostImage::create(url, SocialPostImage::Photo); QVariantMap tmp = createImageData(repostImage); repostImages.append(tmp); } } eventMap.insert(VKPostsModel::RepostImages, repostImages); QVariantList images; Q_FOREACH (const SocialPostImage::ConstPtr &image, post->images()) { images.append(createImageData(image)); } eventMap.insert(VKPostsModel::Images, images); QVariantList accountsVariant; Q_FOREACH (int account, post->accounts()) { accountsVariant.append(account); } eventMap.insert(VKPostsModel::Accounts, accountsVariant); data.append(eventMap); } updateData(data); } libsocialcache-0.2.1/src/qml/vk/vkpostsmodel.h000066400000000000000000000037201462333522700213530ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 Jolla Ltd. * Contact: Antti Seppälä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VKPOSTSMODEL_H #define VKPOSTSMODEL_H #include "abstractsocialcachemodel.h" class VKPostsModelPrivate; class VKPostsModel: public AbstractSocialCacheModel { Q_OBJECT Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged) Q_ENUMS(VKPostsRole) public: enum VKPostsRole { VkId = 0, Name, Body, Timestamp, Icon, Images, Extra, Accounts, RepostType, RepostOwnerName, RepostOwnerAvatar, RepostText, RepostVideo, RepostLink, RepostTimestamp, RepostImages, Link }; explicit VKPostsModel(QObject *parent = 0); QHash roleNames() const; QVariantList accountIdFilter() const; void setAccountIdFilter(const QVariantList &accountIds); void refresh(); Q_INVOKABLE void remove(const QString &postId); Q_INVOKABLE void clear(); signals: void accountIdFilterChanged(); private Q_SLOTS: void postsChanged(); private: Q_DECLARE_PRIVATE(VKPostsModel) }; #endif // VKPOSTSMODEL_H libsocialcache-0.2.1/src/src.pro000066400000000000000000000000671462333522700165510ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = lib qml qml.depends = lib libsocialcache-0.2.1/tests/000077500000000000000000000000001462333522700156105ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tests.pro000066400000000000000000000005131462333522700174730ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = \ tst_abstractsocialcachedatabase \ tst_facebookcontact \ tst_facebookimage \ tst_facebookpost \ tst_facebooknotification \ tst_socialnetworksync \ tst_twitterpost \ tst_socialimage \ tst_onedriveimage \ tst_dropboximage libsocialcache-0.2.1/tests/tst_abstractsocialcachedatabase/000077500000000000000000000000001462333522700241515ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_abstractsocialcachedatabase/main.cpp000066400000000000000000000370761462333522700256160ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "abstractsocialcachedatabase.h" #include "abstractsocialcachedatabase_p.h" #include #include #include #include #include class DummyDatabase: public AbstractSocialCacheDatabase { public: enum Test { None, Insert, Update, Delete, Clean, BenchmarkInsertBatch, BenchmarkInsertNaive, BenchmarkPrepareDeletion, BenchmarkDeleteAlbum, BenchmarkDeletePhotos }; explicit DummyDatabase() : AbstractSocialCacheDatabase(*(new AbstractSocialCacheDatabasePrivate( this, QLatin1String("Test"), QLatin1String("Test"), QLatin1String("test.db"), 1))) , currentTest(None) { } using AbstractSocialCacheDatabase::executeRead; using AbstractSocialCacheDatabase::executeWrite; Test currentTest; private: bool testInsert() { QVariantList ids; ids.append(QVariant(1)); ids.append(QVariant(2)); ids.append(QVariant(3)); QVariantList values; values.append(QLatin1String("a")); values.append(QLatin1String("b")); values.append(QLatin1String("c")); bool success = true; QSqlQuery query = prepare(QStringLiteral( "INSERT INTO tests (" " id, value) " "VALUES (" " :id, :value)")); query.bindValue(QStringLiteral(":id"), ids); query.bindValue(QStringLiteral(":value"), values); executeBatchSocialCacheQuery(query); return success; } bool checkInsert() { QSqlQuery query = prepare(QStringLiteral("SELECT id, value FROM tests")); if (!query.exec()) { return false; } QList expectedIds; expectedIds << 1 << 2 << 3; QList expectedValues; expectedValues << QLatin1String("a") << QLatin1String("b") << QLatin1String("c"); int i = 0; while (query.next()) { if (i == expectedValues.count()) { return false; } if (query.value(0) != expectedIds.at(i) || query.value(1) != expectedValues.at(i)) { return false; } i++; } return true; } bool testUpdate() { QVariantList ids; ids.append(QVariant(1)); ids.append(QVariant(2)); QVariantList values; values.append(QLatin1String("aa")); values.append(QLatin1String("bb")); bool success = true; QSqlQuery query = prepare(QStringLiteral( "UPDATE tests " "SET value = :value " "WHERE id = :id")); query.bindValue(QStringLiteral(":value"), values); query.bindValue(QStringLiteral(":id"), ids); executeBatchSocialCacheQuery(query); return success; } bool checkUpdate() { QSqlQuery query = prepare(QStringLiteral("SELECT id, value FROM tests")); if (!query.exec()) { return false; } QList expectedIds; expectedIds << 1 << 2 << 3; QList expectedValues; expectedValues << QLatin1String("aa") << QLatin1String("bb") << QLatin1String("c"); int i = 0; while (query.next()) { if (query.value(0) != expectedIds.at(i) || query.value(1) != expectedValues.at(i)) { return false; } i++; } return true; } bool testDelete() { QVariantList ids; ids.append(QVariant(3)); bool success = true; QSqlQuery query = prepare(QStringLiteral( "DELETE FROM tests " "WHERE id = :id")); query.bindValue(QStringLiteral(":id"), ids); executeBatchSocialCacheQuery(query); return success; } bool checkDelete() { QSqlQuery query = prepare(QStringLiteral("SELECT id, value FROM tests")); if (!query.exec()) { return false; } QList expectedIds; expectedIds << 1 << 2; QList expectedValues; expectedValues << QLatin1String("aa") << QLatin1String("bb"); int i = 0; while (query.next()) { if (query.value(0) != expectedIds.at(i) || query.value(1) != expectedValues.at(i)) { return false; } i++; } return true; } void clean() { QSqlQuery query = prepare(QStringLiteral("DELETE FROM tests")); query.exec(); } void benchmarkInsertBatch() { QVariantList ids; QVariantList values; for (int i = 0; i < 100; i ++) { ids.append(QVariant()); values.append(QLatin1String("a")); } QSqlQuery query = prepare("INSERT INTO tests(value) VALUES(:value)"); query.bindValue(QLatin1String(":id"), ids); query.bindValue(QLatin1String(":value"), values); query.execBatch(); } void benchmarkInsertNaive() { QSqlQuery query = prepare("INSERT INTO tests(value) VALUES(:value)"); for (int i = 0; i < 100; i ++) { query.bindValue(QLatin1String(":value"), QVariant(QLatin1String("a"))); query.exec(); } } void benchmarkPrepareDeletion() { // We will fill the data with 500 albums // And 500 photos per album QSqlQuery query = prepare(QStringLiteral("DELETE FROM albums")); query.exec(); query = prepare(QStringLiteral("DELETE FROM SQLITE_SEQUENCE WHERE NAME = :name")); query.bindValue(":name", QLatin1String("albums")); query.exec(); query = prepare(QStringLiteral("DELETE FROM photos")); query.exec(); query = prepare(QStringLiteral("DELETE FROM SQLITE_SEQUENCE WHERE NAME = :name")); query.bindValue(":name", QLatin1String("photos")); query.exec(); query = prepare(QStringLiteral( "INSERT INTO albums (value) " "VALUES(:value)")); for (int i = 0; i < 500; i ++) { query.bindValue(QStringLiteral(":value"), QString(QStringLiteral("Album %1")).arg(i + 1)); query.exec(); } for (int i = 0; i < 500; i ++) { for (int j = 0; j < 500; j++) { query = prepare(QStringLiteral( "INSERT INTO photos (albumId, value) " "VALUES (:albumId, :value)")); query.bindValue(QStringLiteral(":albumId"), QVariant(i + 1)); query.bindValue(QStringLiteral(":value"), QString(QStringLiteral("Photo %1 in album %2")).arg(j + 1).arg(i + 1)); query.exec(); } } } void benchmarkDeleteAlbum() { QSqlQuery query = prepare(QStringLiteral("DELETE FROM albums WHERE id = :id")); query.bindValue(":id", QVariant(1)); query.exec(); } void benchmarkDeletePhotos() { QSqlQuery query = prepare(QStringLiteral("DELETE FROM photos WHERE albumId = :id")); query.bindValue(":id", QVariant(1)); query.exec(); } protected: bool read() { switch (currentTest) { case Insert: return checkInsert(); case Update: return checkUpdate(); case Delete: return checkDelete(); case Clean: clean(); return true; case BenchmarkInsertBatch: benchmarkInsertBatch(); return true; case BenchmarkInsertNaive: benchmarkInsertNaive(); return true; case BenchmarkPrepareDeletion: benchmarkPrepareDeletion(); return true; case BenchmarkDeleteAlbum: benchmarkDeleteAlbum(); return true; case BenchmarkDeletePhotos: benchmarkDeletePhotos(); return true; default: return false; } } bool write() { switch (currentTest) { case Insert: return testInsert(); case Update: return testUpdate(); case Delete: return testDelete(); case Clean: clean(); return true; case BenchmarkInsertBatch: benchmarkInsertBatch(); return true; case BenchmarkInsertNaive: benchmarkInsertNaive(); return true; case BenchmarkPrepareDeletion: benchmarkPrepareDeletion(); return true; case BenchmarkDeleteAlbum: benchmarkDeleteAlbum(); return true; case BenchmarkDeletePhotos: benchmarkDeletePhotos(); return true; default: return false; } } bool createTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare( "CREATE TABLE IF NOT EXISTS tests (" "id INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT," "value TEXT)"); if (!query.exec()) { return false; } query.prepare( "CREATE TABLE IF NOT EXISTS albums (" "id INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT," "value TEXT)"); if (!query.exec()) { return false; } query.prepare( "CREATE TABLE IF NOT EXISTS photos (" "id INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT," "albumId INTEGER," "value TEXT)"); if (!query.exec()) { return false; } return true; } bool dropTables(QSqlDatabase database) const { QSqlQuery query(database); query.prepare("DROP TABLE IF EXISTS tests"); if (!query.exec()) { return false; } query.prepare("DROP TABLE IF EXISTS albums"); if (!query.exec()) { return false; } query.prepare("DROP TABLE IF EXISTS photos"); if (!query.exec()) { return false; } return true; } private: Q_DECLARE_PRIVATE(AbstractSocialCacheDatabase) }; class AbstractSocialCacheDatabaseTest: public QObject { Q_OBJECT private: DummyDatabase *db; private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); db = new DummyDatabase; } void testCommits() { db->currentTest = DummyDatabase::Insert; db->executeWrite(); db->wait(); QCOMPARE(db->writeStatus(), AbstractSocialCacheDatabase::Finished); db->executeRead(); db->wait(); QCOMPARE(db->readStatus(), AbstractSocialCacheDatabase::Finished); db->currentTest = DummyDatabase::Update; db->executeWrite(); db->wait(); QCOMPARE(db->writeStatus(), AbstractSocialCacheDatabase::Finished); db->executeRead(); db->wait(); QCOMPARE(db->readStatus(), AbstractSocialCacheDatabase::Finished); db->currentTest = DummyDatabase::Delete; db->executeWrite(); db->wait(); QCOMPARE(db->writeStatus(), AbstractSocialCacheDatabase::Finished); db->executeRead(); db->wait(); QCOMPARE(db->readStatus(), AbstractSocialCacheDatabase::Finished); } //private: void insertionBenchmarkBatch() { clean(); // Beware this takes time QBENCHMARK(benchmarkInsertBatch()); } void insertionBenchmarkNaive() { clean(); // Beware this takes time QBENCHMARK(benchmarkInsertNaive()); } void insertionBenchmarkNaiveTransaction() { clean(); QBENCHMARK(benchmarkInsertNaiveWithTransaction()); } void insertionBenchmarkBatchTransaction() { clean(); QBENCHMARK(benchmarkInsertBatchWithTransaction()); } void heavyInsertionBenchmark() { QBENCHMARK(benchmarkPrepareDeletion()); } void primaryKeyDeletion() { benchmarkPrepareDeletion(); QBENCHMARK_ONCE(benchmarkDeleteAlbum()); } void nonPrimaryKeyDeletion() { benchmarkPrepareDeletion(); QBENCHMARK_ONCE(benchmarkDeletePhotos()); } void cleanupTestCase() { delete db; // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } private: void clean() { db->currentTest = DummyDatabase::Clean; db->executeWrite(); db->wait(); } void benchmarkInsertBatch() { db->currentTest = DummyDatabase::BenchmarkInsertBatch; db->executeRead(); db->wait(); } void benchmarkInsertNaive() { db->currentTest = DummyDatabase::BenchmarkInsertNaive; db->executeRead(); db->wait(); } void benchmarkInsertBatchWithTransaction() { db->currentTest = DummyDatabase::BenchmarkInsertBatch; db->executeWrite(); db->wait(); } void benchmarkInsertNaiveWithTransaction() { db->currentTest = DummyDatabase::BenchmarkInsertNaive; db->executeWrite(); db->wait(); } void benchmarkPrepareDeletion() { db->currentTest = DummyDatabase::BenchmarkPrepareDeletion; db->executeWrite(); db->wait(); } void benchmarkDeleteAlbum() { db->currentTest = DummyDatabase::BenchmarkDeleteAlbum; db->executeWrite(); db->wait(); } void benchmarkDeletePhotos() { db->currentTest = DummyDatabase::BenchmarkDeletePhotos; db->executeWrite(); db->wait(); } }; QTEST_MAIN(AbstractSocialCacheDatabaseTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_abstractsocialcachedatabase/tst_abstractsocialcachedatabase.pro000066400000000000000000000007351462333522700332410ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_abstractsocialcachedatabase QT += sql testlib INCLUDEPATH += ../../src/lib/ HEADERS += ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/semaphore_p.h SOURCES += ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/semaphore_p.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_dropboximage/000077500000000000000000000000001462333522700211625ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_dropboximage/main.cpp000066400000000000000000000470651462333522700226260ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "dropboximagesdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class DropboxImageTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void testAddUser() { DropboxImagesDatabase database; QDateTime time1 (QDate(2013, 1, 2), QTime(12, 34, 56)); database.addUser("a", time1, "c"); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QString dataType = SocialSyncInterface::dataType(SocialSyncInterface::Images); QString dbFile = QLatin1String("dropbox.db"); // DB_NAME in dropboximagesdatabase.cpp QSqlDatabase checkDb = QSqlDatabase::addDatabase("QSQLITE"); checkDb.setDatabaseName(QString("%1/%2/%3").arg(PRIVILEGED_DATA_DIR, dataType, dbFile)); QVERIFY(checkDb.open()); // Check if the user has been written QSqlQuery query (checkDb); query.prepare("SELECT userId, updatedTime, userName "\ "FROM users"); QVERIFY(query.exec()); QVERIFY(query.next()); QCOMPARE(query.value(0).toString(), QLatin1String("a")); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QCOMPARE(query.value(1).toUInt(), time1.toSecsSinceEpoch()); #else QCOMPARE(query.value(1).toUInt(), time1.toTime_t()); #endif QCOMPARE(query.value(2).toString(), QLatin1String("c")); QVERIFY(!query.next()); // Check if we can retrieve the user DropboxUser::ConstPtr user = database.user(QLatin1String("a")); QCOMPARE(user->userId(), QLatin1String("a")); QCOMPARE(user->updatedTime(), time1); QCOMPARE(user->userName(), QLatin1String("c")); QCOMPARE(user->count(), -1); QDateTime time2 (QDate(2012, 3, 4), QTime(10, 11, 12)); // Let's write another user database.addUser("d", time2, "f"); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); // Check if we can retrieve everybody database.queryUsers(); database.wait(); QList users = database.users(); QCOMPARE(users.count(), 2); QCOMPARE(users[0]->userId(), QLatin1String("a")); QCOMPARE(users[0]->updatedTime(), time1); QCOMPARE(users[0]->userName(), QLatin1String("c")); QCOMPARE(users[0]->count(), 0); QCOMPARE(users[1]->userId(), QLatin1String("d")); QCOMPARE(users[1]->updatedTime(), time2); QCOMPARE(users[1]->userName(), QLatin1String("f")); QCOMPARE(users[1]->count(), 0); } void albums() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString user1 = QLatin1String("user1"); const QString user2 = QLatin1String("user2"); const QString album1 = QLatin1String("album1"); const QString album2 = QLatin1String("album2"); const QString album3 = QLatin1String("album3"); DropboxImagesDatabase database; database.addUser(user1, time1, QLatin1String("joe")); database.addUser(user2, time2, QLatin1String("dave")); database.addAlbum(album1, user1, time1, time2, QLatin1String("holidays"), 3, "111"); database.addAlbum(album2, user1, time2, time1, QLatin1String("work"), 2, "222"); database.addAlbum(album3, user2, time1, time2, QLatin1String("holidays"), 4, "333"); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); bool ok = false; QStringList albumIds = database.allAlbumIds(&ok); QCOMPARE(ok, true); QCOMPARE(albumIds.contains(album1), true); QCOMPARE(albumIds.contains(album2), true); QCOMPARE(albumIds.contains(album3), true); DropboxAlbum::ConstPtr album; album = database.album(album1); QCOMPARE(album->albumId(), album1); QCOMPARE(album->userId(), user1); QCOMPARE(album->createdTime(), time1); QCOMPARE(album->updatedTime(), time2); QCOMPARE(album->albumName(), QLatin1String("holidays")); QCOMPARE(album->imageCount(), 3); album = database.album(album2); QCOMPARE(album->albumId(), album2); QCOMPARE(album->userId(), user1); QCOMPARE(album->createdTime(), time2); QCOMPARE(album->updatedTime(), time1); QCOMPARE(album->albumName(), QLatin1String("work")); QCOMPARE(album->imageCount(), 2); album = database.album(album3); QCOMPARE(album->albumId(), album3); QCOMPARE(album->userId(), user2); QCOMPARE(album->createdTime(), time1); QCOMPARE(album->updatedTime(), time2); QCOMPARE(album->albumName(), QLatin1String("holidays")); QCOMPARE(album->imageCount(), 4); QList albums; database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 3); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 2); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.removeAlbum(album2); database.commit(); database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 2); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.removeUser(user2); database.commit(); database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 0); } void images() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString user1 = QLatin1String("user1"); const QString user2 = QLatin1String("user2"); const QString album1 = QLatin1String("album1"); const QString album2 = QLatin1String("album2"); const QString album3 = QLatin1String("album3"); const QString image1 = QLatin1String("image1"); const QString image2 = QLatin1String("image2"); const QString image3 = QLatin1String("image3"); const QString image4 = QLatin1String("image4"); const QString image5 = QLatin1String("image5"); const QString image6 = QLatin1String("image6"); const QString image7 = QLatin1String("image7"); const QString image8 = QLatin1String("image8"); const QString image9 = QLatin1String("image9"); DropboxImagesDatabase database; database.addUser(user1, time1, QLatin1String("joe")); database.addUser(user2, time2, QLatin1String("dave")); database.syncAccount(1, user1); database.syncAccount(2, user2); database.addAlbum(album1, user1, time1, time2, QLatin1String("holidays"), 3, "111"); database.addAlbum(album2, user1, time2, time1, QLatin1String("work"), 2, "222"); database.addAlbum(album3, user2, time1, time2, QLatin1String("holidays"), 4, "333"); database.addImage( image1, album1, user1, time1, time2, QLatin1String("1"), 640, 480, QLatin1String("file:///t1.jpg"), QLatin1String("file:///1.jpg"), QLatin1String("token")); database.addImage( image2, album1, user1, time1, time1, QLatin1String("2"), 480, 640, QLatin1String("file:///t2.jpg"), QLatin1String("file:///2.jpg"), QLatin1String("token")); database.addImage( image3, album1, user1, time2, time1, QLatin1String("3"), 640, 480, QLatin1String("file:///t3.jpg"), QLatin1String("file:///3.jpg"), QLatin1String("token")); database.addImage( image4, album2, user1, time1, time1, QLatin1String("4"), 640, 480, QLatin1String("file:///t4.jpg"), QLatin1String("file:///4.jpg"), QLatin1String("token")); database.addImage( image5, album2, user1, time1, time2, QLatin1String("5"), 480, 640, QLatin1String("file:///t5.jpg"), QLatin1String("file:///5.jpg"), QLatin1String("token")); database.addImage( image6, album3, user2, time1, time2, QLatin1String("6"), 640, 480, QLatin1String("file:///t6.jpg"), QLatin1String("file:///6.jpg"), QLatin1String("token")); database.addImage( image7, album3, user2, time1, time2, QLatin1String("7"), 640, 480, QLatin1String("file:///t7.jpg"), QLatin1String("file:///7.jpg"), QLatin1String("token")); database.addImage( image8, album3, user2, time2, time2, QLatin1String("8"), 640, 480, QLatin1String("file:///t8.jpg"), QLatin1String("file:///8.jpg"), QLatin1String("token")); database.addImage( image9, album3, user2, time1, time2, QLatin1String("9"), 640, 480, QLatin1String("file:///t9.jpg"), QLatin1String("file:///9.jpg"), QLatin1String("token")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); bool ok = false; QStringList imageIds; imageIds = database.allImageIds(&ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image1), true); QCOMPARE(imageIds.contains(image2), true); QCOMPARE(imageIds.contains(image3), true); QCOMPARE(imageIds.contains(image4), true); QCOMPARE(imageIds.contains(image5), true); QCOMPARE(imageIds.contains(image6), true); QCOMPARE(imageIds.contains(image7), true); QCOMPARE(imageIds.contains(image8), true); QCOMPARE(imageIds.contains(image9), true); imageIds = database.imageIds(album1, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image1), true); QCOMPARE(imageIds.contains(image2), true); QCOMPARE(imageIds.contains(image3), true); imageIds = database.imageIds(album2, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image4), true); QCOMPARE(imageIds.contains(image5), true); imageIds = database.imageIds(album3, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image6), true); QCOMPARE(imageIds.contains(image7), true); QCOMPARE(imageIds.contains(image8), true); QCOMPARE(imageIds.contains(image9), true); DropboxImage::ConstPtr image; image = database.image(image1); QCOMPARE(image->imageId(), image1); QCOMPARE(image->albumId(), album1); QCOMPARE(image->userId(), user1); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->updatedTime(), time2); QCOMPARE(image->imageName(), QLatin1String("1")); QCOMPARE(image->width(), 640); QCOMPARE(image->height(), 480); QCOMPARE(image->thumbnailUrl(), QLatin1String("file:///t1.jpg")); QCOMPARE(image->imageUrl(), QLatin1String("file:///1.jpg")); QCOMPARE(image->imageFile(), QString()); QCOMPARE(image->thumbnailFile(), QString()); QCOMPARE(image->accessToken(), QLatin1String("token")); database.updateImageThumbnail(image1, QLatin1String("/t1.jpg")); database.updateImageFile(image1, QLatin1String("/1.jpg")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); image = database.image(image1); QCOMPARE(image->thumbnailFile(), QLatin1String("/t1.jpg")); QCOMPARE(image->imageFile(), QLatin1String("/1.jpg")); QList images; database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 9); database.queryUserImages(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 5); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 4); database.queryAlbumImages(album1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 2); database.queryAlbumImages(album3); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 4); database.removeImage(image7); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 8); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album3); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.removeAlbum(album2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 6); database.queryUserImages(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); ok = false; QMap accounts = database.accounts(&ok); QCOMPARE(ok, true); QCOMPARE(accounts.count(), 2); QCOMPARE(accounts.value(1), user1); QCOMPARE(accounts.value(2), user2); database.removeUser(user2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); database.purgeAccount(1); database.purgeAccount(2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(DropboxImageTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_dropboximage/tst_dropboximage.pro000066400000000000000000000020571462333522700252620ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_dropboximage QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ INCLUDEPATH += ../../src/qml/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/dropboximagesdatabase.h \ ../../src/lib/abstractimagedownloader.h \ ../../src/lib/abstractimagedownloader_p.h \ ../../src/qml/abstractsocialcachemodel.h \ ../../src/qml/abstractsocialcachemodel_p.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/dropboximagesdatabase.cpp \ ../../src/lib/abstractimagedownloader.cpp \ ../../src/qml/abstractsocialcachemodel.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_facebookcontact/000077500000000000000000000000001462333522700216275ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_facebookcontact/main.cpp000066400000000000000000000145321462333522700232640ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "facebookcontactsdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class FacebookContactsTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void contacts() { FacebookContactsDatabase database; const QString friend1 = QLatin1String("friend1"); const QString friend2 = QLatin1String("friend2"); const QString friend3 = QLatin1String("friend3"); const QString friend4 = QLatin1String("friend4"); const QString friend5 = QLatin1String("friend5"); const QString pictureUrl1 = QLatin1String("http://example.com/friend1.jpg"); const QString pictureUrl2 = QLatin1String("http://example.com/friend1.jpg"); const QString coverUrl1 = QLatin1String("http://example.com/cover1.jpg"); const QString coverUrl2 = QLatin1String("http://example.com/cover2.jpg"); const QString pictureFile1 = QLatin1String("/temp/friend1.jpg"); const QString coverFile1 = QLatin1String("/temp/cover1.jpg"); database.addSyncedContact(friend1, 1, pictureUrl1, coverUrl1); database.addSyncedContact(friend2, 1, pictureUrl2, coverUrl2); database.addSyncedContact(friend3, 1, pictureUrl1, coverUrl1); database.addSyncedContact(friend4, 1, pictureUrl1, coverUrl1); database.addSyncedContact(friend1, 2, pictureUrl1, coverUrl1); database.addSyncedContact(friend4, 2, pictureUrl1, coverUrl1); database.addSyncedContact(friend5, 2, pictureUrl1, coverUrl1); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); FacebookContact::ConstPtr contact; contact = database.contact(friend1, 1); QCOMPARE(contact->fbFriendId(), friend1); QCOMPARE(contact->accountId(), 1); QCOMPARE(contact->pictureUrl(), pictureUrl1); QCOMPARE(contact->coverUrl(), coverUrl1); QCOMPARE(contact->pictureFile(), QString()); QCOMPARE(contact->coverFile(), QString()); contact = database.contact(friend1, 2); QCOMPARE(contact->fbFriendId(), friend1); QCOMPARE(contact->accountId(), 2); QCOMPARE(contact->pictureUrl(), pictureUrl1); QCOMPARE(contact->coverUrl(), coverUrl1); QCOMPARE(contact->pictureFile(), QString()); QCOMPARE(contact->coverFile(), QString()); database.updatePictureFile(friend1, pictureFile1); database.updateCoverFile(friend1, coverFile1); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); contact = database.contact(friend1, 1); QCOMPARE(contact->pictureFile(), pictureFile1); QCOMPARE(contact->coverFile(), coverFile1); contact = database.contact(friend1, 2); QCOMPARE(contact->pictureFile(), pictureFile1); QCOMPARE(contact->coverFile(), coverFile1); QList contacts; contacts = database.contacts(1); QCOMPARE(contacts.count(), 4); contacts = database.contacts(2); QCOMPARE(contacts.count(), 3); database.removeContacts(QStringList() << friend1 << friend2); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); contacts = database.contacts(1); QCOMPARE(contacts.count(), 2); contacts = database.contacts(2); QCOMPARE(contacts.count(), 2); database.removeContacts(1); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); contacts = database.contacts(1); QCOMPARE(contacts.count(), 0); contacts = database.contacts(2); QCOMPARE(contacts.count(), 2); database.removeContacts(2); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); contacts = database.contacts(2); QCOMPARE(contacts.count(), 0); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(FacebookContactsTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_facebookcontact/tst_facebookcontact.pro000066400000000000000000000013131462333522700263660ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_facebookcontact QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/facebookcontactsdatabase.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/facebookcontactsdatabase.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_facebookimage/000077500000000000000000000000001462333522700212565ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_facebookimage/main.cpp000066400000000000000000000500251462333522700227100ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "facebookimagesdatabase.h" #include "socialsyncinterface.h" #include "facebook/facebookimagecachemodel.h" #include #include #include #include #include class FacebookImageTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void testAddUser() { FacebookImagesDatabase database; QDateTime time1 (QDate(2013, 1, 2), QTime(12, 34, 56)); database.addUser("a", time1, "c"); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QString dataType = SocialSyncInterface::dataType(SocialSyncInterface::Images); QString dbFile = QLatin1String("facebook.db"); // DB_NAME in facebookimagesdatabase.cpp QSqlDatabase checkDb = QSqlDatabase::addDatabase("QSQLITE"); checkDb.setDatabaseName(QString("%1/%2/%3").arg(PRIVILEGED_DATA_DIR, dataType, dbFile)); QVERIFY(checkDb.open()); // Check if the user has been written QSqlQuery query (checkDb); query.prepare("SELECT fbUserId, updatedTime, userName "\ "FROM users"); QVERIFY(query.exec()); QVERIFY(query.next()); QCOMPARE(query.value(0).toString(), QLatin1String("a")); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QCOMPARE(query.value(1).toUInt(), time1.toSecsSinceEpoch()); #else QCOMPARE(query.value(1).toUInt(), time1.toTime_t()); #endif QCOMPARE(query.value(2).toString(), QLatin1String("c")); QVERIFY(!query.next()); // Check if we can retrieve the user FacebookUser::ConstPtr user = database.user(QLatin1String("a")); QCOMPARE(user->fbUserId(), QLatin1String("a")); QCOMPARE(user->updatedTime(), time1); QCOMPARE(user->userName(), QLatin1String("c")); QCOMPARE(user->count(), -1); QDateTime time2 (QDate(2012, 3, 4), QTime(10, 11, 12)); // Let's write another user database.addUser("d", time2, "f"); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); // Check if we can retrieve everybody database.queryUsers(); database.wait(); QList users = database.users(); QCOMPARE(users.count(), 2); QCOMPARE(users[0]->fbUserId(), QLatin1String("a")); QCOMPARE(users[0]->updatedTime(), time1); QCOMPARE(users[0]->userName(), QLatin1String("c")); QCOMPARE(users[0]->count(), 0); QCOMPARE(users[1]->fbUserId(), QLatin1String("d")); QCOMPARE(users[1]->updatedTime(), time2); QCOMPARE(users[1]->userName(), QLatin1String("f")); QCOMPARE(users[1]->count(), 0); FacebookImageCacheModel model; model.setType(FacebookImageCacheModel::Users); model.refresh(); QCOMPARE(model.count(), 0); QTRY_COMPARE(model.count(), 3); database.removeUser(QLatin1String("a")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); model.refresh(); QTRY_COMPARE(model.count(), 1); database.removeUser(QLatin1String("d")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); model.refresh(); QTRY_COMPARE(model.count(), 0); } void albums() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString user1 = QLatin1String("user1"); const QString user2 = QLatin1String("user2"); const QString album1 = QLatin1String("album1"); const QString album2 = QLatin1String("album2"); const QString album3 = QLatin1String("album3"); FacebookImagesDatabase database; database.addUser(user1, time1, QLatin1String("joe")); database.addUser(user2, time2, QLatin1String("dave")); database.addAlbum(album1, user1, time1, time2, QLatin1String("holidays"), 3); database.addAlbum(album2, user1, time2, time1, QLatin1String("work"), 2); database.addAlbum(album3, user2, time1, time2, QLatin1String("holidays"), 4); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); bool ok = false; QStringList albumIds = database.allAlbumIds(&ok); QCOMPARE(ok, true); QCOMPARE(albumIds.contains(album1), true); QCOMPARE(albumIds.contains(album2), true); QCOMPARE(albumIds.contains(album3), true); FacebookAlbum::ConstPtr album; album = database.album(album1); QCOMPARE(album->fbAlbumId(), album1); QCOMPARE(album->fbUserId(), user1); QCOMPARE(album->createdTime(), time1); QCOMPARE(album->updatedTime(), time2); QCOMPARE(album->albumName(), QLatin1String("holidays")); QCOMPARE(album->imageCount(), 3); album = database.album(album2); QCOMPARE(album->fbAlbumId(), album2); QCOMPARE(album->fbUserId(), user1); QCOMPARE(album->createdTime(), time2); QCOMPARE(album->updatedTime(), time1); QCOMPARE(album->albumName(), QLatin1String("work")); QCOMPARE(album->imageCount(), 2); album = database.album(album3); QCOMPARE(album->fbAlbumId(), album3); QCOMPARE(album->fbUserId(), user2); QCOMPARE(album->createdTime(), time1); QCOMPARE(album->updatedTime(), time2); QCOMPARE(album->albumName(), QLatin1String("holidays")); QCOMPARE(album->imageCount(), 4); QList albums; database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 3); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 2); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.removeAlbum(album2); database.commit(); database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 2); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.removeUser(user2); database.commit(); database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 0); } void images() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString user1 = QLatin1String("user1"); const QString user2 = QLatin1String("user2"); const QString album1 = QLatin1String("album1"); const QString album2 = QLatin1String("album2"); const QString album3 = QLatin1String("album3"); const QString image1 = QLatin1String("image1"); const QString image2 = QLatin1String("image2"); const QString image3 = QLatin1String("image3"); const QString image4 = QLatin1String("image4"); const QString image5 = QLatin1String("image5"); const QString image6 = QLatin1String("image6"); const QString image7 = QLatin1String("image7"); const QString image8 = QLatin1String("image8"); const QString image9 = QLatin1String("image9"); FacebookImagesDatabase database; database.addUser(user1, time1, QLatin1String("joe")); database.addUser(user2, time2, QLatin1String("dave")); database.syncAccount(1, user1); database.syncAccount(2, user2); database.addAlbum(album1, user1, time1, time2, QLatin1String("holidays"), 3); database.addAlbum(album2, user1, time2, time1, QLatin1String("work"), 2); database.addAlbum(album3, user2, time1, time2, QLatin1String("holidays"), 4); database.addImage( image1, album1, user1, time1, time2, QLatin1String("1"), 640, 480, QLatin1String("file:///t1.jpg"), QLatin1String("file:///1.jpg")); database.addImage( image2, album1, user1, time1, time1, QLatin1String("2"), 480, 640, QLatin1String("file:///t2.jpg"), QLatin1String("file:///2.jpg")); database.addImage( image3, album1, user1, time2, time1, QLatin1String("3"), 640, 480, QLatin1String("file:///t3.jpg"), QLatin1String("file:///3.jpg")); database.addImage( image4, album2, user1, time1, time1, QLatin1String("4"), 640, 480, QLatin1String("file:///t4.jpg"), QLatin1String("file:///4.jpg")); database.addImage( image5, album2, user1, time1, time2, QLatin1String("5"), 480, 640, QLatin1String("file:///t5.jpg"), QLatin1String("file:///5.jpg")); database.addImage( image6, album3, user2, time1, time2, QLatin1String("6"), 640, 480, QLatin1String("file:///t6.jpg"), QLatin1String("file:///6.jpg")); database.addImage( image7, album3, user2, time1, time2, QLatin1String("7"), 640, 480, QLatin1String("file:///t7.jpg"), QLatin1String("file:///7.jpg")); database.addImage( image8, album3, user2, time2, time2, QLatin1String("8"), 640, 480, QLatin1String("file:///t8.jpg"), QLatin1String("file:///8.jpg")); database.addImage( image9, album3, user2, time1, time2, QLatin1String("9"), 640, 480, QLatin1String("file:///t9.jpg"), QLatin1String("file:///9.jpg")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); bool ok = false; QStringList imageIds; imageIds = database.allImageIds(&ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image1), true); QCOMPARE(imageIds.contains(image2), true); QCOMPARE(imageIds.contains(image3), true); QCOMPARE(imageIds.contains(image4), true); QCOMPARE(imageIds.contains(image5), true); QCOMPARE(imageIds.contains(image6), true); QCOMPARE(imageIds.contains(image7), true); QCOMPARE(imageIds.contains(image8), true); QCOMPARE(imageIds.contains(image9), true); imageIds = database.imageIds(album1, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image1), true); QCOMPARE(imageIds.contains(image2), true); QCOMPARE(imageIds.contains(image3), true); imageIds = database.imageIds(album2, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image4), true); QCOMPARE(imageIds.contains(image5), true); imageIds = database.imageIds(album3, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image6), true); QCOMPARE(imageIds.contains(image7), true); QCOMPARE(imageIds.contains(image8), true); QCOMPARE(imageIds.contains(image9), true); FacebookImage::ConstPtr image; image = database.image(image1); QCOMPARE(image->fbImageId(), image1); QCOMPARE(image->fbAlbumId(), album1); QCOMPARE(image->fbUserId(), user1); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->updatedTime(), time2); QCOMPARE(image->imageName(), QLatin1String("1")); QCOMPARE(image->width(), 640); QCOMPARE(image->height(), 480); QCOMPARE(image->thumbnailUrl(), QLatin1String("file:///t1.jpg")); QCOMPARE(image->imageUrl(), QLatin1String("file:///1.jpg")); QCOMPARE(image->imageFile(), QString()); QCOMPARE(image->thumbnailFile(), QString()); database.updateImageThumbnail(image1, QLatin1String("/t1.jpg")); database.updateImageFile(image1, QLatin1String("/1.jpg")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); image = database.image(image1); QCOMPARE(image->thumbnailFile(), QLatin1String("/t1.jpg")); QCOMPARE(image->imageFile(), QLatin1String("/1.jpg")); QList images; database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 9); database.queryUserImages(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 5); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 4); database.queryAlbumImages(album1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 2); database.queryAlbumImages(album3); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 4); database.removeImage(image7); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 8); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album3); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.removeAlbum(album2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 6); database.queryUserImages(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); ok = false; QMap accounts = database.accounts(&ok); QCOMPARE(ok, true); QCOMPARE(accounts.count(), 2); QCOMPARE(accounts.value(1), user1); QCOMPARE(accounts.value(2), user2); database.removeUser(user2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); database.purgeAccount(1); database.purgeAccount(2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); } // TODO: more tests void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(FacebookImageTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_facebookimage/tst_facebookimage.pro000066400000000000000000000025631462333522700254540ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_facebookimage QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ INCLUDEPATH += ../../src/qml/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/facebookimagesdatabase.h \ ../../src/lib/abstractimagedownloader.h \ ../../src/lib/abstractimagedownloader_p.h \ ../../src/qml/abstractsocialcachemodel.h \ ../../src/qml/abstractsocialcachemodel_p.h \ ../../src/qml/facebook/facebookimagecachemodel.h \ ../../src/qml/facebook/facebookimagedownloader_p.h \ ../../src/qml/facebook/facebookimagedownloader.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/facebookimagesdatabase.cpp \ ../../src/lib/abstractimagedownloader.cpp \ ../../src/qml/abstractsocialcachemodel.cpp \ ../../src/qml/facebook/facebookimagecachemodel.cpp \ ../../src/qml/facebook/facebookimagedownloader.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_facebooknotification/000077500000000000000000000000001462333522700226625ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_facebooknotification/main.cpp000066400000000000000000000275301462333522700243210ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "facebooknotificationsdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class FacebookNotificationsTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void notifications() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); QDateTime time3(QDate(2014, 1, 2), QTime(12, 34, 56)); const QString id1 = QLatin1String("id1"); const QString id2 = QLatin1String("id2"); const QString id3 = QLatin1String("id3"); const QString from1 = QLatin1String("from1"); const QString from2 = QLatin1String("from2"); const QString from3 = QLatin1String("from3"); const QString to1 = QLatin1String("to1"); const QString to2 = QLatin1String("to2"); const QString to3 = QLatin1String("to3"); const QString title1 = QLatin1String("title1"); const QString title2 = QLatin1String("title2"); const QString title3 = QLatin1String("title3"); const QString link1 = QLatin1String("link1"); const QString link2 = QLatin1String("link2"); const QString link3 = QLatin1String("link3"); const QString app1 = QLatin1String("app1"); const QString app2 = QLatin1String("app2"); const QString app3 = QLatin1String("app3"); const QString object1 = QLatin1String("object1"); const QString object2 = QLatin1String("object2"); const QString object3 = QLatin1String("object3"); const bool unread1 = true; const bool unread2 = false; const bool unread3 = false; int account1 = 1; int account2 = 2; QString clientId = QLatin1String("clientId"); FacebookNotificationsDatabase database; database.addFacebookNotification(id1, from1, to1, time1, time1, title1, link1, app1, object1, unread1, account1, clientId); database.addFacebookNotification(id2, from2, to2, time2, time2, title2, link2, app2, object2, unread2, account1, clientId); database.addFacebookNotification(id3, from3, to3, time3, time3, title3, link3, app3, object3, unread3, account2, clientId); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QList notifications; notifications = database.notifications(); QCOMPARE(notifications.count(), 3); FacebookNotification::ConstPtr notification; do { notification = notifications.takeFirst(); } while (notification->facebookId() != id1 && notifications.count() > 0); QCOMPARE(notification->facebookId(), id1); QCOMPARE(notification->from(), from1); QCOMPARE(notification->to(), to1); QCOMPARE(notification->createdTime(), time1); QCOMPARE(notification->updatedTime(), time1); QCOMPARE(notification->title(), title1); QCOMPARE(notification->link(), link1); QCOMPARE(notification->application(), app1); QCOMPARE(notification->object(), object1); QCOMPARE(notification->unread(), unread1); QCOMPARE(notification->accountId(), account1); QCOMPARE(notification->clientId(), clientId); QStringList toBeDeleted; toBeDeleted.append(id1); toBeDeleted.append(id2); database.removeNotifications(toBeDeleted); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 1); notification = notifications.takeFirst(); QCOMPARE(notification->facebookId(), id3); QCOMPARE(notification->from(), from3); QCOMPARE(notification->to(), to3); QCOMPARE(notification->createdTime(), time3); QCOMPARE(notification->updatedTime(), time3); QCOMPARE(notification->title(), title3); QCOMPARE(notification->link(), link3); QCOMPARE(notification->application(), app3); QCOMPARE(notification->object(), object3); QCOMPARE(notification->unread(), unread3); QCOMPARE(notification->accountId(), account2); QCOMPARE(notification->clientId(), clientId); database.removeNotification(id3); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 0); database.addFacebookNotification(id1, from1, to1, time1, time1, title1, link1, app1, object1, unread1, account1, clientId); database.addFacebookNotification(id2, from2, to2, time2, time2, title2, link2, app2, object2, unread2, account1, clientId); database.addFacebookNotification(id3, from3, to3, time3, time3, title3, link3, app3, object3, unread3, account2, clientId); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 3); database.removeNotifications(account1); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 1); database.removeNotifications(account2); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 0); database.addFacebookNotification(id1, from1, to1, time1, time1, title1, link1, app1, object1, unread1, account1, clientId); database.removeNotification(id1); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 0); database.addFacebookNotification(id1, from1, to1, time1, time1, title1, link1, app1, object1, unread1, account1, clientId); database.addFacebookNotification(id2, from2, to2, time2, time2, title2, link2, app2, object2, unread2, account1, clientId); database.addFacebookNotification(id3, from3, to3, time3, time3, title3, link3, app3, object3, unread3, account2, clientId); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 3); database.removeAllNotifications(); database.wait(); notifications = database.notifications(); QCOMPARE(notifications.count(), 0); } void purgeOldNotifications() { QDateTime time1 = QDateTime::currentDateTime(); QDateTime time2; #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) time2.setSecsSinceEpoch(time1.toSecsSinceEpoch() - 10 * 24 * 60 * 60); #else time2.setTime_t(time1.toTime_t() - 10 * 24 * 60 * 60); #endif const QString id1 = QLatin1String("id1"); const QString id2 = QLatin1String("id2"); const QString from1 = QLatin1String("from1"); const QString from2 = QLatin1String("from2"); const QString to1 = QLatin1String("to1"); const QString to2 = QLatin1String("to2"); const QString title1 = QLatin1String("title1"); const QString title2 = QLatin1String("title2"); const QString link1 = QLatin1String("link1"); const QString link2 = QLatin1String("link2"); const QString app1 = QLatin1String("app1"); const QString app2 = QLatin1String("app2"); const QString object1 = QLatin1String("object1"); const QString object2 = QLatin1String("object2"); const bool unread1 = true; const bool unread2 = false; int account1 = 1; QString clientId = QLatin1String("clientId"); FacebookNotificationsDatabase database; database.addFacebookNotification(id1, from1, to1, time1, time1, title1, link1, app1, object1, unread1, account1, clientId); database.addFacebookNotification(id2, from2, to2, time2, time2, title2, link2, app2, object2, unread2, account1, clientId); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QList notifications; notifications = database.notifications(); QCOMPARE(notifications.count(), 2); database.purgeOldNotifications(9); database.sync(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); notifications = database.notifications(); QCOMPARE(notifications.count(), 1); FacebookNotification::ConstPtr notification = notifications.takeFirst(); QCOMPARE(notification->facebookId(), id1); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(FacebookNotificationsTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_facebooknotification/tst_facebooknotification.pro000066400000000000000000000015301462333522700304550ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_facebooknotification QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/abstractsocialpostcachedatabase.h \ ../../src/lib/facebooknotificationsdatabase.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/abstractsocialpostcachedatabase.cpp \ ../../src/lib/facebooknotificationsdatabase.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_facebookpost/000077500000000000000000000000001462333522700211615ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_facebookpost/main.cpp000066400000000000000000000162151462333522700226160ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "facebookpostsdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class FacebookPostsTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void posts() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString id1 = QLatin1String("id1"); const QString id2 = QLatin1String("id2"); const QString id3 = QLatin1String("id3"); const QString name1 = QLatin1String("name1"); const QString name2 = QLatin1String("name2"); const QString name3 = QLatin1String("name3"); const QString body1 = QLatin1String("body1"); const QString body2 = QLatin1String("body2"); const QString body3 = QLatin1String("body3"); const QString icon1 = QLatin1String("/icon.jpg"); const QString image1 = QLatin1String("http://example.com/image1.jpg"); const QString image2 = QLatin1String("http://example.com/image2.jpg"); const QString image3 = QLatin1String("http://example.com/image3.jpg"); const QString attachment1 = QLatin1String("attachment1"); const QString caption1 = QLatin1String("caption1"); const QString description1 = QLatin1String("description1"); const QString url1 = QLatin1String("http://example.com/attachment.png"); const QString client1 = QLatin1String("client1"); FacebookPostsDatabase database; database.addFacebookPost( id1, name1, body1, time1, icon1, QList >() << qMakePair(image1, SocialPostImage::Photo) << qMakePair(image2, SocialPostImage::Video), attachment1, caption1, description1, url1, true, true, client1, 1); database.addFacebookPost( id1, name1, body1, time1, icon1, QList >() << qMakePair(image1, SocialPostImage::Photo) << qMakePair(image2, SocialPostImage::Video), attachment1, caption1, description1, url1, true, true, client1, 2); database.addFacebookPost( id2, name2, body2, time2, icon1, QList >() << qMakePair(image3, SocialPostImage::Photo), QString(), QString(), QString(), QString(), false, false, client1, 1); database.addFacebookPost( id3, name3, body3, time2, icon1, QList >(), QString(), QString(), QString(), QString(), true, true, client1, 2); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); database.refresh(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); QList posts; posts = database.posts(); QCOMPARE(posts.count(), 3); SocialPost::ConstPtr post; do { post = posts.takeFirst(); } while (post->identifier() != id1 && posts.count() > 0); QCOMPARE(post->identifier(), id1); QCOMPARE(post->name(), name1); QCOMPARE(post->body(), body1); QCOMPARE(post->timestamp(), time1); QCOMPARE(post->icon(), icon1); QCOMPARE(post->images().count(), 2); QCOMPARE(FacebookPostsDatabase::attachmentName(post), attachment1); QCOMPARE(FacebookPostsDatabase::attachmentCaption(post), caption1); QCOMPARE(FacebookPostsDatabase::attachmentDescription(post), description1); QCOMPARE(FacebookPostsDatabase::attachmentUrl(post), url1); QCOMPARE(FacebookPostsDatabase::allowLike(post), true); QCOMPARE(FacebookPostsDatabase::allowComment(post), true); QCOMPARE(FacebookPostsDatabase::clientId(post), client1); QCOMPARE(post->accounts().count(), 2); database.removePosts(2); database.commit(); database.refresh(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); posts = database.posts(); QCOMPARE(posts.count(), 2); database.removePosts(1); database.commit(); database.refresh(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); posts = database.posts(); QCOMPARE(posts.count(), 0); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(FacebookPostsTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_facebookpost/tst_facebookpost.pro000066400000000000000000000015001462333522700252500ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_facebookpost QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/abstractsocialpostcachedatabase.h \ ../../src/lib/facebookpostsdatabase.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/abstractsocialpostcachedatabase.cpp \ ../../src/lib/facebookpostsdatabase.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_onedriveimage/000077500000000000000000000000001462333522700213205ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_onedriveimage/main.cpp000066400000000000000000000506161462333522700227600ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "onedriveimagesdatabase.h" #include "onedriveimagecachemodel.h" #include "socialsyncinterface.h" #include #include #include #include #include class OneDriveImageTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void testAddUser() { OneDriveImagesDatabase database; QDateTime time1 (QDate(2013, 1, 2), QTime(12, 34, 56)); database.addUser("a", time1, "c", 1); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QString dataType = SocialSyncInterface::dataType(SocialSyncInterface::Images); QString dbFile = QLatin1String("onedrive.db"); // DB_NAME in onedriveimagesdatabase.cpp QSqlDatabase checkDb = QSqlDatabase::addDatabase("QSQLITE"); checkDb.setDatabaseName(QString("%1/%2/%3").arg(PRIVILEGED_DATA_DIR, dataType, dbFile)); QVERIFY(checkDb.open()); // Check if the user has been written QSqlQuery query (checkDb); query.prepare("SELECT userId, updatedTime, userName, accountId "\ "FROM users"); QVERIFY(query.exec()); QVERIFY(query.next()); QCOMPARE(query.value(0).toString(), QLatin1String("a")); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QCOMPARE(query.value(1).toUInt(), time1.toSecsSinceEpoch()); #else QCOMPARE(query.value(1).toUInt(), time1.toTime_t()); #endif QCOMPARE(query.value(2).toString(), QLatin1String("c")); QCOMPARE(query.value(3).toInt(), 1); QVERIFY(!query.next()); // Check if we can retrieve the user OneDriveUser::ConstPtr user = database.user(QLatin1String("a")); QCOMPARE(user->userId(), QLatin1String("a")); QCOMPARE(user->updatedTime(), time1); QCOMPARE(user->userName(), QLatin1String("c")); QCOMPARE(user->accountId(), 1); QCOMPARE(user->count(), -1); QDateTime time2 (QDate(2012, 3, 4), QTime(10, 11, 12)); // Let's write another user database.addUser("d", time2, "f", 5); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); // Check if we can retrieve everybody database.queryUsers(); database.wait(); QList users = database.users(); QCOMPARE(users.count(), 2); QCOMPARE(users[0]->userId(), QLatin1String("a")); QCOMPARE(users[0]->updatedTime(), time1); QCOMPARE(users[0]->userName(), QLatin1String("c")); QCOMPARE(users[0]->accountId(), 1); QCOMPARE(users[0]->count(), 0); QCOMPARE(users[1]->userId(), QLatin1String("d")); QCOMPARE(users[1]->updatedTime(), time2); QCOMPARE(users[1]->userName(), QLatin1String("f")); QCOMPARE(users[1]->accountId(), 5); QCOMPARE(users[1]->count(), 0); OneDriveImageCacheModel model; model.setType(OneDriveImageCacheModel::Users); model.refresh(); QCOMPARE(model.count(), 0); QTRY_COMPARE(model.count(), 3); database.removeUser(QLatin1String("a")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); model.refresh(); QTRY_COMPARE(model.count(), 1); database.removeUser(QLatin1String("d")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); model.refresh(); QTRY_COMPARE(model.count(), 0); } void albums() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString user1 = QLatin1String("user1"); const QString user2 = QLatin1String("user2"); const QString album1 = QLatin1String("album1"); const QString album2 = QLatin1String("album2"); const QString album3 = QLatin1String("album3"); OneDriveImagesDatabase database; database.addUser(user1, time1, QLatin1String("joe"), 2); database.addUser(user2, time2, QLatin1String("dave"), 3); database.addAlbum(album1, user1, time1, time2, QLatin1String("holidays"), 3); database.addAlbum(album2, user1, time2, time1, QLatin1String("work"), 2); database.addAlbum(album3, user2, time1, time2, QLatin1String("holidays"), 4); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); bool ok = false; QStringList albumIds = database.allAlbumIds(&ok); QCOMPARE(ok, true); QCOMPARE(albumIds.contains(album1), true); QCOMPARE(albumIds.contains(album2), true); QCOMPARE(albumIds.contains(album3), true); OneDriveAlbum::ConstPtr album; album = database.album(album1); QCOMPARE(album->albumId(), album1); QCOMPARE(album->userId(), user1); QCOMPARE(album->createdTime(), time1); QCOMPARE(album->updatedTime(), time2); QCOMPARE(album->albumName(), QLatin1String("holidays")); QCOMPARE(album->imageCount(), 3); album = database.album(album2); QCOMPARE(album->albumId(), album2); QCOMPARE(album->userId(), user1); QCOMPARE(album->createdTime(), time2); QCOMPARE(album->updatedTime(), time1); QCOMPARE(album->albumName(), QLatin1String("work")); QCOMPARE(album->imageCount(), 2); album = database.album(album3); QCOMPARE(album->albumId(), album3); QCOMPARE(album->userId(), user2); QCOMPARE(album->createdTime(), time1); QCOMPARE(album->updatedTime(), time2); QCOMPARE(album->albumName(), QLatin1String("holidays")); QCOMPARE(album->imageCount(), 4); QList albums; database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 3); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 2); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.removeAlbum(album2); database.commit(); database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 2); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.removeUser(user2); database.commit(); database.queryAlbums(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 1); database.queryAlbums(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); albums = database.albums(); QCOMPARE(albums.count(), 0); } void images() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString user1 = QLatin1String("user1"); const QString user2 = QLatin1String("user2"); const QString album1 = QLatin1String("album1"); const QString album2 = QLatin1String("album2"); const QString album3 = QLatin1String("album3"); const QString image1 = QLatin1String("image1"); const QString image2 = QLatin1String("image2"); const QString image3 = QLatin1String("image3"); const QString image4 = QLatin1String("image4"); const QString image5 = QLatin1String("image5"); const QString image6 = QLatin1String("image6"); const QString image7 = QLatin1String("image7"); const QString image8 = QLatin1String("image8"); const QString image9 = QLatin1String("image9"); OneDriveImagesDatabase database; database.addUser(user1, time1, QLatin1String("joe"), 3); database.addUser(user2, time2, QLatin1String("dave"), 4); database.syncAccount(1, user1); database.syncAccount(2, user2); database.addAlbum(album1, user1, time1, time2, QLatin1String("holidays"), 3); database.addAlbum(album2, user1, time2, time1, QLatin1String("work"), 2); database.addAlbum(album3, user2, time1, time2, QLatin1String("holidays"), 4); database.addImage( image1, album1, user1, time1, time2, QLatin1String("1"), 640, 480, QLatin1String("file:///t1.jpg"), QLatin1String("file:///1.jpg"), "desc1", 101); database.addImage( image2, album1, user1, time1, time1, QLatin1String("2"), 480, 640, QLatin1String("file:///t2.jpg"), QLatin1String("file:///2.jpg"), "desc2", 102); database.addImage( image3, album1, user1, time2, time1, QLatin1String("3"), 640, 480, QLatin1String("file:///t3.jpg"), QLatin1String("file:///3.jpg"), "desc2", 103); database.addImage( image4, album2, user1, time1, time1, QLatin1String("4"), 640, 480, QLatin1String("file:///t4.jpg"), QLatin1String("file:///4.jpg"), "desc4", 104); database.addImage( image5, album2, user1, time1, time2, QLatin1String("5"), 480, 640, QLatin1String("file:///t5.jpg"), QLatin1String("file:///5.jpg"), "desc5", 105); database.addImage( image6, album3, user2, time1, time2, QLatin1String("6"), 640, 480, QLatin1String("file:///t6.jpg"), QLatin1String("file:///6.jpg"), "desc6", 106); database.addImage( image7, album3, user2, time1, time2, QLatin1String("7"), 640, 480, QLatin1String("file:///t7.jpg"), QLatin1String("file:///7.jpg"), "desc7", 107); database.addImage( image8, album3, user2, time2, time2, QLatin1String("8"), 640, 480, QLatin1String("file:///t8.jpg"), QLatin1String("file:///8.jpg"), "desc8", 108); database.addImage( image9, album3, user2, time1, time2, QLatin1String("9"), 640, 480, QLatin1String("file:///t9.jpg"), QLatin1String("file:///9.jpg"), "desc9", 109); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); bool ok = false; QStringList imageIds; imageIds = database.allImageIds(&ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image1), true); QCOMPARE(imageIds.contains(image2), true); QCOMPARE(imageIds.contains(image3), true); QCOMPARE(imageIds.contains(image4), true); QCOMPARE(imageIds.contains(image5), true); QCOMPARE(imageIds.contains(image6), true); QCOMPARE(imageIds.contains(image7), true); QCOMPARE(imageIds.contains(image8), true); QCOMPARE(imageIds.contains(image9), true); imageIds = database.imageIds(album1, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image1), true); QCOMPARE(imageIds.contains(image2), true); QCOMPARE(imageIds.contains(image3), true); imageIds = database.imageIds(album2, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image4), true); QCOMPARE(imageIds.contains(image5), true); imageIds = database.imageIds(album3, &ok); QCOMPARE(ok, true); QCOMPARE(imageIds.contains(image6), true); QCOMPARE(imageIds.contains(image7), true); QCOMPARE(imageIds.contains(image8), true); QCOMPARE(imageIds.contains(image9), true); OneDriveImage::ConstPtr image; image = database.image(image1); QCOMPARE(image->imageId(), image1); QCOMPARE(image->albumId(), album1); QCOMPARE(image->userId(), user1); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->updatedTime(), time2); QCOMPARE(image->imageName(), QLatin1String("1")); QCOMPARE(image->width(), 640); QCOMPARE(image->height(), 480); QCOMPARE(image->thumbnailUrl(), QLatin1String("file:///t1.jpg")); QCOMPARE(image->imageUrl(), QLatin1String("file:///1.jpg")); QCOMPARE(image->imageFile(), QString()); QCOMPARE(image->thumbnailFile(), QString()); QCOMPARE(image->description(), QLatin1String("desc1")); QCOMPARE(image->accountId(), 101); database.updateImageThumbnail(image1, QLatin1String("/t1.jpg")); database.updateImageFile(image1, QLatin1String("/1.jpg")); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); image = database.image(image1); QCOMPARE(image->thumbnailFile(), QLatin1String("/t1.jpg")); QCOMPARE(image->imageFile(), QLatin1String("/1.jpg")); QList images; database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 9); database.queryUserImages(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 5); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 4); database.queryAlbumImages(album1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 2); database.queryAlbumImages(album3); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 4); database.removeImage(image7); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 8); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album3); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.removeAlbum(album2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 6); database.queryUserImages(user1); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryAlbumImages(album2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); ok = false; QMap accounts = database.accounts(&ok); QCOMPARE(ok, true); QCOMPARE(accounts.count(), 2); QCOMPARE(accounts.value(1), user1); QCOMPARE(accounts.value(2), user2); database.removeUser(user2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 3); database.queryUserImages(user2); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); database.purgeAccount(1); database.purgeAccount(2); database.commit(); database.queryUserImages(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); images = database.images(); QCOMPARE(images.count(), 0); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(OneDriveImageTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_onedriveimage/tst_onedriveimage.pro000066400000000000000000000025311462333522700255530ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_onedriveimage QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ INCLUDEPATH += ../../src/qml/ INCLUDEPATH += ../../src/qml/onedrive HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/onedriveimagesdatabase.h \ ../../src/qml/onedrive/onedriveimagecachemodel.h \ ../../src/qml/onedrive/onedriveimagedownloader.h \ ../../src/lib/abstractimagedownloader.h \ ../../src/lib/abstractimagedownloader_p.h \ ../../src/qml/abstractsocialcachemodel.h \ ../../src/qml/abstractsocialcachemodel_p.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/onedriveimagesdatabase.cpp \ ../../src/qml/onedrive/onedriveimagecachemodel.cpp \ ../../src/qml/onedrive/onedriveimagedownloader.cpp \ ../../src/lib/abstractimagedownloader.cpp \ ../../src/qml/abstractsocialcachemodel.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_socialimage/000077500000000000000000000000001462333522700207575ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_socialimage/main.cpp000066400000000000000000000264671462333522700224260ustar00rootroot00000000000000/* * Copyright (C) 2015 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "socialimagesdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class SocialImageTest: public QObject { Q_OBJECT private: SocialImage::ConstPtr imageFromList(QList &images, const QString &imageUrl) { foreach (SocialImage::ConstPtr image, images) { if (image->imageUrl() == imageUrl) { return image; } } return SocialImage::ConstPtr(); } private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void images() { const int account1 = 1; const int account2 = 2; QDateTime time1 (QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2 (QDate(2015, 1, 2), QTime(12, 43, 56)); // Database stores time_t values so convert to time_t first to get even seconds, // otherwise the tests below will fail to mismatching milliseconds. #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) int exp1 = QDateTime::currentDateTime().addDays(5).toSecsSinceEpoch(); int exp2 = QDateTime::currentDateTime().addDays(10).toSecsSinceEpoch(); QDateTime expires1 (QDateTime::fromSecsSinceEpoch(exp1)); QDateTime expires2 (QDateTime::fromSecsSinceEpoch(exp2)); #else int exp1 = QDateTime::currentDateTime().addDays(5).toTime_t(); int exp2 = QDateTime::currentDateTime().addDays(10).toTime_t(); QDateTime expires1 (QDateTime::fromTime_t(exp1)); QDateTime expires2 (QDateTime::fromTime_t(exp2)); #endif QString id1("id1"); QString id2("id2"); SocialImagesDatabase database; // test insert images database.addImage( account1, QLatin1String("file:///t1.jpg"), QLatin1String("file:///1.jpg"), time1, expires1, id1); database.addImage( account1, QLatin1String("file:///t2.jpg"), QLatin1String("file:///2.jpg"), time2, expires2, id2); database.addImage( account1, QLatin1String("file:///t3.jpg"), QLatin1String("file:///3.jpg"), time2, expires2, id2); database.addImage( account2, QLatin1String("file:///t4.jpg"), QLatin1String("file:///4.jpg"), time2, expires2, id2); // check that the images are availbale from insert queue // already before commit SocialImage::ConstPtr image = database.image(QLatin1String("file:///t1.jpg")); QVERIFY(image != 0); QVERIFY(image != 0); QCOMPARE(image->accountId(), account1); QCOMPARE(image->imageUrl(), QString("file:///t1.jpg")); QCOMPARE(image->imageFile(), QString("file:///1.jpg")); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->expires(), expires1); QCOMPARE(image->imageId(), id1); image = database.imageById(id1); QVERIFY(image != 0); QVERIFY(image != 0); QCOMPARE(image->accountId(), account1); QCOMPARE(image->imageUrl(), QString("file:///t1.jpg")); QCOMPARE(image->imageFile(), QString("file:///1.jpg")); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->expires(), expires1); QCOMPARE(image->imageId(), id1); // then commit and test the rest database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); database.queryImages(account1); database.wait(); QList images = database.images(); QCOMPARE(images.count(), 3); image = imageFromList(images, "file:///t1.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account1); QCOMPARE(image->imageUrl(), QString("file:///t1.jpg")); QCOMPARE(image->imageFile(), QString("file:///1.jpg")); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->expires(), expires1); QCOMPARE(image->imageId(), id1); image = imageFromList(images, "file:///t2.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account1); QCOMPARE(image->imageUrl(), QString("file:///t2.jpg")); QCOMPARE(image->imageFile(), QString("file:///2.jpg")); QCOMPARE(image->createdTime(), time2); QCOMPARE(image->expires(), expires2); QCOMPARE(image->imageId(), id2); image = imageFromList(images, "file:///t3.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account1); QCOMPARE(image->imageUrl(), QString("file:///t3.jpg")); QCOMPARE(image->imageFile(), QString("file:///3.jpg")); QCOMPARE(image->createdTime(), time2); QCOMPARE(image->expires(), expires2); QCOMPARE(image->imageId(), id2); database.queryImages(account2); database.wait(); images = database.images(); QCOMPARE(images.count(), 1); image = imageFromList(images, "file:///t4.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account2); QCOMPARE(image->imageUrl(), QString("file:///t4.jpg")); QCOMPARE(image->imageFile(), QString("file:///4.jpg")); QCOMPARE(image->createdTime(), time2); QCOMPARE(image->expires(), expires2); QCOMPARE(image->imageId(), id2); // test olderThan database.queryImages(account1, QDateTime(QDate(2014, 1, 2), QTime(12, 43, 56))); database.wait(); images = database.images(); QCOMPARE(images.count(), 1); image = imageFromList(images, "file:///t1.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account1); QCOMPARE(image->imageUrl(), QString("file:///t1.jpg")); QCOMPARE(image->imageFile(), QString("file:///1.jpg")); QCOMPARE(image->createdTime(), time1); QCOMPARE(image->expires(), expires1); QCOMPARE(image->imageId(), id1); // test remove image database.removeImage("file:///t1.jpg"); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); database.queryImages(account1); database.wait(); images = database.images(); QCOMPARE(images.count(), 2); image = imageFromList(images, "file:///t1.jpg"); QVERIFY(image == 0); image = imageFromList(images, "file:///t2.jpg"); QVERIFY(image != 0); QList removeImages; removeImages.append(image); database.removeImages(removeImages); database.commit(); database.wait(); database.queryImages(account1); database.wait(); images = database.images(); QCOMPARE(images.count(), 1); image = imageFromList(images, "file:///t2.jpg"); QVERIFY(image == 0); // test purge accounts database.purgeAccount(1); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); database.queryImages(account1); database.wait(); images = database.images(); QCOMPARE(images.count(), 0); database.queryImages(account2); database.wait(); images = database.images(); QCOMPARE(images.count(), 1); image = imageFromList(images, "file:///t4.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account2); QCOMPARE(image->imageUrl(), QString("file:///t4.jpg")); QCOMPARE(image->imageFile(), QString("file:///4.jpg")); QCOMPARE(image->createdTime(), time2); QCOMPARE(image->expires(), expires2); QCOMPARE(image->imageId(), id2); // test expires #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) int currentExp = QDateTime::currentDateTime().addSecs(-1).toSecsSinceEpoch(); #else int currentExp = QDateTime::currentDateTime().addSecs(-1).toTime_t(); #endif database.addImage( account2, QLatin1String("file:///t5.jpg"), QLatin1String("file:///5.jpg"), #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) time2, QDateTime::fromSecsSinceEpoch(currentExp)); #else time2, QDateTime::fromTime_t(currentExp)); #endif database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); database.queryExpired(account2); database.wait(); images = database.images(); QCOMPARE(images.count(), 1); image = imageFromList(images, "file:///t5.jpg"); QVERIFY(image != 0); QCOMPARE(image->accountId(), account2); QCOMPARE(image->imageUrl(), QString("file:///t5.jpg")); QCOMPARE(image->imageFile(), QString("file:///5.jpg")); QCOMPARE(image->createdTime(), time2); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) QCOMPARE(image->expires(), QDateTime::fromSecsSinceEpoch(currentExp)); #else QCOMPARE(image->expires(), QDateTime::fromTime_t(currentExp)); #endif } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(SocialImageTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_socialimage/tst_socialimage.pro000066400000000000000000000020541462333522700246510ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_socialimage QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ INCLUDEPATH += ../../src/qml/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/socialimagesdatabase.h \ ../../src/lib/abstractimagedownloader.h \ ../../src/lib/abstractimagedownloader_p.h \ ../../src/qml/abstractsocialcachemodel.h \ ../../src/qml/abstractsocialcachemodel_p.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/socialimagesdatabase.cpp \ ../../src/lib/abstractimagedownloader.cpp \ ../../src/qml/abstractsocialcachemodel.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_socialnetworksync/000077500000000000000000000000001462333522700222635ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_socialnetworksync/main.cpp000066400000000000000000000101611462333522700237120ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "socialnetworksyncdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class SocialNetworkSyncTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void posts() { const QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); const QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString service1 = QLatin1String("service1"); const QString service2 = QLatin1String("service2"); const QString data1 = QLatin1String("data1"); const QString data2 = QLatin1String("data2"); SocialNetworkSyncDatabase database; database.addSyncTimestamp(service1, data1, 1, time1); database.addSyncTimestamp(service1, data1, 1, time2); database.addSyncTimestamp(service1, data1, 2, time1); database.addSyncTimestamp(service1, data2, 1, time2); database.addSyncTimestamp(service2, data1, 1, time1); database.addSyncTimestamp(service2, data2, 2, time2); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QList accounts; accounts = database.syncedAccounts(service1, data1); QCOMPARE(accounts.contains(1), true); QCOMPARE(accounts.contains(2), true); accounts = database.syncedAccounts(service1, data2); QCOMPARE(accounts.contains(1), true); QCOMPARE(accounts.contains(2), false); accounts = database.syncedAccounts(service2, data2); QCOMPARE(accounts.contains(1), false); QCOMPARE(accounts.contains(2), true); QCOMPARE(database.lastSyncTimestamp(service1, data1, 1), time2); QCOMPARE(database.lastSyncTimestamp(service1, data1, 2), time1); QCOMPARE(database.lastSyncTimestamp(service1, data2, 1), time2); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(SocialNetworkSyncTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_socialnetworksync/tst_socialnetworksync.pro000066400000000000000000000015151462333522700274620ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_socialnetworksync QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/abstractsocialpostcachedatabase.h \ ../../src/lib/socialnetworksyncdatabase.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/abstractsocialpostcachedatabase.cpp \ ../../src/lib/socialnetworksyncdatabase.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target libsocialcache-0.2.1/tests/tst_twitterpost/000077500000000000000000000000001462333522700211125ustar00rootroot00000000000000libsocialcache-0.2.1/tests/tst_twitterpost/main.cpp000066400000000000000000000151671462333522700225540ustar00rootroot00000000000000/* * Copyright (C) 2013 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Nemo Mobile nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." */ #include #include "twitterpostsdatabase.h" #include "socialsyncinterface.h" #include #include #include #include #include class TwitterPostsTest: public QObject { Q_OBJECT private: private slots: // Perform some cleanups // we basically remove the whole ~/.local/share/system/privileged. While it is // damaging on a device, on a desktop system it should not be much // damaging. void initTestCase() { QStandardPaths::setTestModeEnabled(true); QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } void posts() { QDateTime time1(QDate(2013, 1, 2), QTime(12, 34, 56)); QDateTime time2(QDate(2012, 3, 4), QTime(10, 11, 12)); const QString id1 = QLatin1String("id1"); const QString id2 = QLatin1String("id2"); const QString id3 = QLatin1String("id3"); const QString name1 = QLatin1String("name1"); const QString name2 = QLatin1String("name2"); const QString name3 = QLatin1String("name3"); const QString body1 = QLatin1String("body1"); const QString body2 = QLatin1String("body2"); const QString body3 = QLatin1String("body3"); const QString icon1 = QLatin1String("/icon.jpg"); const QString image1 = QLatin1String("http://example.com/image1.jpg"); const QString image2 = QLatin1String("http://example.com/image2.jpg"); const QString image3 = QLatin1String("http://example.com/image3.jpg"); const QString screen1 = QLatin1String("screen1"); const QString retweeter1 = QLatin1String("retweeter1"); const QString key1 = QLatin1String("key1"); const QString secret1 = QLatin1String("secret1"); TwitterPostsDatabase database; database.addTwitterPost( id1, name1, body1, time1, icon1, QList >() << qMakePair(image1, SocialPostImage::Photo) << qMakePair(image2, SocialPostImage::Video), screen1, retweeter1, key1, secret1, 1); database.addTwitterPost( id1, name1, body1, time1, icon1, QList >() << qMakePair(image1, SocialPostImage::Photo) << qMakePair(image2, SocialPostImage::Video), screen1, retweeter1, key1, secret1, 2); database.addTwitterPost( id2, name2, body2, time2, icon1, QList >() << qMakePair(image3, SocialPostImage::Photo), screen1, retweeter1, key1, secret1, 1); database.addTwitterPost( id3, name3, body3, time2, icon1, QList >(), screen1, retweeter1, key1, secret1, 2); database.commit(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); database.refresh(); database.wait(); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); QList posts; posts = database.posts(); QCOMPARE(posts.count(), 3); SocialPost::ConstPtr post; do { post = posts.takeFirst(); } while (post->identifier() != id1 && posts.count() > 0); QCOMPARE(post->identifier(), id1); QCOMPARE(post->name(), name1); QCOMPARE(post->body(), body1); QCOMPARE(post->timestamp(), time1); QCOMPARE(post->icon(), icon1); QCOMPARE(post->images().count(), 2); QCOMPARE(TwitterPostsDatabase::screenName(post), screen1); QCOMPARE(TwitterPostsDatabase::retweeter(post), retweeter1); QCOMPARE(TwitterPostsDatabase::consumerKey(post), key1); QCOMPARE(TwitterPostsDatabase::consumerSecret(post), secret1); QCOMPARE(post->accounts().count(), 2); database.removePosts(2); database.commit(); database.refresh(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); posts = database.posts(); QCOMPARE(posts.count(), 2); database.removePosts(1); database.commit(); database.refresh(); database.wait(); QCOMPARE(database.writeStatus(), AbstractSocialCacheDatabase::Finished); QCOMPARE(database.readStatus(), AbstractSocialCacheDatabase::Finished); posts = database.posts(); QCOMPARE(posts.count(), 0); } void cleanupTestCase() { // Do the same cleanups QDir dir (PRIVILEGED_DATA_DIR); dir.removeRecursively(); } }; QTEST_MAIN(TwitterPostsTest) #include "main.moc" libsocialcache-0.2.1/tests/tst_twitterpost/tst_twitterpost.pro000066400000000000000000000014751462333522700251450ustar00rootroot00000000000000include(../../common.pri) TEMPLATE = app TARGET = tst_twitterpost QT += network sql testlib DEFINES += NO_KEY_PROVIDER INCLUDEPATH += ../../src/lib/ HEADERS += ../../src/lib/semaphore_p.h \ ../../src/lib/socialsyncinterface.h \ ../../src/lib/abstractsocialcachedatabase.h \ ../../src/lib/abstractsocialcachedatabase_p.h \ ../../src/lib/abstractsocialpostcachedatabase.h \ ../../src/lib/twitterpostsdatabase.h SOURCES += ../../src/lib/semaphore_p.cpp \ ../../src/lib/socialsyncinterface.cpp \ ../../src/lib/abstractsocialcachedatabase.cpp \ ../../src/lib/abstractsocialpostcachedatabase.cpp \ ../../src/lib/twitterpostsdatabase.cpp \ main.cpp target.path = /opt/tests/libsocialcache INSTALLS += target