| package org.wordpress.android.datasets; |
| |
| import android.content.ContentValues; |
| import android.database.Cursor; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.database.sqlite.SQLiteException; |
| import android.database.sqlite.SQLiteStatement; |
| |
| import org.wordpress.android.WordPress; |
| import org.wordpress.android.WordPressDB; |
| import org.wordpress.android.models.Comment; |
| import org.wordpress.android.models.CommentList; |
| import org.wordpress.android.models.CommentStatus; |
| import org.wordpress.android.util.AppLog; |
| import org.wordpress.android.util.SqlUtils; |
| import org.wordpress.android.util.StringUtils; |
| |
| /** |
| * replaces the comments table used in versions prior to 2.6.1, which didn't use a primary key |
| * and missed a few important fields |
| */ |
| public class CommentTable { |
| public static final String COMMENTS_TABLE = "comments"; |
| |
| public static void createTables(SQLiteDatabase db) { |
| db.execSQL("CREATE TABLE IF NOT EXISTS " + COMMENTS_TABLE + " (" |
| + " blog_id INTEGER DEFAULT 0," |
| + " post_id INTEGER DEFAULT 0," |
| + " comment_id INTEGER DEFAULT 0," |
| + " comment TEXT," |
| + " published TEXT," |
| + " status TEXT," |
| + " author_name TEXT," |
| + " author_url TEXT," |
| + " author_email TEXT," |
| + " post_title TEXT," |
| + " profile_image_url TEXT," |
| + " PRIMARY KEY (blog_id, post_id, comment_id)" |
| + " );"); |
| } |
| |
| private static void dropTables(SQLiteDatabase db) { |
| db.execSQL("DROP TABLE IF EXISTS " + COMMENTS_TABLE); |
| } |
| |
| public static void reset(SQLiteDatabase db) { |
| AppLog.i(AppLog.T.COMMENTS, "resetting comment table"); |
| dropTables(db); |
| createTables(db); |
| } |
| |
| private static SQLiteDatabase getReadableDb() { |
| return WordPress.wpDB.getDatabase(); |
| } |
| private static SQLiteDatabase getWritableDb() { |
| return WordPress.wpDB.getDatabase(); |
| } |
| |
| /* |
| * purge comments attached to blogs that no longer exist, and remove older comments |
| * TODO: call after hiding or deleting blogs |
| */ |
| private static final int MAX_COMMENTS = 1000; |
| public static int purge(SQLiteDatabase db) { |
| int numDeleted = 0; |
| |
| // get rid of comments on blogs that don't exist or are hidden |
| String sql = " blog_id NOT IN (SELECT DISTINCT id FROM " + WordPressDB.BLOGS_TABLE |
| + " WHERE isHidden = 0)"; |
| numDeleted += db.delete(COMMENTS_TABLE, sql, null); |
| |
| // get rid of older comments if we've reached the max |
| int numExisting = (int)SqlUtils.getRowCount(db, COMMENTS_TABLE); |
| if (numExisting > MAX_COMMENTS) { |
| int numToPurge = numExisting - MAX_COMMENTS; |
| sql = " comment_id IN (SELECT DISTINCT comment_id FROM " + COMMENTS_TABLE |
| + " ORDER BY published LIMIT " + Integer.toString(numToPurge) + ")"; |
| numDeleted += db.delete(COMMENTS_TABLE, sql, null); |
| } |
| |
| return numDeleted; |
| } |
| |
| /** |
| * add a single comment - will update existing comment with same IDs |
| * @param localBlogId - unique id in account table for the blog the comment is from |
| * @param comment - comment object to store |
| */ |
| public static void addComment(int localBlogId, final Comment comment) { |
| if (comment == null) |
| return; |
| |
| ContentValues values = new ContentValues(); |
| values.put("blog_id", localBlogId); |
| values.put("post_id", comment.postID); |
| values.put("comment_id", comment.commentID); |
| values.put("author_name", comment.getAuthorName()); |
| values.put("author_url", comment.getAuthorUrl()); |
| values.put("comment", SqlUtils.maxSQLiteText(comment.getCommentText())); |
| values.put("status", comment.getStatus()); |
| values.put("author_email", comment.getAuthorEmail()); |
| values.put("post_title", comment.getPostTitle()); |
| values.put("published", comment.getPublished()); |
| values.put("profile_image_url", comment.getProfileImageUrl()); |
| |
| getWritableDb().insertWithOnConflict(COMMENTS_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE); |
| } |
| |
| /** |
| * retrieve a single comment |
| * @param localBlogId - unique id in account table for the blog the comment is from |
| * @param commentId - commentId of the actual comment |
| * @return Comment if found, null otherwise |
| */ |
| public static Comment getComment(int localBlogId, long commentId) { |
| String[] args = {Integer.toString(localBlogId), Long.toString(commentId)}; |
| Cursor c = getReadableDb().rawQuery("SELECT * FROM " + COMMENTS_TABLE + " WHERE blog_id=? AND comment_id=?", args); |
| try { |
| if (!c.moveToFirst()) { |
| return null; |
| } |
| return getCommentFromCursor(c); |
| } finally { |
| SqlUtils.closeCursor(c); |
| } |
| } |
| |
| /** |
| * get all comments for a blog |
| * @param localBlogId - unique id in account table for this blog |
| * @return list of comments for this blog |
| */ |
| public static CommentList getCommentsForBlog(int localBlogId) { |
| CommentList comments = new CommentList(); |
| |
| String[] args = {Integer.toString(localBlogId)}; |
| Cursor c = getReadableDb().rawQuery( |
| "SELECT * FROM " + COMMENTS_TABLE + " WHERE blog_id=? ORDER BY published DESC", args); |
| |
| try { |
| while (c.moveToNext()) { |
| Comment comment = getCommentFromCursor(c); |
| comments.add(comment); |
| } |
| |
| return comments; |
| } finally { |
| SqlUtils.closeCursor(c); |
| } |
| } |
| |
| /** |
| * get comments for a blog that have a specific status |
| * @param localBlogId - unique id in account table for this blog |
| * @param filter - status to filter comments by |
| * @return list of comments for this blog |
| */ |
| public static CommentList getCommentsForBlogWithFilter(int localBlogId, CommentStatus filter) { |
| CommentList comments = new CommentList(); |
| Cursor c; |
| |
| //aggregating 'all' to include approved and unapproved comments |
| if (CommentStatus.UNKNOWN.equals(filter)){ |
| //we need to get the filter values for both XMLrpc and REST api as in the case of a migration where existing |
| // data is present on a device, we still need to be able to filter both values |
| String[] args = {Integer.toString(localBlogId), |
| CommentStatus.toString(CommentStatus.APPROVED), |
| CommentStatus.toString(CommentStatus.UNAPPROVED), |
| CommentStatus.toRESTString(CommentStatus.APPROVED), |
| CommentStatus.toRESTString(CommentStatus.UNAPPROVED)}; |
| c = getReadableDb().rawQuery( |
| "SELECT * FROM " + COMMENTS_TABLE + " WHERE blog_id=? AND (status=? OR status=? OR status=? OR status=?) ORDER BY published DESC", args); |
| |
| } else { |
| //we need to get the filter values for both XMLrpc and REST api as in the case of a migration where existing |
| // data is present on a device, we still need to be able to filter both values |
| String[] args = {Integer.toString(localBlogId), CommentStatus.toString(filter), CommentStatus.toRESTString(filter)}; |
| c = getReadableDb().rawQuery( |
| "SELECT * FROM " + COMMENTS_TABLE + " WHERE blog_id=? AND (status=? OR status=?) ORDER BY published DESC", args); |
| } |
| |
| try { |
| while (c.moveToNext()) { |
| Comment comment = getCommentFromCursor(c); |
| comments.add(comment); |
| } |
| |
| return comments; |
| } finally { |
| SqlUtils.closeCursor(c); |
| } |
| } |
| |
| /** |
| * delete all comments for a blog |
| * @param localBlogId - unique id in account table for this blog |
| * @return number of comments deleted |
| */ |
| public static int deleteCommentsForBlog(int localBlogId) { |
| return getWritableDb().delete(COMMENTS_TABLE, "blog_id=?", new String[]{Integer.toString(localBlogId)}); |
| } |
| |
| /** |
| * delete comments for a blog that match a specific status |
| * @param localBlogId - unique id in account table for this blog |
| * @param filter - status to use to filter the query |
| * @return number of comments deleted |
| */ |
| public static int deleteCommentsForBlogWithFilter(int localBlogId, CommentStatus filter) { |
| if (CommentStatus.UNKNOWN.equals(filter)){ |
| //we need to get the filter values for both XMLrpc and REST api as in the case of a migration where existing |
| // data is present on a device, we still need to be able to filter both values |
| String[] args = {Integer.toString(localBlogId), |
| CommentStatus.toString(CommentStatus.APPROVED), |
| CommentStatus.toString(CommentStatus.UNAPPROVED), |
| CommentStatus.toRESTString(CommentStatus.APPROVED), |
| CommentStatus.toRESTString(CommentStatus.UNAPPROVED)}; |
| return getWritableDb().delete(COMMENTS_TABLE, "blog_id=? AND (status=? OR status=? OR status=? OR status=?)", args); |
| |
| } else { |
| //we need to get the filter values for both XMLrpc and REST api as in the case of a migration where existing |
| // data is present on a device, we still need to be able to filter both values |
| String[] args = {Integer.toString(localBlogId), CommentStatus.toString(filter), CommentStatus.toRESTString(filter)}; |
| return getWritableDb().delete(COMMENTS_TABLE, "blog_id=? AND (status=? OR status=?)", args); |
| } |
| } |
| |
| /** |
| * saves comments for passed blog to local db, overwriting existing ones if necessary |
| * @param localBlogId - unique id in account table for this blog |
| * @param comments - list of comments to save |
| * @return true if saved, false on failure |
| */ |
| public static boolean saveComments(int localBlogId, final CommentList comments) { |
| if (comments == null || comments.size() == 0) |
| return false; |
| |
| final String sql = " INSERT OR REPLACE INTO " + COMMENTS_TABLE + "(" |
| + " blog_id," // 1 |
| + " post_id," // 2 |
| + " comment_id," // 3 |
| + " comment," // 4 |
| + " published," // 5 |
| + " status," // 6 |
| + " author_name," // 7 |
| + " author_url," // 8 |
| + " author_email," // 9 |
| + " post_title," // 10 |
| + " profile_image_url" // 11 |
| + " ) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11)"; |
| |
| SQLiteDatabase db = getWritableDb(); |
| SQLiteStatement stmt = db.compileStatement(sql); |
| db.beginTransaction(); |
| try { |
| try { |
| for (Comment comment: comments) { |
| stmt.bindLong ( 1, localBlogId); |
| stmt.bindLong ( 2, comment.postID); |
| stmt.bindLong ( 3, comment.commentID); |
| stmt.bindString( 4, SqlUtils.maxSQLiteText(comment.getCommentText())); |
| stmt.bindString( 5, comment.getPublished()); |
| stmt.bindString( 6, comment.getStatus()); |
| stmt.bindString( 7, comment.getAuthorName()); |
| stmt.bindString( 8, comment.getAuthorUrl()); |
| stmt.bindString( 9, comment.getAuthorEmail()); |
| stmt.bindString(10, comment.getPostTitle()); |
| stmt.bindString(11, comment.getProfileImageUrl()); |
| stmt.execute(); |
| } |
| |
| db.setTransactionSuccessful(); |
| return true; |
| } catch (SQLiteException e) { |
| AppLog.e(AppLog.T.COMMENTS, e); |
| return false; |
| } |
| } finally { |
| db.endTransaction(); |
| SqlUtils.closeStatement(stmt); |
| } |
| } |
| |
| /** |
| * updates the passed comment |
| * @param localBlogId - unique id in account table for this blog |
| * @param comment - comment to update |
| */ |
| public static void updateComment(int localBlogId, final Comment comment) { |
| // this will replace the existing comment |
| addComment(localBlogId, comment); |
| } |
| |
| /** |
| * updates the status for the passed comment |
| * @param localBlogId - unique id in account table for this blog |
| * @param commentId - id of comment (returned by api) |
| * @param newStatus - status to change to |
| */ |
| public static void updateCommentStatus(int localBlogId, long commentId, String newStatus) { |
| ContentValues values = new ContentValues(); |
| values.put("status", newStatus); |
| String[] args = {Integer.toString(localBlogId), |
| Long.toString(commentId)}; |
| getWritableDb().update(COMMENTS_TABLE, values, "blog_id=? AND comment_id=?", args); |
| } |
| |
| /** |
| * updates the status for the passed list of comments |
| * @param localBlogId - unique id in account table for this blog |
| * @param comments - list of comments to update |
| * @param newStatus - status to change to |
| */ |
| public static void updateCommentsStatus(int localBlogId, final CommentList comments, String newStatus) { |
| if (comments == null || comments.size() == 0) |
| return; |
| getWritableDb().beginTransaction(); |
| try { |
| for (Comment comment: comments) { |
| updateCommentStatus(localBlogId, comment.commentID, newStatus); |
| } |
| getWritableDb().setTransactionSuccessful(); |
| } finally { |
| getWritableDb().endTransaction(); |
| } |
| } |
| |
| /** |
| * updates the post title for the passed comment |
| * @param localBlogId - unique id in account table for this blog |
| * @param postTitle - title to update to |
| * @return true if title updated |
| */ |
| public static boolean updateCommentPostTitle(int localBlogId, long commentId, String postTitle) { |
| ContentValues values = new ContentValues(); |
| values.put("post_title", StringUtils.notNullStr(postTitle)); |
| String[] args = {Integer.toString(localBlogId), |
| Long.toString(commentId)}; |
| int count = getWritableDb().update(COMMENTS_TABLE, values, "blog_id=? AND comment_id=?", args); |
| return (count > 0); |
| } |
| |
| /** |
| * delete a single comment |
| * @param localBlogId - unique id in account table for this blog |
| * @param commentId - commentId of the actual comment |
| * @return true if comment deleted, false otherwise |
| */ |
| public static boolean deleteComment(int localBlogId, long commentId) { |
| String[] args = {Integer.toString(localBlogId), |
| Long.toString(commentId)}; |
| int count = getWritableDb().delete(COMMENTS_TABLE, "blog_id=? AND comment_id=?", args); |
| return (count > 0); |
| } |
| |
| /** |
| * delete a list of comments |
| * @param localBlogId - unique id in account table for this blog |
| * @param comments - list of comments to delete |
| */ |
| public static void deleteComments(int localBlogId, final CommentList comments) { |
| if (comments == null || comments.size() == 0) |
| return; |
| getWritableDb().beginTransaction(); |
| try { |
| for (Comment comment: comments) { |
| deleteComment(localBlogId, comment.commentID); |
| } |
| getWritableDb().setTransactionSuccessful(); |
| } finally { |
| getWritableDb().endTransaction(); |
| } |
| } |
| |
| /** |
| * returns the number of unmoderated comments for a specific blog |
| * @param localBlogId - unique id in account table for this blog |
| */ |
| public static int getUnmoderatedCommentCount(int localBlogId) { |
| String sql = "SELECT COUNT(*) FROM " + COMMENTS_TABLE + " WHERE blog_id=? AND status=?"; |
| String[] args = {Integer.toString(localBlogId), "hold"}; |
| return SqlUtils.intForQuery(getReadableDb(), sql, args); |
| } |
| |
| private static Comment getCommentFromCursor(Cursor c) { |
| final String authorName = c.getString(c.getColumnIndex("author_name")); |
| final String content = c.getString(c.getColumnIndex("comment")); |
| final String published = c.getString(c.getColumnIndex("published")); |
| final String status = c.getString(c.getColumnIndex("status")); |
| final String authorUrl = c.getString(c.getColumnIndex("author_url")); |
| final String authorEmail = c.getString(c.getColumnIndex("author_email")); |
| final String postTitle = c.getString(c.getColumnIndex("post_title")); |
| final String profileImageUrl = c.getString(c.getColumnIndex("profile_image_url")); |
| |
| int postId = c.getInt(c.getColumnIndex("post_id")); |
| int commentId = c.getInt(c.getColumnIndex("comment_id")); |
| |
| return new Comment( |
| postId, |
| commentId, |
| authorName, |
| published, |
| content, |
| status, |
| postTitle, |
| authorUrl, |
| authorEmail, |
| profileImageUrl); |
| } |
| |
| |
| /** |
| * Delete big comments (Maximum 512 * 1024 = 524288) (fix #2855) |
| * @return number of deleted comments |
| */ |
| public static int deleteBigComments(SQLiteDatabase db) { |
| return db.delete(COMMENTS_TABLE, "LENGTH(comment) >= 524288", null); |
| } |
| } |