diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 2fc4178814a2..bbc07ca3563f 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -337,7 +337,7 @@ + android:theme="@style/Calypso.NoActionBar"/> 0); } @@ -769,6 +770,7 @@ public boolean deleteWordPressComBlogs(Context ctx) { int localBlogId = MapUtils.getMapInt(blog, "id"); deleteQuickPressShortcutsForLocalTableBlogId(ctx, localBlogId); deleteAllPostsForLocalTableBlogId(localBlogId); + PeopleTable.deletePeopleForBlog(localBlogId); } // H4ck alert: We need to delete the Jetpack sites that were added in the initial diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/PeopleTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/PeopleTable.java index 16a71dfc1d10..6139a39f4f6d 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/PeopleTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/PeopleTable.java @@ -65,9 +65,12 @@ public static void save(Person person, SQLiteDatabase database) { database.insertWithOnConflict(PEOPLE_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE); } - public static void savePeople(List peopleList) { + public static void savePeople(List peopleList, int localTableBlogId) { getWritableDb().beginTransaction(); try { + //We have a fresh list, remove the previous list of people in case it was deleted on remote + PeopleTable.deletePeopleForBlog(localTableBlogId); + for (Person person : peopleList) { PeopleTable.save(person); } @@ -77,10 +80,21 @@ public static void savePeople(List peopleList) { } } + public static void deletePeopleForBlog(int localTableBlogId) { + String[] args = new String[]{Integer.toString(localTableBlogId)}; + getWritableDb().delete(PEOPLE_TABLE, "local_blog_id=?", args); + } + + public static void deletePerson(long personID, int localTableBlogId) { + String[] args = new String[]{Long.toString(personID), Integer.toString(localTableBlogId)}; + getWritableDb().delete(PEOPLE_TABLE, "person_id=? AND local_blog_id=?", args); + } + public static List getPeople(int localTableBlogId) { List people = new ArrayList<>(); String[] args = { Integer.toString(localTableBlogId) }; - Cursor c = getReadableDb().rawQuery("SELECT * FROM " + PEOPLE_TABLE + " WHERE local_blog_id=?", args); + Cursor c = getReadableDb().rawQuery("SELECT * FROM " + PEOPLE_TABLE + + " WHERE local_blog_id=? ORDER BY display_name, user_name", args); try { while (c.moveToNext()) { @@ -102,7 +116,8 @@ public static List getPeople(int localTableBlogId) { */ public static Person getPerson(long personId, int localTableBlogId) { String[] args = { Long.toString(personId), Integer.toString(localTableBlogId) }; - Cursor c = getReadableDb().rawQuery("SELECT * FROM " + PEOPLE_TABLE + " WHERE person_id=? AND local_blog_id=?", args); + Cursor c = getReadableDb().rawQuery("SELECT * FROM " + PEOPLE_TABLE + + " WHERE person_id=? AND local_blog_id=?", args); try { if (!c.moveToFirst()) { return null; @@ -123,6 +138,7 @@ private static Person getPersonFromCursor(Cursor c, int localTableBlogId) { String avatarUrl = c.getString(c.getColumnIndex("avatar_url")); String role = c.getString(c.getColumnIndex("role")); - return new Person(personId, blogId, localTableBlogId, username, firstName, lastName, displayName, avatarUrl, role); + return new Person(personId, blogId, localTableBlogId, username, + firstName, lastName, displayName, avatarUrl, role); } } diff --git a/WordPress/src/main/java/org/wordpress/android/models/Capability.java b/WordPress/src/main/java/org/wordpress/android/models/Capability.java index 28436c73269a..2ae03aeac063 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/Capability.java +++ b/WordPress/src/main/java/org/wordpress/android/models/Capability.java @@ -5,9 +5,9 @@ * 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 + PROMOTE_USERS("promote_users"), // Check if user can change another user's role + REMOVE_USERS("remove_users"); // Check if user can remove another user private final String label; diff --git a/WordPress/src/main/java/org/wordpress/android/models/Person.java b/WordPress/src/main/java/org/wordpress/android/models/Person.java index f121b810ad90..8eca72227541 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/Person.java +++ b/WordPress/src/main/java/org/wordpress/android/models/Person.java @@ -50,7 +50,7 @@ public static Person fromJSON(JSONObject json, String blogId, int localTableBlog String username = json.optString("login"); String firstName = json.optString("first_name"); String lastName = json.optString("last_name"); - String displayName = json.optString("nice_name"); + String displayName = json.optString("name"); String avatarUrl = json.optString("avatar_URL"); // We don't support multiple roles, so the first role is picked just as it's in Calypso String role = json.getJSONArray("roles").optString(0); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleAdapter.java index a726d5f106cb..1806d8716cc7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleAdapter.java @@ -88,10 +88,10 @@ private class PeopleViewHolder { private final TextView txtRole; PeopleViewHolder(View row) { - imgAvatar = (WPNetworkImageView) row.findViewById(R.id.people_list_row_avatar); - txtDisplayName = (TextView) row.findViewById(R.id.people_list_row_display_name); - txtUsername = (TextView) row.findViewById(R.id.people_list_row_username); - txtRole = (TextView) row.findViewById(R.id.people_list_row_role); + imgAvatar = (WPNetworkImageView) row.findViewById(R.id.person_avatar); + txtDisplayName = (TextView) row.findViewById(R.id.person_display_name); + txtUsername = (TextView) row.findViewById(R.id.person_username); + txtRole = (TextView) row.findViewById(R.id.person_role); } } } \ No newline at end of file diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java index 2005f2b9ff6e..4131e7457693 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java @@ -1,10 +1,13 @@ package org.wordpress.android.ui.people; +import android.app.AlertDialog; import android.app.FragmentManager; import android.app.FragmentTransaction; +import android.content.DialogInterface; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; import android.view.MenuItem; import org.wordpress.android.R; @@ -15,10 +18,12 @@ import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.accounts.BlogUtils; import org.wordpress.android.ui.people.utils.PeopleUtils; +import org.wordpress.android.util.ToastUtils; import java.util.List; -public class PeopleManagementActivity extends AppCompatActivity implements PeopleListFragment.OnPersonSelectedListener { +public class PeopleManagementActivity extends AppCompatActivity + implements PeopleListFragment.OnPersonSelectedListener, RoleChangeDialogFragment.OnChangeListener { private static final String KEY_PEOPLE_LIST_FRAGMENT = "people-list-fragment"; private static final String KEY_PERSON_DETAIL_FRAGMENT = "person-detail-fragment"; @@ -26,12 +31,16 @@ public class PeopleManagementActivity extends AppCompatActivity implements Peopl public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.people_management_activity); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { + actionBar.setTitle(R.string.people); actionBar.setHomeButtonEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); } - setContentView(R.layout.people_management_activity); int localBlogId = BlogUtils.getBlogLocalId(WordPress.getCurrentBlog()); Blog blog = WordPress.getBlog(localBlogId); @@ -57,9 +66,7 @@ public void finish() { @Override public void onBackPressed() { - if (getFragmentManager().getBackStackEntryCount() > 0 ){ - getFragmentManager().popBackStack(); - } else { + if (!navigateBackToPeopleListFragment()) { super.onBackPressed(); } } @@ -69,6 +76,9 @@ public boolean onOptionsItemSelected(final MenuItem item) { if (item.getItemId() == android.R.id.home) { onBackPressed(); return true; + } else if (item.getItemId() == R.id.remove_person) { + confirmRemovePerson(); + return true; } return super.onOptionsItemSelected(item); } @@ -77,25 +87,15 @@ private void refreshUsersList(String dotComBlogId, final int localTableBlogId) { PeopleUtils.fetchUsers(dotComBlogId, localTableBlogId, new PeopleUtils.FetchUsersCallback() { @Override public void onSuccess(List peopleList) { - PeopleTable.savePeople(peopleList); - - FragmentManager fragmentManager = getFragmentManager(); - PeopleListFragment peopleListFragment = (PeopleListFragment) fragmentManager - .findFragmentByTag(KEY_PEOPLE_LIST_FRAGMENT); - PersonDetailFragment personDetailFragment = (PersonDetailFragment) fragmentManager - .findFragmentByTag(KEY_PERSON_DETAIL_FRAGMENT); - - if (peopleListFragment != null) { - peopleListFragment.refreshPeopleList(); - } - if (personDetailFragment != null) { - personDetailFragment.refreshPersonDetails(); - } + PeopleTable.savePeople(peopleList, localTableBlogId); + refreshOnScreenFragmentDetails(); } @Override public void onError() { - //TODO: show some kind of error to the user + ToastUtils.showToast(PeopleManagementActivity.this, + R.string.error_fetch_people_list, + ToastUtils.Duration.LONG); } }); } @@ -120,4 +120,124 @@ public void onPersonSelected(Person person) { fragmentTransaction.commit(); } } + + @Override + public void onRoleChanged(long personID, int localTableBlogId, String newRole) { + Person person = PeopleTable.getPerson(personID, localTableBlogId); + if (person == null || newRole == null || newRole.equalsIgnoreCase(person.getRole())) { + return; + } + PeopleUtils.updateRole(person.getBlogId(), person.getPersonID(), newRole, localTableBlogId, + new PeopleUtils.UpdateUserCallback() { + @Override + public void onSuccess(Person person) { + PeopleTable.save(person); + refreshOnScreenFragmentDetails(); + } + + @Override + public void onError() { + ToastUtils.showToast(PeopleManagementActivity.this, + R.string.error_update_role, + ToastUtils.Duration.LONG); + } + }); + } + + private void confirmRemovePerson() { + Person person = getCurrentPerson(); + if (person == null) { + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Calypso_AlertDialog); + builder.setTitle(getString(R.string.person_remove_confirmation_title, person.getDisplayName())); + builder.setMessage(getString(R.string.person_remove_confirmation_message, person.getDisplayName())); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.remove, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + removeSelectedPerson(); + } + }); + builder.show(); + } + + private void removeSelectedPerson() { + Person person = getCurrentPerson(); + if (person == null) { + return; + } + PeopleUtils.removePerson(person.getBlogId(), + person.getPersonID(), + person.getLocalTableBlogId(), + new PeopleUtils.RemoveUserCallback() { + @Override + public void onSuccess(long personID, int localTableBlogId) { + // remove the person from db, navigate back to list fragment and refresh it + Person person = PeopleTable.getPerson(personID, localTableBlogId); + String text; + if (person != null) { + PeopleTable.deletePerson(personID, localTableBlogId); + text = getString(R.string.person_removed, person.getUsername()); + } else { + text = getString(R.string.person_removed_general); + } + + ToastUtils.showToast(PeopleManagementActivity.this, text, ToastUtils.Duration.LONG); + + navigateBackToPeopleListFragment(); + refreshPeopleListFragment(); + } + + @Override + public void onError() { + ToastUtils.showToast(PeopleManagementActivity.this, + R.string.error_remove_user, + ToastUtils.Duration.LONG); + } + }); + } + + // This helper method is used after a successful network request + private void refreshOnScreenFragmentDetails() { + FragmentManager fragmentManager = getFragmentManager(); + PersonDetailFragment personDetailFragment = (PersonDetailFragment) fragmentManager + .findFragmentByTag(KEY_PERSON_DETAIL_FRAGMENT); + + if (personDetailFragment != null) { + personDetailFragment.refreshPersonDetails(); + } + + refreshPeopleListFragment(); + } + + private void refreshPeopleListFragment() { + FragmentManager fragmentManager = getFragmentManager(); + PeopleListFragment peopleListFragment = (PeopleListFragment) fragmentManager + .findFragmentByTag(KEY_PEOPLE_LIST_FRAGMENT); + if (peopleListFragment != null) { + peopleListFragment.refreshPeopleList(); + } + } + + private boolean navigateBackToPeopleListFragment() { + if (getFragmentManager().getBackStackEntryCount() > 0) { + getFragmentManager().popBackStack(); + return true; + } + return false; + } + + private Person getCurrentPerson() { + FragmentManager fragmentManager = getFragmentManager(); + PersonDetailFragment personDetailFragment = (PersonDetailFragment) fragmentManager + .findFragmentByTag(KEY_PERSON_DETAIL_FRAGMENT); + + if (personDetailFragment == null) { + return null; + } + + return personDetailFragment.loadPerson(); + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/PersonDetailFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/people/PersonDetailFragment.java index fdc53bef2c2c..76c19b86dc83 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/people/PersonDetailFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/PersonDetailFragment.java @@ -2,15 +2,28 @@ import android.animation.Animator; import android.animation.AnimatorInflater; +import android.app.AlertDialog; import android.app.Fragment; +import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.RadioButton; import android.widget.TextView; import org.wordpress.android.R; +import org.wordpress.android.WordPress; import org.wordpress.android.datasets.PeopleTable; +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.Person; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.GravatarUtils; @@ -27,8 +40,8 @@ public class PersonDetailFragment extends Fragment { private WPNetworkImageView mAvatarImageView; private TextView mDisplayNameTextView; private TextView mUsernameTextView; + private LinearLayout mRoleContainer; private TextView mRoleTextView; - private TextView mRemoveTextView; public static PersonDetailFragment newInstance(long personID, int localTableBlogID) { PersonDetailFragment personDetailFragment = new PersonDetailFragment(); @@ -39,9 +52,15 @@ public static PersonDetailFragment newInstance(long personID, int localTableBlog return personDetailFragment; } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.person_detail, menu); + super.onCreateOptionsMenu(menu, inflater); + } + /** - * Sets the enter & pop animation for the fragment. In order to keep the animation even after the configuration - * changes, this method is used instead of FragmentTransaction for the animation. + * Sets the enter & pop animation for the fragment. In order to keep the animation even after the configuration + * changes, this method is used instead of FragmentTransaction for the animation. */ @Override public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) { @@ -62,8 +81,15 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mAvatarImageView = (WPNetworkImageView) rootView.findViewById(R.id.person_avatar); mDisplayNameTextView = (TextView) rootView.findViewById(R.id.person_display_name); mUsernameTextView = (TextView) rootView.findViewById(R.id.person_username); + mRoleContainer = (LinearLayout) rootView.findViewById(R.id.person_role_container); mRoleTextView = (TextView) rootView.findViewById(R.id.person_role); - mRemoveTextView = (TextView) rootView.findViewById(R.id.person_remove); + + Account account = AccountHelper.getDefaultAccount(); + boolean isCurrentUser = account.getUserId() == mPersonID; + Blog blog = WordPress.getBlog(mLocalTableBlogID); + if (!isCurrentUser && blog != null && blog.hasCapability(Capability.REMOVE_USERS)) { + setHasOptionsMenu(true); + } return rootView; } @@ -78,7 +104,7 @@ public void onResume() { public void refreshPersonDetails() { if (!isAdded()) return; - Person person = PeopleTable.getPerson(mPersonID, mLocalTableBlogID); + Person person = loadPerson(); if (person != null) { int avatarSz = getResources().getDimensionPixelSize(R.dimen.avatar_sz_large); String avatarUrl = GravatarUtils.fixGravatarUrl(person.getAvatarUrl(), avatarSz); @@ -87,14 +113,8 @@ public void refreshPersonDetails() { mDisplayNameTextView.setText(person.getDisplayName()); mUsernameTextView.setText(person.getUsername()); mRoleTextView.setText(StringUtils.capitalize(person.getRole())); - mRemoveTextView.setText(String.format(getString(R.string.remove_user), person.getFirstName().toUpperCase())); - mRemoveTextView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - //TODO: remove user - } - }); + setupRoleContainerForCapability(); } else { AppLog.w(AppLog.T.PEOPLE, "Person returned null from DB for personID: " + mPersonID + " & localTableBlogID: " + mLocalTableBlogID); @@ -106,4 +126,47 @@ public void setPersonDetails(long personID, int localTableBlogID) { mLocalTableBlogID = localTableBlogID; refreshPersonDetails(); } + + // Checks current user's capabilities to decide whether she can change the role or not + private void setupRoleContainerForCapability() { + Blog blog = WordPress.getBlog(mLocalTableBlogID); + Account account = AccountHelper.getDefaultAccount(); + boolean isCurrentUser = account.getUserId() == mPersonID; + boolean canChangeRole = (blog != null) && !isCurrentUser && blog.hasCapability(Capability.PROMOTE_USERS); + if (canChangeRole) { + mRoleContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showRoleChangeDialog(); + } + }); + } else { + // Remove the selectableItemBackground if the user can't be edited + clearRoleContainerBackground(); + // Change transparency to give a visual cue to the user that it's disabled + mRoleContainer.setAlpha(0.5f); + } + } + + private void showRoleChangeDialog() { + Person person = loadPerson(); + if (person == null) { + return; + } + RoleChangeDialogFragment.newInstance(person.getPersonID(), person.getLocalTableBlogId(), person.getRole()) + .show(getFragmentManager(), null); + } + + @SuppressWarnings("deprecation") + private void clearRoleContainerBackground() { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) { + mRoleContainer.setBackgroundDrawable(null); + } else { + mRoleContainer.setBackground(null); + } + } + + public Person loadPerson() { + return PeopleTable.getPerson(mPersonID, mLocalTableBlogID); + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/RoleChangeDialogFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/people/RoleChangeDialogFragment.java new file mode 100644 index 000000000000..6cd733931feb --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/RoleChangeDialogFragment.java @@ -0,0 +1,170 @@ +package org.wordpress.android.ui.people; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.RadioButton; +import android.widget.TextView; + +import org.wordpress.android.R; + +public class RoleChangeDialogFragment extends DialogFragment { + private static final String PERSON_ID_TAG = "person_id"; + private static final String PERSON_LOCAL_TABLE_BLOG_ID_TAG = "local_table_blog_id"; + private static final String ROLE_TAG = "role"; + + private RoleListAdapter mRoleListAdapter; + private OnChangeListener mListener; + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + String role = mRoleListAdapter.getSelectedRole(); + outState.putString(ROLE_TAG, role); + } + + public static RoleChangeDialogFragment newInstance(long personID, int localTableBlogId, String role) { + RoleChangeDialogFragment roleChangeDialogFragment = new RoleChangeDialogFragment(); + Bundle args = new Bundle(); + + args.putLong(PERSON_ID_TAG, personID); + args.putInt(PERSON_LOCAL_TABLE_BLOG_ID_TAG, localTableBlogId); + if (role != null) { + args.putString(ROLE_TAG, role); + } + + roleChangeDialogFragment.setArguments(args); + return roleChangeDialogFragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + try { + mListener = (OnChangeListener) context; + } catch (ClassCastException e) { + throw new ClassCastException(context.toString() + " must implement OnChangeListener"); + } + } + + // We need to override this for devices pre API 23 + @SuppressWarnings("deprecation") + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnChangeListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnChangeListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.Calypso_AlertDialog); + builder.setTitle(R.string.role); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (mListener != null) { + String role = mRoleListAdapter.getSelectedRole(); + Bundle args = getArguments(); + if (args != null) { + long personID = args.getLong(PERSON_ID_TAG); + int localTableBlogId = args.getInt(PERSON_LOCAL_TABLE_BLOG_ID_TAG); + mListener.onRoleChanged(personID, localTableBlogId, role); + } + } + } + }); + + if (mRoleListAdapter == null) { + final String[] roles = getResources().getStringArray(R.array.roles); + mRoleListAdapter = new RoleListAdapter(getActivity(), R.layout.role_list_row, roles); + } + if (savedInstanceState != null) { + String savedRole = savedInstanceState.getString(ROLE_TAG); + mRoleListAdapter.setSelectedRole(savedRole); + } else { + Bundle args = getArguments(); + if (args != null) { + String role = args.getString(ROLE_TAG); + mRoleListAdapter.setSelectedRole(role); + } + } + builder.setAdapter(mRoleListAdapter, null); + + return builder.create(); + } + + private class RoleListAdapter extends ArrayAdapter { + private String mSelectedRole; + + public RoleListAdapter(Context context, int resource, String[] objects) { + super(context, resource, objects); + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = View.inflate(getContext(), R.layout.role_list_row, null); + } + + final RadioButton radioButton = (RadioButton) convertView.findViewById(R.id.radio); + TextView mainText = (TextView) convertView.findViewById(R.id.role_label); + String role = getItem(position); + mainText.setText(role); + + if (radioButton != null) { + radioButton.setChecked(role.equalsIgnoreCase(mSelectedRole)); + radioButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + changeSelection(position); + } + }); + } + + convertView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + changeSelection(position); + } + }); + + return convertView; + } + + private void changeSelection(int position) { + mSelectedRole = getItem(position); + notifyDataSetChanged(); + } + + public String getSelectedRole() { + return mSelectedRole; + } + + public void setSelectedRole(String role) { + mSelectedRole = role; + } + } + + // Container Activity must implement this interface + public interface OnChangeListener { + void onRoleChanged(long personID, int localTableBlogId, String newRole); + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java b/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java index 3ab39d7255bf..d8c688dad602 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java @@ -12,7 +12,9 @@ import org.wordpress.android.util.AppLog.T; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class PeopleUtils { @@ -48,7 +50,7 @@ public void onErrorResponse(VolleyError volleyError) { WordPress.getRestClientUtilsV1_1().get(path, listener, errorListener); } - public static void updateRole(final String blogId, String userID, String newRole, final int localTableBlogId, + public static void updateRole(final String blogId, long personID, String newRole, final int localTableBlogId, final UpdateUserCallback callback) { com.wordpress.rest.RestRequest.Listener listener = new RestRequest.Listener() { @Override @@ -79,19 +81,41 @@ public void onErrorResponse(VolleyError volleyError) { } }; - try { - JSONObject jsonObject = new JSONObject(); - JSONArray roles = new JSONArray(); - roles.put(newRole); - jsonObject.put("roles", roles); - - String path = String.format("sites/%s/users/%s", blogId, userID); - WordPress.getRestClientUtilsV1_1().post(path, jsonObject, null, listener, errorListener); - } catch (JSONException e) { - if (callback != null) { - callback.onError(); + Map params = new HashMap<>(); + params.put("roles", newRole.toLowerCase()); + String path = String.format("sites/%s/users/%d", blogId, personID); + WordPress.getRestClientUtilsV1_1().post(path, params, null, listener, errorListener); + } + + public static void removePerson(String blogId, final long personID, final int localTableBlogId, + final RemoveUserCallback callback) { + com.wordpress.rest.RestRequest.Listener listener = new RestRequest.Listener() { + @Override + public void onResponse(JSONObject jsonObject) { + if (jsonObject != null && callback != null) { + // check if the call was successful + boolean success = jsonObject.optBoolean("success"); + if (success) { + callback.onSuccess(personID, localTableBlogId); + } else { + callback.onError(); + } + } } - } + }; + + RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() { + @Override + public void onErrorResponse(VolleyError volleyError) { + AppLog.e(T.API, volleyError); + if (callback != null) { + callback.onError(); + } + } + }; + + String path = String.format("sites/%s/users/%d/delete", blogId, personID); + WordPress.getRestClientUtilsV1_1().post(path, listener, errorListener); } private static List peopleListFromJSON(JSONArray jsonArray, String blogId, int localTableBlogId) @@ -116,6 +140,10 @@ public interface FetchUsersCallback extends Callback { void onSuccess(List peopleList); } + public interface RemoveUserCallback extends Callback { + void onSuccess(long personID, int localTableBlogId); + } + public interface UpdateUserCallback extends Callback { void onSuccess(Person person); } diff --git a/WordPress/src/main/res/drawable/gridicons_trash.xml b/WordPress/src/main/res/drawable/gridicons_trash.xml new file mode 100644 index 000000000000..a1ff3f401b4c --- /dev/null +++ b/WordPress/src/main/res/drawable/gridicons_trash.xml @@ -0,0 +1,9 @@ + + + diff --git a/WordPress/src/main/res/drawable/people_list_divider.xml b/WordPress/src/main/res/drawable/people_list_divider.xml new file mode 100644 index 000000000000..09d74f3d5075 --- /dev/null +++ b/WordPress/src/main/res/drawable/people_list_divider.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/WordPress/src/main/res/layout/people_list_fragment.xml b/WordPress/src/main/res/layout/people_list_fragment.xml index 98e98a5e7578..d74a617eadae 100644 --- a/WordPress/src/main/res/layout/people_list_fragment.xml +++ b/WordPress/src/main/res/layout/people_list_fragment.xml @@ -2,15 +2,14 @@ + android:layout_height="match_parent" + android:background="@color/white"> + android:layout_marginTop="@dimen/margin_medium" + android:divider="@drawable/people_list_divider" /> diff --git a/WordPress/src/main/res/layout/people_list_row.xml b/WordPress/src/main/res/layout/people_list_row.xml index 45417b26d3c6..8a7c678a524b 100644 --- a/WordPress/src/main/res/layout/people_list_row.xml +++ b/WordPress/src/main/res/layout/people_list_row.xml @@ -1,56 +1,32 @@ - - - + android:textColor="@color/grey" + android:textSize="@dimen/text_sz_small" + tools:text="role"/> - - - + android:layout_toLeftOf="@id/person_role" + android:layout_toStartOf="@id/person_role"/> diff --git a/WordPress/src/main/res/layout/people_management_activity.xml b/WordPress/src/main/res/layout/people_management_activity.xml index f78b1f2c997e..c03271ff8614 100644 --- a/WordPress/src/main/res/layout/people_management_activity.xml +++ b/WordPress/src/main/res/layout/people_management_activity.xml @@ -1,6 +1,16 @@ - + + + + + + + diff --git a/WordPress/src/main/res/layout/person_detail.xml b/WordPress/src/main/res/layout/person_detail.xml new file mode 100644 index 000000000000..352ab2a6eaeb --- /dev/null +++ b/WordPress/src/main/res/layout/person_detail.xml @@ -0,0 +1,41 @@ + + + + + + + + + + \ No newline at end of file diff --git a/WordPress/src/main/res/layout/person_detail_fragment.xml b/WordPress/src/main/res/layout/person_detail_fragment.xml index 17ddd3ee5f5d..f872141cd396 100644 --- a/WordPress/src/main/res/layout/person_detail_fragment.xml +++ b/WordPress/src/main/res/layout/person_detail_fragment.xml @@ -1,97 +1,63 @@ + android:background="@color/white" + android:clickable="true"> - + layout="@layout/person_detail"/> - + + android:textSize="@dimen/text_sz_large" + tools:text="@string/role"/> - - + tools:text="@string/role"/> - + - - - + android:layout_height="@dimen/people_list_divider_height" + android:layout_below="@id/person_role_container" + android:background="@color/grey_lighten_20"/> - + diff --git a/WordPress/src/main/res/layout/role_list_row.xml b/WordPress/src/main/res/layout/role_list_row.xml new file mode 100644 index 000000000000..07891321dafd --- /dev/null +++ b/WordPress/src/main/res/layout/role_list_row.xml @@ -0,0 +1,41 @@ + + + + + + + + diff --git a/WordPress/src/main/res/menu/person_detail.xml b/WordPress/src/main/res/menu/person_detail.xml new file mode 100644 index 000000000000..0d5fa9557ef0 --- /dev/null +++ b/WordPress/src/main/res/menu/person_detail.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/WordPress/src/main/res/values/dimens.xml b/WordPress/src/main/res/values/dimens.xml index c024e456126d..251ea322adab 100644 --- a/WordPress/src/main/res/values/dimens.xml +++ b/WordPress/src/main/res/values/dimens.xml @@ -275,6 +275,9 @@ 5dp - 84dp + 40dp + 72dp + 20dp 1dp + 72dp diff --git a/WordPress/src/main/res/values/roles.xml b/WordPress/src/main/res/values/roles.xml index 6d454c26ee33..db9574b9de74 100644 --- a/WordPress/src/main/res/values/roles.xml +++ b/WordPress/src/main/res/values/roles.xml @@ -1,6 +1,6 @@ - + Administrator diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 50e7d0d371c6..65972e770d7d 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -1023,6 +1023,9 @@ Couldn\'t retrieve site info Couldn\'t save site info This post or page was published on another site + Couldn\'t retrieve users for the site + Couldn\'t update role of the user + Couldn\'t remove user Add media @@ -1363,7 +1366,10 @@ People Edit User Role - REMOVE %s + Remove %1$s + If you remove %1$s, that user will no longer be able to access this site, but any content that was created by %1$s will remain on the site.\n\nWould you still like to remove this user? + Successfully removed @%1$s + User removed successfully My Profile