diff --git a/app/models/Document.js b/app/models/Document.js index 70488fb2..a2769f7e 100644 --- a/app/models/Document.js +++ b/app/models/Document.js @@ -11,8 +11,6 @@ import type { User } from 'types'; import BaseModel from './BaseModel'; import Collection from './Collection'; -const DEFAULT_TITLE = 'Untitled document'; - class Document extends BaseModel { isSaving: boolean = false; hasPendingChanges: boolean = false; @@ -172,14 +170,6 @@ class Document extends BaseModel { lastRevision: this.revision, }); } else { - if (!this.title) { - this.title = DEFAULT_TITLE; - this.text = this.text.replace( - new RegExp(`^# `), - `# ${DEFAULT_TITLE}` - ); - } - const data = { parentDocument: undefined, collection: this.collection.id, diff --git a/server/api/documents.test.js b/server/api/documents.test.js index e1eec7bb..d579af82 100644 --- a/server/api/documents.test.js +++ b/server/api/documents.test.js @@ -247,6 +247,22 @@ describe('#documents.create', async () => { expect(newDocument.collection.id).toBe(collection.id); }); + it('should fallback to a default title', async () => { + const { user, collection } = await seed(); + const res = await server.post('/api/documents.create', { + body: { + token: user.getJwtToken(), + collection: collection.id, + title: ' ', + text: ' ', + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data.title).toBe('Untitled document'); + expect(body.data.text).toBe('# Untitled document'); + }); + it('should create as a child', async () => { const { user, document, collection } = await seed(); const res = await server.post('/api/documents.create', { @@ -288,6 +304,26 @@ describe('#documents.update', async () => { expect(body.data.collection.documents[1].title).toBe('Updated title'); }); + it('should fallback to a default title', async () => { + const { user, document } = await seed(); + + const res = await server.post('/api/documents.update', { + body: { + token: user.getJwtToken(), + id: document.id, + title: ' ', + text: ' ', + lastRevision: document.revision, + }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.data.title).toBe('Untitled document'); + expect(body.data.text).toBe('# Untitled document'); + expect(body.data.collection.documents[1].title).toBe('Untitled document'); + }); + it('should fail if document lastRevision does not match', async () => { const { user, document } = await seed(); diff --git a/server/models/Document.js b/server/models/Document.js index 32270207..a1507326 100644 --- a/server/models/Document.js +++ b/server/models/Document.js @@ -13,6 +13,7 @@ import Revision from './Revision'; const Markdown = new MarkdownSerializer(); const URL_REGEX = /^[a-zA-Z0-9-]*-([a-zA-Z0-9]{10,15})$/; +const DEFAULT_TITLE = 'Untitled document'; // $FlowIssue invalid flow-typed slug.defaults.mode = 'rfc3986'; @@ -36,14 +37,19 @@ const createUrlId = doc => { }; const beforeSave = async doc => { - const { emoji } = parseTitle(doc.text); + const { emoji, title } = parseTitle(doc.text); + // emoji in the title is split out for easier display doc.emoji = emoji; - doc.revisionCount += 1; - // Collaborators + // ensure document has a title + if (!title) { + doc.title = DEFAULT_TITLE; + doc.text = doc.text.replace(/^.*$/m, `# ${DEFAULT_TITLE}`); + } + + // calculate collaborators let ids = []; - // Only get previous user IDs if the document already exists if (doc.id) { ids = await Revision.findAll({ attributes: [[DataTypes.literal('DISTINCT "userId"'), 'userId']], @@ -52,10 +58,14 @@ const beforeSave = async doc => { }, }).map(rev => rev.userId); } - // We'll add the current user as revision hasn't been generated yet + + // add the current user as revision hasn't been generated yet ids.push(doc.lastModifiedById); doc.collaboratorIds = _.uniq(ids); + // increment revision + doc.revisionCount += 1; + return doc; }; diff --git a/server/presenters/document.js b/server/presenters/document.js index 4414522f..2120cf9d 100644 --- a/server/presenters/document.js +++ b/server/presenters/document.js @@ -17,8 +17,9 @@ async function present(ctx: Object, document: Document, options: ?Options) { ctx.cache.set(document.id, document); // For empty document content, return the title - if (document.text.trim().length === 0) - document.text = `# ${document.title || 'Untitled document'}`; + if (!document.text.trim()) { + document.text = `# ${document.title}`; + } const data = { id: document.id,