mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
share: add allowIndexing (#7896)
* share: add `allowIndexing` ## Ticket Closes 7486 * i18n: follow existing no-punctuation style
This commit is contained in:
committed by
GitHub
parent
9747c6ba5d
commit
ca17b41c53
@@ -1,12 +1,13 @@
|
||||
import debounce from "lodash/debounce";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { observer } from "mobx-react";
|
||||
import { CopyIcon, GlobeIcon, InfoIcon } from "outline-icons";
|
||||
import { CopyIcon, GlobeIcon, InfoIcon, QuestionMarkIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import Flex from "@shared/components/Flex";
|
||||
import Squircle from "@shared/components/Squircle";
|
||||
import { s } from "@shared/styles";
|
||||
import { UrlHelper } from "@shared/utils/UrlHelper";
|
||||
@@ -50,6 +51,19 @@ function PublicAccess({ document, share, sharedParent }: Props) {
|
||||
setUrlId(share?.urlId);
|
||||
}, [share?.urlId]);
|
||||
|
||||
const handleIndexingChanged = React.useCallback(
|
||||
async (event) => {
|
||||
try {
|
||||
await share?.save({
|
||||
allowIndexing: event.currentTarget.checked,
|
||||
});
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
[share]
|
||||
);
|
||||
|
||||
const handlePublishedChange = React.useCallback(
|
||||
async (event) => {
|
||||
try {
|
||||
@@ -175,6 +189,32 @@ function PublicAccess({ document, share, sharedParent }: Props) {
|
||||
</ShareLinkInput>
|
||||
) : null}
|
||||
|
||||
{share?.published && (
|
||||
<ListItem
|
||||
title={
|
||||
<Flex>
|
||||
{t("Search engine indexing")}
|
||||
<Tooltip
|
||||
content={t(
|
||||
"Disable this setting to discourage search engines from indexing the page"
|
||||
)}
|
||||
>
|
||||
<QuestionMarkIcon size={18} />
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
}
|
||||
actions={
|
||||
<Switch
|
||||
aria-label={t("Search engine indexing")}
|
||||
checked={share?.allowIndexing ?? false}
|
||||
onChange={handleIndexingChanged}
|
||||
width={26}
|
||||
height={14}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{share?.published && !share.includeChildDocuments ? (
|
||||
<Text as="p" type="tertiary" size="xsmall">
|
||||
<StyledInfoIcon size={18} />
|
||||
|
||||
@@ -55,6 +55,10 @@ class Share extends Model {
|
||||
@observable
|
||||
url: string;
|
||||
|
||||
@Field
|
||||
@observable
|
||||
allowIndexing: boolean;
|
||||
|
||||
/** The user that shared the document. */
|
||||
@Relation(() => User, { onDelete: "null" })
|
||||
createdBy: User;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.addColumn("shares", "allowIndexing", {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true,
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface) => {
|
||||
await queryInterface.removeColumn("shares", "allowIndexing");
|
||||
},
|
||||
};
|
||||
@@ -184,6 +184,10 @@ class Share extends IdModel<
|
||||
@Column(DataType.UUID)
|
||||
documentId: string;
|
||||
|
||||
@Default(true)
|
||||
@Column
|
||||
allowIndexing: boolean;
|
||||
|
||||
revoke(userId: string) {
|
||||
this.revokedAt = new Date();
|
||||
this.revokedById = userId;
|
||||
|
||||
@@ -12,6 +12,7 @@ export default function presentShare(share: Share, isAdmin = false) {
|
||||
urlId: share.urlId,
|
||||
createdBy: presentUser(share.user),
|
||||
includeChildDocuments: share.includeChildDocuments,
|
||||
allowIndexing: share.allowIndexing,
|
||||
lastAccessedAt: share.lastAccessedAt || undefined,
|
||||
views: share.views || 0,
|
||||
domain: share.domain,
|
||||
|
||||
@@ -51,6 +51,7 @@ export const SharesUpdateSchema = BaseSchema.extend({
|
||||
id: z.string().uuid(),
|
||||
includeChildDocuments: z.boolean().optional(),
|
||||
published: z.boolean().optional(),
|
||||
allowIndexing: z.boolean().optional(),
|
||||
urlId: z
|
||||
.string()
|
||||
.regex(UrlHelper.SHARE_URL_SLUG_REGEX, {
|
||||
|
||||
@@ -230,7 +230,8 @@ router.post(
|
||||
auth(),
|
||||
validate(T.SharesUpdateSchema),
|
||||
async (ctx: APIContext<T.SharesUpdateReq>) => {
|
||||
const { id, includeChildDocuments, published, urlId } = ctx.input.body;
|
||||
const { id, includeChildDocuments, published, urlId, allowIndexing } =
|
||||
ctx.input.body;
|
||||
|
||||
const { user } = ctx.state.auth;
|
||||
authorize(user, "share", user.team);
|
||||
@@ -257,6 +258,10 @@ router.post(
|
||||
share.urlId = urlId;
|
||||
}
|
||||
|
||||
if (allowIndexing !== undefined) {
|
||||
share.allowIndexing = allowIndexing;
|
||||
}
|
||||
|
||||
await share.save();
|
||||
await Event.createFromContext(ctx, {
|
||||
name: "shares.update",
|
||||
|
||||
+10
-2
@@ -54,6 +54,7 @@ export const renderApp = async (
|
||||
rootShareId?: string;
|
||||
isShare?: boolean;
|
||||
analytics?: Integration<IntegrationType.Analytics>[];
|
||||
allowIndexing?: boolean;
|
||||
} = {}
|
||||
) => {
|
||||
const {
|
||||
@@ -61,6 +62,7 @@ export const renderApp = async (
|
||||
description = "A modern team knowledge base for your internal documentation, product specs, support answers, meeting notes, onboarding, & more…",
|
||||
canonical = "",
|
||||
shortcutIcon = `${env.CDN_URL || ""}/images/favicon-32.png`,
|
||||
allowIndexing = true,
|
||||
} = options;
|
||||
|
||||
if (ctx.request.path === "/realtime/") {
|
||||
@@ -91,6 +93,10 @@ export const renderApp = async (
|
||||
</script>
|
||||
`;
|
||||
|
||||
const noIndexTag = allowIndexing
|
||||
? ""
|
||||
: '<meta name="robots" content="noindex, nofollow">';
|
||||
|
||||
const scriptTags = env.isProduction
|
||||
? `<script type="module" nonce="${ctx.state.cspNonce}" src="${
|
||||
env.CDN_URL || ""
|
||||
@@ -112,6 +118,7 @@ export const renderApp = async (
|
||||
.replace(/\{lang\}/g, unicodeCLDRtoISO639(env.DEFAULT_LANGUAGE))
|
||||
.replace(/\{title\}/g, escape(title))
|
||||
.replace(/\{description\}/g, escape(description))
|
||||
.replace(/\{noindex\}/g, noIndexTag)
|
||||
.replace(
|
||||
/\{manifest-url\}/g,
|
||||
options.isShare ? "" : "/static/manifest.webmanifest"
|
||||
@@ -131,8 +138,8 @@ export const renderShare = async (ctx: Context, next: Next) => {
|
||||
const documentSlug = ctx.params.documentSlug;
|
||||
|
||||
// Find the share record if publicly published so that the document title
|
||||
// can be be returned in the server-rendered HTML. This allows it to appear in
|
||||
// unfurls with more reliablity
|
||||
// can be returned in the server-rendered HTML. This allows it to appear in
|
||||
// unfurls with more reliability
|
||||
let share, document, team;
|
||||
let analytics: Integration<IntegrationType.Analytics>[] = [];
|
||||
|
||||
@@ -188,5 +195,6 @@ export const renderShare = async (ctx: Context, next: Next) => {
|
||||
canonical: share
|
||||
? `${share.canonicalUrl}${documentSlug && document ? document.url : ""}`
|
||||
: undefined,
|
||||
allowIndexing: share?.allowIndexing,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="description" content="{description}" />
|
||||
<meta name="darkreader-lock" />
|
||||
{noindex}
|
||||
<link rel="manifest" href="{manifest-url}" />
|
||||
<link rel="canonical" href="{canonical-url}" data-react-helmet="true" />
|
||||
{prefetch}
|
||||
|
||||
@@ -350,6 +350,8 @@
|
||||
"Anyone with the link can access because the parent document, <2>{{documentTitle}}</2>, is shared": "Anyone with the link can access because the parent document, <2>{{documentTitle}}</2>, is shared",
|
||||
"Allow anyone with the link to access": "Allow anyone with the link to access",
|
||||
"Publish to internet": "Publish to internet",
|
||||
"Search engine indexing": "Search engine indexing",
|
||||
"Disable this setting to discourage search engines from indexing the page": "Disable this setting to discourage search engines from indexing the page",
|
||||
"Nested documents are not shared on the web. Toggle sharing to enable access, this will be the default behavior in the future": "Nested documents are not shared on the web. Toggle sharing to enable access, this will be the default behavior in the future",
|
||||
"{{ userName }} was added to the document": "{{ userName }} was added to the document",
|
||||
"{{ count }} people added to the document": "{{ count }} people added to the document",
|
||||
|
||||
Reference in New Issue
Block a user