Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions WordPress/src/main/java/org/wordpress/android/WordPressDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public class WordPressDB {
public static final String COLUMN_NAME_VIDEO_PRESS_SHORTCODE = "videoPressShortcode";
public static final String COLUMN_NAME_UPLOAD_STATE = "uploadState";

private static final int DATABASE_VERSION = 45;
private static final int DATABASE_VERSION = 46;

private static final String CREATE_TABLE_BLOGS = "create table if not exists accounts (id integer primary key autoincrement, "
+ "url text, blogName text, username text, password text, imagePlacement text, centerThumbnail boolean, fullSizeImage boolean, maxImageWidth text, maxImageWidthId integer);";
Expand Down Expand Up @@ -226,6 +226,9 @@ public class WordPressDB {
// add plan_product_name_short to blog
private static final String ADD_BLOGS_PLAN_PRODUCT_NAME_SHORT = "alter table accounts add plan_product_name_short text default '';";

// add capabilities to blog
private static final String ADD_BLOGS_CAPABILITIES = "alter table accounts add capabilities text default '';";

// used for migration
private static final String DEPRECATED_WPCOM_USERNAME_PREFERENCE = "wp_pref_wpcom_username";
private static final String DEPRECATED_ACCESS_TOKEN_PREFERENCE = "wp_pref_wpcom_access_token";
Expand Down Expand Up @@ -257,7 +260,7 @@ public WordPressDB(Context ctx) {
boolean isNewInstall = (currentVersion == 0);

if (!isNewInstall && currentVersion != DATABASE_VERSION) {
AppLog.d(T.DB, "updgrading database from version " + currentVersion + " to " + DATABASE_VERSION);
AppLog.d(T.DB, "upgrading database from version " + currentVersion + " to " + DATABASE_VERSION);
}

switch (currentVersion) {
Expand Down Expand Up @@ -413,6 +416,9 @@ public WordPressDB(Context ctx) {
case 44:
PeopleTable.createTables(db);
currentVersion++;
case 45:
db.execSQL(ADD_BLOGS_CAPABILITIES);
currentVersion++;
}
db.setVersion(DATABASE_VERSION);
}
Expand Down Expand Up @@ -523,6 +529,7 @@ public boolean addBlog(Blog blog) {
}
values.put("isAdmin", blog.isAdmin());
values.put("isHidden", blog.isHidden());
values.put("capabilities", blog.getCapabilities());
return db.insert(BLOGS_TABLE, null, values) > -1;
}

Expand Down Expand Up @@ -723,6 +730,7 @@ public boolean saveBlog(Blog blog) {
values.put("isHidden", blog.isHidden());
values.put("plan_product_id", blog.getPlanID());
values.put("plan_product_name_short", blog.getPlanShortName());
values.put("capabilities", blog.getCapabilities());
if (blog.getWpVersion() != null) {
values.put("wpVersion", blog.getWpVersion());
} else {
Expand Down Expand Up @@ -815,7 +823,7 @@ public Blog instantiateBlogByLocalId(int localId) {
"blogId", "dotcomFlag", "dotcom_username", "dotcom_password", "api_key",
"api_blogid", "wpVersion", "postFormats", "isScaledImage",
"scaledImgWidth", "homeURL", "blog_options", "isAdmin", "isHidden",
"plan_product_id", "plan_product_name_short"};
"plan_product_id", "plan_product_name_short", "capabilities"};
Cursor c = db.query(BLOGS_TABLE, fields, "id=?", new String[]{Integer.toString(localId)}, null, null, null);

Blog blog = null;
Expand Down Expand Up @@ -873,6 +881,7 @@ public Blog instantiateBlogByLocalId(int localId) {
blog.setHidden(c.getInt(c.getColumnIndex("isHidden")) > 0);
blog.setPlanID(c.getLong(c.getColumnIndex("plan_product_id")));
blog.setPlanShortName(c.getString(c.getColumnIndex("plan_product_name_short")));
blog.setCapabilities(c.getString(c.getColumnIndex("capabilities")));
}
}
c.close();
Expand Down
27 changes: 26 additions & 1 deletion WordPress/src/main/java/org/wordpress/android/models/Blog.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class Blog {
private String httppassword = "";
private String postFormats;
private String blogOptions = "{}";
private String capabilities;
private boolean isAdmin;
private boolean isHidden;
private long planID;
Expand All @@ -47,7 +48,7 @@ public class Blog {
public Blog() {
}

public Blog(int localTableBlogId, String url, String homeURL, String blogName, String username, String password, String imagePlacement, boolean featuredImageCapable, boolean fullSizeImage, boolean scaledImage, int scaledImageWidth, String maxImageWidth, int maxImageWidthId, int remoteBlogId, String dotcom_username, String dotcom_password, String api_key, String api_blogid, boolean dotcomFlag, String wpVersion, String httpuser, String httppassword, String postFormats, String blogOptions, boolean isAdmin, boolean isHidden) {
public Blog(int localTableBlogId, String url, String homeURL, String blogName, String username, String password, String imagePlacement, boolean featuredImageCapable, boolean fullSizeImage, boolean scaledImage, int scaledImageWidth, String maxImageWidth, int maxImageWidthId, int remoteBlogId, String dotcom_username, String dotcom_password, String api_key, String api_blogid, boolean dotcomFlag, String wpVersion, String httpuser, String httppassword, String postFormats, String blogOptions, String capabilities, boolean isAdmin, boolean isHidden) {
this.localTableBlogId = localTableBlogId;
this.url = url;
this.homeURL = homeURL;
Expand All @@ -72,6 +73,7 @@ public Blog(int localTableBlogId, String url, String homeURL, String blogName, S
this.httppassword = httppassword;
this.postFormats = postFormats;
this.blogOptions = blogOptions;
this.capabilities = capabilities;
this.isAdmin = isAdmin;
this.isHidden = isHidden;
}
Expand Down Expand Up @@ -497,7 +499,30 @@ public void setPlanID(long planID) {
public String getPlanShortName() {
return StringUtils.notNullStr(planShortName);
}

public void setPlanShortName(String name) {
this.planShortName = StringUtils.notNullStr(name);
}

public String getCapabilities() {
return capabilities;
}

public void setCapabilities(String capabilities) {
this.capabilities = capabilities;
}

public boolean hasCapability(Capability capability) {
// If a capability is missing it means the user don't have it.
if (capabilities == null || capabilities.isEmpty() || capability == null) {
return false;
}
try {
JSONObject jsonObject = new JSONObject(capabilities);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's pretty safe to assume that we will use checkCapability() quite often and so it makes sense to optimise this and avoid instantiating a new JSONObject (rather costly operation that includes traversing and tokenizing the string) every time we need to check a capability. Have you considered (and possibly dismissed?) the option of having the Blog object have the capabilities field be a JSONObject directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't considered before, but thinking about it now, I don't think I agree that we're going to check the "capabilities" as often as you think. Aside from People Management, I think we're only going to check list_users for once. Also, we tend to read the blog from db a lot and if we stored it as JSONObject we'd do that expensive operation even when it's not used.

I think a middle ground solution here would be to just cache the capabilities as a JSONObject when it's first requested, so we avoid the situations where we query the db for a network request or something (rather than building the UI). What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mostly referring to the "rest" of the capabilities (like edit_users and others that are very likely we will employ soon) but I agree that all in all we're probably gonna be loading the model more often than we'll need the capabilities checked.

I did consider the option of caching the object on first use but opted against suggesting it because of the ugliness of the code around how to determine whether the object is cached or not (checking for null won't be enough since the json can be malformed, leading to null anyway). It would need a boolean instance variable to flag the fact we tried to load the json and that was the bit I considered ugly and dropped the suggestion. That was a thought experiment though so I could be wrong and this can end up be nice!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean. What I'd like to do in this case is, keep this as is for now since it's not getting merged to develop just yet. Once we start using other capabilities, we can re-visit this to make a more conscious decision. Depending on the trade-offs we can go with either of these 3 solutions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @hypest here, and I'd go even deeper and store a custom POJO - it actually depends if these capabilities can be changed, can we add new capabilites, can we do something with these new capabilities within the app (like display them even if they won't be used by the app itself).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capabilities for now is just for checking them for certain People Management actions. I don't think there will be anyway to update capabilities without updating someone's role. They are tied to each other.

I'll make the change suggested by @hypest in another PR before this gets merged into develop. Thanks for the feedback @maxme!

return jsonObject.optBoolean(capability.getLabel());
} catch (JSONException e) {
AppLog.e(T.PEOPLE, "Capabilities is not a valid json: " + capabilities);
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.wordpress.android.models;

/**
* Used to decide what can the current user do in a particular blog
* A list of capabilities can be found in: https://codex.wordpress.org/Roles_and_Capabilities#Capabilities
*/
public enum Capability {
EDIT_USERS("edit_users"), // Check if user can change another user's role
LIST_USERS("list_users"), // Check if user can visit People page
PROMOTE_USERS("promote_users"); // Check if user can invite another user

private final String label;

Capability(String label) {
this.label = label;
}

public String getLabel() {
return label;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ public static boolean addBlogs(List<Map<String, Object>> blogList, String userna
planID = MapUtils.getMapLong(blogMap, "planID");
}
String planShortName = MapUtils.getMapStr(blogMap, "plan_product_name_short");
String capabilities = MapUtils.getMapStr(blogMap, "capabilities");

retValue |= addOrUpdateBlog(blogName, xmlrpc, homeUrl, blogId, username, password, httpUsername,
httpPassword, isAdmin, isVisible, planID, planShortName);
httpPassword, isAdmin, isVisible, planID, planShortName, capabilities);
}
return retValue;
}
Expand Down Expand Up @@ -106,7 +107,7 @@ public static boolean isAnyInvalidXmlrpcUrl(List<Map<String, Object>> blogList)
public static boolean addOrUpdateBlog(String blogName, String xmlRpcUrl, String homeUrl, String blogId,
String username, String password, String httpUsername, String httpPassword,
boolean isAdmin, boolean isVisible,
long planID, String planShortName) {
long planID, String planShortName, String capabilities) {
Blog blog;
if (!WordPress.wpDB.isBlogInDatabase(Integer.parseInt(blogId), xmlRpcUrl)) {
// The blog isn't in the app, so let's create it
Expand All @@ -129,6 +130,7 @@ public static boolean addOrUpdateBlog(String blogName, String xmlRpcUrl, String
blog.setHidden(!isVisible);
blog.setPlanID(planID);
blog.setPlanShortName(planShortName);
blog.setCapabilities(capabilities);
WordPress.wpDB.saveBlog(blog);
return true;
} else {
Expand All @@ -150,6 +152,10 @@ public static boolean addOrUpdateBlog(String blogName, String xmlRpcUrl, String
blog.setPlanShortName(planShortName);
blogUpdated = true;
}
if (blog.getCapabilities() == null || !blog.getCapabilities().equals(capabilities)) {
blog.setCapabilities(capabilities);
blogUpdated = true;
}
if (blogUpdated) {
WordPress.wpDB.saveBlog(blog);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public void onSuccess(JSONObject createSiteResponse) {
String blogId = details.getString("blogid");
String username = AccountHelper.getDefaultAccount().getUserName();
BlogUtils.addOrUpdateBlog(blogName, xmlRpcUrl, homeUrl, blogId, username, null, null, null,
true, true, PlansConstants.DEFAULT_PLAN_ID_FOR_NEW_BLOG, null);
true, true, PlansConstants.DEFAULT_PLAN_ID_FOR_NEW_BLOG, null, null);
ToastUtils.showToast(getActivity(), R.string.new_blog_wpcom_created);
} catch (JSONException e) {
AppLog.e(T.NUX, "Invalid JSON response from site/new", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ private List<Map<String, Object>> convertJSONObjectToSiteList(JSONObject jsonObj
site.put("isAdmin", jsonSite.get("user_can_manage"));
site.put("isVisible", jsonSite.get("visible"));

// store capabilities as a text blob
site.put("capabilities", jsonSite.getString("capabilities"));

JSONObject plan = jsonSite.getJSONObject("plan");
site.put("planID", plan.get("product_id"));
site.put("plan_product_name_short", plan.get("product_name_short"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.wordpress.android.models.Account;
import org.wordpress.android.models.AccountHelper;
import org.wordpress.android.models.Blog;
import org.wordpress.android.models.Capability;
import org.wordpress.android.models.CommentStatus;
import org.wordpress.android.ui.ActivityLauncher;
import org.wordpress.android.ui.RequestCodes;
Expand Down Expand Up @@ -61,6 +62,7 @@ public class MySiteFragment extends Fragment
private WPTextView mBlogSubtitleTextView;
private LinearLayout mLookAndFeelHeader;
private RelativeLayout mThemesContainer;
private RelativeLayout mPeopleView;
private RelativeLayout mPlanContainer;
private View mConfigurationHeader;
private View mSettingsView;
Expand Down Expand Up @@ -142,6 +144,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
mBlogSubtitleTextView = (WPTextView) rootView.findViewById(R.id.my_site_subtitle_label);
mLookAndFeelHeader = (LinearLayout) rootView.findViewById(R.id.my_site_look_and_feel_header);
mThemesContainer = (RelativeLayout) rootView.findViewById(R.id.row_themes);
mPeopleView = (RelativeLayout) rootView.findViewById(R.id.row_people);
mPlanContainer = (RelativeLayout) rootView.findViewById(R.id.row_plan);
mConfigurationHeader = rootView.findViewById(R.id.row_configuration);
mSettingsView = rootView.findViewById(R.id.row_settings);
Expand Down Expand Up @@ -224,7 +227,7 @@ public void onClick(View v) {
}
});

rootView.findViewById(R.id.row_people).setOnClickListener(new View.OnClickListener() {
mPeopleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityLauncher.viewCurrentBlogPeople(getActivity());
Expand Down Expand Up @@ -357,9 +360,14 @@ private void refreshBlogDetails(@Nullable final Blog blog) {
mThemesContainer.setVisibility(themesVisibility);

// show settings for all self-hosted to expose Delete Site
int settingsVisibility = blog.isAdmin() || !blog.isDotcomFlag() ? View.VISIBLE : View.GONE;
boolean isAdminOrSelfHosted = blog.isAdmin() || !blog.isDotcomFlag();
boolean canListPeople = blog.hasCapability(Capability.LIST_USERS);
mSettingsView.setVisibility(isAdminOrSelfHosted ? View.VISIBLE : View.GONE);
mPeopleView.setVisibility(canListPeople ? View.VISIBLE : View.GONE);

// if either people or settings is visible, configuration header should be visible
int settingsVisibility = (isAdminOrSelfHosted || canListPeople) ? View.VISIBLE : View.GONE;
mConfigurationHeader.setVisibility(settingsVisibility);
mSettingsView.setVisibility(settingsVisibility);

mBlavatarImageView.setImageUrl(GravatarUtils.blavatarFromUrl(blog.getUrl(), mBlavatarSz), WPNetworkImageView.ImageType.BLAVATAR);

Expand Down