Merge branch 'master' into clean-dependencies

This commit is contained in:
Jim LAURIE 2018-10-17 16:14:26 +02:00 committed by GitHub
commit 3d149ffc68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
379 changed files with 12215 additions and 11998 deletions

View File

@ -1,18 +1,22 @@
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
<!-- ISSUE TEMPLATE -->
<!-- =============================================================================== -->
<!-- ⚠️ If you are not using the current Strapi release, you will be asked to update. -->
<!-- Please see the wiki for guides on upgrading to the latest release. -->
<!-- =============================================================================== -->
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
<!-- ⚠️ Before writing your issue make sure you are using:-->
<!-- Node 9.x.x -->
<!-- npm 5.x.x -->
<!-- The latest version of Strapi. -->
<!-- Node 10.x.x -->
<!-- npm 6.x.x -->
**Informations**
- **Node.js version**:
- **npm version**:
- **Strapi version**:
- **Database**:
- **Operating system**:
- **Node.js version**:
- **npm version**:
- **Strapi version**: <!-- Please make sure you are on the latest version -->
- **Database**:
- **Operating system**:
**What is the current behavior?**
@ -28,19 +32,3 @@
**Suggested solutions**
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
<!-- FEATURE REQUEST TEMPLATE -->
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
**What is the expected behavior?**
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->

View File

@ -5,17 +5,24 @@ about: Create a report to help us improve 🤔.
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
<!-- =============================================================================== -->
<!-- ⚠️ If you are not using the current Strapi release, you will be asked to update. -->
<!-- Please see the wiki for guides on upgrading to the latest release. -->
<!-- =============================================================================== -->
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
<!-- ⚠️ Before writing your issue make sure you are using:-->
<!-- Node 9.x.x -->
<!-- npm 5.x.x -->
<!-- Node 10.x.x -->
<!-- npm 6.x.x -->
<!-- The latest version of Strapi. -->
**Informations**
- **Node.js version**:
- **npm version**:
- **Strapi version**:
- **Database**:
- **Operating system**:
- **Node.js version**:
- **npm version**:
- **Strapi version**: <!-- Please make sure you are on the latest version -->
- **Database**:
- **Operating system**:
**What is the current behavior?**
@ -31,7 +38,3 @@ about: Create a report to help us improve 🤔.
**Suggested solutions**
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->

View File

@ -5,8 +5,8 @@ about: Suggest an idea for this project 💡!
<!-- ⚠️ If you do not respect this template your issue will be closed. -->
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->
**What is the expected behavior?**
<!-- ⚠️ Make sure to browse the opened and closed issues before submitting your issue. -->

2
.gitignore vendored
View File

@ -109,7 +109,7 @@ coverage
# Documentation
############################
_book
dist
############################
# Builds

View File

@ -38,14 +38,14 @@ Then, please follow the instructions below:
[Go to the repository](https://github.com/strapi/strapi) and fork it to your own GitHub account.
#### 2. 💿 Clone the repository
#### 2. 💿 Clone from your repository
```bash
git clone git@github.com:strapi/strapi.git
git clone git@github.com:YOUR_USERNAME/strapi.git
```
#### 3. ⏳ Installation
Go to the root of the repository.
```bash
cd strapi

View File

@ -80,16 +80,21 @@ strapi start
Congratulations, you made it! Enjoy 🎉
<br>
### Try on Heroku
You can also give it a try using Heroku! Be aware that one of the content type builder won't work due to the writing files restriction on the Heroku servers.
You can also give it a try using Heroku in one click!
<a href="https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app">
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
</a>
<br>
Be aware that one of the content type builder won't work due to the writing files restriction on the Heroku servers. If you do want to change content types, you need to follow these steps:
1. Click the button above and deploy your app
2. Clone that repo by using `heroku git:clone -a ` followed by your repo's name
3. Go into the cloned projects' folder using `cd` followed by your repo's name
4. Add the Heroku boilerplate as a remote by running `git remote add boilerplate https://github.com/strapi/strapi-heroku-app`
5. Pull from this new origin by running `git pull boilerplate master`
## Features

165
docs/.vuepress/config.js Normal file
View File

@ -0,0 +1,165 @@
const container = require('markdown-it-container')
let ogprefix = 'og: http://ogp.me/ns#'
let title = 'Strapi Documentation'
let description = 'API creation made simple, secure and fast.'
let color = '#2F80ED'
let author = 'Strapi'
let url = 'https://strapi.io/documentation/'
module.exports = {
head: [
['link', { rel: 'icon', href: `/rocket.png` }],
['meta', { name: 'theme-color', content: color }],
['meta', { prefix: ogprefix, property: 'og:title', content: title }],
['meta', { prefix: ogprefix, property: 'twitter:title', content: title }],
['meta', { prefix: ogprefix, property: 'og:type', content: 'article' }],
['meta', { prefix: ogprefix, property: 'og:url', content: url }],
['meta', { prefix: ogprefix, property: 'og:description', content: description }],
['meta', { prefix: ogprefix, property: 'og:image', content: `${url}rocket.png` }],
['meta', { prefix: ogprefix, property: 'og:article:author', content: author }],
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
// ['link', { rel: 'apple-touch-icon', href: `/assets/apple-touch-icon.png` }],
// ['link', { rel: 'mask-icon', href: '/assets/safari-pinned-tab.svg', color: color }],
['meta', { name: 'msapplication-TileImage', content: '/rocket.png' }],
['meta', { name: 'msapplication-TileColor', content: color }],
],
markdown: {
anchor: {
permalink: true,
},
config: md => {
md
.use(require('markdown-it-decorate'))
.use(...createContainer('intro'))
.use(...createContainer('note'))
}
},
title,
description,
base: '/documentation/',
themeConfig: {
versions: [
['Version 3.x.x', '/3.x.x/'],
['Version 1.x.x', '/1.x.x/'],
],
repo: 'strapi/strapi',
docsDir: 'docs',
editLinks: true,
editLinkText: 'Improve this page',
serviceWorker: true,
sidebar: {
'/3.x.x/': [
{
collapsable: false,
title: 'Getting started',
children: [
'/3.x.x/getting-started/installation',
'/3.x.x/getting-started/quick-start',
'/3.x.x/concepts/concepts',
],
},
{
collapsable: false,
title: 'Guides',
children: [
'/3.x.x/guides/authentication.md',
'/3.x.x/configurations/configurations.md',
'/3.x.x/guides/controllers.md',
'/3.x.x/guides/deployment.md',
'/3.x.x/guides/email.md',
'/3.x.x/guides/upload.md',
'/3.x.x/guides/filters.md',
'/3.x.x/guides/graphql.md',
'/3.x.x/guides/i18n.md',
'/3.x.x/guides/models.md',
'/3.x.x/guides/policies.md',
'/3.x.x/guides/public-assets.md',
'/3.x.x/guides/requests.md',
'/3.x.x/guides/responses.md',
'/3.x.x/guides/routing.md',
'/3.x.x/guides/services.md',
],
},
{
collapsable: false,
title: 'Globals',
children: [
'/3.x.x/api-reference/reference',
'/3.x.x/cli/CLI',
'/3.x.x/configurations/configurations',
],
},
{
collapsable: false,
title: 'Advanced',
children: [
'/3.x.x/advanced/customize-admin',
'/3.x.x/advanced/hooks',
'/3.x.x/advanced/logging',
'/3.x.x/advanced/middlewares',
'/3.x.x/advanced/usage-tracking',
],
},
{
collapsable: false,
title: 'Resources',
children: [
['https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md', 'Contributing guide'],
['https://github.com/strapi/strapi/wiki', 'Migration guides'],
'/3.x.x/tutorials/',
],
},
],
'/1.x.x/': [
{
collapsable: false,
title: 'UsefulLinks',
children: [
['/1.x.x/', 'Introduction'],
['https://strapi.io', 'Strapi Website'],
['https://github.com/strapi/strapi', 'GitHub Repository'],
['https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md', 'Contribution Guide'],
],
},
'/1.x.x/admin.md',
'/1.x.x/configuration.md',
'/1.x.x/email.md',
'/1.x.x/introduction.md',
'/1.x.x/queries.md',
'/1.x.x/response.md',
'/1.x.x/sessions.md',
'/1.x.x/testing.md',
'/1.x.x/views.md',
'/1.x.x/blueprints.md',
'/1.x.x/context.md',
'/1.x.x/graphql.md',
'/1.x.x/logging.md',
'/1.x.x/router.md',
'/1.x.x/upload.md',
'/1.x.x/cli.md',
'/1.x.x/customization.md',
'/1.x.x/internationalization.md',
'/1.x.x/models.md',
'/1.x.x/request.md',
'/1.x.x/services.md',
'/1.x.x/users.md',
],
},
},
}
function createContainer(className) {
return [container, className, {
render(tokens, idx) {
const token = tokens[idx]
if (token.nesting === 1) {
return `<div class="${className} custom-block">\n`
} else {
return `</div>\n`
}
}
}]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,131 @@
<template>
<form id="search-form" class="algolia-search-wrapper search-box">
<input id="algolia-search-input" class="search-query">
</form>
</template>
<script>
export default {
props: ['options'],
mounted () {
this.initialize()
},
methods: {
initialize () {
Promise.all([
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
]).then(([docsearch]) => {
docsearch = docsearch.default
docsearch(Object.assign(this.options, {
debug: true,
inputSelector: '#algolia-search-input'
}))
})
}
},
watch: {
options (newValue) {
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
this.initialize(newValue)
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.algolia-search-wrapper
& > span
vertical-align middle
.algolia-autocomplete
line-height normal
.ds-dropdown-menu
background-color #fff
border 1px solid #999
border-radius 4px
font-size 16px
margin 6px 0 0
padding 4px
text-align left
&:before
border-color #999
[class*=ds-dataset-]
border none
padding 0
.ds-suggestions
margin-top 0
.ds-suggestion
border-bottom 1px solid $borderColor
.algolia-docsearch-suggestion--highlight
color #2c815b
.algolia-docsearch-suggestion
border-color $borderColor
padding 0
.algolia-docsearch-suggestion--category-header
padding 5px 10px
margin-top 0
background $accentColor
color #fff
font-weight 600
.algolia-docsearch-suggestion--highlight
background rgba(255, 255, 255, 0.6)
.algolia-docsearch-suggestion--wrapper
padding 0
.algolia-docsearch-suggestion--title
font-weight 600
margin-bottom 0
color $textColor
.algolia-docsearch-suggestion--subcategory-column
vertical-align top
padding 5px 7px 5px 5px
border-color $borderColor
background #f1f3f5
&:after
display none
.algolia-docsearch-suggestion--subcategory-column-text
color #555
.algolia-docsearch-footer
border-color $borderColor
.ds-cursor .algolia-docsearch-suggestion--content
background-color #e7edf3 !important
color $textColor
@media (min-width: $MQMobile)
.algolia-search-wrapper
.algolia-autocomplete
.algolia-docsearch-suggestion
.algolia-docsearch-suggestion--subcategory-column
float none
width 150px
min-width 150px
display table-cell
.algolia-docsearch-suggestion--content
float none
display table-cell
width 100%
vertical-align top
.ds-dropdown-menu
min-width 515px !important
@media (max-width: $MQMobile)
.algolia-search-wrapper
.ds-dropdown-menu
min-width calc(100vw - 4rem) !important
max-width calc(100vw - 4rem) !important
.algolia-docsearch-suggestion--wrapper
padding 5px 7px 5px 5px !important
.algolia-docsearch-suggestion--subcategory-column
padding 0 !important
background white !important
.algolia-docsearch-suggestion--subcategory-column-text:after
content " > "
font-size 10px
line-height 14.4px
display inline-block
width 5px
margin -3px 3px 0
vertical-align middle
</style>

View File

@ -0,0 +1,32 @@
<template>
<component v-bind="linkProps(to)">
<slot></slot>
</component>
</template>
<script>
export default {
props: {
to: {
type: String,
required: true
}
},
methods: {
linkProps (url) {
if (url.match(/^(http(s)?|ftp):\/\//)) {
return {
is: 'a',
href: url,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'router-link',
to: url
}
}
}
}
</script>

View File

@ -0,0 +1,154 @@
<template>
<div class="dropdown-wrapper" :class="{ open }">
<a class="dropdown-title" @click="toggle">
<span class="title">{{ item.text }}</span>
<span class="arrow" :class="open ? 'down' : 'right'"></span>
</a>
<DropdownTransition>
<ul class="nav-dropdown" v-show="open">
<li
class="dropdown-item"
v-for="(subItem, index) in item.items"
:key="subItem.link || index">
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
<ul class="dropdown-subitem-wrapper" v-if="subItem.type === 'links'">
<li
class="dropdown-subitem"
v-for="childSubItem in subItem.items"
:key="childSubItem.link">
<NavLink :item="childSubItem"/>
</li>
</ul>
<NavLink v-else :item="subItem"/>
</li>
</ul>
</DropdownTransition>
</div>
</template>
<script>
import NavLink from './NavLink.vue'
import DropdownTransition from './DropdownTransition.vue'
export default {
components: { NavLink, DropdownTransition },
data () {
return {
open: false
}
},
props: {
item: {
required: true
}
},
methods: {
toggle () {
this.open = !this.open
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.dropdown-wrapper
.dropdown-title
display block
&:hover
border-color transparent
.arrow
vertical-align middle
margin-top -1px
margin-left 0.4rem
.nav-dropdown
.dropdown-item
color inherit
line-height 1.7rem
h4
margin 0.45rem 0 0
border-top 1px solid #eee
padding 0.45rem 1.5rem 0 1.25rem
.dropdown-subitem-wrapper
padding 0
list-style none
.dropdown-subitem
font-size 0.9em
a
display block
line-height 1.7rem
position relative
border-bottom none
font-weight 400
margin-bottom 0
padding 0 1.5rem 0 1.25rem
&:hover
color $accentColor
&.router-link-active
color $accentColor
&::after
content ""
width 0
height 0
border-left 5px solid $accentColor
border-top 3px solid transparent
border-bottom 3px solid transparent
position absolute
top calc(50% - 2px)
left 9px
&:first-child h4
margin-top 0
padding-top 0
border-top 0
@media (max-width: $MQMobile)
.dropdown-wrapper
&.open .dropdown-title
margin-bottom 0.5rem
.nav-dropdown
transition height .1s ease-out
overflow hidden
.dropdown-item
h4
border-top 0
margin-top 0
padding-top 0
h4, & > a
font-size 15px
line-height 2rem
.dropdown-subitem
font-size 14px
padding-left 1rem
@media (min-width: $MQMobile)
.dropdown-wrapper
height 1.8rem
&:hover .nav-dropdown
// override the inline style.
display block !important
.dropdown-title .arrow
// make the arrow always down at desktop
border-left 4px solid transparent
border-right 4px solid transparent
border-top 6px solid $arrowBgColor
border-bottom 0
.nav-dropdown
display none
// Avoid height shaked by clicking
height auto !important
box-sizing border-box;
max-height calc(100vh - 2.7rem)
overflow-y auto
position absolute
top 100%
right 0
background-color #fff
padding 0.6rem 0
border 1px solid #ddd
border-bottom-color #ccc
text-align left
border-radius 0.25rem
white-space nowrap
margin 0
</style>

View File

@ -0,0 +1,29 @@
<template>
<transition name="dropdown"
@enter="setHeight"
@after-enter="unsetHeight"
@before-leave="setHeight">
<slot></slot>
</transition>
</template>
<script>
export default {
name: 'DropdownTransition',
methods: {
setHeight (items) {
// explicitly set height so that it can be transitioned
items.style.height = items.scrollHeight + 'px'
},
unsetHeight (items) {
items.style.height = ''
}
}
}
</script>
<style lang="stylus">
.dropdown-enter, .dropdown-leave-to
height 0 !important
</style>

View File

@ -0,0 +1,134 @@
<template>
<div class="home">
<div class="hero">
<img v-if="data.heroImage" :src="$withBase(data.heroImage)" alt="hero">
<h1>{{ data.heroText || $title || 'Hello' }}</h1>
<p class="description">
{{ data.tagline || $description || 'Welcome to your VuePress site' }}
</p>
<p class="action" v-if="data.actionText && data.actionLink">
<NavLink class="action-button" :item="actionLink"/>
</p>
</div>
<div class="features" v-if="data.features && data.features.length">
<div class="feature" v-for="feature in data.features">
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
</div>
<Content custom/>
<div class="footer" v-if="data.footer">
{{ data.footer }}
</div>
</div>
</template>
<script>
import NavLink from './NavLink.vue'
export default {
components: { NavLink },
computed: {
data () {
return this.$page.frontmatter
},
actionLink () {
return {
link: this.data.actionLink,
text: this.data.actionText
}
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.home
padding $navbarHeight 2rem 0
max-width 960px
margin 0px auto
.hero
text-align center
img
max-height 280px
display block
margin 3rem auto 1.5rem
h1
font-size 3rem
h1, .description, .action
margin 1.8rem auto
.description
max-width 35rem
font-size 1.6rem
line-height 1.3
color lighten($textColor, 40%)
.action-button
display inline-block
font-size 1.2rem
color #fff
background-color $accentColor
padding 0.8rem 1.6rem
border-radius 4px
transition background-color .1s ease
box-sizing border-box
border-bottom 1px solid darken($accentColor, 10%)
&:hover
background-color lighten($accentColor, 10%)
.features
border-top 1px solid $borderColor
padding 1.2rem 0
margin-top 2.5rem
display flex
flex-wrap wrap
align-items flex-start
align-content stretch
justify-content space-between
.feature
flex-grow 1
flex-basis 30%
max-width 30%
h2
font-size 1.4rem
font-weight 500
border-bottom none
padding-bottom 0
color lighten($textColor, 10%)
p
color lighten($textColor, 25%)
.footer
padding 2.5rem
border-top 1px solid $borderColor
text-align center
color lighten($textColor, 25%)
@media (max-width: $MQMobile)
.home
.features
flex-direction column
.feature
max-width 100%
padding 0 2.5rem
@media (max-width: $MQMobileNarrow)
.home
padding-left 1.5rem
padding-right 1.5rem
.hero
img
max-height 210px
margin 2rem auto 1.2rem
h1
font-size 2rem
h1, .description, .action
margin 1.2rem auto
.description
font-size 1.2rem
.action-button
font-size 1rem
padding 0.6rem 1.2rem
.feature
h2
font-size 1.25rem
</style>

View File

@ -0,0 +1,147 @@
<template>
<div class="theme-container"
:class="pageClasses"
@touchstart="onTouchStart"
@touchend="onTouchEnd">
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar"/>
<div class="sidebar-mask" @click="toggleSidebar(false)"></div>
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
<div slot="top">
<select @input="changeVersion($event.target.value)" class="version-selector">
<option v-for="version in versions" :value="version.path" :selected="!!~$page.path.indexOf(version.path)">{{version.name}}</option>
</select>
</div>
<slot name="sidebar-bottom" slot="bottom"/>
</Sidebar>
<div class="custom-layout" v-if="$page.frontmatter.layout">
<component :is="$page.frontmatter.layout"/>
</div>
<Home v-else-if="$page.frontmatter.home"/>
<Page v-else :sidebar-items="sidebarItems">
<slot name="page-top" slot="top"/>
<slot name="page-bottom" slot="bottom"/>
</Page>
</div>
</template>
<script>
import Vue from 'vue'
import nprogress from 'nprogress'
import Home from './Home.vue'
import Navbar from './Navbar.vue'
import Page from './Page.vue'
import Sidebar from './Sidebar.vue'
import { resolveSidebarItems } from './util'
export default {
components: { Home, Page, Sidebar, Navbar },
data () {
return {
isSidebarOpen: false
}
},
computed: {
versions() {
const { themeConfig } = this.$site
return themeConfig.versions.map(version => ({
name: version[0],
path: version[1],
}))
},
shouldShowNavbar () {
const { themeConfig } = this.$site
const { frontmatter } = this.$page
if (
frontmatter.navbar === false ||
themeConfig.navbar === false) {
return false
}
return (
this.$title ||
themeConfig.logo ||
themeConfig.repo ||
themeConfig.nav ||
this.$themeLocaleConfig.nav
)
},
shouldShowSidebar () {
const { frontmatter } = this.$page
return (
!frontmatter.layout &&
!frontmatter.home &&
frontmatter.sidebar !== false &&
this.sidebarItems.length
)
},
sidebarItems () {
return resolveSidebarItems(
this.$page,
this.$route,
this.$site,
this.$localePath
)
},
pageClasses () {
const userPageClass = this.$page.frontmatter.pageClass
return [
{
'no-navbar': !this.shouldShowNavbar,
'sidebar-open': this.isSidebarOpen,
'no-sidebar': !this.shouldShowSidebar
},
userPageClass
]
}
},
mounted () {
window.addEventListener('scroll', this.onScroll)
// configure progress bar
nprogress.configure({ showSpinner: false })
this.$router.beforeEach((to, from, next) => {
if (to.path !== from.path && !Vue.component(to.name)) {
nprogress.start()
}
next()
})
this.$router.afterEach(() => {
nprogress.done()
this.isSidebarOpen = false
})
},
methods: {
changeVersion(to) {
this.$router.push(to)
},
toggleSidebar (to) {
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
},
// side swipe
onTouchStart (e) {
this.touchStart = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY
}
},
onTouchEnd (e) {
const dx = e.changedTouches[0].clientX - this.touchStart.x
const dy = e.changedTouches[0].clientY - this.touchStart.y
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
if (dx > 0 && this.touchStart.x <= 80) {
this.toggleSidebar(true)
} else {
this.toggleSidebar(false)
}
}
}
}
}
</script>
<style src="prismjs/themes/prism-tomorrow.css"></style>
<style src="./styles/theme.styl" lang="stylus"></style>

View File

@ -0,0 +1,46 @@
<template>
<router-link
class="nav-link"
:to="link"
v-if="!isExternal(link)"
:exact="exact"
>{{ item.text }}</router-link>
<a
v-else
:href="link"
class="nav-link external"
:target="isMailto(link) || isTel(link) ? null : '_blank'"
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
>
{{ item.text }}
<OutboundLink/>
</a>
</template>
<script>
import { isExternal, isMailto, isTel, ensureExt } from './util'
export default {
props: {
item: {
required: true
}
},
computed: {
link () {
return ensureExt(this.item.link)
},
exact () {
if (this.$site.locales) {
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
}
return this.link === '/'
}
},
methods: {
isExternal,
isMailto,
isTel
}
}
</script>

View File

@ -0,0 +1,132 @@
<template>
<nav class="nav-links" v-if="userLinks.length || repoLink">
<!-- user links -->
<div
class="nav-item"
v-for="item in userLinks"
:key="item.link">
<DropdownLink v-if="item.type === 'links'" :item="item"/>
<NavLink v-else :item="item"/>
</div>
<!-- repo link -->
<a v-if="repoLink"
:href="repoLink"
class="repo-link"
target="_blank"
rel="noopener noreferrer">
{{ repoLabel }}
<OutboundLink/>
</a>
</nav>
</template>
<script>
import DropdownLink from './DropdownLink.vue'
import { resolveNavLinkItem } from './util'
import NavLink from './NavLink.vue'
export default {
components: { NavLink, DropdownLink },
computed: {
userNav () {
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
},
nav () {
const { locales } = this.$site
if (locales && Object.keys(locales).length > 1) {
const currentLink = this.$page.path
const routes = this.$router.options.routes
const themeLocales = this.$site.themeConfig.locales || {}
const languageDropdown = {
text: this.$themeLocaleConfig.selectText || 'Languages',
items: Object.keys(locales).map(path => {
const locale = locales[path]
const text = themeLocales[path] && themeLocales[path].label || locale.lang
let link
// Stay on the current page
if (locale.lang === this.$lang) {
link = currentLink
} else {
// Try to stay on the same page
link = currentLink.replace(this.$localeConfig.path, path)
// fallback to homepage
if (!routes.some(route => route.path === link)) {
link = path
}
}
return { text, link }
})
}
return [...this.userNav, languageDropdown]
}
return this.userNav
},
userLinks () {
return (this.nav || []).map(link => {
return Object.assign(resolveNavLinkItem(link), {
items: (link.items || []).map(resolveNavLinkItem)
})
})
},
repoLink () {
const { repo } = this.$site.themeConfig
if (repo) {
return /^https?:/.test(repo)
? repo
: `https://github.com/${repo}`
}
},
repoLabel () {
if (!this.repoLink) return
if (this.$site.themeConfig.repoLabel) {
return this.$site.themeConfig.repoLabel
}
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
const platforms = ['GitHub', 'GitLab', 'Bitbucket']
for (let i = 0; i < platforms.length; i++) {
const platform = platforms[i]
if (new RegExp(platform, 'i').test(repoHost)) {
return platform
}
}
return 'Source'
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.nav-links
display inline-block
a
line-height 1.4rem
color inherit
&:hover, &.router-link-active
color $accentColor
.nav-item
cursor pointer
position relative
display inline-block
margin-left 1.5rem
line-height 2rem
.repo-link
margin-left 1.5rem
@media (max-width: $MQMobile)
.nav-links
.nav-item, .repo-link
margin-left 0
@media (min-width: $MQMobile)
.nav-links a
&:hover, &.router-link-active
color $textColor
.nav-item > a:not(.external)
&:hover, &.router-link-active
margin-bottom -2px
border-bottom 2px solid lighten($accentColor, 8%)
</style>

View File

@ -0,0 +1,71 @@
<template>
<header class="navbar">
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
<router-link :to="$localePath" class="home-link">
<img class="logo"
v-if="$site.themeConfig.logo"
:src="$withBase($site.themeConfig.logo)">
<span class="site-name"
v-if="$siteTitle"
:class="{ 'can-hide': $site.themeConfig.logo }">
{{ $siteTitle }}
</span>
</router-link>
<div class="links">
<AlgoliaSearchBox v-if="isAlgoliaSearch" :options="algolia"/>
<SearchBox v-else-if="$site.themeConfig.search !== false"/>
<NavLinks class="can-hide"/>
</div>
</header>
</template>
<script>
import SidebarButton from './SidebarButton.vue'
import AlgoliaSearchBox from '@AlgoliaSearchBox'
import SearchBox from './SearchBox.vue'
import NavLinks from './NavLinks.vue'
export default {
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
computed: {
algolia () {
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
},
isAlgoliaSearch () {
return this.algolia && this.algolia.apiKey && this.algolia.indexName
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.navbar
padding 0.7rem 1.5rem
line-height $navbarHeight - 1.4rem
position relative
a, span, img
display inline-block
.logo
height $navbarHeight - 1.4rem
min-width $navbarHeight - 1.4rem
margin-right 0.8rem
vertical-align top
.site-name
font-size 1.3rem
font-weight 600
color $textColor
position relative
.links
font-size 0.9rem
position absolute
right 1.5rem
top 0.7rem
@media (max-width: $MQMobile)
.navbar
padding-left 4rem
.can-hide
display none
</style>

View File

@ -0,0 +1,26 @@
<template>
<div class="theme-container">
<div class="content">
<h1>404</h1>
<blockquote>{{ getMsg() }}</blockquote>
<router-link to="/">Take me home.</router-link>
</div>
</div>
</template>
<script>
const msgs = [
`There's nothing here.`,
`How did we get here?`,
`That's a Four-Oh-Four.`,
`Looks like we've got some broken links.`
]
export default {
methods: {
getMsg () {
return msgs[Math.floor(Math.random() * msgs.length)]
}
}
}
</script>

View File

@ -0,0 +1,207 @@
<template>
<div class="page">
<Content :custom="false"/>
<div class="page-edit">
<div class="edit-link" v-if="editLink">
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
<OutboundLink/>
</div>
<div class="last-updated" v-if="lastUpdated">
<span class="prefix">{{ lastUpdatedText }}: </span>
<span class="time">{{ lastUpdated }}</span>
</div>
</div>
<div class="page-nav" v-if="prev || next">
<p class="inner">
<span v-if="prev" class="prev">
<router-link v-if="prev" class="prev" :to="prev.path">
{{ prev.title || prev.path }}
</router-link>
</span>
<span v-if="next" class="next">
<router-link v-if="next" :to="next.path">
{{ next.title || next.path }}
</router-link>
</span>
</p>
</div>
<slot name="bottom"/>
</div>
</template>
<script>
import { resolvePage, normalize, outboundRE, endingSlashRE } from './util'
export default {
props: ['sidebarItems'],
computed: {
lastUpdated () {
if (this.$page.lastUpdated) {
return new Date(this.$page.lastUpdated).toLocaleString(this.$lang)
}
},
lastUpdatedText () {
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
return this.$themeLocaleConfig.lastUpdated
}
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
return this.$site.themeConfig.lastUpdated
}
return 'Last Updated'
},
prev () {
const prev = this.$page.frontmatter.prev
if (prev === false) {
return
} else if (prev) {
return resolvePage(this.$site.pages, prev, this.$route.path)
} else {
return resolvePrev(this.$page, this.sidebarItems)
}
},
next () {
const next = this.$page.frontmatter.next
if (next === false) {
return
} else if (next) {
return resolvePage(this.$site.pages, next, this.$route.path)
} else {
return resolveNext(this.$page, this.sidebarItems)
}
},
editLink () {
if (this.$page.frontmatter.editLink === false) {
return
}
const {
repo,
editLinks,
docsDir = '',
docsBranch = 'master',
docsRepo = repo
} = this.$site.themeConfig
let path = normalize(this.$page.path)
if (endingSlashRE.test(path)) {
path += 'README.md'
} else {
path += '.md'
}
if (docsRepo && editLinks) {
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path)
}
},
editLinkText () {
return (
this.$themeLocaleConfig.editLinkText ||
this.$site.themeConfig.editLinkText ||
`Edit this page`
)
}
},
methods: {
createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
const bitbucket = /bitbucket.org/
if (bitbucket.test(repo)) {
const base = outboundRE.test(docsRepo)
? docsRepo
: repo
return (
base.replace(endingSlashRE, '') +
`/${docsBranch}` +
(docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
path +
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
)
}
const base = outboundRE.test(docsRepo)
? docsRepo
: `https://github.com/${docsRepo}`
return (
base.replace(endingSlashRE, '') +
`/edit/${docsBranch}` +
(docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
path
)
}
}
}
function resolvePrev (page, items) {
return find(page, items, -1)
}
function resolveNext (page, items) {
return find(page, items, 1)
}
function find (page, items, offset) {
const res = []
items.forEach(item => {
if (item.type === 'group') {
res.push(...item.children || [])
} else {
res.push(item)
}
})
for (let i = 0; i < res.length; i++) {
const cur = res[i]
if (cur.type === 'page' && cur.path === page.path) {
return res[i + offset]
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
@require './styles/wrapper.styl'
.page
padding-bottom 2rem
.page-edit
@extend $wrapper
padding-top 1rem
padding-bottom 1rem
overflow auto
.edit-link
display inline-block
a
color lighten($textColor, 25%)
margin-right 0.25rem
.last-updated
float right
font-size 0.9em
.prefix
font-weight 500
color lighten($textColor, 25%)
.time
font-weight 400
color #aaa
.page-nav
@extend $wrapper
padding-top 1rem
padding-bottom 0
.inner
min-height 2rem
margin-top 0
border-top 1px solid $borderColor
padding-top 1rem
overflow auto // clear float
.next
float right
@media (max-width: $MQMobile)
.page-edit
.edit-link
margin-bottom .5rem
.last-updated
font-size .8em
float none
text-align left
</style>

View File

@ -0,0 +1,221 @@
<template>
<div class="search-box">
<input
@input="query = $event.target.value"
aria-label="Search"
:value="query"
autocomplete="off"
spellcheck="false"
@focus="focused = true"
@blur="focused = false"
@keyup.enter="go(focusIndex)"
@keyup.up="onUp"
@keyup.down="onDown">
<ul class="suggestions"
v-if="showSuggestions"
:class="{ 'align-right': alignRight }"
@mouseleave="unfocus">
<li class="suggestion" v-for="(s, i) in suggestions"
:class="{ focused: i === focusIndex }"
@mousedown="go(i)"
@mouseenter="focus(i)">
<a :href="s.path" @click.prevent>
<span class="page-title">{{ s.title || s.path }}</span>
<span v-if="s.header" class="header">&gt; {{ s.header.title }}</span>
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
query: '',
focused: false,
focusIndex: 0
}
},
computed: {
showSuggestions () {
return (
this.focused &&
this.suggestions &&
this.suggestions.length
)
},
suggestions () {
const query = this.query.trim().toLowerCase()
if (!query) {
return
}
const { pages, themeConfig } = this.$site
const max = themeConfig.searchMaxSuggestions || 5
const searchContext = this.$route.path.split('/').slice(1)[0]
const localePath = this.$localePath
const matches = item => (
item.title &&
item.title.toLowerCase().indexOf(query) > -1
)
const res = []
for (let i = 0; i < pages.length; i++) {
if (res.length >= max) break
const p = pages[i]
// filter out results that do not match current ersion context
if (~p.path.slice(1).indexOf(searchContext)) continue
// filter out results that do not match current locale
if (this.getPageLocalePath(p) !== localePath) continue
if (matches(p)) {
res.push(p)
} else if (p.headers) {
for (let j = 0; j < p.headers.length; j++) {
if (res.length >= max) break
const h = p.headers[j]
if (matches(h)) {
res.push(Object.assign({}, p, {
path: p.path + '#' + h.slug,
header: h
}))
}
}
}
}
return res
},
// make suggestions align right when there are not enough items
alignRight () {
const navCount = (this.$site.themeConfig.nav || []).length
const repo = this.$site.repo ? 1 : 0
return navCount + repo <= 2
}
},
methods: {
getPageLocalePath (page) {
for (const localePath in this.$site.locales || {}) {
if (localePath !== '/' && page.path.indexOf(localePath) === 0) {
return localePath
}
}
return '/'
},
onUp () {
if (this.showSuggestions) {
if (this.focusIndex > 0) {
this.focusIndex--
} else {
this.focusIndex = this.suggestions.length - 1
}
}
},
onDown () {
if (this.showSuggestions) {
if (this.focusIndex < this.suggestions.length - 1) {
this.focusIndex++
} else {
this.focusIndex = 0
}
}
},
go (i) {
if (!this.showSuggestions) {
return
}
this.$router.push(this.suggestions[i].path)
this.query = ''
this.focusIndex = 0
},
focus (i) {
this.focusIndex = i
},
unfocus () {
this.focusIndex = -1
}
}
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.search-box
display inline-block
position relative
margin-right 0.5rem
input
cursor text
width 10rem
color lighten($textColor, 25%)
display inline-block
border 1px solid darken($borderColor, 10%)
border-radius 2rem
font-size 0.9rem
line-height 2rem
padding 0 0.5rem 0 2rem
outline none
transition all .2s ease
background #fff url(./search.svg) 0.6rem 0.5rem no-repeat
background-size 1rem
&:focus
cursor auto
border-color $accentColor
.suggestions
background #fff
width 20rem
position absolute
top 1.5rem
border 1px solid darken($borderColor, 10%)
border-radius 6px
padding 0.4rem
list-style-type none
&.align-right
right 0
.suggestion
line-height 1.4
padding 0.4rem 0.6rem
border-radius 4px
cursor pointer
a
color lighten($textColor, 35%)
.page-title
font-weight 600
.header
font-size 0.9em
margin-left 0.25em
&.focused
background-color #f3f4f5
a
color $accentColor
@media (max-width: $MQNarrow)
.search-box
input
cursor pointer
width 0
border-color transparent
position relative
left 1rem
&:focus
cursor text
left 0
width 10rem
@media (max-width: $MQNarrow) and (min-width: $MQMobile)
.search-box
.suggestions
left 0
@media (max-width: $MQMobile)
.search-box
margin-right 0
.suggestions
right 0
@media (max-width: $MQMobileNarrow)
.search-box
.suggestions
width calc(100vw - 4rem)
input:focus
width 8rem
</style>

View File

@ -0,0 +1,104 @@
<template>
<div class="sidebar">
<NavLinks/>
<slot name="top"/>
<ul class="sidebar-links" v-if="items.length">
<li v-for="(item, i) in items" :key="i">
<SidebarGroup v-if="item.type === 'group'"
:item="item"
:first="i === 0"
:open="i === openGroupIndex"
:collapsable="item.collapsable"
@toggle="toggleGroup(i)"/>
<SidebarLink v-else :item="item"/>
</li>
</ul>
<slot name="bottom"/>
</div>
</template>
<script>
import SidebarGroup from './SidebarGroup.vue'
import SidebarLink from './SidebarLink.vue'
import NavLinks from './NavLinks.vue'
import { isActive } from './util'
export default {
components: { SidebarGroup, SidebarLink, NavLinks },
props: ['items'],
data () {
return {
openGroupIndex: 0
}
},
created () {
this.refreshIndex()
},
watch: {
'$route' () {
this.refreshIndex()
}
},
methods: {
refreshIndex () {
const index = resolveOpenGroupIndex(
this.$route,
this.items
)
if (index > -1) {
this.openGroupIndex = index
}
},
toggleGroup (index) {
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
},
isActive (page) {
return isActive(this.$route, page.path)
}
}
}
function resolveOpenGroupIndex (route, items) {
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
return i
}
}
return -1
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.sidebar
ul
padding 0
margin 0
list-style-type none
a
display inline-block
.nav-links
display none
border-bottom 1px solid $borderColor
padding 0.5rem 0 0.75rem 0
a
font-weight 600
.nav-item, .repo-link
display block
line-height 1.25rem
font-size 1.1em
padding 0.5rem 0 0.5rem 1.5rem
.sidebar-links
padding 1.5rem 0
@media (max-width: $MQMobile)
.sidebar
.nav-links
display block
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
top calc(1rem - 2px)
.sidebar-links
padding 1rem 0
</style>

View File

@ -0,0 +1,28 @@
<template>
<div class="sidebar-button" @click="$emit('toggle-sidebar')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512">
<path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class=""></path>
</svg>
</div>
</template>
<style lang="stylus">
@import './styles/config.styl'
.sidebar-button
display none
width 1.25rem
height 1.25rem
position absolute
padding 0.6rem
top 0.6rem
left 1rem
.icon
display block
width 1.25rem
height 1.25rem
@media (max-width: $MQMobile)
.sidebar-button
display block
</style>

View File

@ -0,0 +1,63 @@
<template>
<div class="sidebar-group" :class="{ first, collapsable }">
<p class="sidebar-heading" :class="{ open }" @click="$emit('toggle')">
<span>{{ item.title }}</span>
<span class="arrow"
v-if="collapsable"
:class="open ? 'down' : 'right'"></span>
</p>
<DropdownTransition>
<ul class="sidebar-group-items" ref="items" v-if="open || !collapsable">
<li v-for="child in item.children">
<SidebarLink :item="child"/>
</li>
</ul>
</DropdownTransition>
</div>
</template>
<script>
import SidebarLink from './SidebarLink.vue'
import DropdownTransition from './DropdownTransition.vue'
export default {
name: 'SidebarGroup',
props: ['item', 'first', 'open', 'collapsable'],
components: { SidebarLink, DropdownTransition }
}
</script>
<style lang="stylus">
.sidebar-group
&:not(.first)
margin-top 1em
.sidebar-group
padding-left 0.5em
&:not(.collapsable)
.sidebar-heading
cursor auto
color inherit
.sidebar-heading
color #999
transition color .15s ease
cursor pointer
font-size 1.1em
font-weight bold
// text-transform uppercase
padding 0 1.5rem
margin-top 0
margin-bottom 0.5rem
&.open, &:hover
color inherit
.arrow
position relative
top -0.12em
left 0.5em
&:.open .arrow
top -0.18em
.sidebar-group-items
transition height .1s ease-out
overflow hidden
</style>

View File

@ -0,0 +1,98 @@
<script>
import { isActive, hashRE, groupHeaders } from './util'
import AppLink from './AppLink'
export default {
functional: true,
components: {AppLink},
props: ['item'],
render (h, { parent: { $page, $site, $route }, props: { item }}) {
// use custom active class matching logic
// due to edge case of paths ending with / + hash
const selfActive = isActive($route, item.path)
// for sidebar: auto pages, a hash link should be active if one of its child
// matches
const active = item.type === 'auto'
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
: selfActive
const link = renderLink(h, item.path, item.title || item.path, active)
const configDepth = $page.frontmatter.sidebarDepth != null
? $page.frontmatter.sidebarDepth
: $site.themeConfig.sidebarDepth
const maxDepth = configDepth == null ? 1 : configDepth
if (item.type === 'auto') {
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
} else if (active && item.headers && !hashRE.test(item.path)) {
const children = groupHeaders(item.headers)
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
} else {
return link
}
}
}
function renderLink (h, to, text, active) {
if (~to.indexOf('http')) return h('a',
{
attrs: {
href: to
},
class: 'sidebar-link'
},
text)
return h('router-link', {
props: {
to,
activeClass: '',
exactActiveClass: ''
},
class: {
active,
'sidebar-link': true
}
}, text)
}
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
if (!children || depth > maxDepth) return null
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
const active = isActive(route, path + '#' + c.slug)
return h('li', { class: 'sidebar-sub-header' }, [
renderLink(h, '#' + c.slug, c.title, active),
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
])
}))
}
</script>
<style lang="stylus">
@import './styles/config.styl'
.sidebar .sidebar-sub-headers
padding-left 1rem
font-size 0.95em
a.sidebar-link
font-weight 400
display inline-block
color $textColor
border-left 0.25rem solid transparent
padding 0.35rem 1rem 0.35rem 1.25rem
line-height 1.4
width: 100%
box-sizing: border-box
&:hover
color $accentColor
&.active
font-weight 600
color $accentColor
border-left-color $accentColor
.sidebar-group &
padding-left 2rem
.sidebar-sub-headers &
padding-top 0.25rem
padding-bottom 0.25rem
border-left none
&.active
font-weight 500
</style>

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13"><g stroke-width="2" stroke="#aaa" fill="none"><path d="M11.29 11.71l-4-4"/><circle cx="5" cy="5" r="4"/></g></svg>

After

Width:  |  Height:  |  Size: 216 B

View File

@ -0,0 +1,22 @@
@require './config'
.arrow
display inline-block
width 0
height 0
&.up
border-left 4px solid transparent
border-right 4px solid transparent
border-bottom 6px solid $arrowBgColor
&.down
border-left 4px solid transparent
border-right 4px solid transparent
border-top 6px solid $arrowBgColor
&.right
border-top 4px solid transparent
border-bottom 4px solid transparent
border-left 6px solid $arrowBgColor
&.left
border-top 4px solid transparent
border-bottom 4px solid transparent
border-right 6px solid $arrowBgColor

View File

@ -0,0 +1,127 @@
@require './config'
.content
code
color lighten($textColor, 20%)
padding 0.25rem 0.5rem
margin 0
font-size 0.85em
background-color rgba(27,31,35,0.05)
border-radius 3px
.content
pre, pre[class*="language-"]
line-height 1.4
padding 1.25rem 1.5rem
margin 0.85rem 0
background transparent
overflow auto
code
color #fff
padding 0
background-color transparent
border-radius 0
div[class*="language-"]
position relative
background-color $codeBgColor
border-radius 6px
.highlight-lines
user-select none
padding-top 1.3rem
position absolute
top 0
left 0
width 100%
line-height 1.4
.highlighted
background-color rgba(0, 0, 0, 66%)
pre
position relative
z-index 1
&::before
position absolute
z-index 3
top 0.8em
right 1em
font-size 0.75rem
color rgba(255, 255, 255, 0.4)
&:not(.line-numbers-mode)
.line-numbers-wrapper
display none
&.line-numbers-mode
.highlight-lines .highlighted
position relative
&:before
content ' '
position absolute
z-index 3
left 0
top 0
display block
width $lineNumbersWrapperWidth
height 100%
background-color rgba(0, 0, 0, 66%)
pre
padding-left $lineNumbersWrapperWidth + 1 rem
vertical-align middle
.line-numbers-wrapper
position absolute
top 0
width $lineNumbersWrapperWidth
text-align center
color rgba(255, 255, 255, 0.3)
padding 1.25rem 0
line-height 1.4
br
user-select none
.line-number
position relative
z-index 4
user-select none
font-size 0.85em
&::after
content ''
position absolute
z-index 2
top 0
left 0
width $lineNumbersWrapperWidth
height 100%
border-radius 6px 0 0 6px
border-right 1px solid rgba(0, 0, 0, 66%)
background-color $codeBgColor
for lang in js ts html md vue css sass scss less stylus go java c sh yaml
div{'[class~="language-' + lang + '"]'}
&:before
content ('' + lang)
div[class~="language-javascript"]
&:before
content "js"
div[class~="language-typescript"]
&:before
content "ts"
div[class~="language-markup"]
&:before
content "html"
div[class~="language-markdown"]
&:before
content "md"
div[class~="language-json"]:before
content "json"
div[class~="language-ruby"]:before
content "rb"
div[class~="language-python"]:before
content "py"
div[class~="language-bash"]:before
content "sh"

View File

@ -0,0 +1,19 @@
// colors
$accentColor = #2F80ED
$textColor = #2c3e50
$borderColor = #eaecef
$codeBgColor = #282c34
$arrowBgColor = #ccc
// layout
$navbarHeight = 3.6rem
$sidebarWidth = 20rem
$contentWidth = 740px
// responsive breakpoints
$MQNarrow = 959px
$MQMobile = 719px
$MQMobileNarrow = 419px
// code
$lineNumbersWrapperWidth = 3.5rem

View File

@ -0,0 +1,31 @@
.custom-block
.custom-block-title
font-weight 600
margin-bottom -0.4rem
&.tip, &.warning, &.danger, &.note
padding .1rem 1.5rem
border-left-width .5rem
border-left-style solid
margin 1rem 0
&.tip
background-color #f3f5f7
border-color #42b983
&.note
background-color #f3f5f7
border-color $accentColor
&.warning
background-color rgba(255,229,100,.3)
border-color darken(#ffe564, 35%)
color darken(#ffe564, 70%)
.custom-block-title
color darken(#ffe564, 50%)
a
color $textColor
&.danger
background-color #ffe6e6
border-color darken(red, 20%)
color darken(red, 70%)
.custom-block-title
color darken(red, 40%)
a
color $textColor

View File

@ -0,0 +1,37 @@
@require './config'
$mobileSidebarWidth = $sidebarWidth * 0.82
// narrow desktop / iPad
@media (max-width: $MQNarrow)
.sidebar
font-size 15px
width $mobileSidebarWidth
.page
padding-left $mobileSidebarWidth
// wide mobile
@media (max-width: $MQMobile)
.sidebar
top 0
padding-top $navbarHeight
transform translateX(-100%)
transition transform .2s ease
.page
padding-left 0
.theme-container
&.sidebar-open
.sidebar
transform translateX(0)
&.no-navbar
.sidebar
padding-top: 0
// narrow mobile
@media (max-width: $MQMobileNarrow)
h1
font-size 1.9rem
.content
div[class*="language-"]
margin 0.85rem -1.5rem
border-radius 0

View File

@ -0,0 +1,48 @@
#nprogress
pointer-events none
.bar
background $accentColor
position fixed
z-index 1031
top 0
left 0
width 100%
height 2px
.peg
display block
position absolute
right 0px
width 100px
height 100%
box-shadow 0 0 10px $accentColor, 0 0 5px $accentColor
opacity 1.0
transform rotate(3deg) translate(0px, -4px)
.spinner
display block
position fixed
z-index 1031
top 15px
right 15px
.spinner-icon
width 18px
height 18px
box-sizing border-box
border solid 2px transparent
border-top-color $accentColor
border-left-color $accentColor
border-radius 50%
animation nprogress-spinner 400ms linear infinite
.nprogress-custom-parent
overflow hidden
position relative
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar
position absolute
@keyframes nprogress-spinner
0%
transform rotate(0deg)
100%
transform rotate(360deg)

View File

@ -0,0 +1,222 @@
@require './config'
@require './nprogress'
@require './code'
@require './custom-blocks'
@require './arrow'
@require './wrapper'
@require './toc'
html, body
padding 0
margin 0
body
font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
font-size 16px
color $textColor
.intro
text-align center
font-size 1.15em
margin 0 2em
// hide outboundlinks for shields
a img + svg
display none !important
.version-selector
margin 1em 1em 0 1.5em
color lighten($textColor, 25%)
display block
border 1px solid darken($borderColor, 10%)
font-size .9rem
line-height 2rem
padding .5em 1rem .5em 1em
outline none
transition all .2s ease
&:focus
cursor auto
border-color $accentColor
.text-center
text-align center
.flex
display flex
.justify-around
justify-content space-around
.page
padding-left $sidebarWidth
.navbar
position fixed
z-index 20
top 0
left 0
right 0
height $navbarHeight
background-color #fff
box-sizing border-box
border-bottom 1px solid $borderColor
.sidebar-mask
position fixed
z-index 9
top 0
left 0
width 100vw
height 100vh
display none
.sidebar
font-size 15px
background-color #fff
width $sidebarWidth
position fixed
z-index 10
margin 0
top $navbarHeight
left 0
bottom 0
box-sizing border-box
border-right 1px solid $borderColor
overflow-y auto
.content:not(.custom)
@extend $wrapper
> *:first-child
margin-top $navbarHeight
a:hover
text-decoration underline
p.demo
padding 1rem 1.5rem
border 1px solid #ddd
border-radius 4px
img
max-width 100%
.content.custom
padding 0
margin 0
img
max-width 100%
a
font-weight 500
color $accentColor
text-decoration none
p a code
font-weight 400
color $accentColor
kbd
background #eee
border solid 0.15rem #ddd
border-bottom solid 0.25rem #ddd
border-radius 0.15rem
padding 0 0.15em
blockquote
font-size 1.2rem
color #999
border-left .25rem solid #dfe2e5
margin-left 0
padding-left 1rem
ul, ol
padding-left 1.2em
strong
font-weight 600
h1, h2, h3, h4, h5, h6
font-weight 600
line-height 1.25
.content:not(.custom) > &
margin-top (0.5rem - $navbarHeight)
padding-top ($navbarHeight + 1rem)
margin-bottom 0
&:first-child
margin-top -1.5rem
margin-bottom 1rem
+ p, + pre, + .custom-block
margin-top 2rem
&:hover .header-anchor
opacity: 1
h1
font-size 2.2rem
h2
font-size 1.65rem
padding-bottom .3rem
border-bottom 1px solid $borderColor
h3
font-size 1.35rem
a.header-anchor
font-size 0.85em
float left
margin-left -0.87em
padding-right 0.23em
margin-top 0.125em
opacity 0
&:hover
text-decoration none
code, kbd, .line-number
font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
p, ul, ol
line-height 1.7
hr
border 0
border-top 1px solid $borderColor
table
border-collapse collapse
margin 1rem 0
display: block
overflow-x: auto
tr
border-top 1px solid #dfe2e5
&:nth-child(2n)
background-color #f6f8fa
th, td
border 1px solid #dfe2e5
padding .6em 1em
.custom-layout
padding-top $navbarHeight
.theme-container
&.sidebar-open
.sidebar-mask
display: block
&.no-navbar
.content:not(.custom) > h1, h2, h3, h4, h5, h6
margin-top 1.5rem
padding-top 0
.sidebar
top 0
.custom-layout
padding-top 0
@media (min-width: ($MQMobile + 1px))
.theme-container.no-sidebar
.sidebar
display none
.page
padding-left 0
@require './mobile.styl'

View File

@ -0,0 +1,3 @@
.table-of-contents
.badge
vertical-align middle

View File

@ -0,0 +1,9 @@
$wrapper
max-width $contentWidth
margin 0 auto
padding 2rem 2.5rem
@media (max-width: $MQNarrow)
padding 2rem
@media (max-width: $MQMobileNarrow)
padding 1.5rem

View File

@ -0,0 +1,218 @@
export const hashRE = /#.*$/
export const extRE = /\.(md|html)$/
export const endingSlashRE = /\/$/
export const outboundRE = /^(https?:|mailto:|tel:)/
export function normalize (path) {
return path
.replace(hashRE, '')
.replace(extRE, '')
}
export function getHash (path) {
const match = path.match(hashRE)
if (match) {
return match[0]
}
}
export function isExternal (path) {
return outboundRE.test(path)
}
export function isMailto (path) {
return /^mailto:/.test(path)
}
export function isTel (path) {
return /^tel:/.test(path)
}
export function ensureExt (path) {
if (isExternal(path)) {
return path
}
const hashMatch = path.match(hashRE)
const hash = hashMatch ? hashMatch[0] : ''
const normalized = normalize(path)
if (endingSlashRE.test(normalized)) {
return path
}
return normalized + '.html' + hash
}
export function isActive (route, path) {
const routeHash = route.hash
const linkHash = getHash(path)
if (linkHash && routeHash !== linkHash) {
return false
}
const routePath = normalize(route.path)
const pagePath = normalize(path)
return routePath === pagePath
}
export function resolvePage (pages, rawPath, base) {
if (rawPath.includes('http')) return {
type: 'external-link',
path: rawPath
}
if (base) rawPath = resolvePath(rawPath, base)
const path = normalize(rawPath)
for (let i = 0; i < pages.length; i++) {
if (normalize(pages[i].path) === path) {
return Object.assign({}, pages[i], {
type: 'page',
path: ensureExt(rawPath)
})
}
}
console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
return {}
}
function resolvePath (relative, base, append) {
const firstChar = relative.charAt(0)
if (firstChar === '/') {
return relative
}
if (firstChar === '?' || firstChar === '#') {
return base + relative
}
const stack = base.split('/')
// remove trailing segment if:
// - not appending
// - appending to trailing slash (last segment is empty)
if (!append || !stack[stack.length - 1]) {
stack.pop()
}
// resolve relative path
const segments = relative.replace(/^\//, '').split('/')
for (let i = 0; i < segments.length; i++) {
const segment = segments[i]
if (segment === '..') {
stack.pop()
} else if (segment !== '.') {
stack.push(segment)
}
}
// ensure leading slash
if (stack[0] !== '') {
stack.unshift('')
}
return stack.join('/')
}
export function resolveSidebarItems (page, route, site, localePath) {
const { pages, themeConfig } = site
const localeConfig = localePath && themeConfig.locales
? themeConfig.locales[localePath] || themeConfig
: themeConfig
const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
if (pageSidebarConfig === 'auto') {
return resolveHeaders(page)
}
const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
if (!sidebarConfig) {
return []
} else {
const { base, config } = resolveMatchingConfig(route, sidebarConfig)
return config
? config.map(item => resolveItem(item, pages, base))
: []
}
}
function resolveHeaders (page) {
const headers = groupHeaders(page.headers || [])
return [{
type: 'group',
collapsable: false,
title: page.title,
children: headers.map(h => ({
type: 'auto',
title: h.title,
basePath: page.path,
path: page.path + '#' + h.slug,
children: h.children || []
}))
}]
}
export function groupHeaders (headers) {
// group h3s under h2
headers = headers.map(h => Object.assign({}, h))
let lastH2
headers.forEach(h => {
if (h.level === 2) {
lastH2 = h
} else if (lastH2) {
(lastH2.children || (lastH2.children = [])).push(h)
}
})
return headers.filter(h => h.level === 2)
}
export function resolveNavLinkItem (linkItem) {
return Object.assign(linkItem, {
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
})
}
export function resolveMatchingConfig (route, config) {
if (Array.isArray(config)) {
return {
base: '/',
config: config
}
}
for (const base in config) {
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
return {
base,
config: config[base]
}
}
}
return {}
}
function ensureEndingSlash (path) {
return /(\.html|\/)$/.test(path)
? path
: path + '/'
}
function resolveItem (item, pages, base, isNested) {
if (typeof item === 'string') {
return resolvePage(pages, item, base)
} else if (Array.isArray(item)) {
return Object.assign(resolvePage(pages, item[0], base), {
title: item[1]
})
} else {
if (isNested) {
console.error(
'[vuepress] Nested sidebar groups are not supported. ' +
'Consider using navbar + categories instead.'
)
}
const children = item.children || []
return {
type: 'group',
title: item.title,
children: children.map(child => resolveItem(child, pages, base, true)),
collapsable: item.collapsable !== false
}
}
}

View File

@ -1,5 +0,0 @@
# Languages
* [English](en/)

View File

@ -1,8 +1,11 @@
# Strapi [![Build Status](https://travis-ci.org/wistityhq/strapi.svg?branch=master)](https://travis-ci.org/wistityhq/strapi) [![Slack Status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io)
::: intro
# Strapi
[Website](http://strapi.io/) - [Getting Started](#user-content-getting-started-in-a-minute) - [Documentation](http://strapi.io/documentation/introduction) - [Support](http://strapi.io/support)
[![Build Status](https://travis-ci.org/wistityhq/strapi.svg?branch=master)](https://travis-ci.org/wistityhq/strapi)
[![Slack Status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io)
Strapi is an open-source Node.js rich framework for building applications and services.
:::
Strapi enables developers to focus on writing reusable application logic instead of spending time
building infrastructure. It is designed for building practical, production-ready Node.js applications
@ -111,11 +114,10 @@ Strapi comes with a simple but yet powerful dashboard.
## Resources
- [Roadmap](ROADMAP.md)
- [Contributing guide](CONTRIBUTING.md)
- [MIT License](LICENSE.md)
- [Contributing guide]((https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md))
- [MIT License]((https://github.com/strapi/strapi/blob/master/LICENSE.md))
## Links
- [Strapi website](http://strapi.io/)
- [Strapi website](https://strapi.io/)
- [Strapi news on Twitter](https://twitter.com/strapijs)

View File

@ -1,42 +0,0 @@
{
"title": "Strapi documentation",
"author": "Strapi",
"gitbook": "3.2.2",
"plugins": [
"edit-link",
"anchorjs",
"-search",
"-fontsettings",
"-sharing",
"versions",
"ga",
"github",
"feathers-collapsible-menu"
],
"pluginsConfig": {
"edit-link": {
"label": "Edit This Page",
"base": "https://github.com/strapi/strapi/tree/master/docs/1.x.x"
},
"ga": {
"token": "UA-54313258-1"
},
"github": {
"url": "https://github.com/strapi/strapi"
},
"versions": {
"options": [
{
"value": "/documentation/1.x.x/",
"text": "Versions 1.x.x",
"selected": true
},
{
"value": "/documentation/3.x.x/",
"text": "Versions 3.x.x",
"selected": false
}
]
}
}
}

View File

@ -1,5 +0,0 @@
# Languages
* [English](en/)

13
docs/3.x.x/README.md Normal file
View File

@ -0,0 +1,13 @@
::: intro
![Logo](https://cldup.com/7umchwdUBh.png)
### API creation made simple, secure and fast.
The most advanced open-source Content Management Framework to build powerful API with no effort.
[![npm version](https://img.shields.io/npm/v/strapi.svg)](https://www.npmjs.org/package/strapi)
[![npm downloads](https://img.shields.io/npm/dm/strapi.svg)](https://www.npmjs.org/package/strapi)
[![Build status](https://travis-ci.org/strapi/strapi.svg?branch=master)](https://travis-ci.org/strapi/strapi)
[![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io)
[![Heroku Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app)<!-- {height=20} -->
<!-- {p:.flex.justify-around} -->
:::

View File

@ -76,7 +76,9 @@ Run `npm start` from the `./admin` folder. That's all.
You should be able to see the admin at [http://localhost:4000/admin](http://localhost:4000/admin).
> Note: In development, all the plugins of your app are mounted in the same build as the administration panel.
::: note
In development, all the plugins of your app are mounted in the same build as the administration panel.
:::
### Colors
@ -95,7 +97,9 @@ Fonts can also be overridden:
To change the top-left displayed admin panel's logo, replace the image located at `./admin/admin/src/assets/images/logo-strapi.png`.
Note: make sure the size of your image is the same as the existing one (434px x 120px).
::: note
make sure the size of your image is the same as the existing one (434px x 120px).
:::
***
@ -109,7 +113,11 @@ npm run setup
This will replace the folder's content located at `./admin/admin/build`. Visit http://localhost:1337/admin/ to make sure your updates have been taken in account.
After you have built the admininistration you can now create a new project to develop your API with the changes implemented. **Note:** You should now create a project without `--dev`
After you have built the admininistration you can now create a new project to develop your API with the changes implemented.
::: note
You should now create a project without `--dev`
:::
***
@ -178,7 +186,9 @@ It's very common to deploy the front-end and the back-end on different servers.
The administration URL will be http://yourfrontend.com and every request from the panel will hit the backend at http://yourbackend.com. The plugins will be injected through the `origin` (means the API itself). In other words, the plugins URLs will be `http://yourbackend.com/dashboard/content-manager/main.js`.
> Note: How it is possible? The API (the Strapi server) owns the plugin and these plugins are exposed through `http://yourbackend.com/admin/**/main.js`
::: note
How it is possible? The API (the Strapi server) owns the plugin and these plugins are exposed through `http://yourbackend.com/admin/**/main.js`
:::
The DOM should look like this:
@ -198,7 +208,9 @@ The DOM should look like this:
</html>
```
> Note: The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
::: note
The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
:::
#### Deploy the administration panel and the plugins on another server than the API
@ -275,5 +287,6 @@ The generated `index.html` will look like this:
</body>
</html>
```
> Note: The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
::: note
The plugins are injected using the `./admin/admin/build/config/plugins.json`. To see the plugins URLs in the `index.html`, you need to launch the administration panel in the browser.
:::

View File

@ -58,7 +58,9 @@ The core of Strapi embraces a small list of middlewares for performances, securi
- xframe
- xss
> Note: The following middlewares cannot be disabled: responses, router, logger and boom.
::: note
The following middlewares cannot be disabled: responses, router, logger and boom.
:::
## Structure

View File

@ -21,7 +21,9 @@ Here is the list of the collected data and why we need them.
We are not collecting sensitive data such as databases configurations, environment or custom variables. The data are encrypted and anonymised.
> GDPR: The collected data are non-sensitive or personal data. We are compliant with the European recommendations (see our [Privacy Policy](https://strapi.io/privacy)).
::: warning GDPR
The collected data are non-sensitive or personal data. We are compliant with the European recommendations (see our [Privacy Policy](https://strapi.io/privacy)).
:::
## Disable

View File

@ -35,7 +35,9 @@ Returns the Koa instance.
Returns a `Promise`. When resolved, it means that the `./config/functions/bootstrap.js` has been executed. Otherwise, it throws an error.
> Note: You can also access to the bootstrap function through `strapi.config.functions.boostrap`.
::: note
You can also access to the bootstrap function through `strapi.config.functions.boostrap`.
:::
## strapi.config
@ -45,7 +47,9 @@ Returns an object that represents the configurations of the project. Every JavaS
Returns an object of the controllers wich is available in the project. Every JavaScript file located in the `./api/**/controllers` folder will be parsed into the `strapi.controllers` object. Thanks to this object, you can access to every controller's actions everywhere in the project.
> Note: This object doesn't include the admin's controllers and plugin's controllers.
::: note
This object doesn't include the admin's controllers and plugin's controllers.
:::
## strapi.hook

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

View File

Before

Width:  |  Height:  |  Size: 288 KiB

After

Width:  |  Height:  |  Size: 288 KiB

View File

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

View File

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 135 KiB

View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

View File

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View File

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

View File

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Before

Width:  |  Height:  |  Size: 875 KiB

After

Width:  |  Height:  |  Size: 875 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View File

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 254 KiB

View File

@ -1,43 +0,0 @@
{
"title": "Strapi documentation",
"author": "Strapi",
"gitbook": "3.2.2",
"plugins": [
"edit-link",
"anchorjs",
"search",
"-fontsettings",
"-sharing",
"versions",
"ga",
"github",
"feathers-collapsible-menu",
"block-align"
],
"pluginsConfig": {
"edit-link": {
"label": "Edit This Page",
"base": "https://github.com/strapi/strapi/tree/master/docs/3.x.x"
},
"ga": {
"token": "UA-54313258-1"
},
"github": {
"url": "https://github.com/strapi/strapi"
},
"versions": {
"options": [
{
"value": "/documentation/1.x.x/",
"text": "Versions 1.x.x",
"selected": false
},
{
"value": "/documentation/3.x.x/",
"text": "Versions 3.x.x",
"selected": true
}
]
}
}
}

View File

@ -55,7 +55,9 @@ options: [--tpl <name>|--plugin <name>]
Example: `strapi generate:api product --tpl bookshelf`
> Note: The first letter of the filename will be uppercased.
::: note
The first letter of the filename will be uppercased.
:::
## strapi generate:controller
Create a new controller
@ -79,7 +81,9 @@ options: [--api <name>|--plugin <name>]
- **strapi generate:controller &#60;name&#62; --plugin &#60;plugin&#62;**<br/>
Generates an empty controller called **&#60;name&#62;** in the `./plugins/<plugin>/controllers` folder.
> Note: The first letter of the filename will be uppercased.
::: note
The first letter of the filename will be uppercased.
:::
## strapi generate:model
Create a new model
@ -112,7 +116,9 @@ options: [--api <name>|--plugin <name>]
- **strapi generate:model &#60;name&#62; --plugin &#60;plugin&#62;**<br/>
Generates an empty model called **&#60;name&#62;** in the `./plugins/<plugin>/models` folder.
> Note: The first letter of the filename will be uppercased.
::: note
The first letter of the filename will be uppercased.
:::
## strapi generate:service
Create a new service
@ -136,7 +142,9 @@ options: [--api <name>|--plugin <name>]
- **strapi generate:service &#60;name&#62; --plugin &#60;plugin&#62;**<br/>
Generates an empty service called **&#60;name&#62;** in the `./plugins/<plugin>/services` folder.
> Note: The first letter of the filename will be uppercased.
::: note
The first letter of the filename will be uppercased.
:::
## strapi generate:policy
Create a new policy
@ -200,7 +208,9 @@ options: [--dev]
> Checkout the [CONTRIBUTING guide](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) for more details about the local Strapi development workflow.
> **Note: You have to restart the server to load the plugin into your project.**
::: warning
You have to restart the server to load the plugin into your project.
:::
Please refer to the [plugins documentation](../plugin-development/quick-start.md) to know more.

View File

@ -63,7 +63,9 @@ By default, your project's structure will look like this:
- [`/public`](../concepts/concepts.html#public-assets): contains the file accessible to the outside world.
> Tips: Inside the `/config` folder, every folder will be parsed and injected into the global object `strapi.config`. Let's say, you added a folder named `credentials` with two files `stripe.json` and `paypal.json` into it. The content of these files will be accessible through `strapi.config.credentials.stripe` and `strapi.config.credentials.paypal`.
::: note
Inside the `/config` folder, every folder will be parsed and injected into the global object `strapi.config`. Let's say, you added a folder named `credentials` with two files `stripe.json` and `paypal.json` into it. The content of these files will be accessible through `strapi.config.credentials.stripe` and `strapi.config.credentials.paypal`.
:::
***

View File

@ -95,7 +95,9 @@ Here are some use cases:
CRON tasks allow you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute).
> Note: Make sure the `enabled` cron config is set to `true` in your environment's variables.
::: note
Make sure the `enabled` cron config is set to `true` in your environment's variables.
:::
The cron format consists of:
@ -153,7 +155,9 @@ Each JSON file located in the folder must have the name of its corresponding tra
Most of the application's configurations are defined by environment. It means that you can specify settings for each environment (`development`, `production`, `test`, etc.).
> Note: You can access the config of the current environment through `strapi.config.currentEnvironment`.
::: note
You can access the config of the current environment through `strapi.config.currentEnvironment`.
:::
***
@ -246,6 +250,33 @@ Most of the application's configurations are defined by environment. It means th
> Please refer to the [dynamic configurations section](#dynamic-configurations) to use global environment variable to configure the databases.
#### MLab Example
**Path —** `./config/environments/**/database.json`.
```json
{
"defaultConnection": "default",
"connections": {
"default": {
"connector": "strapi-hook-mongoose",
"settings": {
"client": "mongo",
"host": "ds123456.mlab.com",
"port": 12345,
"database": "mlab_db_name",
"username": "mlab_user_name",
"password": "mlab_pass"
},
"options": {
"authenticationDatabase": "mlab_db_name",
"ssl": false
}
}
}
}
```
> Please note that you must give your MLab database name as the authenticationDatabase and your password can not contain the "@" symbol.
@ -270,10 +301,10 @@ Most of the application's configurations are defined by environment. It means th
- `parser`
- `enabled`(boolean): Enable or disable parser. Default value: `true`.
- `multipart` (boolean): Enable or disable multipart bodies parsing. Default value: `true`.
- `router`
- `prefix` (string): API url prefix (eg. `/v1`).
> Note: The session doesn't work with `mongo` as a client. The package that we should use is broken for now.
::: note
The session doesn't work with `mongo` as a client. The package that we should use is broken for now.
:::
***
@ -413,7 +444,9 @@ In any JSON configurations files in your project, you can inject dynamic values
}
```
> Note: You can't execute functions inside the curly braces. Only strings are allowed.
::: note
You can't execute functions inside the curly braces. Only strings are allowed.
:::
***

View File

@ -1,41 +0,0 @@
{% center %}
![Logo](https://cldup.com/7umchwdUBh.png)
### API creation made simple, secure and fast.
The most advanced open-source Content Management Framework to build powerful API with no effort.
<br />
[![npm version](https://img.shields.io/npm/v/strapi.svg)](https://www.npmjs.org/package/strapi)
[![npm downloads](https://img.shields.io/npm/dm/strapi.svg)](https://www.npmjs.org/package/strapi)
[![Build status](https://travis-ci.org/strapi/strapi.svg?branch=master)](https://travis-ci.org/strapi/strapi)
[![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io)
<a href="https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app">
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy" height="20px">
</a>
{% endcenter %}
## v3@alpha.14.2 is available!
We've been working on a major update for Strapi during the past months, rewriting the core framework and the dashboard.
This documentation is only related to Strapi v3@alpha.14.2 ([v1 documentation is still available](http://strapi.io/documentation/1.x.x)).
**[Get Started](getting-started/installation.md)**<br />
Learn how to install Strapi and start developing your API.
**[Command Line Interface](cli/CLI.md)**<br />
Get to know our CLI to make features the hacker way!
**[Concepts](concepts/concepts.md)**<br />
Get to know more about Strapi and how it works.
**[Guides](configurations/configurations.md)**<br />
Get familiar with Strapi. Discover concrete examples on how to develop the features you need.
**[Plugin Development](plugin-development/quick-start.md)**<br />
Understand how to develop your own plugin.
**[API Reference](api-reference/reference.md)**<br />
Learn about Strapi's API, the `strapi` object that is available in your backend.
**[Migration guide](migration/migration-guide.md)**<br />

View File

@ -1,39 +0,0 @@
# Installation
Installation is very easy and only takes a few seconds.
## Requirements
Please make sure your computer/server meets the following requirements:
- [Node.js](https://nodejs.org) >= 9: Node.js is a server platform which runs JavaScript. Installation guide [here](https://nodejs.org/en/download/).
- [MongoDB](https://www.mongodb.com/) >= 2.6: MongoDB is a powerful document store. Installation guide [here](https://www.mongodb.com/download-center?j#community).
## Setup
Time to install Strapi!
```bash
npm install strapi@alpha -g
```
Note: if you encounter npm permissions issues, [change the permissions to npm default directory](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-1-change-the-permission-to-npms-default-directory).
It takes about 20 seconds with a good Internet connection. You can take a coffee ☕️ if you have a slow one.
Having troubles during the installation? Check if someone already had the same issue https://github.com/strapi/strapi/issues. If not, you can [post one](https://github.com/strapi/strapi/issues/new), or ask for help https://strapi.io/support.
## Check installation
Once completed, please check that the installation went well, by running:
```bash
strapi -v
```
That should print `3.0.0-alpha.x`.
Strapi is installed globally on your computer. Type `strapi` in your terminal you will have access to every available command lines.
***
Congrats! Now that Strapi is installed you can [create your first API](quick-start.md).

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
li.header{
color: #101622 !important;
font-weight: 600;
letter-spacing: 0.07rem;
-webkit-font-smoothing: subpixel-antialiased;
}
li.chapter > a{
transition: background-color 0.15s ease;
-webkit-transition: background-color 0.15s ease;
}
li.chapter:hover > a{
background: #eee !important;
text-decoration: none !important;
}
.versions-select select{
height: 36px;
border: 1px solid #ccc;
}
.book-summary{
background-color: #f7f7f7 !important;
}

View File

@ -0,0 +1,46 @@
# Installation
Installation is very easy and only takes a few seconds.
## Requirements
Please make sure your computer/server meets the following requirements:
- [Node.js](https://nodejs.org) >= 10.x: Node.js is a server platform which runs JavaScript. Installation guide [here](https://nodejs.org/en/download/).
- [NPM](https://www.npmjs.com/) >= 6.x: NPM is the package manager for Javascript. Installation guide [here](https://nodejs.org/en/download/).
*(Make sure the database that you are using meets the requirement.)*
- [MongoDB](https://www.mongodb.com/) >= 3.x: MongoDB is a powerful document store. Installation guide [here](https://www.mongodb.com/download-center?j#community).
- [MySQL](https://www.mysql.com/) >= 5.6: MySQL is an open-source relational database management system. Installation guide [here](https://dev.mysql.com/downloads/).
- [MariaDB](https://mariadb.org/) >= 10.1: MarialDB is a fork of MySQL and is guaranteed to stay open source. Installation guide [here](https://mariadb.org/download/).
- [PostgreSQL](https://www.postgresql.org/) >= 10: PostgreSQL is an open-source object-relational database management system. Installation guide [here](https://www.postgresql.org/download/).
## Setup
Time to install Strapi!
```bash
npm install strapi@alpha -g
```
::: note
If you encounter npm permissions issues, [change the permissions to npm default directory](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-1-change-the-permission-to-npms-default-directory).
:::
It takes about 20 seconds with a good Internet connection. You can take a coffee ☕️ if you have a slow one.
Having troubles during the installation? Check if someone already had the same issue https://github.com/strapi/strapi/issues. If not, you can [post one](https://github.com/strapi/strapi/issues/new), or ask for help https://strapi.io/support.
## Check installation
Once completed, please check that the installation went well, by running:
```bash
strapi -v
```
That should print `3.0.0-alpha.x`.
Strapi is installed globally on your computer. Type `strapi` in your terminal you will have access to every available command lines.
***
Congrats! Now that Strapi is installed you can [create your first API](quick-start.md).

View File

@ -3,21 +3,18 @@
This section explains how to handle Strapi for the first time, ([check out our tutorial video](https://www.youtube.com/watch?v=yMl5IcFHA74)).
**Table of contents:**
- [Create your first project](#create-your-first-project)
- [Create your first API](#create-your-first-api)
- [Content Type Builder plugin](#content-type-builder-plugin)
- [Files structure](#files-structure)
- [Manage your data](#manage-your-data)
- [Content Manager Plugin](#content-manager-plugin)
- [Create a product](#create-a-product)
- [Edit a product](#edit-a-product)
- [Delete a product](#delete-a-product)
- [Consume your API](#consume-your-api)
- [List entries](#list-entries)
- [Get a specific entry](#get-a-specific-entry)
- [Create data (POST)](#create-data-post)
- [Update data (PUT)](#update-data-put)
- [Delete data (DELETE)](#delete-data-delete)
- [Quick start](#quick-start)
- [Create your first project](#create-your-first-project)
- [Create your first user](#create-your-first-user)
- [Create your first API](#create-your-first-api)
- [Files structure](#files-structure)
- [Manage your data](#manage-your-data)
- [Consume your API](#consume-your-api)
- [List entries (GET)](#list-entries-get)
- [Get a specific entry (GET)](#get-a-specific-entry-get)
- [Create data (POST)](#create-data-post)
- [Update data (PUT)](#update-data-put)
- [Delete data (DELETE)](#delete-data-delete)
***
@ -92,7 +89,9 @@ At this point, your application is empty. To create your first API is to use the
**#4 —** Save. That's it!
> Note: See the [CLI documentation](../cli/CLI.md#strapi-generateapi) for more information on how to do it the hacker way.
::: note
See the [CLI documentation](../cli/CLI.md#strapi-generateapi) for more information on how to do it the hacker way.
:::
### Files structure

View File

@ -1,6 +1,8 @@
# Authentication
> ⚠️ This feature requires the Users & Permissions plugin (installed by default).
::: warning
This feature requires the Users & Permissions plugin (installed by default).
:::
## Register a new user

View File

@ -39,4 +39,6 @@ module.exports = {
};
```
> Note: A route handler can only access the controllers defined in the `./api/**/controllers` folders.
::: note
A route handler can only access the controllers defined in the `./api/**/controllers` folders.
:::

View File

@ -1,6 +1,6 @@
# Deployment
#### #1 - Configurate
#### #1 - Configure
Update the `production` settings with the IP and domain name where the project will be running.
@ -32,7 +32,9 @@ cd /path/to/the/project
npm run setup
```
> Note: To display the build logs use the --debug option `npm run setup --debug`.
::: note
To display the build logs use the --debug option `npm run setup --debug`.
:::
#### #3 - Launch the server
@ -42,7 +44,9 @@ Run the server with the `production` settings.
NODE_ENV=production npm start
```
> We highly recommend to use [pm2](https://github.com/Unitech/pm2/) to manage your process.
::: warning
We highly recommend to use [pm2](https://github.com/Unitech/pm2/) to manage your process.
:::
### Advanced configurations

View File

@ -1,6 +1,8 @@
# Email
> ⚠️ This feature requires the Email plugin (installed by default).
::: warning
This feature requires the Email plugin (installed by default).
:::
Thanks to the plugin `Email`, you can send email on your server or externals providers such as Sendgrid.

View File

@ -2,7 +2,9 @@
See the [filters' concepts](../concepts/concepts.md#filters) for details.
> Note: by default, the filters can only be used from `find` endpoints generated by the Content Type Builder and the [CLI](../cli/CLI.md). If you need to implement a filters system somewhere else, read the [programmatic usage](#programmatic-usage) section.
::: note
by default, the filters can only be used from `find` endpoints generated by the Content Type Builder and the [CLI](../cli/CLI.md). If you need to implement a filters system somewhere else, read the [programmatic usage](#programmatic-usage) section.
:::
## Available operators
@ -74,7 +76,9 @@ Requests system can be implemented in custom code sections.
To extract the filters from an JavaScript object or a request, you need to call the [`strapi.utils.models.convertParams` helper](../api-reference/reference.md#strapiutils).
> Note: The returned objects is formatted according to the ORM used by the model.
::: note
The returned objects is formatted according to the ORM used by the model.
:::
#### Example

Some files were not shown because too many files have changed in this diff Show More