aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/wordpress/android/datasets/CommentTable.java
blob: 9545ec10f019cc958685bb01471d228abc1254c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
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);
    }
}