mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-04-08 05:02:03 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e69b733f90 | ||
|
|
c726fa78f5 | ||
|
|
2e050be111 | ||
|
|
e5f2a06491 | ||
|
|
6e5d2ad20a | ||
|
|
af088d4b3a | ||
|
|
92bbafeb5d | ||
|
|
9981f450c0 | ||
|
|
44abfeb013 | ||
|
|
03c14859d5 | ||
|
|
320d8cac5f | ||
|
|
33377263d9 | ||
|
|
7f50532e8b | ||
|
|
cf804be04e | ||
|
|
65556c0444 | ||
|
|
88d26cd200 | ||
|
|
2308e14d3e | ||
|
|
5bcc2561ae |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"communityGuidelinesWarning": "Please keep in mind that your Display Name, profile photo, and blurb must comply with the <a href='https://habitica.com/static/community-guidelines' target='_blank'>Community Guidelines</a> (e.g. no profanity, no adult topics, no insults, etc). If you have any questions about whether or not something is appropriate, feel free to email <a href='mailto:leslie@habitica.com' target='blank'>leslie@habitica.com</a>!",
|
||||
"communityGuidelinesWarning": "Please keep in mind that your Display Name, profile photo, and blurb must comply with the <a href='https://habitica.com/static/community-guidelines' target='_blank'>Community Guidelines</a> (e.g. no profanity, no adult topics, no insults, etc). If you have any questions about whether or not something is appropriate, feel free to email <a href='mailto:admin@habitica.com' target='blank'>admin@habitica.com</a>!",
|
||||
"statsAch": "Stats & Achievements",
|
||||
"profile": "Profile",
|
||||
"avatar": "Customize Avatar",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"commGuidePara011b": "on GitHub/Wikia",
|
||||
"commGuidePara011c": "on Wikia",
|
||||
"commGuidePara011d": "on GitHub",
|
||||
"commGuidePara012": "If you have an issue or concern about a particular Mod, please send an email to Lemoness (<a href=\"mailto:leslie@habitica.com\">leslie@habitica.com</a>).",
|
||||
"commGuidePara012": "If you have an issue or concern about a particular Mod, please send an email to the staff (<a href=\"mailto:admin@habitica.com\">admin@habitica.com</a>).",
|
||||
"commGuidePara013": "In a community as big as Habitica, users come and go, and sometimes a moderator needs to lay down their noble mantle and relax. The following are Moderators Emeritus. They no longer act with the power of a Moderator, but we would still like to honor their work!",
|
||||
"commGuidePara014": "Moderators Emeritus:",
|
||||
"commGuideHeadingPublicSpaces": "Public Spaces In Habitica",
|
||||
@@ -42,7 +42,7 @@
|
||||
"commGuideList02E": "<strong>Avoid extended discussions of divisive topics outside of the Back Corner.</strong> If you feel that someone has said something rude or hurtful, do not engage them. A single, polite comment, such as \"That joke makes me feel uncomfortable,\" is fine, but being harsh or unkind in response to harsh or unkind comments heightens tensions and makes Habitica a more negative space. Kindness and politeness helps others understand where you are coming from.",
|
||||
"commGuideList02F": "<strong>Comply immediately with any Mod request</strong> to cease a discussion or move it to the Back Corner. Last words, parting shots and conclusive zingers should all be delivered (courteously) at your \"table\" in the Back Corner, if allowed.",
|
||||
"commGuideList02G": "<strong>Take time to reflect instead of responding in anger</strong> if someone tells you that something you said or did made them uncomfortable. There is great strength in being able to sincerely apologize to someone. If you feel that the way they responded to you was inappropriate, contact a mod rather than calling them out on it publicly.",
|
||||
"commGuideList02H": "<strong>Divisive/contentious conversations should be reported to mods.</strong> If you feel that a conversation is getting heated, overly emotional, or hurtful, cease to engage. Instead, email <a href=\"mailto:leslie@habitica.com\">leslie@habitica.com</a> to let us know about it. It's our job to keep you safe.",
|
||||
"commGuideList02H": "<strong>Divisive/contentious conversations should be reported to mods.</strong> If you feel that a conversation is getting heated, overly emotional, or hurtful, cease to engage. Instead, email the staff at <a href=\"mailto:admin@habitica.com\">admin@habitica.com</a> to let us know about it. It's our job to keep you safe.",
|
||||
"commGuideList02I": "<strong>Do not spam.</strong> Spamming may include, but is not limited to: posting the same comment or query in multiple places, posting links without explanation or context, posting nonsensical messages, or posting many messages in a row. Asking for gems or a subscription in any of the chat spaces or via Private Message is also considered spamming.",
|
||||
|
||||
"commGuidePara019": "<strong>In private spaces,</strong> users have more freedom to discuss whatever topics they would like, but they still may not violate the Terms and Conditions, including posting any discriminatory, violent, or threatening content. Note that, because Challenge names appear in the winner's public profile, ALL Challenge names must obey the public space guidelines, even if they appear in a private space.",
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"surveysMultiple": "Helped Habitica grow on <%= surveys %> occasions, either by filling out a survey or helping with a major testing effort. Thank you!",
|
||||
"currentSurvey": "Current Survey",
|
||||
"surveyWhen": "The badge will be awarded to all participants when surveys have been processed, in late March.",
|
||||
"blurbInbox": "This is where your private messages are stored! You can send someone a message by clicking on the envelope icon next to their name in Tavern, Party, or Guild Chat. If you've received an inappropriate PM, you should email a screenshot of it to Lemoness (<a href=\"mailto:leslie@habitica.com\">leslie@habitica.com</a>)",
|
||||
"blurbInbox": "This is where your private messages are stored! You can send someone a message by clicking on the envelope icon next to their name in Tavern, Party, or Guild Chat. If you've received an inappropriate PM, you should email a screenshot of it to the staff (<a href=\"mailto:admin@habitica.com\">admin@habitica.com</a>)",
|
||||
"blurbGuildsPage": "Guilds are common-interest chat groups created by the players, for players. Browse through the list and join the Guilds that interest you!",
|
||||
"blurbChallenges": "Challenges are created by your fellow players. Joining a Challenge will add its tasks to your task dashboard, and winning a Challenge will give you an achievement and often a gem prize!",
|
||||
"blurbHallPatrons": "This is the Hall of Patrons, where we honor the noble adventurers who backed Habitica's original Kickstarter. We thank them for helping us bring Habitica to life!",
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
"playButtonFull": "Play Habitica",
|
||||
"presskit": "Press Kit",
|
||||
"presskitDownload": "Download all images:",
|
||||
"presskitText": "Thanks for your interest in Habitica! The following images can be used for articles or videos about Habitica. For more information, please contact Siena Leslie at leslie@habitica.com.",
|
||||
"presskitText": "Thanks for your interest in Habitica! The following images can be used for articles or videos about Habitica. For more information, please contact the staff at admin@habitica.com.",
|
||||
"privacy": "Privacy Policy",
|
||||
"psst": "Psst",
|
||||
"punishByline": "Break bad habits and procrastination cycles with immediate consequences.",
|
||||
@@ -248,7 +248,7 @@
|
||||
"passwordResetEmailHtml": "Password for <strong><%= username %></strong> has been reset to <strong><%= newPassword %></strong><br /><br />Important! Both username and password are case-sensitive -- you must enter both exactly as shown here. We recommend copying and pasting both instead of typing them.<br /><br />Log in at <%= baseUrl %>. After you have logged in, head to <%= baseUrl %>/#/options/settings/settings and change your password.",
|
||||
"invalidLoginCredentialsLong": "Uh-oh - your username or password is incorrect.\n- Make sure your username or email is typed correctly.\n- You may have signed up with Facebook, not email. Double-check by trying Facebook login.\n- If you forgot your password, click \"Forgot Password\".",
|
||||
"invalidCredentials": "There is no account that uses those credentials.",
|
||||
"accountSuspended": "Account has been suspended, please contact leslie@habitica.com with your User ID \"<%= userId %>\" for assistance.",
|
||||
"accountSuspended": "Account has been suspended, please contact admin@habitica.com with your User ID \"<%= userId %>\" for assistance.",
|
||||
"onlyFbSupported": "Only Facebook is supported currently.",
|
||||
"cantDetachFb": "Account lacks another authentication method, can't detach Facebook.",
|
||||
"onlySocialAttachLocal": "Local authentication can be added to only a social account.",
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"dataTool": "Data Display Tool",
|
||||
"resources": "Resources",
|
||||
"askQuestionNewbiesGuild": "Ask a Question (Newbies Guild)",
|
||||
"tavernTalk": "Tavern Talk",
|
||||
"tavernAlert1": "To report a bug, visit",
|
||||
"tavernAlert2": "the Report a Bug Guild",
|
||||
"moderatorIntro1": "Tavern and guild moderators are: ",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
"messageAlreadyPurchasedGear": "You purchased this gear in the past, but do not currently own it. You can buy it again in the rewards column on the tasks page.",
|
||||
"messageAlreadyOwnGear": "You already own this item. Equip it by going to the equipment page.",
|
||||
"messageHealthAlreadyMax": "You already have maximum health.",
|
||||
|
||||
"armoireEquipment": "<%= image %> You found a piece of rare Equipment in the Armoire: <%= dropText %>! Awesome!",
|
||||
"armoireFood": "<%= image %> You rummage in the Armoire and find <%= dropArticle %><%= dropText %>. What's that doing in here?",
|
||||
|
||||
@@ -15,6 +15,10 @@ module.exports = function buyHealthPotion (user, req = {}, analytics) {
|
||||
throw new NotAuthorized(i18n.t('cannotBuyItem', req.language));
|
||||
}
|
||||
|
||||
if (user.stats.hp >= 50) {
|
||||
throw new NotAuthorized(i18n.t('messageHealthAlreadyMax', req.language));
|
||||
}
|
||||
|
||||
user.stats.hp += 15;
|
||||
if (user.stats.hp > 50) {
|
||||
user.stats.hp = 50;
|
||||
|
||||
2
npm-shrinkwrap.json
generated
2
npm-shrinkwrap.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"version": "3.34.1",
|
||||
"version": "3.35.0",
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.0.9",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "3.34.1",
|
||||
"version": "3.35.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"accepts": "^1.3.2",
|
||||
@@ -150,7 +150,7 @@
|
||||
"mongoskin": "~2.1.0",
|
||||
"phantomjs": "^1.9",
|
||||
"protractor": "^3.1.1",
|
||||
"require-again": "^1.0.1",
|
||||
"require-again": "^2.0.0",
|
||||
"rewire": "^2.3.3",
|
||||
"shelljs": "^0.7.0",
|
||||
"sinon": "^1.17.2",
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('POST /chat/:chatId/flag', () => {
|
||||
let user, admin, anotherUser, group;
|
||||
const TEST_MESSAGE = 'Test Message';
|
||||
|
||||
before(async () => {
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({balance: 1});
|
||||
admin = await generateUser({balance: 1, 'contributor.admin': true});
|
||||
anotherUser = await generateUser();
|
||||
@@ -83,6 +83,41 @@ describe('POST /chat/:chatId/flag', () => {
|
||||
expect(messageToCheck.flagCount).to.equal(5);
|
||||
});
|
||||
|
||||
it('allows admin to flag a message in a private group', async () => {
|
||||
let privateGroup = await user.post('/groups', {
|
||||
name: 'Test party',
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
});
|
||||
let { message } = await user.post(`/groups/${privateGroup._id}/chat`, {message: TEST_MESSAGE});
|
||||
|
||||
let flagResult = await admin.post(`/groups/${privateGroup._id}/chat/${message.id}/flag`);
|
||||
|
||||
expect(flagResult.flags[admin._id]).to.equal(true);
|
||||
expect(flagResult.flagCount).to.equal(5);
|
||||
|
||||
let groupWithFlags = await user.get(`/groups/${privateGroup._id}`);
|
||||
let messageToCheck = find(groupWithFlags.chat, {id: message.id});
|
||||
|
||||
expect(messageToCheck).to.not.exist;
|
||||
});
|
||||
|
||||
it('does not allow non member to flag message in private group', async () => {
|
||||
let privateGroup = await user.post('/groups', {
|
||||
name: 'Test party',
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
});
|
||||
let { message } = await user.post(`/groups/${privateGroup._id}/chat`, {message: TEST_MESSAGE});
|
||||
|
||||
await expect(anotherUser.post(`/groups/${privateGroup._id}/chat/${message.id}/flag`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('groupNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when user tries to flag a message that is already flagged', async () => {
|
||||
let { message } = await anotherUser.post(`/groups/${group._id}/chat`, {message: TEST_MESSAGE});
|
||||
|
||||
|
||||
@@ -54,6 +54,25 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
|
||||
expect(messages[0].flagCount).to.eql(0);
|
||||
expect(messages[0].flags).to.have.property(admin._id, true);
|
||||
});
|
||||
|
||||
it('clears flags in a private group', async () => {
|
||||
let { group, members } = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
let privateMessage = await members[0].post(`/groups/${group._id}/chat`, { message: 'Some message' });
|
||||
privateMessage = privateMessage.message;
|
||||
|
||||
await admin.post(`/groups/${group._id}/chat/${privateMessage.id}/flag`);
|
||||
await admin.post(`/groups/${group._id}/chat/${privateMessage.id}/clearflags`);
|
||||
|
||||
let messages = await members[0].get(`/groups/${group._id}/chat`);
|
||||
expect(messages[0].flagCount).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
context('admin user, group with multiple messages', () => {
|
||||
|
||||
@@ -68,6 +68,7 @@ describe('PUT /heroes/:heroId', () => {
|
||||
expect(hero.contributor.level).to.equal(1);
|
||||
expect(hero.purchased.ads).to.equal(true);
|
||||
expect(hero.auth.blocked).to.equal(true);
|
||||
expect(hero.preferences.sleep).to.equal(true);
|
||||
expect(hero.notifications.length).to.equal(1);
|
||||
expect(hero.notifications[0].type).to.equal('NEW_CONTRIBUTOR_LEVEL');
|
||||
});
|
||||
|
||||
@@ -33,20 +33,19 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns error when user does not have the challenge', async () => {
|
||||
let userWithoutChallenge = await generateUser();
|
||||
|
||||
await expect(userWithoutChallenge.post(`/tasks/challenge/${challenge._id}`, {
|
||||
it('allows leader to add tasks to a challenge when not a member', async () => {
|
||||
await user.post(`/challenges/${challenge._id}/leave`);
|
||||
let task = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||
text: 'test habit',
|
||||
type: 'habit',
|
||||
up: false,
|
||||
down: true,
|
||||
notes: 1976,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('challengeNotFound'),
|
||||
});
|
||||
|
||||
let {tasksOrder} = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
expect(tasksOrder.habits).to.include(task.id);
|
||||
});
|
||||
|
||||
it('returns error when user tries to create task with a alias', async () => {
|
||||
|
||||
@@ -31,6 +31,7 @@ describe('POST /user/buy/:key', () => {
|
||||
it('buys a potion', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 400,
|
||||
'stats.hp': 40,
|
||||
});
|
||||
|
||||
let potion = content.potion;
|
||||
@@ -42,6 +43,19 @@ describe('POST /user/buy/:key', () => {
|
||||
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
|
||||
});
|
||||
|
||||
it('returns an error if user tries to buy a potion with full health', async () => {
|
||||
await user.update({
|
||||
'stats.gp': 40,
|
||||
'stats.hp': 50,
|
||||
});
|
||||
|
||||
await expect(user.post('/user/buy/potion'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageHealthAlreadyMax'),
|
||||
});
|
||||
});
|
||||
it('buys a piece of gear', async () => {
|
||||
let key = 'armor_warrior_1';
|
||||
|
||||
|
||||
@@ -61,5 +61,20 @@ describe('shared.ops.buyHealthPotion', () => {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not purchase if hp is full', (done) => {
|
||||
user.stats.hp = 50;
|
||||
user.stats.gp = 40;
|
||||
try {
|
||||
buyHealthPotion(user);
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMax'));
|
||||
expect(user.stats.hp).to.eql(50);
|
||||
expect(user.stats.gp).to.eql(40);
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -169,6 +169,12 @@ header .hero-stats
|
||||
display: table-cell
|
||||
width: 100%
|
||||
|
||||
// To align invite button with .herobox-wrap
|
||||
.party-invite-wrap
|
||||
display: table-cell
|
||||
vertical-align: middle
|
||||
height: 10.5em
|
||||
|
||||
button.party-invite
|
||||
display: block
|
||||
margin: 57px auto
|
||||
margin: auto 10px
|
||||
|
||||
@@ -159,14 +159,27 @@ api.likeChat = {
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/groups/:groupId/chat/:chatId/flag Flag a group chat message
|
||||
* @apiVersion 3.0.0
|
||||
* @apiDescription A message will be hidden from chat if two or more users flag a message. It will be hidden immediately if a moderator flags the message. An email is sent to the moderators about every flagged message.
|
||||
* @apiName FlagChat
|
||||
* @apiGroup Chat
|
||||
*
|
||||
* @apiParam {UUID} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted)
|
||||
* @apiParam {UUID} groupId The group id ('party' for the user party and 'habitrpg' for tavern are accepted)
|
||||
* @apiParam {UUID} chatId The chat message id
|
||||
*
|
||||
* @apiSuccess {Object} data The flagged chat message
|
||||
* @apiSuccess {UUID} data.id The id of the message
|
||||
* @apiSuccess {String} data.text The text of the message
|
||||
* @apiSuccess {Number} data.timestamp The timestamp of the message in milliseconds
|
||||
* @apiSuccess {Object} data.likes The likes of the message
|
||||
* @apiSuccess {Object} data.flags The flags of the message
|
||||
* @apiSuccess {Number} data.flagCount The number of flags the message has
|
||||
* @apiSuccess {UUID} data.uuid The user id of the author of the message
|
||||
* @apiSuccess {String} data.user The username of the author of the message
|
||||
*
|
||||
* @apiError GroupNotFound Group could not be found or you don't have access
|
||||
* @apiError ChatNotFound Chat message with specified id could not be found
|
||||
* @apiError FlagOwnMessage Chat messages cannot be flagged by the author of the message
|
||||
* @apiError AlreadyFlagged Chat messages cannot be flagged more than once by a user
|
||||
*/
|
||||
api.flagChat = {
|
||||
method: 'POST',
|
||||
@@ -182,7 +195,11 @@ api.flagChat = {
|
||||
let validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
let group = await Group.getGroup({user, groupId});
|
||||
let group = await Group.getGroup({
|
||||
user,
|
||||
groupId,
|
||||
optionalMembership: user.contributor.admin,
|
||||
});
|
||||
if (!group) throw new NotFound(res.t('groupNotFound'));
|
||||
let message = _.find(group.chat, {id: req.params.chatId});
|
||||
|
||||
@@ -253,16 +270,20 @@ api.flagChat = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/groups/:groupId/chat/:chatId/clearflags Clear a group chat message's flags
|
||||
* @apiDescription Admin-only
|
||||
* @apiVersion 3.0.0
|
||||
* @api {post} /api/v3/groups/:groupId/chat/:chatId/clearflags Clear flags
|
||||
* @apiDescription Resets the flag count on a chat message. Retains the id of the user's that have flagged the message. (Only visible to moderators)
|
||||
* @apiPermission Moderators
|
||||
* @apiName ClearFlags
|
||||
* @apiGroup Chat
|
||||
*
|
||||
* @apiParam {UUID} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted)
|
||||
* @apiParam {UUID} groupId The group id ('party' for the user party and 'habitrpg' for tavern are accepted)
|
||||
* @apiParam {UUID} chatId The chat message id
|
||||
*
|
||||
* @apiSuccess {Object} data An empty object
|
||||
*
|
||||
* @apiError MustBeAdmin Must be a moderator to use this route
|
||||
* @apiError GroupNotFound Group could not be found or you don't have access
|
||||
* @apiError ChatNotFound Chat message with specified id could not be found
|
||||
*/
|
||||
api.clearChatFlags = {
|
||||
method: 'Post',
|
||||
@@ -283,7 +304,11 @@ api.clearChatFlags = {
|
||||
throw new NotAuthorized(res.t('messageGroupChatAdminClearFlagCount'));
|
||||
}
|
||||
|
||||
let group = await Group.getGroup({user, groupId});
|
||||
let group = await Group.getGroup({
|
||||
user,
|
||||
groupId,
|
||||
optionalMembership: user.contributor.admin,
|
||||
});
|
||||
if (!group) throw new NotFound(res.t('groupNotFound'));
|
||||
|
||||
let message = _.find(group.chat, {id: chatId});
|
||||
|
||||
@@ -169,7 +169,10 @@ api.updateHero = {
|
||||
_.set(hero, updateData.itemPath, updateData.itemVal); // Sanitization at 5c30944 (deemed unnecessary)
|
||||
}
|
||||
|
||||
if (updateData.auth && _.isBoolean(updateData.auth.blocked)) hero.auth.blocked = updateData.auth.blocked;
|
||||
if (updateData.auth && _.isBoolean(updateData.auth.blocked)) {
|
||||
hero.auth.blocked = updateData.auth.blocked;
|
||||
hero.preferences.sleep = true; // when blocking, have them rest at an inn to prevent damage
|
||||
}
|
||||
if (updateData.flags && _.isBoolean(updateData.flags.chatRevoked)) hero.flags.chatRevoked = updateData.flags.chatRevoked;
|
||||
|
||||
let savedHero = await hero.save();
|
||||
|
||||
@@ -120,7 +120,7 @@ api.createChallengeTasks = {
|
||||
let challenge = await Challenge.findOne({_id: challengeId}).exec();
|
||||
|
||||
// If the challenge does not exist, or if it exists but user is not the leader -> throw error
|
||||
if (!challenge || user.challenges.indexOf(challengeId) === -1) throw new NotFound(res.t('challengeNotFound'));
|
||||
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
|
||||
if (challenge.leader !== user._id) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks'));
|
||||
|
||||
let tasks = await _createTasks(req, res, user, challenge);
|
||||
|
||||
@@ -1138,8 +1138,7 @@ api.userRebirth = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/user/block/:uuid Block and unblock a user
|
||||
* @apiDescription Must be an admin to make this request.
|
||||
* @api {post} /api/v3/user/block/:uuid Block / unblock a user from sending you a PM
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName BlockUser
|
||||
* @apiGroup User
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
include ./challenge-box
|
||||
|
||||
.col-md-8.tavern(ng-controller='ChatCtrl')
|
||||
h3=env.t('tavernTalk')
|
||||
h3=env.t('tavern')
|
||||
include ./chat-box
|
||||
.alert.alert-info.alert-sm
|
||||
!= ' ' + env.t('tavernAlert1') + ' <a href="https://habitica.com/#/options/groups/guilds/a29da26b-37de-4a71-b0c6-48e72a900dac" target="_blank">' + env.t('tavernAlert2') + '</a>!<br />' + env.t('moderatorIntro1')
|
||||
|
||||
@@ -31,9 +31,10 @@
|
||||
| {{Math.floor(user.stats.mp)}} / {{user.fns.statsComputed().maxMP}}
|
||||
// party
|
||||
.party(ng-controller='PartyCtrl')
|
||||
button.party-invite.btn.btn-primary(ng-click="inviteOrStartParty(party)",
|
||||
ng-if="(!party.members || party.memberCount === 1) && user.preferences.displayInviteToPartyWhenPartyIs1",
|
||||
popover="{{!party.members ? env.t('startAParty') : env.t('addToParty')}}", popover-placement="left", popover-trigger="mouseenter")
|
||||
span=env.t("battleWithFriends")
|
||||
.herobox-wrap(ng-repeat='profile in partyMinusSelf')
|
||||
+herobox()
|
||||
.party-invite-wrap
|
||||
button.party-invite.btn.btn-primary(ng-click="inviteOrStartParty(party)",
|
||||
ng-if="(!party.members || party.memberCount < 4 ) && user.preferences.displayInviteToPartyWhenPartyIs1",
|
||||
popover="{{!party.members ? env.t('startAParty') : env.t('addToParty')}}", popover-placement="left", popover-trigger="mouseenter")
|
||||
span=env.t("battleWithFriends")
|
||||
|
||||
@@ -1,35 +1,54 @@
|
||||
h2 8/23/2016 - AUGUST SUBSCRIBER ITEMS, ANDROID UPDATE, AND BLOG POSTS
|
||||
h2 8/30/2016 - LAST CHANCE FOR THUNDERSTORM ITEMS; BACK TO SCHOOL GUILD SPOTLIGHT
|
||||
hr
|
||||
tr
|
||||
td
|
||||
.promo_mystery_201608.pull-right
|
||||
h3 August Subscriber Items Revealed
|
||||
p The August Subscriber Item has been revealed: the Thunderstormer Set! You still have seven days to <a href='/#/options/settings/subscription'>subscribe</a> and receive the item set.
|
||||
br
|
||||
p Subscribers also receive the ability to buy Gems for Gold -- the longer you subscribe, the more Gems you can buy per month! There are other perks as well, such as longer access to uncompressed data. Best of all, your support directly keeps Habitica running. Thank you very much -- it means a lot to us!
|
||||
h3 Last Chance for Thunderstormer Set
|
||||
p Reminder: this is the final day to <a href='/#/options/settings/subscription'>subscribe</a> and receive the Thunderstorm Item Set! Thanks so much for your support <3
|
||||
p.small.muted by Lemoness
|
||||
tr
|
||||
td
|
||||
h3 Android Update: Better Messaging and Invitations
|
||||
p We've released <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica&hl=en' target='_blank'>a new Android update</a> which includes better messaging, a search bar for Guilds, and an easier way to invite friends to your party! Be sure to download it now for a better Habitica experience!
|
||||
br
|
||||
p If you like the improvements that we’ve been making to our app, please consider reviewing this new version. It really helps us out! We hope you enjoy the update.
|
||||
p.small.muted by viirus and jjbillings
|
||||
span.Mount_Body_Dragon-Thunderstorm.pull-right
|
||||
span.Mount_Head_Dragon-Thunderstorm.pull-right(style='margin:0')
|
||||
h3 Last Chance for Thunderstorm Hatching Potions
|
||||
p Reminder: this is the final day to <a href='/#/options/inventory/drops'>buy Thunderstorm Hatching Potions!</a> They won't be back until next year, so don't delay!
|
||||
p.small.muted by Balduranne
|
||||
tr
|
||||
td
|
||||
h3 New Blog Posts: Habitica Roadtrip and Quest System
|
||||
p There are two new posts on the Habitica blog!
|
||||
br
|
||||
p First, we're announcing the new BEHIND THE SCENES blog series. These posts will highlight some of the work that goes into Habitica and the shenanigans of the team. We're opening with a fun one: <a href='https://habitica.wordpress.com/2016/08/23/behind-the-scenes-habitica-roadtrip/' target='_blank'>a description of the recent Habitica Roadtrip that some of our staff and admins took together</a>! Enjoy.
|
||||
br
|
||||
p Plus, there's a new <a href='https://habitica.wordpress.com/2016/08/17/quests-for-the-best/' target='_blank'>Wiki spotlight</a> on the blog, this time focusing on quests! Learn everything you wanted to know about one of our most motivating mechanics.
|
||||
p.small.muted by Lemoness, Redphoenix, and the Wiki Wizards
|
||||
h3 Guild Spotlight: Back to School
|
||||
p There's a new <a href='https://habitica.wordpress.com/2016/08/29/prepare-to-vanquish-the-school-year/' target='_blank'>Guild Spotlight on the blog</a> that highlights the Guilds that can help you as you head back to school! Check it out now to find Habitica's best scholarly communities.
|
||||
p.small.muted by Lemoness
|
||||
|
||||
if menuItem !== 'oldNews'
|
||||
hr
|
||||
a(href='/static/old-news', target='_blank') Read older news
|
||||
|
||||
mixin oldNews
|
||||
h2 8/23/2016 - AUGUST SUBSCRIBER ITEMS, ANDROID UPDATE, AND BLOG POSTS
|
||||
tr
|
||||
td
|
||||
.promo_mystery_201608.pull-right
|
||||
h3 August Subscriber Items Revealed
|
||||
p The August Subscriber Item has been revealed: the Thunderstormer Set! You still have seven days to <a href='/#/options/settings/subscription'>subscribe</a> and receive the item set.
|
||||
br
|
||||
p Subscribers also receive the ability to buy Gems for Gold -- the longer you subscribe, the more Gems you can buy per month! There are other perks as well, such as longer access to uncompressed data. Best of all, your support directly keeps Habitica running. Thank you very much -- it means a lot to us!
|
||||
p.small.muted by Lemoness
|
||||
tr
|
||||
td
|
||||
h3 Android Update: Better Messaging and Invitations
|
||||
p We've released <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica&hl=en' target='_blank'>a new Android update</a> which includes better messaging, a search bar for Guilds, and an easier way to invite friends to your party! Be sure to download it now for a better Habitica experience!
|
||||
br
|
||||
p If you like the improvements that we’ve been making to our app, please consider reviewing this new version. It really helps us out! We hope you enjoy the update.
|
||||
p.small.muted by viirus, TheHollidayInn, and jjbillings
|
||||
tr
|
||||
td
|
||||
h3 New Blog Posts: Habitica Roadtrip and Quest System
|
||||
p There are two new posts on the Habitica blog!
|
||||
br
|
||||
p First, we're announcing the new BEHIND THE SCENES blog series. These posts will highlight some of the work that goes into Habitica and the shenanigans of the team. We're opening with a fun one: <a href='https://habitica.wordpress.com/2016/08/23/behind-the-scenes-habitica-roadtrip/' target='_blank'>a description of the recent Habitica Roadtrip that some of our staff and admins took together</a>! Enjoy.
|
||||
br
|
||||
p Plus, there's a new <a href='https://habitica.wordpress.com/2016/08/17/quests-for-the-best/' target='_blank'>Wiki spotlight</a> on the blog, this time focusing on quests! Learn everything you wanted to know about one of our most motivating mechanics.
|
||||
p.small.muted by Lemoness, Redphoenix, and the Wiki Wizards
|
||||
h2 8/18/2016 - NEW PET QUEST, iOS UPDATE, AND STAFF SPOTLIGHT
|
||||
tr
|
||||
td
|
||||
|
||||
@@ -23,7 +23,7 @@ block content
|
||||
br
|
||||
=env.t('reportCommunityIssues')
|
||||
| :
|
||||
a(href='mailto:leslie@habitica.com') leslie@habitica.com
|
||||
a(href='mailto:admin@habitica.com') admin@habitica.com
|
||||
br
|
||||
=env.t('subscriptionPaymentIssues')
|
||||
| :
|
||||
|
||||
Reference in New Issue
Block a user