@@ -6,7 +6,7 @@ import OutlineLogo from 'shared/components/OutlineLogo';
|
||||
function Branding() {
|
||||
return (
|
||||
<Link href={process.env.URL}>
|
||||
<OutlineLogo /> Outline
|
||||
<OutlineLogo size={16} fill="#000" /> Outline
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
BIN
public/fonts/LatoLatin-Semibold.woff
Normal file
BIN
public/images/airtable.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/images/codepen.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/images/figma.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
public/images/framer.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/images/github-gist.png
Executable file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/images/invision.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/images/loom.png
Executable file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/images/lucidchart.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/images/marvel.png
Executable file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/images/mode-analytics.png
Executable file
|
After Width: | Height: | Size: 993 B |
BIN
public/images/numeracy.png
Executable file
|
After Width: | Height: | Size: 567 B |
BIN
public/images/realtime-board.png
Executable file
|
After Width: | Height: | Size: 829 B |
BIN
public/images/screenshots/airtable.png
Executable file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/images/screenshots/figma.png
Executable file
|
After Width: | Height: | Size: 39 KiB |
BIN
public/images/screenshots/framer.png
Executable file
|
After Width: | Height: | Size: 75 KiB |
BIN
public/images/screenshots/invision.png
Executable file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/images/screenshots/lucidchart.png
Executable file
|
After Width: | Height: | Size: 46 KiB |
BIN
public/images/screenshots/marvel.png
Executable file
|
After Width: | Height: | Size: 106 KiB |
BIN
public/images/screenshots/numeracy.png
Executable file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/images/screenshots/realtime-board.png
Executable file
|
After Width: | Height: | Size: 57 KiB |
BIN
public/images/screenshots/slack-channel.png
Executable file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/images/screenshots/slack-search.png
Executable file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/images/screenshots/typeform.png
Executable file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/slack.png
Executable file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/images/trello.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/images/typeform.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/images/vimeo.png
Executable file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/youtube.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
@@ -23,7 +23,12 @@ export default function errorHandling() {
|
||||
}
|
||||
}
|
||||
|
||||
if (message.match('Authorization error')) {
|
||||
if (message.match(/Not found/i)) {
|
||||
ctx.status = 404;
|
||||
error = 'not_found';
|
||||
}
|
||||
|
||||
if (message.match(/Authorization error/i)) {
|
||||
ctx.status = 403;
|
||||
error = 'authorization_error';
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function About() {
|
||||
<Helmet>
|
||||
<title>About Us</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<Header background="#F22C5F">
|
||||
<h1>About Us</h1>
|
||||
<p>The team behind Outline</p>
|
||||
</Header>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { groupBy, map } from 'lodash';
|
||||
import format from 'date-fns/format';
|
||||
import styled from 'styled-components';
|
||||
import Grid from 'styled-components-grid';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Markdown from './components/Markdown';
|
||||
import Header from './components/Header';
|
||||
import Content from './components/Content';
|
||||
|
||||
@@ -15,39 +16,84 @@ type Release = {
|
||||
created_at: string,
|
||||
};
|
||||
|
||||
function Changelog({ releases }: { releases: Release[] }) {
|
||||
type Props = { releases: Release[] };
|
||||
|
||||
function Changelog({ releases }: Props) {
|
||||
const categories = groupBy(releases, i =>
|
||||
format(new Date(i.created_at), 'MMMM, YYYY')
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Helmet>
|
||||
<title>Changelog</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<Header background="#00ADFF">
|
||||
<h1>Changelog</h1>
|
||||
<p>
|
||||
We’re building in public. Here’s what we’ve been changing recently.
|
||||
</p>
|
||||
<p>We’re building in public. Here’s what has changed recently.</p>
|
||||
</Header>
|
||||
<Content>
|
||||
{releases.map(release => (
|
||||
<Article key={release.id}>
|
||||
<Heading id={release.name}>
|
||||
<a href={`#${release.name}`}>{release.name}</a>
|
||||
</Heading>
|
||||
<Time dateTime={release.created_at}>
|
||||
{format(new Date(release.created_at), 'MMMM Do, YYYY')}
|
||||
</Time>
|
||||
<ReactMarkdown source={release.body} />
|
||||
</Article>
|
||||
))}
|
||||
<Grid>
|
||||
<Grid.Unit
|
||||
size={{ tablet: 1 / 4 }}
|
||||
visible={{ mobile: false, tablet: true }}
|
||||
>
|
||||
<nav>
|
||||
{map(categories, (releases, category) => (
|
||||
<React.Fragment key={category}>
|
||||
<h3>{category.split(',')[0]}</h3>
|
||||
<List>
|
||||
{releases.map(release => (
|
||||
<li key={release.id}>
|
||||
<MenuItem href={`#${release.name}`}>
|
||||
{release.name}
|
||||
</MenuItem>
|
||||
</li>
|
||||
))}
|
||||
</List>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</nav>
|
||||
</Grid.Unit>
|
||||
<Grid.Unit size={{ tablet: 3 / 4 }}>
|
||||
{releases.map(release => (
|
||||
<Article key={release.id}>
|
||||
<Heading id={release.name}>
|
||||
<a href={`#${release.name}`}>{release.name}</a>
|
||||
</Heading>
|
||||
<Time dateTime={release.created_at}>
|
||||
{format(new Date(release.created_at), 'MMMM Do, YYYY')}
|
||||
</Time>
|
||||
<Markdown source={release.body} />
|
||||
</Article>
|
||||
))}
|
||||
</Grid.Unit>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const MenuItem = styled.a`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: ${props => props.theme.text};
|
||||
`;
|
||||
|
||||
const List = styled.ul`
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const Heading = styled.h1`
|
||||
margin-top: 0.5em;
|
||||
|
||||
a {
|
||||
color: ${props => props.theme.text};
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -66,13 +112,6 @@ const Article = styled.div`
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
zoom: 50%;
|
||||
box-shadow: 0 10px 80px rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Changelog;
|
||||
|
||||
@@ -166,7 +166,7 @@ const Mask = styled.div`
|
||||
`;
|
||||
|
||||
const Features = styled.div`
|
||||
background: hsl(180, 58%, 85%);
|
||||
background: #00adff;
|
||||
padding: 0 2em;
|
||||
width: 100%;
|
||||
`;
|
||||
@@ -200,7 +200,7 @@ const Feature = styled(Grid.Unit)`
|
||||
`;
|
||||
|
||||
const Footer = styled.div`
|
||||
background: hsl(127, 58%, 85%);
|
||||
background: #aa34f0;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding: 4em 2em;
|
||||
@@ -210,6 +210,10 @@ const Footer = styled.div`
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
${breakpoint('tablet')`
|
||||
margin: 2em 0;
|
||||
padding: 6em 4em;
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function Privacy() {
|
||||
<Helmet>
|
||||
<title>Privacy Policy</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<Header background="#F4F7FA">
|
||||
<h1>Privacy Policy</h1>
|
||||
<p>How we collect and use your information</p>
|
||||
</Header>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import styled from 'styled-components';
|
||||
import Centered from './Centered';
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
background?: string,
|
||||
};
|
||||
|
||||
const Header = ({ children }: Props) => {
|
||||
const Header = ({ children, background }: Props) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Wrapper background={background}>
|
||||
<Centered>{children}</Centered>
|
||||
</Wrapper>
|
||||
);
|
||||
@@ -17,9 +19,25 @@ const Header = ({ children }: Props) => {
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
padding: 2em;
|
||||
background: ${props => props.theme.contentHeaderBackground};
|
||||
padding: 8em 0 3em;
|
||||
position: relative;
|
||||
|
||||
margin-top: -70px;
|
||||
margin-bottom: 2em;
|
||||
text-align: center;
|
||||
background: ${props => props.background || 'transparent'};
|
||||
z-index: -1;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -30px;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
background: ${props => props.background || 'transparent'};
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 22px;
|
||||
@@ -29,9 +47,17 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.5em;
|
||||
font-size: 2em;
|
||||
margin: 0 0 0.1em;
|
||||
}
|
||||
|
||||
${breakpoint('tablet')`
|
||||
padding: 8em 3em 3em 3em;
|
||||
|
||||
h1 {
|
||||
font-size: 4em;
|
||||
}
|
||||
`};
|
||||
`;
|
||||
|
||||
export default Header;
|
||||
|
||||
@@ -78,7 +78,7 @@ function Layout({ children, loggedIn, sessions }: Props) {
|
||||
const Body = styled.body`
|
||||
padding: 0 30px;
|
||||
|
||||
${breakpoint('tablet')`
|
||||
${breakpoint('desktop')`
|
||||
padding: 0;
|
||||
`};
|
||||
`;
|
||||
|
||||
33
server/pages/components/Markdown.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// @flow
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export default styled(ReactMarkdown)`
|
||||
blockquote {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
background-color: ${props => props.theme.smoke};
|
||||
border-left: 6px solid ${props => props.theme.smokeDark};
|
||||
padding: 15px 30px 15px 15px;
|
||||
font-style: italic;
|
||||
font-size: 16px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
zoom: 50%;
|
||||
box-shadow: 0 10px 80px rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 15px;
|
||||
background: ${props => props.theme.smoke};
|
||||
padding: 2px 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
`;
|
||||
@@ -4,6 +4,7 @@ import { sortBy } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import Centered from './Centered';
|
||||
import OutlineLogo from '../../../shared/components/OutlineLogo';
|
||||
import TeamLogo from '../../../shared/components/TeamLogo';
|
||||
import { fadeAndScaleIn } from '../../../shared/styles/animations';
|
||||
import {
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
changelog,
|
||||
features,
|
||||
about,
|
||||
integrations,
|
||||
privacy,
|
||||
githubUrl,
|
||||
twitterUrl,
|
||||
@@ -36,20 +38,19 @@ function TopNavigation({ sessions, loggedIn }: Props) {
|
||||
|
||||
return (
|
||||
<Nav>
|
||||
<Brand href={process.env.URL}>Outline</Brand>
|
||||
<Brand href={process.env.URL}>
|
||||
<OutlineLogo size={18} fill="#000" /> Outline
|
||||
</Brand>
|
||||
<Menu>
|
||||
<MenuItemDesktop>
|
||||
<a href={features()}>Features</a>
|
||||
</MenuItemDesktop>
|
||||
<MenuItemDesktop>
|
||||
<a href={about()}>About</a>
|
||||
<a href={integrations()}>Integrations</a>
|
||||
</MenuItemDesktop>
|
||||
<MenuItemDesktop>
|
||||
<a href={changelog()}>Changelog</a>
|
||||
</MenuItemDesktop>
|
||||
<MenuItemDesktop>
|
||||
<a href={twitterUrl()}>Twitter</a>
|
||||
</MenuItemDesktop>
|
||||
<MenuItem>
|
||||
<a href={developers()}>API</a>
|
||||
</MenuItem>
|
||||
@@ -109,6 +110,9 @@ function BottomNavigation() {
|
||||
<div>
|
||||
<a href={privacy()}>Privacy</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href={about()}>About</a>
|
||||
</div>
|
||||
</BottomNav>
|
||||
);
|
||||
}
|
||||
@@ -118,11 +122,11 @@ const MenuLinkStyle = props => `
|
||||
font-weight: 500;
|
||||
|
||||
a {
|
||||
color: ${props.theme.slate};
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: ${props.theme.slateDark};
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
@@ -142,17 +146,17 @@ const MenuItem = styled.li`
|
||||
props.highlighted &&
|
||||
`
|
||||
position: relative;
|
||||
border: 2px solid ${props.theme.slate};
|
||||
border: 2px solid rgba(0, 0, 0, 0.6);
|
||||
border-radius: 4px;
|
||||
padding: 6px 8px;
|
||||
margin-top: -6px;
|
||||
margin-bottom: -6px;
|
||||
|
||||
&:hover {
|
||||
border: 2px solid ${props.theme.slateDark};
|
||||
border: 2px solid rgba(0, 0, 0, 0.4);
|
||||
|
||||
> a {
|
||||
color: ${props.theme.slateDark};
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +250,8 @@ const BottomNav = styled.nav`
|
||||
`;
|
||||
|
||||
const Brand = styled.a`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
text-decoration: none;
|
||||
|
||||
@@ -3,160 +3,20 @@ import * as React from 'react';
|
||||
import Grid from 'styled-components-grid';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import styled from 'styled-components';
|
||||
import Header from './components/Header';
|
||||
|
||||
const Container = styled.div`
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2em;
|
||||
|
||||
pre {
|
||||
padding: 0.5em 1em;
|
||||
background: #f9fbfc;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e8ebed;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
|
||||
thead {
|
||||
td {
|
||||
padding: 5px 12px 5px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
tbody,
|
||||
thead {
|
||||
td {
|
||||
padding: 5px 12px 5px 0;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
code {
|
||||
font-size: 1.08em;
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Header from '../components/Header';
|
||||
import Content from '../components/Content';
|
||||
|
||||
export default function Pricing() {
|
||||
return (
|
||||
<Grid>
|
||||
<Helmet>
|
||||
<title>API Documentation - Outline</title>
|
||||
<title>API Reference - Outline</title>
|
||||
</Helmet>
|
||||
<Header>
|
||||
<h1>Documentation</h1>
|
||||
<p>The API is the heart and soul of Outline.</p>
|
||||
<Header background="#AA34F0">
|
||||
<h1>API Reference</h1>
|
||||
<p>Outline is built on an open, best-in-class, API</p>
|
||||
</Header>
|
||||
<Container>
|
||||
<p>
|
||||
As developers, it’s our mission to make the API as great as possible.
|
||||
While Outline is still in public beta, we might make small
|
||||
adjustments, including breaking changes to the API.
|
||||
</p>
|
||||
<h2>Making requests</h2>
|
||||
<p>
|
||||
Outline’s API follows simple RPC style conventions where each API
|
||||
endpoint is a method on{' '}
|
||||
<code>https://www.getoutline.com/api/<METHOD></code>. Both{' '}
|
||||
<code>GET</code> and <code>POST</code> methods are supported but it’s
|
||||
recommeded that you make all call using <code>POST</code>. Only HTTPS
|
||||
is supported in production.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For <code>GET</code> requests query string parameters are expected
|
||||
(e.g.
|
||||
<code>/api/document.info?id=...&token=...</code>). When making{' '}
|
||||
<code>POST</code> requests, request parameters are parsed depending on{' '}
|
||||
<code>Content-Type</code> header. To make a call using JSON payload,
|
||||
one must pass <code>Content-Type: application/json</code> header:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Example POST request:</strong>
|
||||
</p>
|
||||
<pre>
|
||||
<code>
|
||||
{`curl https://www.getoutline.com/api/documents.info
|
||||
-X POST
|
||||
-H 'authorization: Bearer API_KEY'
|
||||
-H 'content-type: application/json'
|
||||
-H 'accept: application/json'
|
||||
-d '{"id": "outline-api-NTpezNwhUP"}'
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<strong>Example GET request:</strong>
|
||||
</p>
|
||||
<pre>
|
||||
<code>
|
||||
{`curl https://www.getoutline.com/api/documents.info?id=outline-api-NTpezNwhUP&token=API_KEY
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<h2>Authentication</h2>
|
||||
|
||||
<p>
|
||||
To access private API endpoints, you must provide a valid API key. You
|
||||
can create new API keys in your{' '}
|
||||
<a href={`${process.env.URL}/settings`}>account settings</a>. Be
|
||||
careful when handling your keys as they give access to all of your
|
||||
documents.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To authenticate with Outline API, you can supply the API key as a
|
||||
header (<code>Authorization: Bearer YOUR_API_KEY</code>) or as part of
|
||||
the payload using <code>token</code> parameter. If you’re making{' '}
|
||||
<code>GET</code> requests, header based authentication is recommended
|
||||
so that your keys don’t leak into logs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some API endpoints allow unauhenticated requests for public resources
|
||||
and they can be called without an API key.
|
||||
</p>
|
||||
|
||||
<h2>Errors</h2>
|
||||
|
||||
<p>
|
||||
All successful API requests will be returned with <code>200</code>{' '}
|
||||
status code and <code>ok: true</code> in the response payload. If
|
||||
there’s an error while making the request, appropriate status code is
|
||||
returned with the <code>error</code> message:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
<code>
|
||||
{`{
|
||||
"ok": false,
|
||||
"error: "Not Found"
|
||||
}
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<h2>Methods</h2>
|
||||
<Content>
|
||||
<Methods>
|
||||
<Method method="auth.info" label="Get current auth">
|
||||
<Description>
|
||||
@@ -166,6 +26,11 @@ export default function Pricing() {
|
||||
<Arguments />
|
||||
</Method>
|
||||
|
||||
<Method method="users.list" label="List team's users">
|
||||
<Description>List all of the users in the team.</Description>
|
||||
<Arguments pagination />
|
||||
</Method>
|
||||
|
||||
<Method method="users.info" label="Get current user">
|
||||
<Description>
|
||||
This method returns the profile info for the user identified by
|
||||
@@ -331,11 +196,11 @@ export default function Pricing() {
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
UUID - <code>id</code> field of the document
|
||||
UUID - <Code>id</Code> field of the document
|
||||
</li>
|
||||
<li>
|
||||
URI identifier - Human readable identifier used in Outline
|
||||
URLs (e.g. <code>outline-api-i48ZEZc5zjXndcP</code>)
|
||||
URLs (e.g. <Code>outline-api-i48ZEZc5zjXndcP</Code>)
|
||||
</li>
|
||||
</ul>
|
||||
</Description>
|
||||
@@ -360,14 +225,14 @@ export default function Pricing() {
|
||||
This method allows you to publish a new document under an existing
|
||||
collection. By default a document is set to the parent collection
|
||||
root. If you want to create a subdocument, you can pass{' '}
|
||||
<code>parentDocument</code> to set parent document.
|
||||
<Code>parentDocument</Code> to set parent document.
|
||||
</Description>
|
||||
<Arguments>
|
||||
<Argument
|
||||
id="collection"
|
||||
description={
|
||||
<span>
|
||||
<code>ID</code> of the collection to which the document is
|
||||
<Code>ID</Code> of the collection to which the document is
|
||||
created
|
||||
</span>
|
||||
}
|
||||
@@ -387,7 +252,7 @@ export default function Pricing() {
|
||||
id="parentDocument"
|
||||
description={
|
||||
<span>
|
||||
<code>ID</code> of the parent document within the collection
|
||||
<Code>ID</Code> of the parent document within the collection
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
@@ -395,7 +260,7 @@ export default function Pricing() {
|
||||
id="publish"
|
||||
description={
|
||||
<span>
|
||||
<code>true</code> by default. Pass <code>false</code> to
|
||||
<Code>true</Code> by default. Pass <Code>false</Code> to
|
||||
create a draft.
|
||||
</span>
|
||||
}
|
||||
@@ -422,7 +287,7 @@ export default function Pricing() {
|
||||
id="publish"
|
||||
description={
|
||||
<span>
|
||||
Pass <code>true</code> to publish a draft.
|
||||
Pass <Code>true</Code> to publish a draft.
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
@@ -430,7 +295,7 @@ export default function Pricing() {
|
||||
id="autosave"
|
||||
description={
|
||||
<span>
|
||||
Pass <code>true</code> to signify an autosave. This skips
|
||||
Pass <Code>true</Code> to signify an autosave. This skips
|
||||
creating a revision.
|
||||
</span>
|
||||
}
|
||||
@@ -439,7 +304,7 @@ export default function Pricing() {
|
||||
id="done"
|
||||
description={
|
||||
<span>
|
||||
Pass <code>true</code> to signify the end of an editing
|
||||
Pass <Code>true</Code> to signify the end of an editing
|
||||
session. This will trigger documents.update hooks.
|
||||
</span>
|
||||
}
|
||||
@@ -630,14 +495,6 @@ export default function Pricing() {
|
||||
</Arguments>
|
||||
</Method>
|
||||
|
||||
<Method method="team.users" label="List team's users">
|
||||
<Description>
|
||||
List team`s users. This endpoint is only available for admin
|
||||
users.
|
||||
</Description>
|
||||
<Arguments pagination />
|
||||
</Method>
|
||||
|
||||
<Method method="shares.list" label="List shared document links">
|
||||
<Description>
|
||||
List all your currently shared document links.
|
||||
@@ -666,13 +523,22 @@ export default function Pricing() {
|
||||
</Arguments>
|
||||
</Method>
|
||||
</Methods>
|
||||
</Container>
|
||||
</Content>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const MethodList = styled.ul`
|
||||
margin-bottom: 80px;
|
||||
const MenuItem = styled.a`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: ${props => props.theme.text};
|
||||
`;
|
||||
|
||||
const List = styled.ul`
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const Methods = (props: { children: React.Node }) => {
|
||||
@@ -680,16 +546,26 @@ const Methods = (props: { children: React.Node }) => {
|
||||
const methods = children.map(child => child.props.method);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MethodList>
|
||||
{methods.map(method => (
|
||||
<li key={method}>
|
||||
<a href={`#${method}`}>{method}</a>
|
||||
</li>
|
||||
))}
|
||||
</MethodList>
|
||||
{children}
|
||||
</div>
|
||||
<React.Fragment>
|
||||
<Grid>
|
||||
<Grid.Unit
|
||||
size={{ tablet: 1 / 4 }}
|
||||
visible={{ mobile: false, tablet: true }}
|
||||
>
|
||||
<nav>
|
||||
<h2>Reference</h2>
|
||||
<List>
|
||||
{methods.map(method => (
|
||||
<li key={method}>
|
||||
<MenuItem href={`#${method}`}>{method}</MenuItem>
|
||||
</li>
|
||||
))}
|
||||
</List>
|
||||
</nav>
|
||||
</Grid.Unit>
|
||||
<Grid.Unit size={{ tablet: 3 / 4 }}>{children}</Grid.Unit>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -716,8 +592,33 @@ type ArgumentsProps = {
|
||||
children?: React.Node | string,
|
||||
};
|
||||
|
||||
const Table = styled.table`
|
||||
border-collapse: collapse;
|
||||
|
||||
thead {
|
||||
td {
|
||||
padding: 5px 12px 5px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
tbody,
|
||||
thead {
|
||||
td {
|
||||
padding: 5px 12px 5px 0;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Arguments = (props: ArgumentsProps) => (
|
||||
<table>
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Argument</td>
|
||||
@@ -733,9 +634,23 @@ const Arguments = (props: ArgumentsProps) => (
|
||||
)}
|
||||
{props.children}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table>
|
||||
);
|
||||
|
||||
const Heading = styled.h3`
|
||||
code {
|
||||
font-size: 1em;
|
||||
padding: 2px 4px;
|
||||
background: #333;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
`;
|
||||
|
||||
const Code = styled.code`
|
||||
font-size: 15px;
|
||||
`;
|
||||
|
||||
const Method = (props: MethodProps) => {
|
||||
const children = React.Children.toArray(props.children);
|
||||
const description = children.find(child => child.type === Description);
|
||||
@@ -743,13 +658,13 @@ const Method = (props: MethodProps) => {
|
||||
|
||||
return (
|
||||
<MethodContainer>
|
||||
<h3 id={props.method}>
|
||||
<code>{props.method}</code> - {props.label}
|
||||
</h3>
|
||||
<Heading id={props.method}>
|
||||
<code>{props.method}</code> {props.label}
|
||||
</Heading>
|
||||
<div>{description}</div>
|
||||
<Request>HTTP request & arguments</Request>
|
||||
<p>
|
||||
<code>{`${process.env.URL}/api/${props.method}`}</code>
|
||||
<Code>{`${process.env.URL}/api/${props.method}`}</Code>
|
||||
</p>
|
||||
{apiArgs}
|
||||
</MethodContainer>
|
||||
@@ -765,7 +680,7 @@ type ArgumentProps = {
|
||||
const Argument = (props: ArgumentProps) => (
|
||||
<tr>
|
||||
<td>
|
||||
<code>{props.id}</code>
|
||||
<Code>{props.id}</Code>
|
||||
</td>
|
||||
<td>
|
||||
<i>{props.required ? 'required' : 'optional'}</i>
|
||||
@@ -773,6 +688,7 @@ const Argument = (props: ArgumentProps) => (
|
||||
<td>{props.description}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
const PaginationArguments = () => [
|
||||
<Argument id="offset" description="Pagination offset" />,
|
||||
<Argument id="limit" description="Pagination limit" />,
|
||||
169
server/pages/developers/index.js
Normal file
@@ -0,0 +1,169 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import Grid from 'styled-components-grid';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import styled from 'styled-components';
|
||||
import Header from '../components/Header';
|
||||
import Content from '../components/Content';
|
||||
|
||||
export default function Pricing() {
|
||||
return (
|
||||
<Grid>
|
||||
<Helmet>
|
||||
<title>Developers - Outline</title>
|
||||
</Helmet>
|
||||
<Header background="#AA34F0">
|
||||
<h1>Developers</h1>
|
||||
<p>Outline is built on an open, best-in-class, API</p>
|
||||
</Header>
|
||||
<Content>
|
||||
<Grid>
|
||||
<Grid.Unit
|
||||
size={{ tablet: 1 / 4 }}
|
||||
visible={{ mobile: false, tablet: true }}
|
||||
>
|
||||
<nav>
|
||||
<h2>Introduction</h2>
|
||||
<List>
|
||||
<li>
|
||||
<MenuItem href="#requests">Making requests</MenuItem>
|
||||
</li>
|
||||
<li>
|
||||
<MenuItem href="#authentication">Authentication</MenuItem>
|
||||
</li>
|
||||
<li>
|
||||
<MenuItem href="#errors">Errors</MenuItem>
|
||||
</li>
|
||||
</List>
|
||||
<h2>API</h2>
|
||||
<List>
|
||||
<li>
|
||||
<MenuItem href="/developers/api">Reference</MenuItem>
|
||||
</li>
|
||||
</List>
|
||||
</nav>
|
||||
</Grid.Unit>
|
||||
<Grid.Unit size={{ tablet: 3 / 4 }}>
|
||||
<p>
|
||||
As developers, it’s our mission to make the API as great as
|
||||
possible. While Outline is still in public beta, we might make
|
||||
small adjustments, including breaking changes to the API.
|
||||
</p>
|
||||
|
||||
<h2>Making requests</h2>
|
||||
<p>
|
||||
Outline’s API follows simple RPC style conventions where each API
|
||||
endpoint is a method on{' '}
|
||||
<Code>https://www.getoutline.com/api/<METHOD></Code>. Both{' '}
|
||||
<Code>GET</Code> and <Code>POST</Code> methods are supported but
|
||||
it’s recommeded that you make all call using <Code>POST</Code>.
|
||||
Only HTTPS is supported in production.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For <Code>GET</Code> requests query string parameters are expected
|
||||
(e.g.
|
||||
<Code>/api/document.info?id=...&token=...</Code>). When making{' '}
|
||||
<Code>POST</Code> requests, request parameters are parsed
|
||||
depending on <Code>Content-Type</Code> header. To make a call
|
||||
using JSON payload, one must pass{' '}
|
||||
<Code>Content-Type: application/json</Code> header:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Example POST request:</strong>
|
||||
</p>
|
||||
<Pre>
|
||||
<Code>
|
||||
{`curl https://www.getoutline.com/api/documents.info
|
||||
-X POST
|
||||
-H 'authorization: Bearer API_KEY'
|
||||
-H 'content-type: application/json'
|
||||
-H 'accept: application/json'
|
||||
-d '{"id": "outline-api-NTpezNwhUP"}'
|
||||
`}
|
||||
</Code>
|
||||
</Pre>
|
||||
|
||||
<p>
|
||||
<strong>Example GET request:</strong>
|
||||
</p>
|
||||
<Pre>
|
||||
<Code>
|
||||
{`curl https://www.getoutline.com/api/documents.info?id=outline-api-NTpezNwhUP&token=API_KEY
|
||||
`}
|
||||
</Code>
|
||||
</Pre>
|
||||
|
||||
<h2>Authentication</h2>
|
||||
|
||||
<p>
|
||||
To access private API endpoints, you must provide a valid API key.
|
||||
You can create new API keys in your{' '}
|
||||
<a href={`${process.env.URL}/settings`}>account settings</a>. Be
|
||||
careful when handling your keys as they give access to all of your
|
||||
documents.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To authenticate with Outline API, you can supply the API key as a
|
||||
header (<Code>Authorization: Bearer YOUR_API_KEY</Code>) or as
|
||||
part of the payload using <Code>token</Code> parameter. If you’re
|
||||
making <Code>GET</Code> requests, header based authentication is
|
||||
recommended so that your keys don’t leak into logs.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Some API endpoints allow unauhenticated requests for public
|
||||
resources and they can be called without an API key.
|
||||
</p>
|
||||
|
||||
<h2>Errors</h2>
|
||||
|
||||
<p>
|
||||
All successful API requests will be returned with <Code>200</Code>{' '}
|
||||
status code and <Code>ok: true</Code> in the response payload. If
|
||||
there’s an error while making the request, appropriate status code
|
||||
is returned with the <Code>error</Code> message:
|
||||
</p>
|
||||
|
||||
<Pre>
|
||||
<Code>
|
||||
{`{
|
||||
"ok": false,
|
||||
"error: "Not Found"
|
||||
}
|
||||
`}
|
||||
</Code>
|
||||
</Pre>
|
||||
</Grid.Unit>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const Pre = styled.pre`
|
||||
padding: 0.5em 1em;
|
||||
background: #f9fbfc;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e8ebed;
|
||||
overflow: scroll;
|
||||
`;
|
||||
|
||||
const Code = styled.code`
|
||||
font-size: 15px;
|
||||
`;
|
||||
|
||||
const MenuItem = styled.a`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: ${props => props.theme.text};
|
||||
`;
|
||||
|
||||
const List = styled.ul`
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
`;
|
||||
48
server/pages/integrations/Integration.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import Grid from 'styled-components-grid';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Markdown from '../components/Markdown';
|
||||
import Header from '../components/Header';
|
||||
import Content from '../components/Content';
|
||||
import Menu from './Menu';
|
||||
import integrations from './content';
|
||||
|
||||
type TIntegration = {
|
||||
slug: string,
|
||||
name: string,
|
||||
url: string,
|
||||
description: string,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
integration: TIntegration,
|
||||
content: string,
|
||||
};
|
||||
|
||||
export default function Integration({ integration, content }: Props) {
|
||||
return (
|
||||
<Grid>
|
||||
<Helmet>
|
||||
<title>{integration.name} Integration – Outline</title>
|
||||
</Helmet>
|
||||
<Header background="#F4F7FA">
|
||||
<h1>{integration.name} Integration</h1>
|
||||
<p>{integration.description}</p>
|
||||
</Header>
|
||||
<Content>
|
||||
<Grid>
|
||||
<Grid.Unit
|
||||
size={{ tablet: 1 / 4 }}
|
||||
visible={{ mobile: false, tablet: true }}
|
||||
>
|
||||
<Menu integrations={integrations} active={integration.slug} />
|
||||
</Grid.Unit>
|
||||
<Grid.Unit size={{ tablet: 3 / 4 }}>
|
||||
<Markdown source={content} />
|
||||
</Grid.Unit>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
57
server/pages/integrations/Menu.js
Normal file
@@ -0,0 +1,57 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { map, groupBy } from 'lodash';
|
||||
|
||||
type Props = {
|
||||
integrations: *,
|
||||
active: string,
|
||||
};
|
||||
|
||||
export default function IntegrationMenu({ integrations, active }: Props) {
|
||||
const categories = groupBy(integrations, i => i.category);
|
||||
|
||||
return (
|
||||
<nav>
|
||||
{map(categories, (integrations, category) => (
|
||||
<React.Fragment key={category}>
|
||||
<h3>{category}</h3>
|
||||
<List>
|
||||
{integrations.map(i => (
|
||||
<li key={i.slug}>
|
||||
<MenuItem
|
||||
href={`/integrations/${i.slug}`}
|
||||
active={i.slug === active}
|
||||
>
|
||||
<Logo src={`/images/${i.slug}.png`} alt={i.name} />
|
||||
<span>{i.name}</span>
|
||||
</MenuItem>
|
||||
</li>
|
||||
))}
|
||||
</List>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
const MenuItem = styled.a`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: ${props => (props.active ? '600' : 'inherit')};
|
||||
color: ${props => props.theme.text};
|
||||
`;
|
||||
|
||||
const Logo = styled.img`
|
||||
user-select: none;
|
||||
height: 18px;
|
||||
border-radius: 2px;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const List = styled.ul`
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
`;
|
||||
7
server/pages/integrations/airtable.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a share link to an [Airtable](https://airtable.com) table and it will be immediately converted into an interactive, live embed.
|
||||
|
||||
Embedding a table in your knowledge base will stay upto date with your original data source automatically.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
5
server/pages/integrations/codepen.md
Normal file
@@ -0,0 +1,5 @@
|
||||
In an Outline document, paste a share link to a [Codepen](https://codepen.io) card and it will be immediately converted into an embedded version where you can view the source or the result.
|
||||
|
||||
Pen's can be anything from a simple code snippet, to an embedded 3D graphic, visualization and more.
|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
100
server/pages/integrations/content.json
Normal file
@@ -0,0 +1,100 @@
|
||||
[
|
||||
{
|
||||
"slug": "figma",
|
||||
"name": "Figma",
|
||||
"url": "https://figma.com",
|
||||
"category": "Design",
|
||||
"description": "Figma is a collaborative interface design tool"
|
||||
},
|
||||
{
|
||||
"slug": "framer",
|
||||
"name": "Framer",
|
||||
"url": "https://framer.com",
|
||||
"category": "Design",
|
||||
"description": "Framer is an interactive design and prototyping tool"
|
||||
},
|
||||
{
|
||||
"slug": "invision",
|
||||
"name": "InVision",
|
||||
"url": "https://invision.com",
|
||||
"category": "Design",
|
||||
"description": "InVision is an online design and prototyping tool"
|
||||
},
|
||||
{
|
||||
"slug": "marvel",
|
||||
"name": "Marvel",
|
||||
"url": "https://marvelapp.com",
|
||||
"category": "Design",
|
||||
"description": "The all-in-one platform powering design"
|
||||
},
|
||||
{
|
||||
"slug": "airtable",
|
||||
"name": "Airtable",
|
||||
"url": "https://airtable.com",
|
||||
"category": "Collaboration",
|
||||
"description": "Part spreadsheet, part database, and entirely flexible"
|
||||
},
|
||||
{
|
||||
"slug": "lucidchart",
|
||||
"name": "Lucidchart",
|
||||
"url": "https://lucidchart.com",
|
||||
"category": "Collaboration",
|
||||
"description": "Create flowcharts and technical diagrams with ease"
|
||||
},
|
||||
{
|
||||
"slug": "realtime-board",
|
||||
"name": "Realtime Board",
|
||||
"url": "https://realtimeboard.com",
|
||||
"category": "Collaboration",
|
||||
"description": "Simple whiteboarding for cross-functional team collaboration"
|
||||
},
|
||||
{
|
||||
"slug": "slack",
|
||||
"name": "Slack",
|
||||
"url": "https://slack.com",
|
||||
"category": "Collaboration",
|
||||
"description": "Chat, collaboration, and file sharing for teams"
|
||||
},
|
||||
{
|
||||
"slug": "trello",
|
||||
"name": "Trello",
|
||||
"url": "https://trello.com",
|
||||
"category": "Collaboration",
|
||||
"description": "Boards, lists, and cards to organize your projects"
|
||||
},
|
||||
{
|
||||
"slug": "typeform",
|
||||
"name": "Typeform",
|
||||
"url": "https://typeform.com",
|
||||
"category": "Collaboration",
|
||||
"description": "Data collection tool and surveys, for professionals"
|
||||
},
|
||||
{
|
||||
"slug": "codepen",
|
||||
"name": "Codepen",
|
||||
"url": "https://codepen.io",
|
||||
"category": "Developers",
|
||||
"description": "A social development environment and editor"
|
||||
},
|
||||
{
|
||||
"slug": "github-gist",
|
||||
"name": "GitHub Gist",
|
||||
"url": "https://gist.github.com",
|
||||
"category": "Developers",
|
||||
"description": "Sharable code snippets, hosted by GitHub"
|
||||
},
|
||||
{
|
||||
"slug": "mode-analytics",
|
||||
"name": "Mode Analytics",
|
||||
"url": "https://modeanalytics.com",
|
||||
"category": "Developers",
|
||||
"description": "Connect and analyze data from any data source"
|
||||
},
|
||||
{
|
||||
"slug": "numeracy",
|
||||
"name": "Numeracy",
|
||||
"url": "https://numeracy.io",
|
||||
"category": "Developers",
|
||||
"description": "A SQL pad for writing, iterating, and exploring data"
|
||||
}
|
||||
]
|
||||
7
server/pages/integrations/figma.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a link to a [Figma](https://figma.com) design and we will instantly convert it to an interactive, live preview.
|
||||
|
||||
Because Figma is an online design tool you can see design work happening in realtime, right within Outline. Embed design specs, product designs, or marketing materials easily.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
7
server/pages/integrations/framer.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a link to a [Framer](https://framer.com) design or prototype hosted on Framer Cloud and it will be instantly turned into an interactive, live preview.
|
||||
|
||||
Host your prototypes, designs, and mocks inside Outline to document your product or design system. [Learn more about sharing links](https://blog.framer.com/framer-cloud-access-d6bdb192510d) with Framer cloud.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
5
server/pages/integrations/github-gist.md
Normal file
@@ -0,0 +1,5 @@
|
||||
In an Outline document, paste a link to a public [Gist](https://gist.github.com) and it will be immediately converted into an embedded version. Embedding code in your knowledge base is a great way to document best practices.
|
||||
|
||||
Outline also supports native code blocks, simply start a line with three backticks (```) to create a code block with syntax highlighting.
|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
77
server/pages/integrations/index.js
Normal file
@@ -0,0 +1,77 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { map, groupBy } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
import Grid from 'styled-components-grid';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Header from '../components/Header';
|
||||
import Content from '../components/Content';
|
||||
import integrations from './content';
|
||||
|
||||
const categories = groupBy(integrations, i => i.category);
|
||||
|
||||
function Integrations() {
|
||||
return (
|
||||
<Grid>
|
||||
<Helmet>
|
||||
<title>Integrations</title>
|
||||
</Helmet>
|
||||
<Header background="#FFB500">
|
||||
<h1>Integrations</h1>
|
||||
<p>
|
||||
Outline is designed to integrate with your existing workflow and
|
||||
tools.
|
||||
</p>
|
||||
</Header>
|
||||
<Content>
|
||||
{map(categories, (integrations, category) => (
|
||||
<div key={category}>
|
||||
<h2>{category}</h2>
|
||||
<Category>
|
||||
{integrations.map(i => (
|
||||
<Grid.Unit size={{ desktop: 1 / 4 }} key={i.slug}>
|
||||
<Integration href={`/integrations/${i.slug}`}>
|
||||
<Logo src={`/images/${i.slug}.png`} alt={i.name} />
|
||||
<h3>{i.name}</h3>
|
||||
<p>{i.description}</p>
|
||||
</Integration>
|
||||
</Grid.Unit>
|
||||
))}
|
||||
</Category>
|
||||
</div>
|
||||
))}
|
||||
</Content>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const Logo = styled.img`
|
||||
height: 60px;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
const Category = styled(Grid)`
|
||||
margin: 0 -1em;
|
||||
`;
|
||||
|
||||
const Integration = styled.a`
|
||||
display: block;
|
||||
padding: 2em 2em 1em;
|
||||
margin: 1em;
|
||||
border-radius: 4px;
|
||||
border: 2px solid ${props => props.theme.slateLight};
|
||||
color: ${props => props.theme.text};
|
||||
font-size: 16px;
|
||||
transition: background 200ms ease-in-out;
|
||||
|
||||
h3,
|
||||
p {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: ${props => props.theme.slateLight};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Integrations;
|
||||
7
server/pages/integrations/invision.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a share link to an [InVision](https://invision.com) prototype and it will be immediately converted into an interactive, live embed.
|
||||
|
||||
Embedding prototypes in your knowledge base is a great way to create product specs or document user feedback. [Learn more about sharing links](https://support.invisionapp.com/hc/en-us/sections/200697249-Sharing-Prototypes) from InVision.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
7
server/pages/integrations/lucidchart.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a share link to a [Lucidchart](https://lucidchart.com) diagram or chart and it will be immediately converted into an interactive embedded version.
|
||||
|
||||
Embed diagrams in your knowledge base to commuicate flows, technical diagrams, and more alongside your written documentation
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
7
server/pages/integrations/marvel.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a share link to a [Marvel](https://marvelapp.com) prototype and it will be immediately converted into an interactive, live embed.
|
||||
|
||||
Embedding prototypes in your knowledge base is a great way to create product specs with engineers or document user feedback.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
3
server/pages/integrations/mode-analytics.md
Normal file
@@ -0,0 +1,3 @@
|
||||
In an Outline document, paste a link to a public [Mode Analytics](https://modeanalytics.com) report and it will be converted into an interactive, embedded graph or table. Embedded graphs are perfect for communicating business metrics and KPI's.
|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
5
server/pages/integrations/numeracy.md
Normal file
@@ -0,0 +1,5 @@
|
||||
In an Outline document, paste a link to any [Numeracy](https://numeracy.co) chart and it will be converted into an interactive, embedded chart or table. Embedding graphs are perfect for communicating business metrics and KPI's.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
7
server/pages/integrations/realtime-board.md
Normal file
@@ -0,0 +1,7 @@
|
||||
In an Outline document, paste a share link to a [Realtime Board](https://realtimeboard.com/) whiteboard and it will be immediately converted into a realtime, interactive embed.
|
||||
|
||||
Embedded whiteboards in your knowledge base to commuicate plans and ideas, technical diagrams, designs, timelines and more alongside your written documentation
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
20
server/pages/integrations/slack.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## Sign In with Slack
|
||||
|
||||
Sign In with Slack means your team doesn't have to worry about invites, passwords, or managing new team members. Everyone on your team can login with their existing Slack account and will automatically join your private knowledgebase.
|
||||
|
||||
> Note: Your team will also get a matching custom subdomain that you can link to from elsewhere.
|
||||
|
||||
|
||||
## Search your Knowledgebase
|
||||
|
||||
Optionally [Connect to Slack](https://www.getoutline.com/settings/integrations/slack) to enable the `/outline` slack command. Once enabled team members can easily search your wiki from within Slack by typing `/outline search term`.
|
||||
|
||||

|
||||
|
||||
## Notifications
|
||||
|
||||
Outline can optionally post into any Slack #channel when documents are created or edited. You can also choose to route notifications based on the Collection.
|
||||
|
||||

|
||||
|
||||
> To setup channel notifications head to [your integration settings](https://www.getoutline.com/settings/integrations/slack) in the Outline admin
|
||||
5
server/pages/integrations/trello.md
Normal file
@@ -0,0 +1,5 @@
|
||||
In an Outline document, paste a share link to a [Trello](https://trello.com) card and it will be immediately converted into an embedded preview.
|
||||
|
||||
Embed trello cards in your knowledge base to commuicate roadmap items, ideas, and more alongside your written documentation. Not sure how? [Learn more about sharing from Trello](https://help.trello.com/article/824-sharing-links-to-cards-and-boards).
|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
5
server/pages/integrations/typeform.md
Normal file
@@ -0,0 +1,5 @@
|
||||
In an Outline document, paste a share link to a [Typeform survey](https://typeform.com) and it will be immediately converted into an embedded version of the survey. All you have to do is share the doc and wait for responses to roll in.
|
||||
|
||||

|
||||
|
||||
> This integration works without any additional settings or authentication.
|
||||
@@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import fs from 'fs-extra';
|
||||
import { find } from 'lodash';
|
||||
import path from 'path';
|
||||
import Koa from 'koa';
|
||||
import Router from 'koa-router';
|
||||
@@ -18,7 +20,11 @@ import About from './pages/About';
|
||||
import Changelog from './pages/Changelog';
|
||||
import Privacy from './pages/Privacy';
|
||||
import Pricing from './pages/Pricing';
|
||||
import Api from './pages/Api';
|
||||
import Integrations from './pages/integrations';
|
||||
import integrations from './pages/integrations/content';
|
||||
import Integration from './pages/integrations/Integration';
|
||||
import Developers from './pages/developers';
|
||||
import Api from './pages/developers/Api';
|
||||
import SubdomainSignin from './pages/SubdomainSignin';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
@@ -34,7 +40,11 @@ const renderapp = async ctx => {
|
||||
};
|
||||
|
||||
// serve static assets
|
||||
koa.use(serve(path.resolve(__dirname, '../public')));
|
||||
koa.use(
|
||||
serve(path.resolve(__dirname, '../public'), {
|
||||
maxage: 60 * 60 * 24 * 30 * 1000,
|
||||
})
|
||||
);
|
||||
|
||||
router.get('/_health', ctx => (ctx.body = 'OK'));
|
||||
|
||||
@@ -54,8 +64,24 @@ if (process.env.NODE_ENV === 'production') {
|
||||
// static pages
|
||||
router.get('/about', ctx => renderpage(ctx, <About />));
|
||||
router.get('/pricing', ctx => renderpage(ctx, <Pricing />));
|
||||
router.get('/developers', ctx => renderpage(ctx, <Api />));
|
||||
router.get('/developers', ctx => renderpage(ctx, <Developers />));
|
||||
router.get('/developers/api', ctx => renderpage(ctx, <Api />));
|
||||
router.get('/privacy', ctx => renderpage(ctx, <Privacy />));
|
||||
router.get('/integrations/:slug', async ctx => {
|
||||
const slug = ctx.params.slug;
|
||||
const integration = find(integrations, i => i.slug === slug);
|
||||
if (!integration) throw new Error('Not found');
|
||||
|
||||
const content = await fs.readFile(
|
||||
path.resolve(__dirname, `pages/integrations/${slug}.md`)
|
||||
);
|
||||
|
||||
return renderpage(
|
||||
ctx,
|
||||
<Integration integration={integration} content={content} />
|
||||
);
|
||||
});
|
||||
router.get('/integrations', ctx => renderpage(ctx, <Integrations />));
|
||||
router.get('/changelog', async ctx => {
|
||||
const data = await fetch(
|
||||
'https://api.github.com/repos/outline/outline/releases'
|
||||
|
||||
25
shared/components/OutlineLogo.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
size?: number,
|
||||
fill?: string,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
function OutlineLogo({ size = 32, fill = '#333', className }: Props) {
|
||||
return (
|
||||
<svg
|
||||
fill={fill}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 64 64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path d="M32,57.6 L32,59.1606101 C32,61.3697491 30.209139,63.1606101 28,63.1606101 C27.3130526,63.1606101 26.6376816,62.9836959 26.038955,62.6469122 L2.03895504,49.1469122 C0.779447116,48.438439 -4.3614532e-15,47.1057033 -7.10542736e-15,45.6606101 L-7.10542736e-15,18.3393899 C-7.28240024e-15,16.8942967 0.779447116,15.561561 2.03895504,14.8530878 L26.038955,1.35308779 C27.9643866,0.270032565 30.4032469,0.952913469 31.4863021,2.87834498 C31.8230858,3.47707155 32,4.15244252 32,4.83938994 L32,6.4 L34.8506085,5.54481746 C36.9665799,4.91002604 39.1965137,6.11075966 39.8313051,8.22673106 C39.9431692,8.59961116 40,8.98682435 40,9.3761226 L40,11 L43.5038611,10.5620174 C45.6959408,10.2880074 47.6951015,11.8429102 47.9691115,14.0349899 C47.9896839,14.1995692 48,14.3652688 48,14.5311289 L48,49.4688711 C48,51.6780101 46.209139,53.4688711 44,53.4688711 C43.8341399,53.4688711 43.6684404,53.458555 43.5038611,53.4379826 L40,53 L40,54.6238774 C40,56.8330164 38.209139,58.6238774 36,58.6238774 C35.6107017,58.6238774 35.2234886,58.5670466 34.8506085,58.4551825 L32,57.6 Z M32,53.4238774 L36,54.6238774 L36,9.3761226 L32,10.5761226 L32,53.4238774 Z M40,15.0311289 L40,48.9688711 L44,49.4688711 L44,14.5311289 L40,15.0311289 Z M5.32907052e-15,44.4688711 L5.32907052e-15,19.5311289 L3.55271368e-15,44.4688711 Z M4,18.3393899 L4,45.6606101 L28,59.1606101 L28,4.83938994 L4,18.3393899 Z M8,21 L12,19 L12,45 L8,43 L8,21 Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default OutlineLogo;
|
||||
@@ -1,9 +0,0 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import logo from './logo.png';
|
||||
|
||||
function OutlineLogo() {
|
||||
return <img src={logo} width={16} height={16} alt="Outline Logo" />;
|
||||
}
|
||||
|
||||
export default OutlineLogo;
|
||||
|
Before Width: | Height: | Size: 569 B |
@@ -2,6 +2,11 @@
|
||||
import theme from './theme';
|
||||
|
||||
export default `
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('/fonts/LatoLatin-Semibold.woff') format('woff');
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -46,6 +51,7 @@ export default `
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont;
|
||||
font-weight: 500;
|
||||
line-height: 1.25;
|
||||
margin-top: 1em;
|
||||
@@ -71,8 +77,8 @@ export default `
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 0;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border - bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
height: 0;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -73,6 +73,10 @@ export function about(): string {
|
||||
return `${process.env.URL}/about`;
|
||||
}
|
||||
|
||||
export function integrations(): string {
|
||||
return `${process.env.URL}/integrations`;
|
||||
}
|
||||
|
||||
export function privacy(): string {
|
||||
return `${process.env.URL}/privacy`;
|
||||
}
|
||||
|
||||