Skip to content

Commit

Permalink
Allow Impersonation From Admin Panel (#39)
Browse files Browse the repository at this point in the history
* refactor: move modules to `common`

* fix: use correct app import

* chore: move translations to shared key

* fix: export common extend in admin

* feat: create shared button

* refactor: use shared button in forum frontend

* feat: allow passing redirect href to Button

* feat: extend `UserListPage` with new Button

* chore: require `flarum/core` 1.8.6

* chore

* chore: try 1.8.7
  • Loading branch information
DavideIadeluca authored Oct 22, 2024
1 parent 1ee1e2f commit 907149d
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 48 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
}
],
"require": {
"flarum/core": "^1.2.0"
"flarum/core": "^1.8.7"
},
"extra": {
"flarum-extension": {
Expand Down
3 changes: 3 additions & 0 deletions js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { default as commonExtend } from '../common/extend';

export default [...commonExtend];
16 changes: 16 additions & 0 deletions js/src/admin/extendUserListPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import app from 'flarum/admin/app';
import { extend } from 'flarum/common/extend';
import UserListPage from 'flarum/admin/components/UserListPage';
import LoginAsUserButton from '../common/components/#AsUserButton';

import type ItemList from 'flarum/common/utils/ItemList';
import type User from 'flarum/common/models/User';
import type Mithril from 'mithril';

export default function extendUserListPage() {
extend(UserListPage.prototype, 'userActionItems', function (items: ItemList<Mithril.Children>, user: User) {
const forumBaseUrl = app.forum.attribute('baseUrl');

items.add('impersonate', <LoginAsUserButton user={user} redirectTo={`${forumBaseUrl}/u/${user.slug()}`} />);
});
}
5 changes: 5 additions & 0 deletions js/src/admin/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import app from 'flarum/admin/app';
import extendUserListPage from './extendUserListPage';

export { default as extend } from './extend';

app.initializers.add('fof-impersonate', () => {
app.extensionData.for('fof-impersonate').registerPermission(
Expand All @@ -17,4 +20,6 @@ app.initializers.add('fof-impersonate', () => {
label: app.translator.trans('fof-impersonate.admin.settings.require_reason'),
});
}

extendUserListPage();
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import app from 'flarum/forum/app';
import app from 'flarum/common/app';
import Modal, { IInternalModalAttrs } from 'flarum/common/components/Modal';

import Button from 'flarum/common/components/Button';
Expand Down Expand Up @@ -36,15 +36,15 @@ export default class ImpersonateModal extends Modal<ImpersonateModalAttrs> {
}

title(): NestedStringArray {
return app.translator.trans('fof-impersonate.forum.modal.title');
return app.translator.trans('fof-impersonate.lib.modal.title');
}

content(): Mithril.Children {
return (
<div className="Modal-body">
<div>
<p>
{app.translator.trans('fof-impersonate.forum.modal.label', {
{app.translator.trans('fof-impersonate.lib.modal.label', {
username: username(this.user),
})}
</p>
Expand All @@ -57,8 +57,8 @@ export default class ImpersonateModal extends Modal<ImpersonateModalAttrs> {
value={this.reason()}
placeholder={
this.reasonRequired
? app.translator.trans('fof-impersonate.forum.modal.placeholder_required')
: app.translator.trans('fof-impersonate.forum.modal.placeholder_optional')
? app.translator.trans('fof-impersonate.lib.modal.placeholder_required')
: app.translator.trans('fof-impersonate.lib.modal.placeholder_optional')
}
oninput={withAttr('value', this.reason)}
rows="4"
Expand All @@ -74,7 +74,7 @@ export default class ImpersonateModal extends Modal<ImpersonateModalAttrs> {
type: 'submit',
loading: this.loading,
},
app.translator.trans('fof-impersonate.forum.modal.impersonate_username', {
app.translator.trans('fof-impersonate.lib.modal.impersonate_username', {
username: username(this.user),
})
)}
Expand Down
30 changes: 30 additions & 0 deletions js/src/common/components/#AsUserButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import app from 'flarum/common/app';
import Button from 'flarum/common/components/Button';
import type { IButtonAttrs } from 'flarum/common/components/Button';
import type User from 'flarum/common/models/User';
import ImpersonateModal from './ImpersonateModal';

export interface ILoginAsUserButtonAttrs extends IButtonAttrs {
user: User;
redirectTo?: string;
}

export default class LoginAsUserButton extends Button<ILoginAsUserButtonAttrs> {
view() {
return (
<Button icon="fas fa-id-card" onclick={this.loginAsUser.bind(this)}>
{app.translator.trans('fof-impersonate.lib.user_controls.impersonate_button')}
</Button>
);
}

loginAsUser(): void {
const { user, redirectTo } = this.attrs;
app.modal.show(ImpersonateModal, {
callback: () => {
redirectTo ? (window.location.href = redirectTo) : window.location.reload();
},
user,
});
}
}
12 changes: 12 additions & 0 deletions js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Extend from 'flarum/common/extenders';
import User from 'flarum/common/models/User';
import Impersonate from './models/Impersonate';

export default [
new Extend.Store() //
.add('impersonate', Impersonate),

new Extend.Model(User) //
.attribute<boolean>('canFoFImpersonate')
.attribute<boolean>('impersonateReasonRequired'),
];
File renamed without changes.
13 changes: 2 additions & 11 deletions js/src/forum/extend.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
import Extend from 'flarum/common/extenders';
import User from 'flarum/common/models/User';
import Impersonate from './model/Impersonate';
import { default as commonExtend } from '../common/extend';

export default [
new Extend.Store() //
.add('impersonate', Impersonate),

new Extend.Model(User) //
.attribute<boolean>('canFoFImpersonate')
.attribute<boolean>('impersonateReasonRequired'),
];
export default [...commonExtend];
29 changes: 0 additions & 29 deletions js/src/forum/index.ts

This file was deleted.

14 changes: 14 additions & 0 deletions js/src/forum/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { extend } from 'flarum/common/extend';
import app from 'flarum/forum/app';
import UserControls from 'flarum/forum/utils/UserControls';
import LoginAsUserButton from '../common/components/#AsUserButton';

export { default as extend } from './extend';

app.initializers.add('fof-impersonate', () => {
extend(UserControls, 'moderationControls', (items, user) => {
if (user.canFoFImpersonate()) {
items.add('fof-impersonate-login', <LoginAsUserButton user={user} />);
}
});
});
2 changes: 1 addition & 1 deletion resources/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fof-impersonate:
require_reason: Require a reason to be entered before proceeding to impersonate another user.
no_settings_available: No settings are currently available. Try installing fof/moderator-notes

forum:
lib:
modal:
title: Login as another user
label: With great power comes great responsibility. By continuing, you will become {username} until you log out.
Expand Down

0 comments on commit 907149d

Please # to comment.