package org.wordpress.android.models; import android.content.ContentValues; import android.database.Cursor; import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Holds blog settings and provides methods to (de)serialize .com and self-hosted network calls. */ public class SiteSettingsModel { public static final int RELATED_POSTS_ENABLED_FLAG = 0x1; public static final int RELATED_POST_HEADER_FLAG = 0x2; public static final int RELATED_POST_IMAGE_FLAG = 0x4; // Settings table column names public static final String ID_COLUMN_NAME = "id"; public static final String ADDRESS_COLUMN_NAME = "address"; public static final String USERNAME_COLUMN_NAME = "username"; public static final String PASSWORD_COLUMN_NAME = "password"; public static final String TITLE_COLUMN_NAME = "title"; public static final String TAGLINE_COLUMN_NAME = "tagline"; public static final String LANGUAGE_COLUMN_NAME = "language"; public static final String PRIVACY_COLUMN_NAME = "privacy"; public static final String LOCATION_COLUMN_NAME = "location"; public static final String OPTIMIZED_IMAGE_COLUMN_NAME = "optimizedImage"; public static final String MAX_IMAGE_WIDTH_COLUMN_NAME = "maxImageWidth"; public static final String IMAGE_ENCODER_QUALITY_COLUMN_NAME = "imageEncoderQuality"; public static final String DEF_CATEGORY_COLUMN_NAME = "defaultCategory"; public static final String DEF_POST_FORMAT_COLUMN_NAME = "defaultPostFormat"; public static final String CATEGORIES_COLUMN_NAME = "categories"; public static final String POST_FORMATS_COLUMN_NAME = "postFormats"; public static final String CREDS_VERIFIED_COLUMN_NAME = "credsVerified"; public static final String RELATED_POSTS_COLUMN_NAME = "relatedPosts"; public static final String ALLOW_COMMENTS_COLUMN_NAME = "allowComments"; public static final String SEND_PINGBACKS_COLUMN_NAME = "sendPingbacks"; public static final String RECEIVE_PINGBACKS_COLUMN_NAME = "receivePingbacks"; public static final String SHOULD_CLOSE_AFTER_COLUMN_NAME = "shouldCloseAfter"; public static final String CLOSE_AFTER_COLUMN_NAME = "closeAfter"; public static final String SORT_BY_COLUMN_NAME = "sortBy"; public static final String SHOULD_THREAD_COLUMN_NAME = "shouldThread"; public static final String THREADING_COLUMN_NAME = "threading"; public static final String SHOULD_PAGE_COLUMN_NAME = "shouldPage"; public static final String PAGING_COLUMN_NAME = "paging"; public static final String MANUAL_APPROVAL_COLUMN_NAME = "manualApproval"; public static final String IDENTITY_REQUIRED_COLUMN_NAME = "identityRequired"; public static final String USER_ACCOUNT_REQUIRED_COLUMN_NAME = "userAccountRequired"; public static final String WHITELIST_COLUMN_NAME = "whitelist"; public static final String MODERATION_KEYS_COLUMN_NAME = "moderationKeys"; public static final String BLACKLIST_KEYS_COLUMN_NAME = "blacklistKeys"; public static final String SETTINGS_TABLE_NAME = "site_settings"; public static final String ADD_OPTIMIZED_IMAGE = "alter table " + SETTINGS_TABLE_NAME + " add " + OPTIMIZED_IMAGE_COLUMN_NAME + " BOOLEAN;"; public static final String ADD_IMAGE_RESIZE_WIDTH = "alter table " + SETTINGS_TABLE_NAME + " add " + MAX_IMAGE_WIDTH_COLUMN_NAME + " INTEGER;"; public static final String ADD_IMAGE_COMPRESSION_QUALITY = "alter table " + SETTINGS_TABLE_NAME + " add " + IMAGE_ENCODER_QUALITY_COLUMN_NAME + " INTEGER;"; public static final String CREATE_SETTINGS_TABLE_SQL = "CREATE TABLE IF NOT EXISTS " + SETTINGS_TABLE_NAME + " (" + ID_COLUMN_NAME + " INTEGER PRIMARY KEY, " + ADDRESS_COLUMN_NAME + " TEXT, " + USERNAME_COLUMN_NAME + " TEXT, " + PASSWORD_COLUMN_NAME + " TEXT, " + TITLE_COLUMN_NAME + " TEXT, " + TAGLINE_COLUMN_NAME + " TEXT, " + LANGUAGE_COLUMN_NAME + " INTEGER, " + PRIVACY_COLUMN_NAME + " INTEGER, " + LOCATION_COLUMN_NAME + " BOOLEAN, " + DEF_CATEGORY_COLUMN_NAME + " TEXT, " + DEF_POST_FORMAT_COLUMN_NAME + " TEXT, " + CATEGORIES_COLUMN_NAME + " TEXT, " + POST_FORMATS_COLUMN_NAME + " TEXT, " + CREDS_VERIFIED_COLUMN_NAME + " BOOLEAN, " + RELATED_POSTS_COLUMN_NAME + " INTEGER, " + ALLOW_COMMENTS_COLUMN_NAME + " BOOLEAN, " + SEND_PINGBACKS_COLUMN_NAME + " BOOLEAN, " + RECEIVE_PINGBACKS_COLUMN_NAME + " BOOLEAN, " + SHOULD_CLOSE_AFTER_COLUMN_NAME + " BOOLEAN, " + CLOSE_AFTER_COLUMN_NAME + " INTEGER, " + SORT_BY_COLUMN_NAME + " INTEGER, " + SHOULD_THREAD_COLUMN_NAME + " BOOLEAN, " + THREADING_COLUMN_NAME + " INTEGER, " + SHOULD_PAGE_COLUMN_NAME + " BOOLEAN, " + PAGING_COLUMN_NAME + " INTEGER, " + MANUAL_APPROVAL_COLUMN_NAME + " BOOLEAN, " + IDENTITY_REQUIRED_COLUMN_NAME + " BOOLEAN, " + USER_ACCOUNT_REQUIRED_COLUMN_NAME + " BOOLEAN, " + WHITELIST_COLUMN_NAME + " BOOLEAN, " + MODERATION_KEYS_COLUMN_NAME + " TEXT, " + BLACKLIST_KEYS_COLUMN_NAME + " TEXT" + ");"; public boolean isInLocalTable; public boolean hasVerifiedCredentials; public long localTableId; public String address; public String username; public String password; public String title; public String tagline; public String language; public int languageId; public int privacy; public boolean location; public boolean optimizedImage; public int maxImageWidth; public int imageQualitySetting; public int defaultCategory; public CategoryModel[] categories; public String defaultPostFormat; public Map<String, String> postFormats; public boolean showRelatedPosts; public boolean showRelatedPostHeader; public boolean showRelatedPostImages; public boolean allowComments; public boolean sendPingbacks; public boolean receivePingbacks; public boolean shouldCloseAfter; public int closeCommentAfter; public int sortCommentsBy; public boolean shouldThreadComments; public int threadingLevels; public boolean shouldPageComments; public int commentsPerPage; public boolean commentApprovalRequired; public boolean commentsRequireIdentity; public boolean commentsRequireUserAccount; public boolean commentAutoApprovalKnownUsers; public int maxLinks; public List<String> holdForModeration; public List<String> blacklist; @Override public boolean equals(Object other) { if (!(other instanceof SiteSettingsModel)) return false; SiteSettingsModel otherModel = (SiteSettingsModel) other; return localTableId == otherModel.localTableId && equals(address, otherModel.address) && equals(username, otherModel.username) && equals(password, otherModel.password) && equals(title, otherModel.title) && equals(tagline, otherModel.tagline) && equals(defaultPostFormat, otherModel.defaultPostFormat) && languageId == otherModel.languageId && privacy == otherModel.privacy && location == otherModel.location && optimizedImage == otherModel.optimizedImage && imageQualitySetting == otherModel.imageQualitySetting && maxImageWidth == otherModel.maxImageWidth && defaultCategory == otherModel.defaultCategory && showRelatedPosts == otherModel.showRelatedPosts && showRelatedPostHeader == otherModel.showRelatedPostHeader && showRelatedPostImages == otherModel.showRelatedPostImages && allowComments == otherModel.allowComments && sendPingbacks == otherModel.sendPingbacks && receivePingbacks == otherModel.receivePingbacks && closeCommentAfter == otherModel.closeCommentAfter && sortCommentsBy == otherModel.sortCommentsBy && threadingLevels == otherModel.threadingLevels && commentsPerPage == otherModel.commentsPerPage && commentApprovalRequired == otherModel.commentApprovalRequired && commentsRequireIdentity == otherModel.commentsRequireIdentity && commentsRequireUserAccount == otherModel.commentsRequireUserAccount && commentAutoApprovalKnownUsers == otherModel.commentAutoApprovalKnownUsers && maxLinks == otherModel.maxLinks && equals(defaultPostFormat, otherModel.defaultPostFormat) && holdForModeration != null && holdForModeration.equals(otherModel.holdForModeration) && blacklist != null && blacklist.equals(otherModel.blacklist); } /** * Copies data from another {@link SiteSettingsModel}. */ public void copyFrom(SiteSettingsModel other) { if (other == null) return; isInLocalTable = other.isInLocalTable; hasVerifiedCredentials = other.hasVerifiedCredentials; localTableId = other.localTableId; address = other.address; username = other.username; password = other.password; title = other.title; tagline = other.tagline; language = other.language; languageId = other.languageId; privacy = other.privacy; location = other.location; optimizedImage = other.optimizedImage; maxImageWidth = other.maxImageWidth; imageQualitySetting = other.imageQualitySetting; defaultCategory = other.defaultCategory; categories = other.categories; defaultPostFormat = other.defaultPostFormat; postFormats = other.postFormats; showRelatedPosts = other.showRelatedPosts; showRelatedPostHeader = other.showRelatedPostHeader; showRelatedPostImages = other.showRelatedPostImages; allowComments = other.allowComments; sendPingbacks = other.sendPingbacks; receivePingbacks = other.receivePingbacks; shouldCloseAfter = other.shouldCloseAfter; closeCommentAfter = other.closeCommentAfter; sortCommentsBy = other.sortCommentsBy; shouldThreadComments = other.shouldThreadComments; threadingLevels = other.threadingLevels; shouldPageComments = other.shouldPageComments; commentsPerPage = other.commentsPerPage; commentApprovalRequired = other.commentApprovalRequired; commentsRequireIdentity = other.commentsRequireIdentity; commentsRequireUserAccount = other.commentsRequireUserAccount; commentAutoApprovalKnownUsers = other.commentAutoApprovalKnownUsers; maxLinks = other.maxLinks; if (other.holdForModeration != null) { holdForModeration = new ArrayList<>(other.holdForModeration); } if (other.blacklist != null) { blacklist = new ArrayList<>(other.blacklist); } } /** * Sets values from a local database {@link Cursor}. */ public void deserializeOptionsDatabaseCursor(Cursor cursor, Map<Integer, CategoryModel> models) { if (cursor == null || !cursor.moveToFirst() || cursor.getCount() == 0) return; localTableId = getIntFromCursor(cursor, ID_COLUMN_NAME); address = getStringFromCursor(cursor, ADDRESS_COLUMN_NAME); username = getStringFromCursor(cursor, USERNAME_COLUMN_NAME); password = getStringFromCursor(cursor, PASSWORD_COLUMN_NAME); title = getStringFromCursor(cursor, TITLE_COLUMN_NAME); tagline = getStringFromCursor(cursor, TAGLINE_COLUMN_NAME); languageId = getIntFromCursor(cursor, LANGUAGE_COLUMN_NAME); privacy = getIntFromCursor(cursor, PRIVACY_COLUMN_NAME); defaultCategory = getIntFromCursor(cursor, DEF_CATEGORY_COLUMN_NAME); defaultPostFormat = getStringFromCursor(cursor, DEF_POST_FORMAT_COLUMN_NAME); location = getBooleanFromCursor(cursor, LOCATION_COLUMN_NAME); optimizedImage = getBooleanFromCursor(cursor, OPTIMIZED_IMAGE_COLUMN_NAME); maxImageWidth = getIntFromCursor(cursor, MAX_IMAGE_WIDTH_COLUMN_NAME); imageQualitySetting = getIntFromCursor(cursor, IMAGE_ENCODER_QUALITY_COLUMN_NAME); hasVerifiedCredentials = getBooleanFromCursor(cursor, CREDS_VERIFIED_COLUMN_NAME); allowComments = getBooleanFromCursor(cursor, ALLOW_COMMENTS_COLUMN_NAME); sendPingbacks = getBooleanFromCursor(cursor, SEND_PINGBACKS_COLUMN_NAME); receivePingbacks = getBooleanFromCursor(cursor, RECEIVE_PINGBACKS_COLUMN_NAME); shouldCloseAfter = getBooleanFromCursor(cursor, SHOULD_CLOSE_AFTER_COLUMN_NAME); closeCommentAfter = getIntFromCursor(cursor, CLOSE_AFTER_COLUMN_NAME); sortCommentsBy = getIntFromCursor(cursor, SORT_BY_COLUMN_NAME); shouldThreadComments = getBooleanFromCursor(cursor, SHOULD_THREAD_COLUMN_NAME); threadingLevels = getIntFromCursor(cursor, THREADING_COLUMN_NAME); shouldPageComments = getBooleanFromCursor(cursor, SHOULD_PAGE_COLUMN_NAME); commentsPerPage = getIntFromCursor(cursor, PAGING_COLUMN_NAME); commentApprovalRequired = getBooleanFromCursor(cursor, MANUAL_APPROVAL_COLUMN_NAME); commentsRequireIdentity = getBooleanFromCursor(cursor, IDENTITY_REQUIRED_COLUMN_NAME); commentsRequireUserAccount = getBooleanFromCursor(cursor, USER_ACCOUNT_REQUIRED_COLUMN_NAME); commentAutoApprovalKnownUsers = getBooleanFromCursor(cursor, WHITELIST_COLUMN_NAME); String moderationKeys = getStringFromCursor(cursor, MODERATION_KEYS_COLUMN_NAME); String blacklistKeys = getStringFromCursor(cursor, BLACKLIST_KEYS_COLUMN_NAME); holdForModeration = new ArrayList<>(); blacklist = new ArrayList<>(); if (!TextUtils.isEmpty(moderationKeys)) { Collections.addAll(holdForModeration, moderationKeys.split("\n")); } if (!TextUtils.isEmpty(blacklistKeys)) { Collections.addAll(blacklist, blacklistKeys.split("\n")); } setRelatedPostsFlags(Math.max(0, getIntFromCursor(cursor, RELATED_POSTS_COLUMN_NAME))); String cachedCategories = getStringFromCursor(cursor, CATEGORIES_COLUMN_NAME); String cachedFormats = getStringFromCursor(cursor, POST_FORMATS_COLUMN_NAME); if (models != null && !TextUtils.isEmpty(cachedCategories)) { String[] split = cachedCategories.split(","); categories = new CategoryModel[split.length]; for (int i = 0; i < split.length; ++i) { int catId = Integer.parseInt(split[i]); categories[i] = models.get(catId); } } if (!TextUtils.isEmpty(cachedFormats)) { String[] split = cachedFormats.split(";"); postFormats = new HashMap<>(); for (String format : split) { String[] kvp = format.split(","); postFormats.put(kvp[0], kvp[1]); } } int cachedRelatedPosts = getIntFromCursor(cursor, RELATED_POSTS_COLUMN_NAME); if (cachedRelatedPosts != -1) { setRelatedPostsFlags(cachedRelatedPosts); } isInLocalTable = true; } /** * Creates the {@link ContentValues} object to store this category data in a local database. */ public ContentValues serializeToDatabase() { ContentValues values = new ContentValues(); values.put(ID_COLUMN_NAME, localTableId); values.put(ADDRESS_COLUMN_NAME, address); values.put(USERNAME_COLUMN_NAME, username); values.put(PASSWORD_COLUMN_NAME, password); values.put(TITLE_COLUMN_NAME, title); values.put(TAGLINE_COLUMN_NAME, tagline); values.put(PRIVACY_COLUMN_NAME, privacy); values.put(LANGUAGE_COLUMN_NAME, languageId); values.put(LOCATION_COLUMN_NAME, location); values.put(OPTIMIZED_IMAGE_COLUMN_NAME, optimizedImage); values.put(MAX_IMAGE_WIDTH_COLUMN_NAME, maxImageWidth); values.put(IMAGE_ENCODER_QUALITY_COLUMN_NAME, imageQualitySetting); values.put(DEF_CATEGORY_COLUMN_NAME, defaultCategory); values.put(CATEGORIES_COLUMN_NAME, categoryIdList(categories)); values.put(DEF_POST_FORMAT_COLUMN_NAME, defaultPostFormat); values.put(POST_FORMATS_COLUMN_NAME, postFormatList(postFormats)); values.put(CREDS_VERIFIED_COLUMN_NAME, hasVerifiedCredentials); values.put(RELATED_POSTS_COLUMN_NAME, getRelatedPostsFlags()); values.put(ALLOW_COMMENTS_COLUMN_NAME, allowComments); values.put(SEND_PINGBACKS_COLUMN_NAME, sendPingbacks); values.put(RECEIVE_PINGBACKS_COLUMN_NAME, receivePingbacks); values.put(SHOULD_CLOSE_AFTER_COLUMN_NAME, shouldCloseAfter); values.put(CLOSE_AFTER_COLUMN_NAME, closeCommentAfter); values.put(SORT_BY_COLUMN_NAME, sortCommentsBy); values.put(SHOULD_THREAD_COLUMN_NAME, shouldThreadComments); values.put(THREADING_COLUMN_NAME, threadingLevels); values.put(SHOULD_PAGE_COLUMN_NAME, shouldPageComments); values.put(PAGING_COLUMN_NAME, commentsPerPage); values.put(MANUAL_APPROVAL_COLUMN_NAME, commentApprovalRequired); values.put(IDENTITY_REQUIRED_COLUMN_NAME, commentsRequireIdentity); values.put(USER_ACCOUNT_REQUIRED_COLUMN_NAME, commentsRequireUserAccount); values.put(WHITELIST_COLUMN_NAME, commentAutoApprovalKnownUsers); String moderationKeys = ""; if (holdForModeration != null) { for (String key : holdForModeration) { moderationKeys += key + "\n"; } } String blacklistKeys = ""; if (blacklist != null) { for (String key : blacklist) { blacklistKeys += key + "\n"; } } values.put(MODERATION_KEYS_COLUMN_NAME, moderationKeys); values.put(BLACKLIST_KEYS_COLUMN_NAME, blacklistKeys); return values; } public int getRelatedPostsFlags() { int flags = 0; if (showRelatedPosts) flags |= RELATED_POSTS_ENABLED_FLAG; if (showRelatedPostHeader) flags |= RELATED_POST_HEADER_FLAG; if (showRelatedPostImages) flags |= RELATED_POST_IMAGE_FLAG; return flags; } public void setRelatedPostsFlags(int flags) { showRelatedPosts = (flags & RELATED_POSTS_ENABLED_FLAG) > 0; showRelatedPostHeader = (flags & RELATED_POST_HEADER_FLAG) > 0; showRelatedPostImages = (flags & RELATED_POST_IMAGE_FLAG) > 0; } /** * Used to serialize post formats to store in a local database. * * @param formats * map of post formats where the key is the format ID and the value is the format name * @return * a String of semi-colon separated KVP's of Post Formats; Post Format ID -> Post Format Name */ private static String postFormatList(Map<String, String> formats) { if (formats == null || formats.size() == 0) return ""; StringBuilder builder = new StringBuilder(); for (String key : formats.keySet()) { builder.append(key).append(",").append(formats.get(key)).append(";"); } builder.setLength(builder.length() - 1); return builder.toString(); } /** * Used to serialize categories to store in a local database. * * @param elements * {@link CategoryModel} array to create String ID list from * @return * a String of comma-separated integer Category ID's */ private static String categoryIdList(CategoryModel[] elements) { if (elements == null || elements.length == 0) return ""; StringBuilder builder = new StringBuilder(); for (CategoryModel element : elements) { builder.append(String.valueOf(element.id)).append(","); } builder.setLength(builder.length() - 1); return builder.toString(); } /** * Helper method to get an integer value from a given column in a Cursor. */ private int getIntFromCursor(Cursor cursor, String columnName) { int columnIndex = cursor.getColumnIndex(columnName); return columnIndex != -1 ? cursor.getInt(columnIndex) : -1; } /** * Helper method to get a String value from a given column in a Cursor. */ private String getStringFromCursor(Cursor cursor, String columnName) { int columnIndex = cursor.getColumnIndex(columnName); return columnIndex != -1 ? cursor.getString(columnIndex) : ""; } /** * Helper method to get a boolean value (stored as an int) from a given column in a Cursor. */ private boolean getBooleanFromCursor(Cursor cursor, String columnName) { int columnIndex = cursor.getColumnIndex(columnName); return columnIndex != -1 && cursor.getInt(columnIndex) != 0; } /** * Helper method to check if two String are equals or not */ private boolean equals(String first, String second) { return (first == null && second == null) || (first != null && first.equals(second)); } }