diff --git a/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js b/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js
index ed78d86658..bd3be00508 100644
--- a/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js
+++ b/test/api/v3/integration/tasks/challenges/PUT-tasks_challenge_challengeId.test.js
@@ -139,6 +139,23 @@ describe('PUT /tasks/:id', () => {
expect(savedHabit.up).to.eql(false);
expect(savedHabit.down).to.eql(false);
});
+
+ it('allows user to update their copy', async () => {
+ const userTasks = await user.get('/tasks/user');
+ const userChallengeTasks = userTasks.filter(task => task.challenge.id === challenge._id);
+ const userCopyOfChallengeTask = userChallengeTasks[0];
+
+ await user.put(`/tasks/${userCopyOfChallengeTask._id}`, {
+ notes: 'some new notes',
+ counterDown: 1,
+ counterUp: 2,
+ });
+ const savedHabit = await user.get(`/tasks/${userCopyOfChallengeTask._id}`);
+
+ expect(savedHabit.notes).to.eql('some new notes');
+ expect(savedHabit.counterDown).to.eql(1);
+ expect(savedHabit.counterUp).to.eql(2);
+ });
});
context('todos', () => {
diff --git a/website/client/components/tasks/taskModal.vue b/website/client/components/tasks/taskModal.vue
index d3bc53bcd6..95548aeed4 100644
--- a/website/client/components/tasks/taskModal.vue
+++ b/website/client/components/tasks/taskModal.vue
@@ -227,7 +227,7 @@
.svg-icon.info-icon(v-html="icons.information", v-b-tooltip.hover.righttop.html="$t('attributeAllocationHelp')")
.attributes
.custom-control.custom-radio.custom-control-inline(v-for="attr in ATTRIBUTES", :key="attr")
- input.custom-control-input(:id="`attribute-${attr}`", type="radio", :value="attr", v-model="task.attribute")
+ input.custom-control-input(:id="`attribute-${attr}`", type="radio", :value="attr", v-model="task.attribute", :disabled="user.preferences.allocationMode !== 'taskbased'")
label.custom-control-label.attr-description(:for="`attribute-${attr}`", v-once, v-b-popover.hover="$t(`${attr}Text`)") {{ $t(attributesStrings[attr]) }}
.delete-task-btn.d-flex.justify-content-center.align-items-middle(@click="destroy()", v-if="purpose !== 'create' && !challengeAccessRequired")
.svg-icon.d-inline-b(v-html="icons.destroy")
diff --git a/website/common/locales/en/tasks.json b/website/common/locales/en/tasks.json
index ac38dfa510..cca1d23567 100644
--- a/website/common/locales/en/tasks.json
+++ b/website/common/locales/en/tasks.json
@@ -44,7 +44,7 @@
"hard": "Hard",
"attributes": "Stats",
"attributeAllocation": "Stat Allocation",
- "attributeAllocationHelp": "Stat allocation is an option that provides methods for Habitica to automatically assign an earned Stat Point to a Stat immediately upon level-up.
You can adjust how Automatic Allocation is handled from the Stats section of your profile.",
+ "attributeAllocationHelp": "Stat allocation is an option that provides methods for Habitica to automatically assign an earned Stat Point to a Stat immediately upon level-up.
You can set your Automatic Allocation method to Task Based in the Stats section of your profile.",
"progress": "Progress",
"daily": "Daily",
"dailies": "Dailies",
diff --git a/website/server/models/task.js b/website/server/models/task.js
index a99c43664b..390364a8f1 100644
--- a/website/server/models/task.js
+++ b/website/server/models/task.js
@@ -144,7 +144,7 @@ TaskSchema.statics.findByIdOrAlias = async function findByIdOrAlias (identifier,
TaskSchema.statics.sanitizeUserChallengeTask = function sanitizeUserChallengeTask (taskObj) {
let initialSanitization = this.sanitize(taskObj);
- return _.pick(initialSanitization, ['streak', 'checklist', 'attribute', 'reminders', 'tags', 'notes', 'collapseChecklist', 'alias', 'yesterDaily']);
+ return _.pick(initialSanitization, ['streak', 'checklist', 'attribute', 'reminders', 'tags', 'notes', 'collapseChecklist', 'alias', 'yesterDaily', 'counterDown', 'counterUp']);
};
// Sanitize checklist objects (disallowing id)