This repository has been archived on 2022-08-14. You can view files and clone it, but cannot push or open issues or pull requests.
outline/server/models/Document.js

133 lines
3.2 KiB
JavaScript
Raw Normal View History

2016-05-26 05:38:45 +00:00
import slug from 'slug';
import _ from 'lodash';
2016-05-26 05:38:45 +00:00
import randomstring from 'randomstring';
2017-04-27 04:47:03 +00:00
import { DataTypes, sequelize } from '../sequelize';
import { convertToMarkdown } from '../../frontend/utils/markdown';
import { truncateMarkdown } from '../utils/truncate';
2016-05-22 14:25:13 +00:00
import User from './User';
2016-06-26 18:23:03 +00:00
import Revision from './Revision';
2016-04-29 05:25:37 +00:00
slug.defaults.mode = 'rfc3986';
const slugify = text =>
slug(text, {
remove: /[.]/g,
});
2016-05-26 05:38:45 +00:00
2017-04-27 04:47:03 +00:00
const createRevision = async doc => {
2016-08-12 13:36:48 +00:00
// Create revision of the current (latest)
await Revision.create({
title: doc.title,
text: doc.text,
html: doc.html,
preview: doc.preview,
userId: doc.lastModifiedById,
documentId: doc.id,
});
};
2017-04-27 04:47:03 +00:00
const documentBeforeSave = async doc => {
2016-06-26 18:23:03 +00:00
doc.html = convertToMarkdown(doc.text);
doc.preview = truncateMarkdown(doc.text, 160);
doc.revisionCount += 1;
// Collaborators
2016-08-18 21:42:53 +00:00
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']],
where: {
documentId: doc.id,
},
}).map(rev => rev.userId);
}
// We'll add the current user as revision hasn't been generated yet
ids.push(doc.lastModifiedById);
doc.collaboratorIds = _.uniq(ids);
2016-06-26 18:23:03 +00:00
return doc;
};
2017-04-27 04:47:03 +00:00
const Document = sequelize.define(
'document',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
urlId: { type: DataTypes.STRING, primaryKey: true },
private: { type: DataTypes.BOOLEAN, defaultValue: true },
title: DataTypes.STRING,
text: DataTypes.TEXT,
html: DataTypes.TEXT,
preview: DataTypes.TEXT,
revisionCount: { type: DataTypes.INTEGER, defaultValue: 0 },
2016-06-22 07:01:57 +00:00
2017-04-27 04:47:03 +00:00
parentDocumentId: DataTypes.UUID,
createdById: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'users',
},
},
2017-04-27 04:47:03 +00:00
lastModifiedById: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'users',
},
},
2017-04-27 04:47:03 +00:00
collaboratorIds: DataTypes.ARRAY(DataTypes.UUID),
2016-06-26 18:23:03 +00:00
},
2017-04-27 04:47:03 +00:00
{
paranoid: true,
hooks: {
beforeValidate: doc => {
doc.urlId = doc.urlId || randomstring.generate(10);
},
beforeCreate: documentBeforeSave,
beforeUpdate: documentBeforeSave,
afterCreate: async doc => await createRevision(doc),
afterUpdate: async doc => await createRevision(doc),
2016-05-26 05:38:45 +00:00
},
2017-04-27 04:47:03 +00:00
instanceMethods: {
getUrl() {
const slugifiedTitle = slugify(this.title);
2017-04-27 04:47:03 +00:00
return `/d/${slugifiedTitle}-${this.urlId}`;
},
2016-06-21 06:12:56 +00:00
},
2017-04-27 04:47:03 +00:00
}
);
2016-04-29 05:25:37 +00:00
2016-05-22 14:25:13 +00:00
Document.belongsTo(User);
2016-04-29 05:25:37 +00:00
2016-08-23 06:37:01 +00:00
Document.searchForUser = async (user, query, options = {}) => {
const limit = options.limit || 15;
const offset = options.offset || 0;
const sql = `
SELECT * FROM documents
WHERE "searchVector" @@ plainto_tsquery('english', :query) AND
"teamId" = '${user.teamId}'::uuid AND
"deletedAt" IS NULL
2016-08-23 06:49:28 +00:00
ORDER BY ts_rank(documents."searchVector", plainto_tsquery('english', :query)) DESC
LIMIT :limit OFFSET :offset;
2016-08-23 06:37:01 +00:00
`;
2017-04-27 04:47:03 +00:00
const documents = await sequelize.query(sql, {
replacements: {
query,
limit,
offset,
},
model: Document,
});
2016-08-23 06:37:01 +00:00
return documents;
2016-09-11 22:48:43 +00:00
};
2016-08-23 06:37:01 +00:00
2016-05-20 03:46:34 +00:00
export default Document;