mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-08-04 04:59:40 +00:00
Merge branch 'develop' into release
This commit is contained in:
commit
37364b0700
28 changed files with 227 additions and 53 deletions
|
|
@ -388,6 +388,23 @@ describe('POST /chat', () => {
|
|||
expect(groupMessages[0].id).to.exist;
|
||||
});
|
||||
|
||||
it('creates a chat with a max length of 3000 chars', async () => {
|
||||
const veryLongMessage = `
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789.
|
||||
THIS PART WON'T BE IN THE MESSAGE (over 3000)
|
||||
`;
|
||||
|
||||
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: veryLongMessage});
|
||||
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
|
||||
|
||||
expect(newMessage.message.id).to.exist;
|
||||
expect(groupMessages[0].id).to.exist;
|
||||
|
||||
expect(newMessage.message.text.length).to.eql(3000);
|
||||
expect(newMessage.message.text).to.not.contain('MESSAGE');
|
||||
expect(groupMessages[0].text.length).to.eql(3000);
|
||||
});
|
||||
|
||||
it('creates a chat with user styles', async () => {
|
||||
const mount = 'test-mount';
|
||||
const pet = 'test-pet';
|
||||
|
|
|
|||
|
|
@ -23,6 +23,17 @@ describe('GET /export/userdata.xml', () => {
|
|||
|
||||
]);
|
||||
|
||||
// add pinnedItem
|
||||
await user.get('/user/toggle-pinned-item/marketGear/gear.flat.shield_rogue_5');
|
||||
|
||||
// add a private message
|
||||
let receiver = await generateUser();
|
||||
|
||||
user.post('/members/send-private-message', {
|
||||
message: 'Your first message, hi!',
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
let response = await user.get('/export/userdata.xml');
|
||||
let {user: res} = await parseStringAsync(response, {explicitArray: false});
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,11 @@ module.exports = {
|
|||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/logout': {
|
||||
'/logout-server': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/export': {
|
||||
target: DEV_BASE_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -331,9 +331,33 @@ export default {
|
|||
];
|
||||
if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true;
|
||||
|
||||
let errorsToShow = [];
|
||||
let usernameCheck = false;
|
||||
let emailCheck = false;
|
||||
let passwordCheck = false;
|
||||
// show only the first error for each param
|
||||
if (errorData.errors) {
|
||||
for (let e of errorData.errors) {
|
||||
if (!usernameCheck && e.param === 'username') {
|
||||
errorsToShow.push(e.message);
|
||||
usernameCheck = true;
|
||||
}
|
||||
if (!emailCheck && e.param === 'email') {
|
||||
errorsToShow.push(e.message);
|
||||
emailCheck = true;
|
||||
}
|
||||
if (!passwordCheck && e.param === 'password') {
|
||||
errorsToShow.push(e.message);
|
||||
passwordCheck = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorsToShow.push(errorMessage);
|
||||
}
|
||||
// dispatch as one snackbar notification
|
||||
this.$store.dispatch('snackbars:add', {
|
||||
title: 'Habitica',
|
||||
text: errorMessage,
|
||||
text: errorsToShow.join(' '),
|
||||
type: 'error',
|
||||
timeout: snackbarTimeout,
|
||||
});
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 45 KiB |
13
website/client/components/auth/logout.vue
Normal file
13
website/client/components/auth/logout.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
methods: {
|
||||
async logout () {
|
||||
return await this.$store.dispatch('auth:logout');
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.logout();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -402,11 +402,6 @@ export default {
|
|||
window.location.href = redirectTo;
|
||||
},
|
||||
async login () {
|
||||
if (!this.username) {
|
||||
alert('Email is required');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$store.dispatch('auth:login', {
|
||||
username: this.username,
|
||||
// email: this.email,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
challenge-modal(v-on:updatedChallenge='updatedChallenge')
|
||||
leave-challenge-modal(:challengeId='challenge._id')
|
||||
close-challenge-modal(:members='members', :challengeId='challenge._id')
|
||||
challenge-member-progress-modal(:memberId='progressMemberId', :challengeId='challenge._id')
|
||||
challenge-member-progress-modal(:challengeId='challenge._id')
|
||||
.col-12.col-md-8.standard-page
|
||||
.row
|
||||
.col-12.col-md-6
|
||||
|
|
@ -231,7 +231,6 @@ export default {
|
|||
creatingTask: {},
|
||||
workingTask: {},
|
||||
taskFormPurpose: 'create',
|
||||
progressMemberId: '',
|
||||
searchTerm: '',
|
||||
memberResults: [],
|
||||
};
|
||||
|
|
@ -345,6 +344,7 @@ export default {
|
|||
this.tasksByType[task.type].splice(index, 1);
|
||||
},
|
||||
showMemberModal () {
|
||||
// @TODO: Change these to options and add a custom event to members modal
|
||||
this.$store.state.memberModalOptions.challengeId = this.challenge._id;
|
||||
this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings
|
||||
this.$store.state.memberModalOptions.group = this.group;
|
||||
|
|
@ -374,8 +374,9 @@ export default {
|
|||
Object.assign(this.challenge, eventData.challenge);
|
||||
},
|
||||
openMemberProgressModal (member) {
|
||||
this.progressMemberId = member._id;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
this.$root.$emit('habitica:challenge:member-progress', {
|
||||
progressMemberId: member._id,
|
||||
});
|
||||
},
|
||||
async exportChallengeCsv () {
|
||||
// let response = await this.$store.dispatch('challenges:exportChallengeCsv', {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
<template lang="pug">
|
||||
b-modal#challenge-member-modal(title="User Progress", size='lg')
|
||||
.row.award-row
|
||||
.col-12.text-center
|
||||
button.btn.btn-primary(v-once, @click='closeChallenge()') {{ $t('awardWinners') }}
|
||||
.row
|
||||
task-column.col-6(
|
||||
v-for="column in columns",
|
||||
|
|
@ -8,12 +11,19 @@
|
|||
:taskListOverride='tasksByType[column]')
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.award-row {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import Column from '../tasks/column';
|
||||
|
||||
export default {
|
||||
props: ['challengeId', 'memberId'],
|
||||
props: ['challengeId'],
|
||||
components: {
|
||||
TaskColumn: Column,
|
||||
},
|
||||
|
|
@ -26,8 +36,19 @@ export default {
|
|||
todo: [],
|
||||
reward: [],
|
||||
},
|
||||
memberId: '',
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('habitica:challenge:member-progress', (data) => {
|
||||
if (!data.progressMemberId) return;
|
||||
this.memberId = data.progressMemberId;
|
||||
this.$root.$emit('bv::show::modal', 'challenge-member-modal');
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$root.$off('habitica:challenge:member-progress');
|
||||
},
|
||||
watch: {
|
||||
async memberId (id) {
|
||||
if (!id) return;
|
||||
|
|
@ -45,5 +66,14 @@ export default {
|
|||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async closeChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:selectChallengeWinner', {
|
||||
challengeId: this.challengeId,
|
||||
winnerId: this.memberId,
|
||||
});
|
||||
this.$router.push('/challenges/myChallenges');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ export default {
|
|||
if (!data.message || !data.groupId) return;
|
||||
this.abuseObject = data.message;
|
||||
this.groupId = data.groupId;
|
||||
this.reportComment = '';
|
||||
this.$root.$emit('bv::show::modal', 'report-flag');
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@
|
|||
:class='{"user-entry": newMessage}',
|
||||
@keydown='updateCarretPosition',
|
||||
@keyup.ctrl.enter='sendMessageShortcut()',
|
||||
@paste='disableMessageSendShortcut()'
|
||||
@paste='disableMessageSendShortcut()',
|
||||
maxlength='3000'
|
||||
)
|
||||
span {{ currentLength }} / 3000
|
||||
autocomplete(
|
||||
:text='newMessage',
|
||||
v-on:select="selectedAutocomplete",
|
||||
|
|
@ -62,6 +64,11 @@
|
|||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentLength () {
|
||||
return this.newMessage.length;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
|
||||
getCoord (e, text) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ div
|
|||
span.dropdown-icon-item
|
||||
.svg-icon.inline(v-html="icons.removeIcon")
|
||||
span.text {{$t('removeManager2')}}
|
||||
b-dropdown-item(@click='viewProgress(member)')
|
||||
span.dropdown-icon-item
|
||||
span.text {{ $t('viewProgress') }}
|
||||
.row(v-if='isLoadMoreAvailable')
|
||||
.col-12.text-center
|
||||
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
|
||||
|
|
@ -475,6 +478,11 @@ export default {
|
|||
groupData.leader = member;
|
||||
this.$root.$emit('updatedGroup', groupData);
|
||||
},
|
||||
viewProgress (member) {
|
||||
this.$root.$emit('habitica:challenge:member-progress', {
|
||||
progressMemberId: member._id,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
.member-stats {
|
||||
padding-left: 12px;
|
||||
padding-right: 24px;
|
||||
margin-right: 1px;
|
||||
opacity: 1;
|
||||
transition: width 0.15s ease-out;
|
||||
}
|
||||
|
|
@ -148,6 +147,7 @@
|
|||
right: 100%;
|
||||
height: calc(100% + 18px);
|
||||
margin-top: -9px;
|
||||
margin-right: 1px;
|
||||
padding-top: 9px;
|
||||
padding-bottom: 24px;
|
||||
padding-right: 16px;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ transition(name="fade")
|
|||
|
||||
.error {
|
||||
background-color: #f74e52;
|
||||
border-radius: 60px;
|
||||
width: 320px !important;
|
||||
padding: 10px 5px;
|
||||
margin-left: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,36 @@
|
|||
<template lang="pug">
|
||||
.container-fluid
|
||||
.container-fluid(role="tablist")
|
||||
.row
|
||||
.col-12.col-md-6.offset-md-3
|
||||
h1 {{ $t('frequentlyAskedQuestions') }}
|
||||
.faq-question(v-for='(heading, index) in headings')
|
||||
h2.accordion(@click='setActivePage(heading)') {{ $t(`faqQuestion${index}`) }}
|
||||
div(v-if='pageState[heading]', v-markdown="$t('webFaqAnswer' + index, replacements)")
|
||||
h1#faq-heading {{ $t('frequentlyAskedQuestions') }}
|
||||
.faq-question(v-for='(heading, index) in headings', :key="index")
|
||||
h2(role="tab", v-b-toggle="heading", @click="handleClick($event)", variant="info") {{ $t(`faqQuestion${index}`) }}
|
||||
b-collapse(:id="heading", :visible="isVisible(heading)", accordion="faq", role="tabpanel")
|
||||
div.card-body(v-markdown="$t('webFaqAnswer' + index, replacements)")
|
||||
hr
|
||||
p(v-markdown="$t('webFaqStillNeedHelp')")
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.faq-question {
|
||||
margin-bottom: 1em;
|
||||
.card-body {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.faq-question h2 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.faq-question .card-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.static-wrapper .faq-question h2 {
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.faq-question a {
|
||||
text-decoration: none;
|
||||
color: #4F2A93;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
|
|
@ -25,6 +43,7 @@
|
|||
<script>
|
||||
// @TODO: env.EMAILS.TECH_ASSISTANCE_EMAIL
|
||||
const TECH_ASSISTANCE_EMAIL = 'admin@habitica.com';
|
||||
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
|
||||
export default {
|
||||
|
|
@ -48,19 +67,15 @@
|
|||
'world-boss',
|
||||
];
|
||||
|
||||
let pageState = {};
|
||||
for (let index in headings) {
|
||||
let heading = headings[index];
|
||||
pageState[heading] = false;
|
||||
}
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
|
||||
return {
|
||||
pageState,
|
||||
headings,
|
||||
replacements: {
|
||||
techAssistanceEmail: TECH_ASSISTANCE_EMAIL,
|
||||
wikiTechAssistanceEmail: `mailto:${TECH_ASSISTANCE_EMAIL}`,
|
||||
},
|
||||
visible: hash && headings.includes(hash) ? hash : null,
|
||||
// @TODO webFaqStillNeedHelp: {
|
||||
// linkStart: '[',
|
||||
// linkEnd: '](/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)',
|
||||
|
|
@ -69,8 +84,13 @@
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
setActivePage (page) {
|
||||
this.pageState[page] = !this.pageState[page];
|
||||
isVisible (heading) {
|
||||
return this.visible && this.visible === heading;
|
||||
},
|
||||
handleClick (e) {
|
||||
if (!e) return;
|
||||
const heading = e.target.nextElementSibling.id;
|
||||
history.pushState({}, heading, `#${heading}`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -616,11 +616,6 @@
|
|||
},
|
||||
// @TODO this is totally duplicate from the registerLogin component
|
||||
async register () {
|
||||
if (this.password !== this.passwordConfirm) {
|
||||
alert('Passwords must match');
|
||||
return;
|
||||
}
|
||||
|
||||
let groupInvite = '';
|
||||
if (this.$route.query && this.$route.query.p) {
|
||||
groupInvite = this.$route.query.p;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@
|
|||
min-height: 556px;
|
||||
}
|
||||
|
||||
.sortable-tasks {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.sortable-tasks + .reward-items {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
|
@ -446,6 +450,7 @@ export default {
|
|||
|
||||
if (this.type !== 'todo') return;
|
||||
this.$root.$on('habitica::resync-requested', () => {
|
||||
if (this.activeFilter.label !== 'complete2') return;
|
||||
this.loadCompletedTodos(true);
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ const PrivacyPage = () => import(/* webpackChunkName: "static" */'./components/s
|
|||
const TermsPage = () => import(/* webpackChunkName: "static" */'./components/static/terms');
|
||||
|
||||
const RegisterLoginReset = () => import(/* webpackChunkName: "auth" */'./components/auth/registerLoginReset');
|
||||
const Logout = () => import(/* webpackChunkName: "auth" */'./components/auth/logout');
|
||||
|
||||
// User Pages
|
||||
// const StatsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/stats');
|
||||
|
|
@ -105,6 +106,7 @@ const router = new VueRouter({
|
|||
routes: [
|
||||
{ name: 'register', path: '/register', component: RegisterLoginReset, meta: {requiresLogin: false} },
|
||||
{ name: 'login', path: '/login', component: RegisterLoginReset, meta: {requiresLogin: false} },
|
||||
{ name: 'logout', path: '/logout', component: Logout },
|
||||
{ name: 'resetPassword', path: '/reset-password', component: RegisterLoginReset, meta: {requiresLogin: false} },
|
||||
{ name: 'tasks', path: '/', component: UserTasks },
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,5 +68,5 @@ export async function socialAuth (store, params) {
|
|||
export function logout () {
|
||||
localStorage.removeItem(LOCALSTORAGE_AUTH_KEY);
|
||||
localStorage.removeItem(LOCALSTORAGE_SOCIAL_AUTH_KEY);
|
||||
window.location.href = '/logout';
|
||||
window.location.href = '/logout-server';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@
|
|||
"locationRequired": "Location of challenge is required ('Add to')",
|
||||
"categoiresRequired": "One or more categories must be selected",
|
||||
"viewProgressOf": "View Progress Of",
|
||||
"viewProgress": "View Progress",
|
||||
"selectMember": "Select Member",
|
||||
"confirmKeepChallengeTasks": "Do you want to keep challenge tasks?",
|
||||
"selectParticipant": "Select a Participant"
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@ import { removePinnedGearByClass, removePinnedItemsByOwnedGear, addPinnedGearByC
|
|||
function resetClass (user, req = {}) {
|
||||
removePinnedGearByClass(user);
|
||||
|
||||
let balanceRemoved = 0;
|
||||
|
||||
if (user.preferences.disableClasses) {
|
||||
user.preferences.disableClasses = false;
|
||||
user.preferences.autoAllocate = false;
|
||||
} else {
|
||||
if (user.balance < 0.75) throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
user.balance -= 0.75;
|
||||
balanceRemoved = 0.75;
|
||||
}
|
||||
|
||||
user.stats.str = 0;
|
||||
|
|
@ -26,19 +29,22 @@ function resetClass (user, req = {}) {
|
|||
user.stats.int = 0;
|
||||
user.stats.points = capByLevel(user.stats.lvl);
|
||||
user.flags.classSelected = false;
|
||||
|
||||
return balanceRemoved;
|
||||
}
|
||||
|
||||
module.exports = function changeClass (user, req = {}, analytics) {
|
||||
let klass = get(req, 'query.class');
|
||||
let balanceRemoved = 0;
|
||||
// user.flags.classSelected is set to false after the user paid the 3 gems
|
||||
if (user.stats.lvl < 10) {
|
||||
throw new NotAuthorized(i18n.t('lvl10ChangeClass', req.language));
|
||||
} else if (!klass) {
|
||||
// if no class is specified, reset points and set user.flags.classSelected to false. User will have paid 3 gems and will be prompted to select class.
|
||||
resetClass(user, req);
|
||||
balanceRemoved = resetClass(user, req);
|
||||
} else if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
|
||||
if (user.flags.classSelected) {
|
||||
resetClass(user, req);
|
||||
balanceRemoved = resetClass(user, req);
|
||||
}
|
||||
|
||||
user.stats.class = klass;
|
||||
|
|
@ -55,8 +61,8 @@ module.exports = function changeClass (user, req = {}, analytics) {
|
|||
analytics.track('change class', {
|
||||
uuid: user._id,
|
||||
class: klass,
|
||||
acquireMethod: 'Gems',
|
||||
gemCost: 3,
|
||||
acquireMethod: balanceRemoved === 0 ? 'Free' : 'Gems',
|
||||
gemCost: balanceRemoved / 0.25,
|
||||
category: 'behavior',
|
||||
headers: req.headers,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -101,21 +101,25 @@ api.registerLocal = {
|
|||
let existingUser = res.locals.user; // If adding local auth to social user
|
||||
|
||||
req.checkBody({
|
||||
email: {
|
||||
notEmpty: {errorMessage: res.t('missingEmail')},
|
||||
isEmail: {errorMessage: res.t('notAnEmail')},
|
||||
},
|
||||
username: {
|
||||
notEmpty: {errorMessage: res.t('missingUsername')},
|
||||
isLength: {options: {min: USERNAME_LENGTH_MIN, max: USERNAME_LENGTH_MAX}, errorMessage: res.t('usernameWrongLength')},
|
||||
notEmpty: true,
|
||||
errorMessage: res.t('missingUsername'),
|
||||
// TODO use the constants in the error message above
|
||||
isLength: {options: {min: USERNAME_LENGTH_MIN, max: USERNAME_LENGTH_MAX}, errorMessage: res.t('usernameWrongLength')},
|
||||
matches: {options: /^[-_a-zA-Z0-9]+$/, errorMessage: res.t('usernameBadCharacters')},
|
||||
},
|
||||
email: {
|
||||
notEmpty: true,
|
||||
errorMessage: res.t('missingEmail'),
|
||||
isEmail: {errorMessage: res.t('notAnEmail')},
|
||||
},
|
||||
password: {
|
||||
notEmpty: {errorMessage: res.t('missingPassword')},
|
||||
notEmpty: true,
|
||||
errorMessage: res.t('missingPassword'),
|
||||
equals: {options: [req.body.confirmPassword], errorMessage: res.t('passwordConfirmationMatch')},
|
||||
},
|
||||
});
|
||||
|
||||
let validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
|
|
@ -476,7 +480,7 @@ api.updateUsername = {
|
|||
notEmpty: {errorMessage: res.t('missingPassword')},
|
||||
},
|
||||
username: {
|
||||
notEmpty: { errorMessage: res.t('missingUsername') },
|
||||
notEmpty: {errorMessage: res.t('missingUsername')},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,6 @@ api.createGroupPlan = {
|
|||
|
||||
// @TODO: Change message
|
||||
if (group.privacy !== 'private') throw new NotAuthorized(res.t('partyMustbePrivate'));
|
||||
group.memberCount = await User.count({ $or: [{ 'party._id': group._id }, { guilds: group._id }] }).exec();
|
||||
group.leader = user._id;
|
||||
user.guilds.push(group._id);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ let api = {};
|
|||
// Internal authentication routes
|
||||
|
||||
// Set a new password after having requested a password reset (GET route to input password)
|
||||
api.resetPasswordSetNewOne = {
|
||||
api.resetPasswordSetNewOne = {
|
||||
method: 'GET',
|
||||
url: '/static/user/auth/local/reset-password-set-new-one',
|
||||
runCron: false,
|
||||
|
|
@ -24,7 +24,7 @@ api.resetPasswordSetNewOne = {
|
|||
// Logout the user from the website.
|
||||
api.logout = {
|
||||
method: 'GET',
|
||||
url: '/logout',
|
||||
url: '/logout-server',
|
||||
async handler (req, res) {
|
||||
if (req.logout) req.logout(); // passportjs method
|
||||
req.session = null;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ api.exportUserHistory = {
|
|||
|
||||
// Convert user to json and attach tasks divided by type
|
||||
// at user.tasks[`${taskType}s`] (user.tasks.{dailys/habits/...})
|
||||
async function _getUserDataForExport (user) {
|
||||
async function _getUserDataForExport (user, xmlMode = false) {
|
||||
let userData = user.toJSON();
|
||||
userData.tasks = {};
|
||||
|
||||
|
|
@ -98,6 +98,33 @@ async function _getUserDataForExport (user) {
|
|||
userData.tasks[`${taskType}s`] = tasksPerType;
|
||||
});
|
||||
|
||||
if (xmlMode) {
|
||||
// object maps cant be parsed
|
||||
userData.inbox.messages = _(userData.inbox.messages)
|
||||
.map(m => {
|
||||
const flags = Object.keys(m.flags);
|
||||
m.flags = flags;
|
||||
|
||||
return m;
|
||||
})
|
||||
.value();
|
||||
|
||||
// _id gets parsed as an bytearray => which gets casted to a chararray => "weird chars"
|
||||
userData.unpinnedItems = userData.unpinnedItems.map(i => {
|
||||
return {
|
||||
path: i.path,
|
||||
type: i.type,
|
||||
};
|
||||
});
|
||||
|
||||
userData.pinnedItems = userData.pinnedItems.map(i => {
|
||||
return {
|
||||
path: i.path,
|
||||
type: i.type,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +164,7 @@ api.exportUserDataXml = {
|
|||
url: '/export/userdata.xml',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
let userData = await _getUserDataForExport(res.locals.user);
|
||||
let userData = await _getUserDataForExport(res.locals.user, true);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'text/xml',
|
||||
|
|
|
|||
|
|
@ -478,7 +478,7 @@ export function chatDefaults (msg, user) {
|
|||
const message = {
|
||||
id,
|
||||
_id: id,
|
||||
text: msg,
|
||||
text: msg.substring(0, 3000),
|
||||
timestamp: Number(new Date()),
|
||||
likes: {},
|
||||
flags: {},
|
||||
|
|
|
|||
Loading…
Reference in a new issue