mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-15 10:06:55 +00:00
Merge pull request #9614 from overleaf/ab-limit-tag-length
[web] Restrict the length of tags to 50 characters GitOrigin-RevId: fc20227e3e2171bf9e27c983105ecc7b198cf882
This commit is contained in:
parent
e65ede1d6a
commit
70e63ca0e3
8 changed files with 86 additions and 9 deletions
|
@ -1,6 +1,8 @@
|
|||
const { Tag } = require('../../models/Tag')
|
||||
const { promisifyAll } = require('../../util/promises')
|
||||
|
||||
const MAX_TAG_LENGTH = 50
|
||||
|
||||
function getAllTags(userId, callback) {
|
||||
Tag.find({ user_id: userId }, callback)
|
||||
}
|
||||
|
@ -9,6 +11,9 @@ function createTag(userId, name, callback) {
|
|||
if (!callback) {
|
||||
callback = function () {}
|
||||
}
|
||||
if (name.length > MAX_TAG_LENGTH) {
|
||||
return callback(new Error('Exceeded max tag length'))
|
||||
}
|
||||
Tag.create({ user_id: userId, name }, function (err, tag) {
|
||||
// on duplicate key error return existing tag
|
||||
if (err && err.code === 11000) {
|
||||
|
@ -22,6 +27,9 @@ function renameTag(userId, tagId, name, callback) {
|
|||
if (!callback) {
|
||||
callback = function () {}
|
||||
}
|
||||
if (name.length > MAX_TAG_LENGTH) {
|
||||
return callback(new Error('Exceeded max tag length'))
|
||||
}
|
||||
Tag.updateOne(
|
||||
{
|
||||
_id: tagId,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"approaching_compile_timeout_limit_upgrade_for_more_compile_time": "",
|
||||
"archive": "",
|
||||
"archive_projects": "",
|
||||
"archived": "",
|
||||
"archived_projects": "",
|
||||
"archiving_projects_wont_affect_collaborators": "",
|
||||
"are_you_still_at": "",
|
||||
|
@ -564,6 +565,7 @@
|
|||
"sync_to_github": "",
|
||||
"tab_connecting": "",
|
||||
"tab_no_longer_connected": "",
|
||||
"tag_name_cannot_exceed_characters": "",
|
||||
"tags": "",
|
||||
"tags_slash_folders": "",
|
||||
"take_short_survey": "",
|
||||
|
@ -593,8 +595,8 @@
|
|||
"too_recently_compiled": "",
|
||||
"total_words": "",
|
||||
"trash": "",
|
||||
"trashed": "",
|
||||
"trash_projects": "",
|
||||
"trashed": "",
|
||||
"trashed_projects": "",
|
||||
"trashing_projects_wont_affect_collaborators": "",
|
||||
"trial_last_day": "",
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Button, Form, Modal } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Tag } from '../../../../../../app/src/Features/Tags/types'
|
||||
import AccessibleModal from '../../../../shared/components/accessible-modal'
|
||||
import useAsync from '../../../../shared/hooks/use-async'
|
||||
import { createTag } from '../../util/api'
|
||||
import { MAX_TAG_LENGTH } from '../../util/tag'
|
||||
|
||||
type CreateTagModalProps = {
|
||||
show: boolean
|
||||
|
@ -21,6 +22,7 @@ export default function CreateTagModal({
|
|||
const { isError, runAsync, status } = useAsync<Tag>()
|
||||
|
||||
const [tagName, setTagName] = useState<string>()
|
||||
const [validationError, setValidationError] = useState<string>()
|
||||
|
||||
const runCreateTag = useCallback(() => {
|
||||
if (tagName) {
|
||||
|
@ -38,6 +40,16 @@ export default function CreateTagModal({
|
|||
[runCreateTag]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (tagName && tagName.length > MAX_TAG_LENGTH) {
|
||||
setValidationError(
|
||||
t('tag_name_cannot_exceed_characters', { maxLength: MAX_TAG_LENGTH })
|
||||
)
|
||||
} else if (validationError) {
|
||||
setValidationError(undefined)
|
||||
}
|
||||
}, [tagName, t, validationError])
|
||||
|
||||
if (!show) {
|
||||
return null
|
||||
}
|
||||
|
@ -68,6 +80,11 @@ export default function CreateTagModal({
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
{validationError && (
|
||||
<div className="modal-footer-left">
|
||||
<span className="text-danger error">{validationError}</span>
|
||||
</div>
|
||||
)}
|
||||
{isError && (
|
||||
<div className="modal-footer-left">
|
||||
<span className="text-danger error">
|
||||
|
@ -81,9 +98,11 @@ export default function CreateTagModal({
|
|||
<Button
|
||||
onClick={() => runCreateTag()}
|
||||
bsStyle="primary"
|
||||
disabled={status === 'pending' || !tagName?.length}
|
||||
disabled={
|
||||
status === 'pending' || !tagName?.length || !!validationError
|
||||
}
|
||||
>
|
||||
{status === 'pending' ? t('creating') + '...' : t('create')}
|
||||
{status === 'pending' ? t('creating') + '…' : t('create')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
|
|
|
@ -70,7 +70,7 @@ export default function DeleteTagModal({
|
|||
bsStyle="primary"
|
||||
disabled={status === 'pending'}
|
||||
>
|
||||
{status === 'pending' ? t('deleting') + '...' : t('delete')}
|
||||
{status === 'pending' ? t('deleting') + '…' : t('delete')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Button, Form, Modal } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Tag } from '../../../../../../app/src/Features/Tags/types'
|
||||
import AccessibleModal from '../../../../shared/components/accessible-modal'
|
||||
import useAsync from '../../../../shared/hooks/use-async'
|
||||
import { renameTag } from '../../util/api'
|
||||
import { MAX_TAG_LENGTH } from '../../util/tag'
|
||||
|
||||
type RenameTagModalProps = {
|
||||
tag?: Tag
|
||||
|
@ -21,6 +22,7 @@ export default function RenameTagModal({
|
|||
const { isError, runAsync, status } = useAsync()
|
||||
|
||||
const [newTagName, setNewTageName] = useState<string>()
|
||||
const [validationError, setValidationError] = useState<string>()
|
||||
|
||||
const runRenameTag = useCallback(
|
||||
(tagId: string) => {
|
||||
|
@ -43,6 +45,16 @@ export default function RenameTagModal({
|
|||
[tag, runRenameTag]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (newTagName && newTagName.length > MAX_TAG_LENGTH) {
|
||||
setValidationError(
|
||||
t('tag_name_cannot_exceed_characters', { maxLength: MAX_TAG_LENGTH })
|
||||
)
|
||||
} else if (validationError) {
|
||||
setValidationError(undefined)
|
||||
}
|
||||
}, [newTagName, t, validationError])
|
||||
|
||||
if (!tag) {
|
||||
return null
|
||||
}
|
||||
|
@ -74,6 +86,11 @@ export default function RenameTagModal({
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
{validationError && (
|
||||
<div className="modal-footer-left">
|
||||
<span className="text-danger error">{validationError}</span>
|
||||
</div>
|
||||
)}
|
||||
{isError && (
|
||||
<div className="modal-footer-left">
|
||||
<span className="text-danger error">
|
||||
|
@ -87,9 +104,11 @@ export default function RenameTagModal({
|
|||
<Button
|
||||
onClick={() => runRenameTag(tag._id)}
|
||||
bsStyle="primary"
|
||||
disabled={status === 'pending' || !newTagName?.length}
|
||||
disabled={
|
||||
status === 'pending' || !newTagName?.length || !!validationError
|
||||
}
|
||||
>
|
||||
{status === 'pending' ? t('renaming') + '...' : t('rename')}
|
||||
{status === 'pending' ? t('renaming') + '…' : t('rename')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export const MAX_TAG_LENGTH = 50
|
|
@ -1862,5 +1862,6 @@
|
|||
"descending": "Descending",
|
||||
"reverse_x_sort_order" : "Reverse __x__ sort order",
|
||||
"create_first_project": "Create First Project",
|
||||
"you_dont_have_any_repositories": "You don’t have any repositories"
|
||||
"you_dont_have_any_repositories": "You don’t have any repositories",
|
||||
"tag_name_cannot_exceed_characters": "Tag name cannot exceed __maxLength__ characters"
|
||||
}
|
||||
|
|
|
@ -65,6 +65,19 @@ describe('TagsHandler', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('when tag is too long', function () {
|
||||
it('should throw an error', function (done) {
|
||||
this.TagsHandler.createTag(
|
||||
this.tag.user_id,
|
||||
'this is a tag that is very very very very very very long',
|
||||
err => {
|
||||
expect(err.message).to.equal('Exceeded max tag length')
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when insert has duplicate key error error', function () {
|
||||
beforeEach(function () {
|
||||
this.duplicateKeyError = new Error('Duplicate')
|
||||
|
@ -255,5 +268,19 @@ describe('TagsHandler', function () {
|
|||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when tag is too long', function () {
|
||||
it('should throw an error', function (done) {
|
||||
this.TagsHandler.renameTag(
|
||||
this.userId,
|
||||
this.tagId,
|
||||
'this is a tag that is very very very very very very long',
|
||||
err => {
|
||||
expect(err.message).to.equal('Exceeded max tag length')
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue