mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Server: Allow manually deleting a user flag
This commit is contained in:
parent
5da820aa0a
commit
3a11885705
@ -1,3 +1,7 @@
|
||||
#user_cancel_subscription_link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-flags ul > li {
|
||||
list-style-type: none;
|
||||
}
|
@ -34,7 +34,7 @@ async function handleUserFlags(ctx: AppContext): Promise<NotificationView> {
|
||||
const user = ctx.joplin.owner;
|
||||
|
||||
const flags = await ctx.joplin.models.userFlag().allByUserId(ctx.joplin.owner.id);
|
||||
const flagStrings = flags.map(f => `- ${userFlagToString(f)}`);
|
||||
const flagStrings = flags.map(f => `- ${userFlagToString(f)}`).join('\n');
|
||||
|
||||
if (!user.enabled || !user.can_upload) {
|
||||
return {
|
||||
|
@ -79,6 +79,8 @@ export default class UserFlagModels extends BaseModel<UserFlag> {
|
||||
}
|
||||
|
||||
public async removeMulti(userId: Uuid, flagTypes: UserFlagType[]) {
|
||||
if (!flagTypes.length) return;
|
||||
|
||||
await this.withTransaction(async () => {
|
||||
for (const flagType of flagTypes) {
|
||||
await this.remove(userId, flagType, { updateUser: false });
|
||||
|
@ -4,7 +4,7 @@ import { RouteType } from '../../utils/types';
|
||||
import { AppContext, HttpMethod } from '../../utils/types';
|
||||
import { bodyFields, formParse } from '../../utils/requestUtils';
|
||||
import { ErrorForbidden, ErrorUnprocessableEntity } from '../../utils/errors';
|
||||
import { User, UserFlagType, Uuid } from '../../services/database/types';
|
||||
import { User, UserFlag, UserFlagType, Uuid } from '../../services/database/types';
|
||||
import config from '../../config';
|
||||
import { View } from '../../services/MustacheService';
|
||||
import defaultView from '../../utils/defaultView';
|
||||
@ -146,11 +146,18 @@ router.get('users/:id', async (path: SubPath, ctx: AppContext, user: User = null
|
||||
postUrl = `${config().baseUrl}/users/${user.id}`;
|
||||
}
|
||||
|
||||
let userFlags: string[] = isNew ? [] : (await models.userFlag().allByUserId(user.id)).map(f => {
|
||||
return userFlagToString(f);
|
||||
interface UserFlagView extends UserFlag {
|
||||
message: string;
|
||||
}
|
||||
|
||||
let userFlagViews: UserFlagView[] = isNew ? [] : (await models.userFlag().allByUserId(user.id)).map(f => {
|
||||
return {
|
||||
...f,
|
||||
message: userFlagToString(f),
|
||||
};
|
||||
});
|
||||
|
||||
if (!owner.is_admin) userFlags = [];
|
||||
if (!owner.is_admin) userFlagViews = [];
|
||||
|
||||
const subscription = !isNew ? await ctx.joplin.models.subscription().byUserId(userId) : null;
|
||||
|
||||
@ -179,8 +186,8 @@ router.get('users/:id', async (path: SubPath, ctx: AppContext, user: User = null
|
||||
view.content.showResetPasswordButton = !isNew && owner.is_admin && user.enabled;
|
||||
view.content.canShareFolderOptions = yesNoDefaultOptions(user, 'can_share_folder');
|
||||
view.content.canUploadOptions = yesNoOptions(user, 'can_upload');
|
||||
view.content.hasFlags = !!userFlags.length;
|
||||
view.content.userFlags = userFlags;
|
||||
view.content.hasFlags = !!userFlagViews.length;
|
||||
view.content.userFlagViews = userFlagViews;
|
||||
view.content.stripePortalUrl = stripePortalUrl();
|
||||
|
||||
view.jsFiles.push('zxcvbn');
|
||||
@ -296,6 +303,7 @@ interface FormFields {
|
||||
// user_cancel_subscription_button: string;
|
||||
impersonate_button: string;
|
||||
stop_impersonate_button: string;
|
||||
delete_user_flags: string;
|
||||
}
|
||||
|
||||
router.post('users', async (path: SubPath, ctx: AppContext) => {
|
||||
@ -326,13 +334,14 @@ router.post('users', async (path: SubPath, ctx: AppContext) => {
|
||||
|
||||
await models.user().save(userToSave, { isNew: false });
|
||||
}
|
||||
// } else if (fields.user_cancel_subscription_button) {
|
||||
// await cancelSubscriptionByUserId(models, userId);
|
||||
// const sessionId = contextSessionId(ctx, false);
|
||||
// if (sessionId) {
|
||||
// await models.session().logout(sessionId);
|
||||
// return redirect(ctx, config().baseUrl);
|
||||
// }
|
||||
// } else if (fields.user_cancel_subscription_button) {
|
||||
// await cancelSubscriptionByUserId(models, userId);
|
||||
// const sessionId = contextSessionId(ctx, false);
|
||||
// if (sessionId) {
|
||||
// await models.session().logout(sessionId);
|
||||
// return redirect(ctx, config().baseUrl);
|
||||
// }
|
||||
|
||||
} else if (fields.stop_impersonate_button) {
|
||||
await stopImpersonating(ctx);
|
||||
return redirect(ctx, config().baseUrl);
|
||||
@ -354,6 +363,16 @@ router.post('users', async (path: SubPath, ctx: AppContext) => {
|
||||
await updateSubscriptionType(models, userId, AccountType.Basic);
|
||||
} else if (fields.update_subscription_pro_button) {
|
||||
await updateSubscriptionType(models, userId, AccountType.Pro);
|
||||
} else if (fields.delete_user_flags) {
|
||||
const userFlagTypes: UserFlagType[] = [];
|
||||
for (const key of Object.keys(fields)) {
|
||||
if (key.startsWith('user_flag_')) {
|
||||
const type = Number(key.substr(10));
|
||||
userFlagTypes.push(type);
|
||||
}
|
||||
}
|
||||
|
||||
await models.userFlag().removeMulti(userId, userFlagTypes);
|
||||
} else {
|
||||
throw new Error('Invalid form button');
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ export async function csrfCheck(ctx: AppContext, isPublicRoute: boolean) {
|
||||
|
||||
const fields = await bodyFields<BodyWithCsrfToken>(ctx.req);
|
||||
if (!fields._csrf) throw new ErrorForbidden('CSRF token is missing');
|
||||
if (Array.isArray(fields._csrf)) throw new Error('Multiple CSRF tokens inside the form!');
|
||||
|
||||
if (!(await ctx.joplin.models.token().isValid(userId, fields._csrf))) {
|
||||
throw new ErrorForbidden(`Invalid CSRF token: ${fields._csrf}`);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<h1 class="title">Your profile</h1>
|
||||
|
||||
<form id="user_form" action="{{{postUrl}}}" method="POST">
|
||||
<form id="user_form" action="{{{postUrl}}}" method="POST" class="block">
|
||||
|
||||
<div class="block">
|
||||
{{> errorBanner}}
|
||||
@ -140,19 +140,24 @@
|
||||
</div>
|
||||
{{/subscription}}
|
||||
|
||||
{{#hasFlags}}
|
||||
<div class="content">
|
||||
<h1 class="title">Flags</h1>
|
||||
{{#userFlags}}
|
||||
<ul>
|
||||
<li>{{.}}</li>
|
||||
</ul>
|
||||
{{/userFlags}}
|
||||
</div>
|
||||
{{/hasFlags}}
|
||||
|
||||
</form>
|
||||
|
||||
{{#hasFlags}}
|
||||
<div class="content user-flags block">
|
||||
<h1 class="title">Flags</h1>
|
||||
<form action="{{{postUrl}}}" method="POST">
|
||||
{{{csrfTag}}}
|
||||
{{#userFlagViews}}
|
||||
<ul>
|
||||
<li><label class="checkbox"><input type="checkbox" name="user_flag_{{type}}"> {{message}}</label></li>
|
||||
</ul>
|
||||
{{/userFlagViews}}
|
||||
<input type="submit" name="delete_user_flags" class="button is-warning" value="Delete selected flags" />
|
||||
<p class="help">Note: normally it should not be needed to manually delete a flag because that's automatically handled by the system. So if it's necessary it means there's a bug that should be fixed.</p>
|
||||
</form>
|
||||
</div>
|
||||
{{/hasFlags}}
|
||||
|
||||
<script>
|
||||
$(() => {
|
||||
document.getElementById("user_form").addEventListener('submit', function(event) {
|
||||
|
Loading…
Reference in New Issue
Block a user