Merge branch 'feature/allow-setting-actor-type' into 'develop'
Allow setting actor_type field via Admin API. Closes #114 See merge request pleroma/admin-fe!161
This commit is contained in:
commit
b4ed2dc869
8 changed files with 125 additions and 23 deletions
|
@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Allow managing user's actor_type field via Admin API
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix following and unfollowing relays from Admin-FE, update mobile UI
|
||||
|
|
|
@ -55,11 +55,11 @@ export async function fetchStatusesByInstance({ instance, authHost, token, pageS
|
|||
'nickname': 'sky',
|
||||
'url': 'http://localhost:4000/users/sky'
|
||||
},
|
||||
'content': 'A nice young couple contacted us from Brazil to decorate their newly acquired apartment.',
|
||||
'created_at': '2020-01-31T18:20:01.000Z',
|
||||
'id': '9rZIr0Jzao5Gjgfmro',
|
||||
'content': 'i love parks&rec',
|
||||
'created_at': '2020-04-12T18:20:01.000Z',
|
||||
'id': 'o5Gjgfmro9rZIr0Jza',
|
||||
'sensitive': false,
|
||||
'url': 'http://localhost:4000/objects/7af9abbd-fb6c-4318-aeb7-6636c138ac98',
|
||||
'url': 'http://localhost:4000/objects/7af9abbd-aeb7-6636c138ac98-fb6c-4318',
|
||||
'visibility': 'unlisted'
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
export let users = [
|
||||
{ active: true, approval_pending: false, deactivated: false, id: '2', nickname: 'allis', local: true, external: false, roles: { admin: true, moderator: false }, tags: [] },
|
||||
{ active: true, approval_pending: false, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: false }, tags: ['mrf_tag:sandbox'] },
|
||||
{ active: false, approval_pending: false, deactivated: true, id: 'abc', nickname: 'john', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['mrf_tag:media-strip'] },
|
||||
{ active: true, approval_pending: true, deactivated: false, id: '100', nickname: 'sally', local: true, external: false, roles: { admin: false, moderator: false }, tags: [] }
|
||||
{ active: true, approval_pending: false, deactivated: false, id: '2', nickname: 'allis', local: true, external: false, roles: { admin: true, moderator: false }, tags: [], actor_type: 'Person' },
|
||||
{ active: true, approval_pending: false, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: false }, tags: ['mrf_tag:sandbox'], actor_type: 'Person' },
|
||||
{ active: false, approval_pending: false, deactivated: true, id: 'abc', nickname: 'john', local: true, external: false, roles: { admin: false, moderator: false }, tags: ['mrf_tag:media-strip'], actor_type: 'Person' },
|
||||
{ active: true, approval_pending: true, deactivated: false, id: '100', nickname: 'sally', local: true, external: false, roles: { admin: false, moderator: false }, tags: [], actor_type: 'Service' }
|
||||
]
|
||||
|
||||
const userProfile = { avatar: 'avatar.jpg', nickname: 'allis', id: '2', tags: [], roles: { admin: true, moderator: false }, local: true, external: false }
|
||||
|
@ -119,3 +119,7 @@ export async function createNewAccount(nickname, email, password, authHost, toke
|
|||
users = [...users, newUser]
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
export async function updateUserCredentials(nickname, credentials, authHost, token) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
|
|
@ -271,7 +271,9 @@ export default {
|
|||
invalidNickname: 'invalid nickname',
|
||||
passwordResetTokenGenerated: 'Password reset token was generated:',
|
||||
linkToResetPassword: 'You can also use this link to reset password:',
|
||||
registrationReason: 'Registration Reason'
|
||||
registrationReason: 'Registration Reason',
|
||||
service: 'Service',
|
||||
person: 'Person'
|
||||
},
|
||||
statuses: {
|
||||
statuses: 'Statuses',
|
||||
|
@ -292,7 +294,8 @@ export default {
|
|||
admin: 'Admin',
|
||||
local: 'Local',
|
||||
external: 'External',
|
||||
accountType: 'Account type',
|
||||
accountType: 'Account Type',
|
||||
actorType: 'Actor Type',
|
||||
nickname: 'Nickname',
|
||||
recentStatuses: 'Recent Statuses',
|
||||
roles: 'Roles',
|
||||
|
|
|
@ -16,7 +16,8 @@ import {
|
|||
forcePasswordReset,
|
||||
approveUserAccount,
|
||||
confirmUserEmail,
|
||||
resendConfirmationEmail
|
||||
resendConfirmationEmail,
|
||||
updateUserCredentials
|
||||
} from '@/api/users'
|
||||
|
||||
const users = {
|
||||
|
@ -277,6 +278,14 @@ const users = {
|
|||
const currentFilters = { ...defaultFilters, ...filters }
|
||||
commit('SET_USERS_FILTERS', currentFilters)
|
||||
dispatch('SearchUsers', { query: state.searchQuery, page: 1 })
|
||||
},
|
||||
async UpdateActorType({ dispatch, getters }, { user, type, _userId, _statusId }) {
|
||||
const updatedUsers = [{ ...user, actor_type: type }]
|
||||
const credentials = { actor_type: type }
|
||||
|
||||
const callApiFn = async() => await updateUserCredentials(user.nickname, credentials, getters.authHost, getters.token)
|
||||
|
||||
dispatch('ApplyChanges', { updatedUsers, callApiFn, userId: _userId, statusId: _statusId })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,16 @@
|
|||
</el-button>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
class="actor-type-dropdown">
|
||||
<el-select v-model="actorType" :placeholder="$t('userProfile.actorType')" class="actor-type-select">
|
||||
<el-option :label="$t('users.service')" value="Service"/>
|
||||
<el-option :label="$t('users.person')" value="Person"/>
|
||||
</el-select>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="showAdminAction(user)"
|
||||
divided
|
||||
@click.native="toggleUserRight(user, 'admin')">
|
||||
{{ user.roles.admin ? $t('users.revokeAdmin') : $t('users.grantAdmin') }}
|
||||
</el-dropdown-item>
|
||||
|
@ -138,6 +146,19 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
actorType: {
|
||||
get() {
|
||||
return this.user.actor_type
|
||||
},
|
||||
set(type) {
|
||||
this.$store.dispatch('UpdateActorType', {
|
||||
user: this.user,
|
||||
type,
|
||||
_userId: this.user.id,
|
||||
_statusId: this.statusId
|
||||
})
|
||||
}
|
||||
},
|
||||
isDesktop() {
|
||||
return this.$store.state.app.device === 'desktop'
|
||||
}
|
||||
|
@ -225,6 +246,39 @@ export default {
|
|||
</script>
|
||||
|
||||
<style rel='stylesheet/scss' lang='scss'>
|
||||
.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided.actor-type-dropdown:before {
|
||||
margin: 0 0;
|
||||
height: 0;
|
||||
}
|
||||
.el-dropdown-menu--small .actor-type-dropdown {
|
||||
padding: 0;
|
||||
}
|
||||
.actor-type-select {
|
||||
width: 100%;
|
||||
input {
|
||||
border-color: transparent;
|
||||
color: #606266;
|
||||
}
|
||||
.el-input__inner:hover {
|
||||
border-color: transparent;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
.el-input.is-focus {
|
||||
border-color: transparent;
|
||||
}
|
||||
.el-input__suffix-inner {
|
||||
pointer-events: none;
|
||||
}
|
||||
.el-select .el-input__inner:focus {
|
||||
border-color: transparent;
|
||||
}
|
||||
.el-input.is-active .el-input__inner, .el-input__inner:focus {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
.actor-type-select .el-input.is-focus .el-input__inner {
|
||||
border-color: transparent;
|
||||
}
|
||||
.moderate-user-button {
|
||||
text-align: left;
|
||||
width: 350px;
|
||||
|
|
|
@ -46,10 +46,19 @@
|
|||
<tbody>
|
||||
<tr class="el-table__row">
|
||||
<td class="name-col">ID</td>
|
||||
<td class="value-col">
|
||||
<td>
|
||||
{{ user.id }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="el-table__row">
|
||||
<td>{{ $t('userProfile.actorType') }}</td>
|
||||
<td>
|
||||
<el-tag
|
||||
:type="userCredentials.actor_type === 'Person' ? 'success' : 'warning'">
|
||||
{{ userCredentials.actor_type }}
|
||||
</el-tag>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="el-table__row">
|
||||
<td>{{ $t('userProfile.tags') }}</td>
|
||||
<td>
|
||||
|
|
|
@ -70,7 +70,7 @@ describe('Search and filter users', () => {
|
|||
describe('Users actions', () => {
|
||||
let store
|
||||
const htmlElement = (trChild, liChild) =>
|
||||
`.el-table__fixed-body-wrapper table tr:nth-child(${trChild}) ul.el-dropdown-menu li:nth-child(${liChild})`
|
||||
`.el-table__fixed-body-wrapper table tr:nth-child(${trChild}) ul.el-dropdown-menu > li:nth-child(${liChild})`
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Vuex.Store(cloneDeep(storeConfig))
|
||||
|
@ -88,7 +88,7 @@ describe('Users actions', () => {
|
|||
const user = store.state.users.fetchedUsers[2]
|
||||
expect(user.roles.admin).toBe(false)
|
||||
expect(user.roles.moderator).toBe(false)
|
||||
wrapper.find(htmlElement(3, 1)).trigger('click')
|
||||
wrapper.find(htmlElement(3, 2)).trigger('click')
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser.roles.admin).toBe(true)
|
||||
|
@ -107,7 +107,7 @@ describe('Users actions', () => {
|
|||
const user = store.state.users.fetchedUsers[2]
|
||||
expect(user.roles.admin).toBe(false)
|
||||
expect(user.roles.moderator).toBe(false)
|
||||
wrapper.find(htmlElement(3, 2)).trigger('click')
|
||||
wrapper.find(htmlElement(3, 3)).trigger('click')
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[2]
|
||||
expect(updatedUser.roles.moderator).toBe(true)
|
||||
|
@ -124,9 +124,9 @@ describe('Users actions', () => {
|
|||
await flushPromises()
|
||||
|
||||
const dropdownMenuItems = wrapper.findAll(
|
||||
`.el-table__fixed-body-wrapper table tr:nth-child(2) ul.el-dropdown-menu li`
|
||||
`.el-table__fixed-body-wrapper table tr:nth-child(2) ul.el-dropdown-menu > li`
|
||||
)
|
||||
expect(dropdownMenuItems.length).toBe(6)
|
||||
expect(dropdownMenuItems.length).toBe(7)
|
||||
done()
|
||||
})
|
||||
|
||||
|
@ -141,7 +141,7 @@ describe('Users actions', () => {
|
|||
|
||||
const user = store.state.users.fetchedUsers[1]
|
||||
expect(user.deactivated).toBe(false)
|
||||
wrapper.find(htmlElement(2, 1)).trigger('click')
|
||||
wrapper.find(htmlElement(2, 2)).trigger('click')
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser.deactivated).toBe(true)
|
||||
|
@ -158,7 +158,7 @@ describe('Users actions', () => {
|
|||
await flushPromises()
|
||||
expect(store.state.users.fetchedUsers[1].deactivated).toBe(false)
|
||||
|
||||
wrapper.find(htmlElement(2, 2)).trigger('click')
|
||||
wrapper.find(htmlElement(2, 3)).trigger('click')
|
||||
store.dispatch('DeleteUsers', { users: [{ active: true, deactivated: false, id: '10', nickname: 'bob', local: false, external: true, roles: { admin: false, moderator: false }, tags: ['mrf_tag:sandbox'] }] })
|
||||
|
||||
await flushPromises()
|
||||
|
@ -180,8 +180,8 @@ describe('Users actions', () => {
|
|||
expect(user1.tags.length).toBe(0)
|
||||
expect(user2.tags.length).toBe(1)
|
||||
|
||||
wrapper.find(htmlElement(1, 5)).trigger('click')
|
||||
wrapper.find(htmlElement(2, 5)).trigger('click')
|
||||
wrapper.find(htmlElement(1, 6)).trigger('click')
|
||||
wrapper.find(htmlElement(2, 6)).trigger('click')
|
||||
|
||||
const updatedUser1 = store.state.users.fetchedUsers[0]
|
||||
const updatedUser2 = store.state.users.fetchedUsers[1]
|
||||
|
@ -201,7 +201,7 @@ describe('Users actions', () => {
|
|||
|
||||
const user = store.state.users.fetchedUsers[1]
|
||||
expect(user.tags.length).toBe(1)
|
||||
wrapper.find(htmlElement(2, 6)).trigger('click')
|
||||
wrapper.find(htmlElement(2, 7)).trigger('click')
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[1]
|
||||
expect(updatedUser.tags.length).toBe(0)
|
||||
|
@ -247,7 +247,7 @@ describe('Users actions', () => {
|
|||
expect(wrapper.vm.resetPasswordDialogOpen).toBe(false)
|
||||
expect(store.state.users.passwordResetToken.token).toBe('')
|
||||
|
||||
wrapper.find(htmlElement(1, 11)).trigger('click')
|
||||
wrapper.find(htmlElement(1, 12)).trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.vm.resetPasswordDialogOpen).toBe(true)
|
||||
|
@ -353,7 +353,26 @@ describe('Creates new account', () => {
|
|||
|
||||
expect(wrapper.vm.validatePassword(validatePasswordRule, '', identity)).toBeInstanceOf(Error)
|
||||
expect(wrapper.vm.validatePassword(validatePasswordRule, '1234', identity)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('updates actor type', async (done) => {
|
||||
const wrapper = mount(Users, {
|
||||
store,
|
||||
localVue,
|
||||
sync: false,
|
||||
stubs: ['router-link']
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const user = store.state.users.fetchedUsers[0]
|
||||
expect(user.actor_type).toBe('Person')
|
||||
|
||||
const findWrapper = (trChild, liChild1, liChild2) =>
|
||||
`.el-table__fixed-body-wrapper table tr:nth-child(${trChild}) ul.el-dropdown-menu > li:nth-child(${liChild1}) ul li:nth-child(${liChild2})`
|
||||
wrapper.find(findWrapper(1, 1, 1)).trigger('click')
|
||||
|
||||
const updatedUser = store.state.users.fetchedUsers[0]
|
||||
expect(updatedUser.actor_type).toBe('Service')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue