mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9be053bd7 | |||
| 60e7f3bb66 | |||
| e9fe6ff572 | |||
| 7423bff401 | |||
| f81638d0e0 | |||
| dd811ca787 | |||
| 2d0690697c | |||
| 6b551749d4 | |||
| 52fc861bcf | |||
| c81c9a9d2d | |||
| 29c742a673 | |||
| dd249021e7 | |||
| 21d3b9c7e0 | |||
| 6665dfff28 | |||
| cdfe3a7fc3 | |||
| 401c91f90b | |||
| ed5320507d |
+94
-2
@@ -1,4 +1,12 @@
|
||||
version: 2
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
docker-publisher:
|
||||
environment:
|
||||
IMAGE_NAME: outlinewiki/outline
|
||||
docker:
|
||||
- image: circleci/buildpack-deps:stretch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/outline
|
||||
@@ -40,4 +48,88 @@ jobs:
|
||||
command: yarn test
|
||||
- run:
|
||||
name: build-webpack
|
||||
command: yarn build:webpack
|
||||
command: yarn build:webpack
|
||||
build-image:
|
||||
executor: docker-publisher
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 20.10.6
|
||||
- run:
|
||||
name: Build Docker image
|
||||
command: docker build -t $IMAGE_NAME:latest .
|
||||
- run:
|
||||
name: Archive Docker image
|
||||
command: docker save -o image.tar $IMAGE_NAME
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./image.tar
|
||||
publish-latest:
|
||||
executor: docker-publisher
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- setup_remote_docker:
|
||||
version: 20.10.6
|
||||
- run:
|
||||
name: Load archived Docker image
|
||||
command: docker load -i /tmp/workspace/image.tar
|
||||
- run:
|
||||
name: Publish Docker Image to Docker Hub
|
||||
command: |
|
||||
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
|
||||
IMAGE_TAG=${CIRCLE_TAG/v/''}
|
||||
docker tag $IMAGE_NAME:latest $IMAGE_NAME:$IMAGE_TAG
|
||||
docker push $IMAGE_NAME:latest
|
||||
docker push $IMAGE_NAME:$IMAGE_TAG
|
||||
publish-tag:
|
||||
executor: docker-publisher
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- setup_remote_docker:
|
||||
version: 20.10.6
|
||||
- run:
|
||||
name: Load archived Docker image
|
||||
command: docker load -i /tmp/workspace/image.tar
|
||||
- run:
|
||||
name: Publish Docker Image to Docker Hub
|
||||
command: |
|
||||
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
|
||||
IMAGE_TAG=${CIRCLE_TAG/v/''}
|
||||
docker tag $IMAGE_NAME:latest $IMAGE_NAME:$IMAGE_TAG
|
||||
docker push $IMAGE_NAME:$IMAGE_TAG
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build:
|
||||
filters:
|
||||
tags:
|
||||
ignore: /^v.*/
|
||||
build-docker:
|
||||
jobs:
|
||||
- build-image:
|
||||
filters:
|
||||
tags:
|
||||
only: /^v.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- publish-latest:
|
||||
requires:
|
||||
- build-image
|
||||
filters:
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- publish-tag:
|
||||
requires:
|
||||
- build-image
|
||||
filters:
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+-.*$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
+40
-13
@@ -1,23 +1,50 @@
|
||||
FROM node:14-alpine
|
||||
# syntax=docker/dockerfile:1.2
|
||||
ARG APP_PATH=/opt/outline
|
||||
FROM node:14-alpine AS deps-common
|
||||
|
||||
ENV APP_PATH /opt/outline
|
||||
RUN mkdir -p $APP_PATH
|
||||
ARG APP_PATH
|
||||
WORKDIR $APP_PATH
|
||||
COPY ./package.json ./yarn.lock ./
|
||||
|
||||
# ---
|
||||
FROM deps-common AS deps-dev
|
||||
RUN yarn install --no-optional --frozen-lockfile && \
|
||||
yarn cache clean
|
||||
|
||||
# ---
|
||||
FROM deps-common AS deps-prod
|
||||
RUN yarn install --production=true --frozen-lockfile && \
|
||||
yarn cache clean
|
||||
|
||||
# ---
|
||||
FROM node:14-alpine AS builder
|
||||
|
||||
ARG APP_PATH
|
||||
WORKDIR $APP_PATH
|
||||
|
||||
COPY package.json ./
|
||||
COPY yarn.lock ./
|
||||
|
||||
RUN yarn --pure-lockfile
|
||||
|
||||
COPY . .
|
||||
COPY --from=deps-dev $APP_PATH/node_modules ./node_modules
|
||||
RUN yarn build
|
||||
|
||||
RUN yarn build && \
|
||||
yarn --production --ignore-scripts --prefer-offline && \
|
||||
rm -rf shared && \
|
||||
rm -rf app
|
||||
# ---
|
||||
FROM node:14-alpine AS runner
|
||||
|
||||
ARG APP_PATH
|
||||
WORKDIR $APP_PATH
|
||||
ENV NODE_ENV production
|
||||
CMD yarn start
|
||||
|
||||
COPY --from=builder $APP_PATH/build ./build
|
||||
COPY --from=builder $APP_PATH/server ./server
|
||||
COPY --from=builder $APP_PATH/public ./public
|
||||
COPY --from=builder $APP_PATH/.sequelizerc ./.sequelizerc
|
||||
COPY --from=deps-prod $APP_PATH/node_modules ./node_modules
|
||||
COPY --from=builder $APP_PATH/package.json ./package.json
|
||||
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nodejs -u 1001 && \
|
||||
chown -R nodejs:nodejs $APP_PATH/build
|
||||
|
||||
USER nodejs
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["yarn", "start"]
|
||||
|
||||
@@ -4,7 +4,7 @@ import Image from "components/Image";
|
||||
import Frame from "./components/Frame";
|
||||
|
||||
const URL_REGEX = new RegExp(
|
||||
"^https?://drive.google.com/file/d/(.*)/(preview|view).?usp=sharing$"
|
||||
"^https?://drive.google.com/file/d/(.*)/(preview|view).?usp=sharing(.*)"
|
||||
);
|
||||
|
||||
type Props = {|
|
||||
@@ -29,7 +29,7 @@ export default class GoogleDrive extends React.Component<Props> {
|
||||
height={16}
|
||||
/>
|
||||
}
|
||||
title="Google Drive Embed"
|
||||
title="Google Drive"
|
||||
canonicalUrl={this.props.attrs.href}
|
||||
border
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import GoogleDrive from "./GoogleDrive";
|
||||
|
||||
describe("GoogleDrive", () => {
|
||||
const match = GoogleDrive.ENABLED[0];
|
||||
|
||||
test("to be enabled on share link", () => {
|
||||
expect(
|
||||
"https://drive.google.com/file/d/1ohkOgmE8MiNx68u6ynBfYkgjeKu_x3ZK/view?usp=sharing".match(
|
||||
@@ -14,6 +15,11 @@ describe("GoogleDrive", () => {
|
||||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
"https://drive.google.com/file/d/1ohkOgmE8MiNx68u6ynBfYkgjeKu_x3ZK/preview?usp=sharing&resourceKey=BG8k4dEt1p2gisnVdlaSpA".match(
|
||||
match
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("to not be enabled elsewhere", () => {
|
||||
|
||||
@@ -18,38 +18,29 @@ function Security() {
|
||||
const team = useCurrentTeam();
|
||||
const { t } = useTranslation();
|
||||
const { showToast } = useToasts();
|
||||
const [sharing, setSharing] = useState(team.documentEmbeds);
|
||||
const [documentEmbeds, setDocumentEmbeds] = useState(team.guestSignin);
|
||||
const [guestSignin, setGuestSignin] = useState(team.sharing);
|
||||
const [data, setData] = useState({
|
||||
sharing: team.sharing,
|
||||
documentEmbeds: team.documentEmbeds,
|
||||
guestSignin: team.guestSignin,
|
||||
});
|
||||
|
||||
const showSuccessMessage = debounce(() => {
|
||||
showToast(t("Settings saved"), { type: "success" });
|
||||
}, 500);
|
||||
const showSuccessMessage = React.useCallback(
|
||||
debounce(() => {
|
||||
showToast(t("Settings saved"), { type: "success" });
|
||||
}, 250),
|
||||
[t, showToast]
|
||||
);
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
async (ev: SyntheticInputEvent<*>) => {
|
||||
switch (ev.target.name) {
|
||||
case "sharing":
|
||||
setSharing(ev.target.checked);
|
||||
break;
|
||||
case "documentEmbeds":
|
||||
setDocumentEmbeds(ev.target.checked);
|
||||
break;
|
||||
case "guestSignin":
|
||||
setGuestSignin(ev.target.checked);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
const newData = { ...data, [ev.target.name]: ev.target.checked };
|
||||
setData(newData);
|
||||
|
||||
await auth.updateTeam({
|
||||
sharing,
|
||||
documentEmbeds,
|
||||
guestSignin,
|
||||
});
|
||||
await auth.updateTeam(newData);
|
||||
|
||||
showSuccessMessage();
|
||||
},
|
||||
[auth, sharing, documentEmbeds, guestSignin, showSuccessMessage]
|
||||
[auth, data, showSuccessMessage]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -67,14 +58,14 @@ function Security() {
|
||||
<Checkbox
|
||||
label={t("Allow email authentication")}
|
||||
name="guestSignin"
|
||||
checked={guestSignin}
|
||||
checked={data.guestSignin}
|
||||
onChange={handleChange}
|
||||
note={t("When enabled, users can sign-in using their email address")}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t("Public document sharing")}
|
||||
name="sharing"
|
||||
checked={sharing}
|
||||
checked={data.sharing}
|
||||
onChange={handleChange}
|
||||
note={t(
|
||||
"When enabled, documents can be shared publicly on the internet by any team member"
|
||||
@@ -83,7 +74,7 @@ function Security() {
|
||||
<Checkbox
|
||||
label={t("Rich service embeds")}
|
||||
name="documentEmbeds"
|
||||
checked={documentEmbeds}
|
||||
checked={data.documentEmbeds}
|
||||
onChange={handleChange}
|
||||
note={t(
|
||||
"Links to supported services are shown as rich embeds within your documents"
|
||||
|
||||
+3
-3
@@ -143,7 +143,7 @@
|
||||
"react-window": "^1.8.6",
|
||||
"reakit": "^1.3.8",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"rich-markdown-editor": "^11.17.0",
|
||||
"rich-markdown-editor": "^11.17.1",
|
||||
"semver": "^7.3.2",
|
||||
"sequelize": "^6.3.4",
|
||||
"sequelize-cli": "^6.2.0",
|
||||
@@ -211,5 +211,5 @@
|
||||
"dot-prop": "^5.2.0",
|
||||
"js-yaml": "^3.13.1"
|
||||
},
|
||||
"version": "0.57.0"
|
||||
}
|
||||
"version": "0.58.2"
|
||||
}
|
||||
|
||||
+10
-2
@@ -310,9 +310,10 @@ router.post("documents.viewed", auth(), pagination(), async (ctx) => {
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
const userId = user.id;
|
||||
|
||||
const views = await View.findAll({
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
order: [[sort, direction]],
|
||||
include: [
|
||||
{
|
||||
@@ -325,9 +326,16 @@ router.post("documents.viewed", auth(), pagination(), async (ctx) => {
|
||||
{
|
||||
model: Star,
|
||||
as: "starred",
|
||||
where: { userId: user.id },
|
||||
where: { userId },
|
||||
separate: true,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
model: Collection.scope({
|
||||
method: ["withMembership", userId],
|
||||
}),
|
||||
as: "collection",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1349,6 +1349,7 @@ describe("#documents.viewed", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.length).toEqual(1);
|
||||
expect(body.data[0].id).toEqual(document.id);
|
||||
expect(body.policies[0].abilities.update).toEqual(true);
|
||||
});
|
||||
|
||||
it("should not return recently viewed but deleted documents", async () => {
|
||||
|
||||
+2
-2
@@ -71,13 +71,13 @@ router.post("users.list", auth(), pagination(), async (ctx) => {
|
||||
}
|
||||
|
||||
const [users, total] = await Promise.all([
|
||||
await User.findAll({
|
||||
User.findAll({
|
||||
where,
|
||||
order: [[sort, direction]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
}),
|
||||
await User.count({
|
||||
User.count({
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -192,13 +192,25 @@ Document.associate = (models) => {
|
||||
|
||||
return {
|
||||
include: [
|
||||
{ model: models.View, as: "views", where: { userId }, required: false },
|
||||
{
|
||||
model: models.View,
|
||||
as: "views",
|
||||
where: { userId },
|
||||
required: false,
|
||||
separate: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
Document.addScope("withStarred", (userId) => ({
|
||||
include: [
|
||||
{ model: models.Star, as: "starred", where: { userId }, required: false },
|
||||
{
|
||||
model: models.Star,
|
||||
as: "starred",
|
||||
where: { userId },
|
||||
required: false,
|
||||
separate: true,
|
||||
},
|
||||
],
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -170,9 +170,9 @@ Team.prototype.provisionFirstCollection = async function (userId) {
|
||||
// For the first collection we go ahead and create some intitial documents to get
|
||||
// the team started. You can edit these in /server/onboarding/x.md
|
||||
const onboardingDocs = [
|
||||
"Support",
|
||||
"Integrations & API",
|
||||
"Our Editor",
|
||||
"Getting Started",
|
||||
"What is Outline",
|
||||
];
|
||||
|
||||
@@ -182,7 +182,7 @@ Team.prototype.provisionFirstCollection = async function (userId) {
|
||||
"utf8"
|
||||
);
|
||||
const document = await Document.create({
|
||||
version: 1,
|
||||
version: 2,
|
||||
isWelcome: true,
|
||||
parentDocumentId: null,
|
||||
collectionId: collection.id,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Some ideas to get you and your team started with learning the basics of Outline, feel free to check them off as you go!
|
||||
|
||||
## Learn the basics
|
||||
|
||||
- [x] Create an Outline account
|
||||
- [ ] **Create a collection** from the left sidebar
|
||||
- [ ] **Create a new doc** from the top right of home or any collection
|
||||
- [ ] Try drag and drop to nest and move documents
|
||||
- [ ] Share a document
|
||||
- [ ] Invite a co-worker 👋
|
||||
|
||||
## More to try
|
||||
|
||||
- [ ] Setup the [Slack integration](/settings/integrations/slack)
|
||||
- [ ] **Create a template** to share a writing structure with your team
|
||||
- [ ] Create a check list to track tasks
|
||||
- [ ] Try embedding a supported [integration](https://www.getoutline.com/integrations)
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
Outline supports many of the most popular tools on the market without any additional settings or configuration. Just paste links to a YouTube video, Figma file, or Google Spreadsheet to get instant live-embeds in your documents. Take a look at the [integrations directory](https://www.getoutline.com/integrations) for a list of all of the tools that are supported.
|
||||
|
||||
\
|
||||
Our integration code is also [open-source](https://github.com/outline/outline) and we encourage third party developers and the community to build support for additional tools!
|
||||
|
||||
\
|
||||
:::info
|
||||
Most integrations work by simply pasting a link from a supported service into a document.
|
||||
:::
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
The heart of Outline is the document editor. We let you write in whichever way you prefer – be it Markdown, WYSIWYG, or taking advantage of the many keyboard shortcuts.
|
||||
The heart of Outline is the document editor. We let you write in whichever way you prefer – be it Markdown, WYSIWYG, or taking advantage of the many keyboard shortcuts (Type `?` to see them all).
|
||||
|
||||
## Markdown
|
||||
|
||||
If you’re comfortable writing markdown then all of the shortcuts you are used to are supported, for example type \*\*bold\*\* to instantly create bold text. If you forget some syntax or are after a quick refresher hit the keyboard icon in the bottom right hand corner for our guide. Learning some of the key Markdown shortcuts will make using Outline faster and more enjoyable!
|
||||
|
||||

|
||||
If you’re comfortable writing markdown then all of the shortcuts you are used to are supported, for example type \*\*bold\*\* to instantly create **bold** text. If you forget some syntax or are after a quick refresher hit the keyboard icon in the bottom right hand corner for our guide. Learning some of the key Markdown shortcuts will definitely make using Outline faster and more enjoyable!
|
||||
|
||||
\
|
||||
:::info
|
||||
You can also paste markdown or html from elsewhere directly into a document.
|
||||
You can also paste markdown, html, or rich text from elsewhere directly into a document.
|
||||
:::
|
||||
|
||||
|
||||
## Rich documents
|
||||
|
||||
The editor supports a variety of content blocks including images, tables, lists, quotes, videos, and more. Type "/" on an empty line or click on the "+" icon to trigger the block insert menu, you can keep typing to filter it down.
|
||||
|
||||
You can also drag and drop images to include them in your document or paste a link to embed content from one of the many supported [integrations](https://www.getoutline.com/integrations)
|
||||
|
||||

|
||||
|
||||
## References
|
||||
|
||||
Linking to another document automatically creates backlinks which are kept up-to-date and shown at the bottom of the document, so you can create a library of linked information and easily answer the question "which other documents link here?".
|
||||
Linking to another document automatically creates backlinks which are kept up-to-date and shown at the bottom of the document, so you can create a library of linked information and easily answer the question "which other documents link here?".
|
||||
|
||||
## Rich documents
|
||||
|
||||
The editor supports a variety of content blocks including images, tables, lists, quotes, task lists, videos, and more. Type `/` on an empty line or click on the `+` icon to trigger the block insert menu, you can keep typing to filter it down. Of course, you can also drag and drop images to include them in your document or paste a link to embed content from one of the many supported [integrations](https://www.getoutline.com/integrations)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
We hate bugs as much as you and do everything possible to keep the application bug-free and performant. If you see any problems with Outline please get in touch with the team – you can email [hello@getoutline.com](mailto:hello@getoutline.com) directly and we’ll get back to you (hopefully with a fix!) as soon as possible.
|
||||
|
||||
## GitHub
|
||||
|
||||
If you have a GitHub account then you can also submit bugs directly to the development team on our public [issue tracker](https://github.com/outline/outline/issues).
|
||||
|
||||
## Ideas
|
||||
|
||||
We’d love to hear your ideas about how Outline can be improved and features you would like to see built. The best place to let the team know is through [GitHub discussions](https://github.com/outline/outline/discussions).
|
||||
@@ -1,16 +1,17 @@
|
||||
Outline is a place to build your team knowledge base, you could think of it like your team’s shared library – a place for important documentation, notes, and ideas to live and be discovered. Some things you might want to keep in Outline:
|
||||
Outline is a place to build your team knowledge base, you could think of it like your team’s shared library – a place for important documentation, notes, and ideas to live and be discovered. Some things you might want to keep in Outline include:
|
||||
|
||||
\
|
||||
- Documentation
|
||||
- Support knowledge base
|
||||
- Product plans and RFCs
|
||||
- Sales playbooks
|
||||
- Support scripts
|
||||
- Onboarding
|
||||
- HR documents
|
||||
- Onboarding checklists
|
||||
- Company policies
|
||||
- Meeting notes
|
||||
- …and more
|
||||
|
||||
## Structure
|
||||
|
||||
Outline allows you to organize documents in "collections", for example these could represent topics like Sales, Product, or HR. Within collections documents can be interlinked and deeply nested to easily build relationships within your knowledge base.
|
||||
Outline allows you to organize documents in "collections", for example these could represent topics like Sales, Product, or HR. You can assign users or groups access to collections. Within collections documents can be interlinked and deeply nested to easily build relationships within your knowledge base.
|
||||
|
||||
## Search
|
||||
|
||||
|
||||
@@ -11566,10 +11566,10 @@ retry-as-promised@^3.2.0:
|
||||
dependencies:
|
||||
any-promise "^1.3.0"
|
||||
|
||||
rich-markdown-editor@^11.17.0:
|
||||
version "11.17.0"
|
||||
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-11.17.0.tgz#9a39f5bd6518de1d0dc6c6ffa352fd3f7f664d96"
|
||||
integrity sha512-zCl9F7eeR3T5O2tiSU9iNGDOKhYBfTMqwWMPSY4ADjHcxELNkF1wOdKb0lqM8ZOfM63DkPEZy/N7cSifReyqvg==
|
||||
rich-markdown-editor@^11.17.1:
|
||||
version "11.17.1"
|
||||
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-11.17.1.tgz#ace87dccf7e7c7f6e093d73accb467794d3cee60"
|
||||
integrity sha512-/V7nPhjASnKVqFRW0CFKCe1+PrRZywVWzjvlpwzAKjpYW8QSWL/fViIkYzWBsynYamCT/A/iLx8ms6KJr6gSjw==
|
||||
dependencies:
|
||||
copy-to-clipboard "^3.0.8"
|
||||
lodash "^4.17.11"
|
||||
|
||||
Reference in New Issue
Block a user