Merge branch 'main' of github.com:outline/outline
This commit is contained in:
@@ -19,6 +19,8 @@ type Props = {
|
||||
templateId?: string | null;
|
||||
/** If the document should be displayed full-width on the screen */
|
||||
fullWidth?: boolean;
|
||||
/** Whether insights should be visible on the document */
|
||||
insightsEnabled?: boolean;
|
||||
/** Whether the text be appended to the end instead of replace */
|
||||
append?: boolean;
|
||||
/** Whether the document should be published to the collection */
|
||||
@@ -46,6 +48,7 @@ export default async function documentUpdater({
|
||||
editorVersion,
|
||||
templateId,
|
||||
fullWidth,
|
||||
insightsEnabled,
|
||||
append,
|
||||
publish,
|
||||
collectionId,
|
||||
@@ -68,6 +71,9 @@ export default async function documentUpdater({
|
||||
if (fullWidth !== undefined) {
|
||||
document.fullWidth = fullWidth;
|
||||
}
|
||||
if (insightsEnabled !== undefined) {
|
||||
document.insightsEnabled = insightsEnabled;
|
||||
}
|
||||
if (text !== undefined) {
|
||||
document = DocumentHelper.applyMarkdownToDocument(document, text, append);
|
||||
}
|
||||
|
||||
15
server/migrations/20230720002422-add-insights-control.js
Normal file
15
server/migrations/20230720002422-add-insights-control.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn("documents", "insightsEnabled", {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true,
|
||||
});
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
await queryInterface.removeColumn("documents", "insightsEnabled");
|
||||
}
|
||||
};
|
||||
@@ -196,6 +196,9 @@ class Document extends ParanoidModel {
|
||||
@Column
|
||||
fullWidth: boolean;
|
||||
|
||||
@Column
|
||||
insightsEnabled: boolean;
|
||||
|
||||
@SimpleLength({
|
||||
max: 255,
|
||||
msg: `editorVersion must be 255 characters or less`,
|
||||
|
||||
@@ -277,6 +277,20 @@ allow(User, "archive", Document, (user, document) => {
|
||||
return user.teamId === document.teamId;
|
||||
});
|
||||
|
||||
allow(User, "updateInsights", Document, (user, document) => {
|
||||
if (!document || !document.isActive || document.isDraft) {
|
||||
return false;
|
||||
}
|
||||
invariant(
|
||||
document.collection,
|
||||
"collection is missing, did you forget to include in the query scope?"
|
||||
);
|
||||
if (cannot(user, "update", document.collection)) {
|
||||
return false;
|
||||
}
|
||||
return user.teamId === document.teamId;
|
||||
});
|
||||
|
||||
allow(User, "unarchive", Document, (user, document) => {
|
||||
if (!document) {
|
||||
return false;
|
||||
|
||||
@@ -41,6 +41,7 @@ async function presentDocument(
|
||||
templateId: document.templateId,
|
||||
collaboratorIds: [],
|
||||
revision: document.revisionCount,
|
||||
insightsEnabled: document.insightsEnabled,
|
||||
fullWidth: document.fullWidth,
|
||||
collectionId: undefined,
|
||||
parentDocumentId: undefined,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { differenceInMinutes, formatDistanceToNowStrict } from "date-fns";
|
||||
import { t } from "i18next";
|
||||
import { head, orderBy } from "lodash";
|
||||
import { dateLocale } from "@shared/utils/date";
|
||||
import { Document, User } from "@server/models";
|
||||
import { Document, User, View } from "@server/models";
|
||||
import { opts } from "@server/utils/i18n";
|
||||
|
||||
export const presentLastOnlineInfoFor = (user: User) => {
|
||||
@@ -26,8 +25,17 @@ export const presentLastOnlineInfoFor = (user: User) => {
|
||||
return info;
|
||||
};
|
||||
|
||||
export const presentLastViewedInfoFor = (user: User, document: Document) => {
|
||||
const lastView = head(orderBy(document.views, ["updatedAt"], ["desc"]));
|
||||
export const presentLastViewedInfoFor = async (
|
||||
user: User,
|
||||
document: Document
|
||||
) => {
|
||||
const lastView = await View.findOne({
|
||||
where: {
|
||||
userId: user.id,
|
||||
documentId: document.id,
|
||||
},
|
||||
order: [["updatedAt", "DESC"]],
|
||||
});
|
||||
const lastViewedAt = lastView ? lastView.updatedAt : undefined;
|
||||
const locale = dateLocale(user.language);
|
||||
|
||||
|
||||
@@ -2,16 +2,17 @@ import { Unfurl, UnfurlType } from "@shared/types";
|
||||
import { Document, User } from "@server/models";
|
||||
import { presentLastOnlineInfoFor, presentLastViewedInfoFor } from "./common";
|
||||
|
||||
function presentMention(
|
||||
async function presentMention(
|
||||
user: User,
|
||||
document: Document
|
||||
): Unfurl<UnfurlType.Mention> {
|
||||
): Promise<Unfurl<UnfurlType.Mention>> {
|
||||
const lastOnlineInfo = presentLastOnlineInfoFor(user);
|
||||
const lastViewedInfo = await presentLastViewedInfoFor(user, document);
|
||||
|
||||
return {
|
||||
type: UnfurlType.Mention,
|
||||
title: user.name,
|
||||
description: `${presentLastOnlineInfoFor(
|
||||
user
|
||||
)} • ${presentLastViewedInfoFor(user, document)}`,
|
||||
description: `${lastOnlineInfo} • ${lastViewedInfo}`,
|
||||
thumbnailUrl: user.avatarUrl,
|
||||
meta: {
|
||||
id: user.id,
|
||||
|
||||
@@ -892,18 +892,8 @@ router.post(
|
||||
auth(),
|
||||
validate(T.DocumentsUpdateSchema),
|
||||
async (ctx: APIContext<T.DocumentsUpdateReq>) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
text,
|
||||
fullWidth,
|
||||
publish,
|
||||
templateId,
|
||||
collectionId,
|
||||
append,
|
||||
apiVersion,
|
||||
done,
|
||||
} = ctx.input.body;
|
||||
const { id, apiVersion, insightsEnabled, publish, collectionId, ...input } =
|
||||
ctx.input.body;
|
||||
const editorVersion = ctx.headers["x-editor-version"] as string | undefined;
|
||||
const { user } = ctx.state.auth;
|
||||
let collection: Collection | null | undefined;
|
||||
@@ -915,6 +905,10 @@ router.post(
|
||||
collection = document?.collection;
|
||||
authorize(user, "update", document);
|
||||
|
||||
if (collection && insightsEnabled !== undefined) {
|
||||
authorize(user, "updateInsights", document);
|
||||
}
|
||||
|
||||
if (publish) {
|
||||
if (!document.collectionId) {
|
||||
assertPresent(
|
||||
@@ -932,16 +926,12 @@ router.post(
|
||||
await documentUpdater({
|
||||
document,
|
||||
user,
|
||||
title,
|
||||
text,
|
||||
fullWidth,
|
||||
...input,
|
||||
publish,
|
||||
collectionId,
|
||||
append,
|
||||
templateId,
|
||||
insightsEnabled,
|
||||
editorVersion,
|
||||
transaction,
|
||||
done,
|
||||
ip: ctx.request.ip,
|
||||
});
|
||||
|
||||
|
||||
@@ -189,6 +189,9 @@ export const DocumentsUpdateSchema = BaseSchema.extend({
|
||||
/** Boolean to denote if the doc should occupy full width */
|
||||
fullWidth: z.boolean().optional(),
|
||||
|
||||
/** Boolean to denote if insights should be visible on the doc */
|
||||
insightsEnabled: z.boolean().optional(),
|
||||
|
||||
/** Boolean to denote if the doc should be published */
|
||||
publish: z.boolean().optional(),
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ router.post(
|
||||
authorize(actor, "read", user);
|
||||
authorize(actor, "read", document);
|
||||
|
||||
ctx.body = presentMention(user, document);
|
||||
ctx.body = await presentMention(user, document);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Router from "koa-router";
|
||||
import { ValidationError } from "@server/errors";
|
||||
import auth from "@server/middlewares/authentication";
|
||||
import { rateLimiter } from "@server/middlewares/rateLimiter";
|
||||
import validate from "@server/middlewares/validate";
|
||||
@@ -23,6 +24,11 @@ router.post(
|
||||
userId: user.id,
|
||||
});
|
||||
authorize(user, "read", document);
|
||||
|
||||
if (!document.insightsEnabled) {
|
||||
throw ValidationError("Insights are not enabled for this document");
|
||||
}
|
||||
|
||||
const views = await View.findByDocument(documentId, { includeSuspended });
|
||||
|
||||
ctx.body = {
|
||||
|
||||
Reference in New Issue
Block a user