├── .gitignore
├── .gitmodules
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── README.md
├── _config.yml
├── assets
└── theme-colors.less
├── circle.yml
├── package.json
├── side-contents.md
├── source
├── CNAME
├── admin.md
├── api-schemas.md
├── architecture_old.md
├── callbacks.md
├── card.md
├── cheatsheet.md
├── cloudinary.md
├── common-problems.md
├── components.md
├── connecting-remotely.md
├── database.md
├── datatable.md
├── debug.md
├── decorators.md
├── deployment.md
├── email.md
├── embed.md
├── error-tracking.md
├── errors.md
├── events.md
├── example-customization.md
├── example-forum.md
├── example-instagram.md
├── example-movies.md
├── example-permissions.md
├── example-simple.md
├── examples.md
├── features-packages.md
├── features.md
├── field-resolvers.md
├── file-architecture.md
├── filtering.md
├── forms-components.md
├── forms-custom.md
├── forms-upload.md
├── forms.md
├── forum-packages.md
├── fragments.md
├── graphql-schema.md
├── groups-permissions.md
├── head-tags.md
├── images
│ ├── how-vulcan-works.png
│ ├── how-vulcan-works.svg
│ ├── terms-parameters.svg
│ └── vulcan-schemas.svg
├── index.md
├── internationalization.md
├── learn.md
├── logo
│ ├── favicon.png
│ ├── icon-apollo-white-200x200.png
│ ├── logo-apollo-space-left.svg
│ ├── logo-apollo-space.svg
│ └── logo-apollo-subbrands-developers-space.svg
├── material-ui.md
├── migration.md
├── mutations.md
├── newsletter.md
├── notifications.md
├── nutshell.md
├── other-components.md
├── packages.md
├── payments.md
├── performance.md
├── plugins.md
├── queries.md
├── reactive-state.md
├── recipes.md
├── redux.md
├── relations.md
├── resolvers.md
├── routing.md
├── schema-properties.md
├── schemas.md
├── security_old.md
├── server-queries.md
├── settings.md
├── sponsorship-tutorial.md
├── storybook.md
├── terms-parameters.md
├── themes.md
├── toc.md
├── tutorials.md
├── ui-components.md
├── unit-testing.md
├── users.md
├── videos.md
├── voting.md
├── vulcan.md
└── why-vulcan.md
├── talk-outline.md
├── themes
└── meteor
│ ├── .gitignore
│ ├── _config.yaml
│ ├── layout
│ ├── layout.ejs
│ ├── page.ejs
│ └── partials
│ │ ├── illustration-compass.ejs
│ │ ├── illustration-github.ejs
│ │ ├── illustration-guide.ejs
│ │ ├── illustration-help.ejs
│ │ ├── illustration-logs.ejs
│ │ ├── illustration-support.ejs
│ │ └── sidebar.ejs
│ └── source
│ ├── fonts
│ ├── percolate.eot
│ ├── percolate.svg
│ ├── percolate.ttf
│ └── percolate.woff
│ ├── images
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── manifest.json
│ ├── mstile-144x144.png
│ ├── mstile-150x150.png
│ ├── mstile-310x150.png
│ ├── mstile-310x310.png
│ ├── mstile-70x70.png
│ ├── safari-pinned-tab.svg
│ ├── vulcan-logo-border-2.svg
│ ├── vulcan-logo-border.png
│ ├── vulcan-logo-color.svg
│ └── vulcan-logo.svg
│ ├── script
│ ├── hotkey.js
│ ├── main.js
│ └── smooth-scroll.min.js
│ └── style
│ ├── _global
│ ├── Checkbox.less
│ ├── Input.less
│ ├── Radio.less
│ ├── Select.less
│ ├── Textarea.less
│ ├── animation.less
│ ├── base.less
│ ├── button.less
│ ├── docsearch.less
│ ├── form.less
│ ├── formatting.less
│ ├── icon.less
│ ├── link.less
│ ├── mobile.less
│ ├── nav.less
│ ├── panel.less
│ ├── syntax.less
│ └── tables.less
│ ├── _theme
│ ├── content.less
│ ├── layout.less
│ ├── nav.less
│ ├── panel.less
│ └── sidebar.less
│ ├── _util
│ ├── clearfix.import.less
│ ├── color.import.less
│ ├── easing.import.less
│ ├── helper.import.less
│ ├── index.import.less
│ ├── lesshat.import.less
│ ├── link.import.less
│ ├── normalize.import.less
│ ├── scrollbar.import.less
│ ├── text.import.less
│ ├── typography.import.less
│ └── ui.import.less
│ └── style.less
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | Thumbs.db
4 | db.json
5 | *.log
6 | node_modules/
7 | public/
8 | .deploy*/
9 | docs.json
10 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VulcanJS/vulcan-docs/3cb0cd142609b9efc00ead23c30de424a304a605/.gitmodules
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at hello@vulcanjs.org. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Etiquette
2 |
3 | - **All PRs should be made to the `devel` branch, not `master`.**
4 |
5 | - Come check-in in the [Vulcan Slack channel](http://slack.telescopeapp.org/). 👋
6 |
7 | - Don't hesitate to issue PRs for any mistakes you find in the docs. 👩🏼💻
8 |
9 | - When you contribute new feature or just changes to existing ones, please also update these docs. 📖
10 |
11 | - Be nice 😉
12 |
13 | ## Branches
14 |
15 | - `master` branch reflects the contents of [Vulcan Docs](https://docs.vulcanjs.org/) and the documentation matches the latest version of the Vulcan packages published on Atmosphere
16 | - `devel` branch is the bleeding edge, documenting the `devel` branch of the Vulcan codebase
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vulcan Docs
2 |
3 | To run:
4 |
5 | ```
6 | git clone --recursive https://github.com/VulcanJS/vulcan-docs.git
7 | cd vulcan-docs
8 | npm install
9 | npm start
10 | ```
11 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | # Hexo Configuration
2 | ## Docs: http://hexo.io/docs/configuration.html
3 | ## Source: https://github.com/hexojs/hexo/
4 |
5 | # Site
6 | title: Vulcan Docs
7 | propertytitle: Vulcan Docs
8 | subtitle: Vulcan Docs
9 | description: Vulcan Docs
10 | logo: "/images/vulcan-logo-border-2.svg"
11 | author:
12 | language:
13 | timezone:
14 | versions:
15 | - "1"
16 | sidebar_categories:
17 | null:
18 | - index
19 | - nutshell
20 | - cheatsheet
21 | - file-architecture
22 | - toc
23 | # - features
24 | # - architecture
25 | # - vulcan
26 | # - features-packages
27 | # - forum-packages
28 | Schemas:
29 | - schemas
30 | - schema-properties
31 | # - field-resolvers
32 | - relations
33 | GraphQL API:
34 | - graphql-schema
35 | - api-schemas
36 | - queries
37 | - filtering
38 | - mutations
39 | - fragments
40 | - server-queries
41 | - connecting-remotely
42 | Features:
43 | - routing
44 | - settings
45 | - groups-permissions
46 | - callbacks
47 | - users
48 | - internationalization
49 | - head-tags
50 | - errors
51 | - reactive-state
52 | Components:
53 | - components
54 | - ui-components
55 | - datatable
56 | - card
57 | - forms-components
58 | - other-components
59 | Forms:
60 | - forms
61 | - decorators
62 | - forms-custom
63 | Server:
64 | # - resolvers
65 | # - mutations
66 | # - redux
67 | # - security
68 | - performance
69 | - database
70 | Other Packages:
71 | - packages
72 | - material-ui
73 | - email
74 | # - newsletter
75 | # - payments
76 | # - admin
77 | # - embed
78 | # - cloudinary
79 | # - voting
80 | - events
81 | - error-tracking
82 | # - forms-upload
83 | - debug
84 | Testing:
85 | - unit-testing
86 | - storybook
87 | # Tutorials:
88 | # - example-simple
89 | # - example-movies
90 | # - example-instagram
91 | # - example-forum
92 | # - example-customization
93 | # - example-permissions
94 | # - sponsorship-tutorial
95 | # - deployment
96 | # - recipes
97 | Resources:
98 | - learn
99 | - examples
100 | - common-problems
101 | - plugins
102 | - themes
103 | - tutorials
104 | - videos
105 | github_repo: vulcanjs/vulcan-docs
106 | content_root: source
107 |
108 | typescript_api_box:
109 | data_file: docs.json
110 |
111 | social_links:
112 | github: "https://github.com/VulcanJS/vulcan-docs"
113 | twitter: "@vulcanjs"
114 |
115 | # API keys
116 | apis:
117 |
118 | # URL
119 | ## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
120 | url: https://docs.vulcanjs.org/
121 | root: /
122 | permalink: :year/:month/:day/:title/
123 | permalink_defaults:
124 |
125 | # Directory
126 | source_dir: source
127 | public_dir: public
128 | tag_dir: tags
129 | archive_dir: archives
130 | category_dir: categories
131 | code_dir: downloads/code
132 | i18n_dir: :lang
133 | skip_render:
134 |
135 | # Writing
136 | new_post_name: :title.md # File name of new posts
137 | default_layout: post
138 | titlecase: false # Transform title into titlecase
139 | external_link: true # Open external links in new tab
140 | filename_case: 0
141 | render_drafts: false
142 | post_asset_folder: false
143 | relative_link: false
144 | future: true
145 | highlight:
146 | enable: true
147 | line_number: true
148 | auto_detect: true
149 | tab_replace:
150 |
151 | # Category & Tag
152 | default_category: uncategorized
153 | category_map:
154 | tag_map:
155 |
156 | # Date / Time format
157 | ## Hexo uses Moment.js to parse and display date
158 | ## You can customize the date format as defined in
159 | ## http://momentjs.com/docs/#/displaying/format/
160 | date_format: YYYY-MM-DD
161 | time_format: HH:mm:ss
162 |
163 | # Pagination
164 | ## Set per_page to 0 to disable pagination
165 | per_page: 10
166 | pagination_dir: page
167 |
168 | # Extensions
169 | ## Plugins: http://hexo.io/plugins/
170 | plugins:
171 | # - hexo-generator-cname
172 |
173 | ## Themes: http://hexo.io/themes/
174 | theme: meteor
175 |
176 | # Deployment
177 | ## Docs: http://hexo.io/docs/deployment.html
178 | deploy:
179 | type: git
180 | repository: https://github.com/VulcanJS/vulcan-docs/
181 | branch: gh-pages
182 |
--------------------------------------------------------------------------------
/assets/theme-colors.less:
--------------------------------------------------------------------------------
1 | @color-primary: #F44A06;
2 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | node:
3 | version: 0.12
4 |
5 | checkout:
6 | post:
7 | - git submodule update --init
8 |
9 | dependencies:
10 | cache_directories:
11 | - "site/node_modules"
12 | override:
13 | - npm install -g hexo-cli
14 | - npm install
15 | - cd code; npm install
16 |
17 | test:
18 | override:
19 | # maybe we will need tests in the future
20 | - echo 'ok!'
21 |
22 | deployment:
23 | s3:
24 | branch: /^(master|version-.*)/
25 | commands:
26 | - npm run deploy
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-site",
3 | "version": "0.0.0",
4 | "private": true,
5 | "hexo": {
6 | "version": "3.8.0"
7 | },
8 | "dependencies": {
9 | "global": "^4.3.2",
10 | "handlebars": "^4.5.3",
11 | "hexo": "^3.8.0",
12 | "hexo-deployer-git": "^1.0.0",
13 | "hexo-generator-archive": "^0.1.2",
14 | "hexo-generator-category": "^0.1.2",
15 | "hexo-generator-cname": "^0.3.0",
16 | "hexo-generator-index": "^0.2.1",
17 | "hexo-generator-tag": "^0.2.0",
18 | "hexo-renderer-ejs": "^0.3.1",
19 | "hexo-renderer-less": "^0.2.0",
20 | "hexo-renderer-marked": "^1.0.1",
21 | "hexo-server": "^0.3.3",
22 | "lodash": "^4.17.19",
23 | "showdown": "^1.9.1"
24 | },
25 | "devDependencies": {
26 | "hexo-browsersync": "^0.3.0"
27 | },
28 | "scripts": {
29 | "start": "hexo serve",
30 | "deploy": "hexo deploy --generate",
31 | "develop-theme": "nodemon -x 'rm db.json; hexo serve' -w assets/ -w code/ -w source/ -w themes/ -w scripts/"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/side-contents.md:
--------------------------------------------------------------------------------
1 | ## Intro
2 |
3 | Vulcan is a new kind of toolkit that lets you build single-page web apps a lot faster. You'll get ready-made components to take care of common tasks such as data loading, user accounts, form handling, and much more.
4 |
5 | ## 3 principles
6 |
7 | ### No Dumb Work
8 |
9 | Vulcan's main goal is to take care of all the repetitive tasks involved in building a web app: hooking up user accounts, coding up forms, setting up CRUD operations… if it's something you've done before, Vulcan should do it for you!
10 |
11 | ### Flexible
12 |
13 | Typically, the more features a framework has, the more rigid it gets. But Vulcan was built with extensibility and flexibility in mind from the start, meaning you can override and extend its features without having to ever touch the core.
14 |
15 | ### Eject Anytime
16 |
17 | There's nothing worse than realizing that the technology you've sunk hundreds of hours into isn't right for your needs. To avoid this, Vulcan is architectured to make it easy to take your React components or GraphQL resolvers, and migrate to a different stack altogether. Outgrowing Vulcan shouldn't be a big deal.
18 |
19 | ## Technology
20 |
21 | Vulcan is based on three open-source technologies that work hand-in-hand:
22 |
23 | ### Front-End: React
24 |
25 | React has rapidly become one of the best ways to build robuts web app UIs. Its component-based approach and vast ecosystem make it ideal for rapid development.
26 |
27 | ### Data Layer: GraphQL
28 |
29 | GraphQL (using the [Apollo](http://apollostack.com) implementation) is an ideal way of getting your data from the server to the client. It's extremely flexible, used by some of the biggest companies in the world, and best of all lets you request exactly the data you need.
30 |
31 | ### Server & Build Tool: Meteor
32 |
33 | Meteor is the glue that binds everything else together. Its ready-made built tool, database, and user accounts make creating new projects a snap.
34 |
35 | ## Features
36 |
37 | ### Data Layer
38 |
39 | Vulcan makes leveraging GraphQL easy by letting you skip all the boilerplate. Just specify the data you need, and let Vulcan's data layer do the rest.
40 |
41 | ### Forms
42 |
43 | What if you never had to code a form ever again? Vulcan generates forms based on your collections schemas, and also handles the submission and validation process for you.
44 |
45 | ### Users
46 |
47 | Every app needs user accounts, and Vulcan gives you easy sign-up and log-in forms, as well as a simple groups and permissions system.
48 |
49 | ### Theming
50 |
51 | Vulcan's theming system lets you take a third-party theme built with React components, and then pick and choose which components you want to replace or extend.
52 |
53 | ### Callback Hooks
54 |
55 | Every server-side operation exposes synchronous and asynchronous hooks so you can extend or modify the server's behavior without having to modify any core files.
56 |
57 | ### Internationalization
58 |
59 | Vulcan comes internationalized out of the box using [react-i18n](#). Add or change strings with a few lines of code.
60 |
61 | ## Packages
62 |
63 | In addition to its core features, Vulcan also comes with a few optional packages that cover a web app's most common requirements.
64 |
65 | ### Posts & Comments
66 |
67 | Many apps are organized around a central item feed, and the Posts package gives you just that. Additionally, the Comments package lets users react to each post.
68 |
69 | ### Categories
70 |
71 | The Categories package enables regular or nested categories to help organize your content.
72 |
73 | ### Newsletter
74 |
75 | The Newsletter package works in combination with the Posts and Commments packages to automatically compile and send daily or weekly recaps of your app's activity.
76 |
77 | ### Notifications
78 |
79 | You can keep users notified of any activity in your app by using the Notifications package to send out email notifications whenever something important happens.
80 |
81 | ### RSS & API
82 |
83 | Automatically generate RSS feeds and a JSON API for your posts and comments.
84 |
85 | ### Voting
86 |
87 | Add upvotes and downvotes to posts and comments.
88 |
89 | ## Team
90 |
91 | ### Sacha Greif
92 |
93 | I learned a lot about making development more approachable by writing [Discover Meteor](http://discovermeteor.com). But you can only do so much with a book, so creating something like Vulcan was a natural next step.
94 |
95 | ### Xavier Cazalot
96 |
97 | ***
98 |
99 | ### You?
100 |
101 | Vulcan is open source and we're always looking for new contributors, no matter if you're a JavaScript guru or coding newbie. Come say hello in [our Slack channel](http://slack.vulcanjs.org)!
102 |
103 | ## Future Goals
104 |
105 | ### Migrate from Meteor (if needed)
106 |
107 | ### Extract tools
108 |
109 | ### become db-agnostic?
110 |
111 | ###
112 |
113 |
114 |
115 |
116 |
117 | ### "Hack Learn Make"
118 |
119 |
--------------------------------------------------------------------------------
/source/CNAME:
--------------------------------------------------------------------------------
1 | docs.vulcanjs.org
--------------------------------------------------------------------------------
/source/admin.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Admin Dashboard
3 | ---
4 |
5 | The `vulcan:admin` package provides a user moderation dashboard accessible to admin users at `/admin`.
6 |
7 | ### Adding Columns
8 |
9 | You can extend the admin dashboard with your own custom columns. First, you'll need to make the relevent data available to the client by adding it to the `UsersAdmin` fragment:
10 |
11 | ```js
12 | import { extendFragment } from 'meteor/vulcan:core';
13 |
14 | extendFragment('UsersAdmin', `
15 | posts{
16 | ...PostsPage
17 | }
18 | `);
19 | ```
20 |
21 | Then, you can create a component for the dashboard cell item:
22 |
23 | ```js
24 | import React from 'react';
25 | import Posts from 'meteor/vulcan:posts';
26 | import { Link } from 'react-router';
27 |
28 | const AdminUsersPosts = ({ user }) =>
29 |
30 | {user.posts && user.posts.map(post =>
31 |
{post.title}
32 | )}
33 |
34 |
35 | export default AdminUsersPosts;
36 | ```
37 |
38 | And finally add the component to the dashboard as a new column using the `addAdminColumn` function:
39 |
40 | ```js
41 | import { addAdminColumn } from 'meteor/vulcan:core';
42 | import AdminUsersPosts from './components/AdminUsersPosts';
43 |
44 | addAdminColumn({
45 | name: 'users.posts',
46 | order: 50,
47 | component: AdminUsersPosts
48 | });
49 | ```
--------------------------------------------------------------------------------
/source/api-schemas.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: API Schemas
3 | ---
4 |
5 | API schemas lets you define schema fields that only exist in your GraphQL API, meaning these "virtual" fields don't appear in your forms and don't get saved to your database.
6 |
7 | ### Defining API Schemas
8 |
9 | These fields support the following properties:
10 |
11 | - `canRead`: same as main schema canRead, with one notable exception: for apiSchema fields, canRead will default to [guests] (e.g. the field is public).
12 | - `description`: this is used as help text for the GraphQL API.
13 | - `typeName`: this is the GraphQL type (and not the JavaScript type, unlike "normal" fields!) the field should get resolved to.
14 | - `arguments`: the field's arguments.
15 | - `resolver`: the field's resolver function.
16 |
17 | Note that canCreate and canUpdate are not supported on apiSchema fields, since these fields by definition can not be stored in the database.
18 |
19 | For example:
20 |
21 | ```js
22 | const apiSchema = {
23 | latestPost: {
24 | canRead: ['members'],
25 | description,
26 | typeName: 'Post',
27 | resolve: () => { //... }
28 | }
29 | }
30 | ```
31 |
32 | ### Adding API Schemas
33 |
34 | You can add an API schema to a collection with:
35 |
36 | ```js
37 | extendCollection(Posts, {
38 | apiSchema,
39 | });
40 | ```
--------------------------------------------------------------------------------
/source/architecture_old.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Architecture
3 | ---
4 |
5 | Here's what you need to know about Vulcan's architecture.
6 |
7 |
8 | ## Package-Based Architecture
9 |
10 | In keeping with this idea of flexibility and modularity, Vulcan's application structure is a bit different than most other Meteor apps.
11 |
12 | In Vulcan, each feature has its own distinct **Meteor package** which can be freely added or removed. [Learn more about packages here](packages.html).
13 |
14 | ## Extend, Don't Edit
15 |
16 | Another key point of Vulcan's philosophy is to never edit the core codebase (i.e. what you get from the Vulcan repo) directly.
17 |
18 | Editing Vulcan's code makes it harder to keep it up to date when things change in the main Vulcan repo, as your modifications might conflict with a new updated version.
19 |
20 | Instead, Vulcan includes many hooks and patterns that enable you to tweak and extend core features from your *own* packages without having to actually modify their code.
21 |
22 | ## Register & Execute
23 |
24 | Many Vulcan objects (such as fragments, routes, components, etc.) follow a “register and execute” pattern, in which:
25 |
26 | 1. All items are first registered in a centralized array.
27 | 2. The items are then all executed at runtime.
28 |
29 | This two-tiered approach has two benefits. First, it means items can be overriden in between registration and execution. For example if a theme registers a component, your custom code can then extend it and the final component object that is created at runtime will be the extended version.
30 |
31 | Second, things like React Router or the app's GraphQL schema can only be initialized once. By having you register items first, Vulcan is able to centralize objects from various sources, and then combine them all in a single initialization call.
32 |
33 | ## The Vulcan Core Package
34 |
35 | Unless mentioned otherwise, all Vulcan utilities function are imported from the `vulcan:core` Meteor package:
36 |
37 | ```js
38 | import { createCollection } from 'meteor/vulcan:core';
39 |
40 | const Posts = createCollection({...});
41 | ```
42 |
43 | Note that a lot of these utilities actually live in the `vulcan:lib` package, but they're re-exported from `vulcan:core` for convenience's sake.
44 |
45 | ## Alternative Approaches
46 |
47 | Throughout the documentation, you'll find “Alternative Approach” sections that explain what a feature does, and how to achieve the same results without using Vulcan (typically, with standard React and/or Apollo code). This is useful if you've hit the limits of what Vulcan offers, and want to refactor parts of your app to use lower-level APIs.
48 |
--------------------------------------------------------------------------------
/source/card.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Card
3 | ---
4 |
5 | Coming soon…
6 |
--------------------------------------------------------------------------------
/source/cheatsheet.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Cheatsheet
3 | ---
4 |
5 |
6 | ## Queries
7 |
8 | ### Input
9 |
10 | ```js
11 | const singleInput = {
12 | filter,
13 | sort,
14 | search,
15 | id,
16 | enableCache,
17 | enableTotal,
18 | contextName,
19 | }
20 |
21 | const multiInput = {
22 | filter,
23 | sort,
24 | search,
25 | offset,
26 | limit,
27 | enableCache,
28 | enableTotal,
29 | contextName,
30 | }
31 | ```
32 |
33 | ### Hooks
34 |
35 | ```js
36 | const { document, loading, error, networkStatus, refetch } = useSingle2({
37 | collection,
38 | fragmentName,
39 | input,
40 | pollInterval,
41 | queryOptions, // passed to Apollo hook
42 | });
43 | ```
44 |
45 | ```js
46 | const { results, loading, error, networkStatus, refetch } = useMulti2({
47 | collection,
48 | fragmentName,
49 | input,
50 | pollInterval,
51 | queryOptions, // passed to Apollo hook
52 | });
53 | ```
54 |
55 | ## Mutations
56 |
57 | ### Hooks
58 |
59 | ```js
60 | const [ createFunction, { data, loading, error }] = useCreate2({
61 | collection,
62 | fragmentName,
63 | mutationOptions, // passed to Apollo hook
64 | });
65 |
66 | // to use the function:
67 | createFunction({ input: { data } });
68 |
69 | ```
70 |
71 | ```js
72 | const [ updateFunction, { data, loading, error }] = useUpdate2({
73 | collection,
74 | fragmentName,
75 | mutationOptions, // passed to Apollo hook
76 | });
77 |
78 | // to use the function:
79 | updateFunction({ input: { id, data } });
80 | ```
81 |
82 | ```js
83 | const [ deleteFunction, { data, loading, error }] = useDelete2({
84 | collection,
85 | fragmentName,
86 | mutationOptions, // passed to Apollo hook
87 | });
88 |
89 | // to use the function:
90 | deleteFunction({ input: { id } });
91 | ```
92 |
93 | ### Mutators
94 |
95 | ```js
96 | await createMutator({
97 | collection, // collection containing document to mutate
98 | data, // data received from client
99 | currentUser, // current user
100 | validate, // boolean, whether to validate the operation
101 | context, // GraphQL context
102 | });
103 |
104 | await updateMutator({
105 | documentId // id of document to mutate
106 | collection,
107 | data,
108 | currentUser,
109 | validate,
110 | context,
111 | });
112 |
113 | await deleteMutator({
114 | documentId,
115 | collection,
116 | currentUser,
117 | validate,
118 | context,
119 | });
120 | ```
121 |
122 | ### Callbacks
123 |
124 | #### Properties
125 |
126 | ```js
127 | // movies.create.*
128 | const properties = {
129 | data,
130 | originalData: clone(data),
131 | currentUser,
132 | collection,
133 | context,
134 | schema,
135 | };
136 |
137 | // movies.update.*
138 | const properties = {
139 | data,
140 | originalData: clone(data),
141 | document,
142 | originalDocument: oldDocument,
143 | currentUser,
144 | collection,
145 | context,
146 | schema,
147 | };
148 |
149 | // movies.delete.*
150 | const properties = {
151 | document,
152 | currentUser,
153 | collection,
154 | context,
155 | schema
156 | };
157 | ```
158 |
159 | #### Mutation Callbacks
160 |
161 | ```js
162 | createCollection({
163 | callbacks {
164 | create: {
165 | validate: [(validationErrors, properties) => { return validationErrors; }],
166 | before: [(document, properties) => { return document; }],
167 | after: [(document, properties) => { return document; }],
168 | async: [(properties) => { /* no return value */ }],
169 | }
170 | update: {
171 | validate: [(validationErrors, properties) => { return validationErrors; }],
172 | before: [(data, properties) => { return data; }],
173 | after: [(document, properties) => { return document; }],
174 | async: [(properties) => { /* no return value */ }],
175 | }
176 | delete: {
177 | validate: [(validationErrors, properties) => { return validationErrors; }],
178 | before: [(document, properties) => { return document; }],
179 | after: [(document, properties) => { return document; }],
180 | async: [(properties) => { /* no return value */ }],
181 | }
182 | }
183 | });
184 | ```
185 |
186 | #### Field Callbacks
187 |
188 | ```js
189 | const schema = {
190 | field: {
191 | onCreate: (properties) => { return fieldValue },
192 | onUpdate: (properties) => { return fieldValue },
193 | onDelete: (properties) => { // return nothing }
194 | }
195 | }
196 |
197 |
198 |
--------------------------------------------------------------------------------
/source/cloudinary.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Cloudinary
3 | ---
4 |
5 | This package is used to **cache** a image already hosted online using [Cloudinary](http://cloudinary.com). To *upload* a local image using Cloudinary check out the [Forms Upload](/forms-upload.html) package instead.
6 |
7 | ## Config
8 |
9 | In your **private** settings:
10 |
11 | ```
12 | "cloudinary": {
13 | "apiKey": "123foo",
14 | "apiSecret": "456bar",
15 | "formats": [
16 | {
17 | "name": "small",
18 | "width": 180,
19 | "height": 100}
20 | , {
21 | "name": "medium",
22 | "width": 400,
23 | "height": 250 }
24 | ]
25 | },
26 | ```
27 |
28 | ## makeCloudinary
29 |
30 | You can enable Cloudinary caching on a collection using the `makeCloudinary` function:
31 |
32 | ```js
33 | import { Posts } from 'meteor/example-forum';
34 | import { makeCloudinary } from 'meteor/vulcan:cloudinary';
35 |
36 | makeCloudinary({collection: Posts, fieldName: 'thumbnailUrl'});
37 | ```
38 |
39 | The `collection` option indicates which collection you want to use Cloudinary with, while the `fieldName` option indicates which field you want to cache (which should contain an image URL).
40 |
41 | Note that at this time, you can only cache a single image field per collection.
42 |
43 | ## Custom Fields
44 |
45 | `makeCloudinary` will add the following custom fields to store image data:
46 |
47 | ```js
48 | [
49 | {
50 | fieldName: 'cloudinaryId',
51 | fieldSchema: {
52 | type: String,
53 | optional: true,
54 | canRead: ['guests'],
55 | }
56 | },
57 | {
58 | fieldName: 'cloudinaryUrls',
59 | fieldSchema: {
60 | type: Array,
61 | optional: true,
62 | canRead: ['guests'],
63 | }
64 | },
65 | {
66 | fieldName: 'cloudinaryUrls.$',
67 | fieldSchema: {
68 | type: Object,
69 | blackbox: true,
70 | optional: true
71 | }
72 | }
73 | ]
74 | ```
75 |
76 | In addition, the `cloudinaryUrl` GraphQL-only field is also available as a shortcut to get a specific image format's URL.
77 |
78 | It optionally takes a `format` argument when used inside a GraphQL query or fragment, for example:
79 |
80 | ```
81 | cloudinaryUrl(format: "small")
82 | ```
83 |
84 | ## Callbacks
85 |
86 | `makeCloudinary` will also add two callbacks on `collection.new.sync` and `collection.edit.sync` to cache your images.
87 |
--------------------------------------------------------------------------------
/source/common-problems.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Common Problems
3 | ---
4 |
5 | Sometimes, things just go wrong. Here are a few tips to debug your Vulcan app.
6 |
7 | ## Common Problems
8 |
9 | ### “Load More” Not Working
10 |
11 | If your app initially loads fine but “Load More” doesn't work, this most likely indicates that while server-side rendering is working, data loading isn't.
12 |
13 | The most common cause is a mismatch between your domain and the URL currently being used as your GraphQL endpoint, which is based on the `ROOT_URL` environment variable.
14 |
15 | To double-check which GraphQL endpoint URL is being used, just use your Devtools Network tab in your browser and inspect any failed requests.
16 |
17 | ## Debugging
18 |
19 | ### Finding Errors
20 |
21 | Locating the source of an issue can be trickier than you think.
22 |
23 | First, you'll find most client-side errors through the browser console (`cmd/ctrl + opt + j` in Chrome).
24 |
25 | If nothing is showing up in the browser console or maybe your app isn't loading at all, make sure you also check the server logs. You'll find them in the terminal window currently running your app locally, or with `mup logs -f` if you've deployed using Meteor Up.
26 |
27 | It's also possible that you've encountered a GraphQL error, which won't be shown in either places. One way to track this down is to open your browser's Devtools Network tab and look for any red `graphql` requests. If you see any failed requests, check their Response tab to view the error message.
28 |
29 | You can also access a query's error message through the Redux or Apollo devtools. In the Redux devtools, click the State tab, drill down to the affected query, and check its `networkError` and `graphQLErrors` properties.
30 |
31 | ### GraphQL Schema Issues
32 |
33 | Although Vulcan includes features for generating your GraphQL schema automatically from your JSON schema, sometimes you'll run into issues and need to take a look under the hood.
34 |
35 | You can review your final generated schema by opening the Meteor shell with command `meteor shell` and then type:
36 |
37 | ```
38 | Vulcan.getGraphQLSchema()
39 | ```
40 |
41 | You'll probably want to paste the output in a text editor and replace `\n` with actual line breaks for better formatting.
42 |
43 | ### SSR Warnings
44 |
45 | Another common type of problem is server-side rendering (SSR) warnings:
46 |
47 | > React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
48 |
49 | This means that the HTML content you've rendered on the server doesn't match up with the markup React has generated on the client. For example, maybe the server thinks the current user is logged out, while the client knows they're logged in. Or maybe the server thinks it's Tuesday, while the client thinks it's Wednesday.
50 |
51 | Whatever the cause, these warnings should not break your app, but they might slow it down a bit.
52 |
53 | ### Data Loading
54 |
55 | If your data doesn't seem to be loading, this could be due to a few reasons:
56 |
57 | * **Component issue:** the client is loading your data, but your component is not displaying it. Check that the data is available in the Redux store, and check the value of your component's `props` using the React devtools.
58 | * **Resolver issue:** if your client isn't loading any data at all, it might be because the resolver isn't returning any. In this scenario, `console.log` will be your best friend.
59 | * **Arguments issue:** maybe the resolver itself is fine, but it's just not receiving the right arguments from the client. Again, adding `console.log` statements to the resolver will usually point you in the right direction.
60 | * **Database issue:** sometimes your code works, but the data you expect just isn't present in your database. You can check this by taking the query being run by the resolver and running it manually in Mongo directly (using `meteor mongo` command).
61 |
62 | ### Mutations
63 |
64 | Mutations can fail for a number of reasons:
65 |
66 | * **Component issue:** is your mutation even getting called? Make sure you double-check those event handlers.
67 | * **Resolver issue:** just like queries, mutations can also have resolver problems.
68 | * **Permissions issue:** it's also possible that the mutation is failing because you don't have the proper rights.
69 |
70 | ### Data Updating
71 |
72 | If your data isn't updating properly after a mutation, this can be due to a fragment mismatch. Make sure that `create` mutations use the same fragment as the query they're updating (`update` mutations on the other hand can safely return partial fragments).
73 |
--------------------------------------------------------------------------------
/source/connecting-remotely.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Connecting Remotely
3 | ---
4 |
5 | You might sometimes need to connect to your GraphQL endpoint (typically accessible at `http://yourapp.com/graphql`) from outside your app.
6 |
7 | ### API Key
8 |
9 | You can make authorized requests to your GraphQL endpoint by defining an arbitrary `vulcan.apiKey` setting in your `settings.json` file (make sure to keep it private by keeping it out of the `public` block), and then including an `apikey` header whose value matches that API key:
10 |
11 | ```
12 | {
13 | "vulcan": {
14 | "apiKey": "123foo"
15 | }
16 | }
17 | ```
18 |
19 | Any request made with that header will automatically be granted admin privileges.
20 |
21 | ### Authenticating as a User
22 |
23 | Another way to perform remote API queries and mutations with special privilages is to emulate one of your app's users. You can do so by reusing their authorization token.
24 |
25 | To figure out the token, log in as the user you want to emulate and inspect any `graphql` request using your devtools' Network tab. In the Headers tab, locate the `Authorization` field of the Request Headers group and make a note of its value.
26 |
27 | 
28 |
29 | This is the header and token you'll need to set when making your API call to the GraphQL endpoint. You can test it using a tool such as [GraphQL Playground](https://github.com/graphcool/graphql-playground)
30 | .
31 |
32 | In GraphQL Playground, select the "HTTP Headers" tab and type in (replacing the value with your own token):
33 |
34 | ```
35 | { "Authorization": "JKdsfahyvfHvsdf234s59pNbHGH7Z2fadsdrfCmR8WimItX" }
36 | ```
37 |
38 | ### GraphQL Clients
39 |
40 | You can send your GraphQL request as a plain text string (inspect the Request Payload object in your devtools' Network tab to see what that looks like) or you can use a GraphQL client to perform the request.
41 |
42 | Here are a few options:
43 |
44 | - Ruby: [graphql-client](https://github.com/github/graphql-client)
45 | - Node: [graphql-request](https://github.com/graphcool/graphql-request)
46 | - iOS: [apollo-ios](https://github.com/apollographql/apollo-ios)
47 | - Android: [apollo-android](https://github.com/apollographql/apollo-android)
--------------------------------------------------------------------------------
/source/database.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Database Layer
3 | ---
4 |
5 | Vulcan includes its own full-featured data layer (consiting of default resolvers and default mutations) that will let you avoid writing any database code most of the time.
6 |
7 | That being said, there will still be cases where you'll need to access your database directly.
8 |
9 | ## Non-Database Methods
10 |
11 | Before addressing how to make database calls, it's worth asking if you should be calling the database directly in the first place. Vulcan features two indirect ways to query for data from the server that, while they still call the database behind the scenes, let you make your app more "database-agnostic" by helping you limit any database-specific code to a few specific places.
12 |
13 | ### Server Queries
14 |
15 | Before discussing databases, it's important to remember that a document extracted from your database will often be different from the same document as provided by the GraphQL API; since the latter can feature [special GraphQL-only fields](/field-resolvers.html#GraphQL-Only-Fields).
16 |
17 | For that reason, Vulcan also offers [server-side GraphQL queries](/server-queries.html) you can use to fetch the “GraphQL version” of a document, even on the server. This will in turn call the same GraphQL resolvers that would be normally called when receiving a query from the client.
18 |
19 | As server-side queries rely on the same resolvers as normal GraphQL queries, you can't use them inside any resolver (or you'd risk an infinite loop).
20 |
21 | ### Using Dataloader
22 |
23 | Instead, inside GraphQL resolvers you'll usually want to go through Vulcan's [Dataloader layer](/performance.html#Caching-amp-Batching) instead of calling the database directly, to make sure you don't make extra database calls.
24 |
25 | ## Connectors
26 |
27 | In Vulcan you usually don't write any database code directly, even when you do want to perform a database operation. Instead, you call a **connector**, which is the function that actually specifies how to perform the operation.
28 |
29 | For example, if you need to create a new `Movies` document you can call the `create` connector:
30 |
31 | ```js
32 | const database = 'mongo';
33 |
34 | Connectors[database].create(Movies, { name: 'Titanic'});
35 | ```
36 |
37 | Which itself will call Mongo's `insert`:
38 |
39 | ```js
40 | Connectors.mongo = {
41 | create: async (collection, document, options) => {
42 | return await collection.insert(document);
43 | }
44 | }
45 | ```
46 |
47 | The advantage of this approach is that if you one day want to use MySQL instead of Mongo, you can simply change the value of `database` in `Connectors[database]` and let the SQL connector do the rest.
48 |
49 | The following connectors are available:
50 |
51 | - `await get(collection, selector, options)`: return a single document.
52 | - `await find(collection, selector, options)`: return a list of documents.
53 | - `await count(collection, selector, options)`: return a count of documents matching the selector.
54 | - `await create(collection, document, options)`: create a new document.
55 | - `await update(collection, selector, modifier, options)`: update a document.
56 | - `await delete(collection, selector, options)`: delete a document.
57 |
58 | Note that selectors can either be objects or `_id` strings.
59 |
60 | Of course, if you don't foresee the need to support multiple databases in your own code, making direct databases call is also perfectly valid.
61 |
62 | ## Databases
63 |
64 | ### MongoDB
65 |
66 | Vulcan is powered by Meteor, which itself comes bundled with MongoDB.
67 |
68 | Meteor collections (and by extension Vulcan collections) support all common MongoDB operations, such as:
69 |
70 | - `collection.find()`
71 | - `collection.findOne()`
72 | - `collection.update()`
73 | - `collection.remove()`
74 |
75 | ### Other Databases
76 |
77 | Vulcan does not currently include out-of-the-box support for other databases. That being said, just like you can make a Mongo call in your resolver, you can also call SQL or any other data source as you normally would in any Node app.
78 |
79 | In that regard, Vulcan is fairly database-agnostic, although it's true that the default resolvers and mutations only currently work with MongoDB.
80 |
--------------------------------------------------------------------------------
/source/debug.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Debugging
3 | ---
4 |
5 | ## Install
6 |
7 | ```
8 | meteor add vulcan:debug
9 | ```
10 |
11 | ## Dashboards
12 |
13 | The package provides a few debugging dashboards:
14 |
15 | - [http://0.0.0.0:3000/debug/groups](http://0.0.0.0:3000/debug/groups): view your currently defined [groups](/groups-permissions.html).
16 | - [http://0.0.0.0:3000/debug/settings](http://0.0.0.0:3000/debug/settings): view your currently defined [settings](/settings.html).
17 | - [http://0.0.0.0:3000/debug/callbacks](http://0.0.0.0:3000/debug/callbacks): view your currently defined [callbacks](/callbacks.html).
18 | - [http://0.0.0.0:3000/debug/emails](http://0.0.0.0:3000/debug/emails): view your currently defined [emails](/emails.html).
19 | - [http://0.0.0.0:3000/debug/routes](http://0.0.0.0:3000/debug/routes): view your currently defined [routes](/routing.html)
20 | - [http://0.0.0.0:3000/debug/components](http://0.0.0.0:3000/debug/components): view your currently defined [components](/theming.html)
21 | - [http://0.0.0.0:3000/debug/i18n](http://0.0.0.0:3000/debug/i18n): view your currently defined [I18n Strings](/internationalization.html)
22 |
23 | You can find a link to all these dashboards from the parent page : [http://0.0.0.0:3000/debug](http://0.0.0.0:3000/debug)
24 |
--------------------------------------------------------------------------------
/source/decorators.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Field Decorators
3 | ---
4 |
5 | (Available in Vulcan 1.15+)
6 |
7 | Decorators are functions you can call on your schema fields to “augment” them and make them work with special inputs such as checkboxes, radio buttons, autocomplete inputs, and more.
8 |
9 | ## Autocomplete
10 |
11 | You can make a schema field use an autocomplete input in your forms by calling the `makeAutocomplete` decorator.
12 |
13 | It takes the field as first argument, and an `options` object as second argument that takes the following properties:
14 |
15 | - `autocompletePropertyName`: the name of the property used to serve as a label on which to autocomplete.
16 | - `queryResolverName` (optional): the name of the GraphQL resolver to query to load the list of autocomplete suggestions.
17 |
18 | For example, if you have a `Post` schema with a `categoriesIds` field, you would want to query the `categories` query resolver to load a list of categories as suggested items as the user types into the autocomplete field; and use a category's `name` as a label.
19 |
20 | Note that for relation fields (where `resolveAs.relation` exists), Vulcan will try to guess the appropriate resolver name if you don't specify it explicitly.
21 |
22 | The autocomplete input will default as a single-value autocomplete, but will act as a multi-value autocomplete for any `Array` field. Note that even for single-value autocompletes, for consistency you should pass a `queryResolverName` pointing to a *multi* resolver, even if only one item is only ever loaded.
23 |
24 | ### Example
25 |
26 | ```js
27 | import { makeAutocomplete } from 'meteor/vulcan:core';
28 |
29 | const postSchema = {
30 | categoriesIds: makeAutocomplete({
31 | type: Array,
32 | arrayItem: {
33 | type: String,
34 | optional: true
35 | },
36 | label: 'Categories',
37 | optional: true,
38 | canCreate: ['members'],
39 | canUpdate: ['owners'],
40 | canRead: ['guests'],
41 | resolveAs: {
42 | fieldName: 'categories',
43 | type: '[Category]',
44 | relation: 'hasMany'
45 | }
46 | },
47 | { queryResolverName: 'categories', autocompletePropertyName: 'name' })
48 | }
49 | ```
50 |
51 | ## Checkboxgroup
52 |
53 | The `makeCheckboxgroup` decorator will add a checkbox group input, and automatically add an `allowedValues` property to your schema based on the field's `options` property to restrict the field to allowed values.
54 |
55 | ```js
56 | import { makeCheckboxgroup } from 'meteor/vulcan:core';
57 |
58 | const schema = {
59 | checkboxGroupField: makeCheckboxgroup({
60 | type: Array,
61 | arrayItem: {
62 | type: String
63 | },
64 | optional: true,
65 | canRead: ['guests'],
66 | canCreate: ['members'],
67 | canUpdate: ['owners'],
68 | options: fieldOptions
69 | })
70 | }
71 | ```
72 |
73 | ## Radiogroup
74 |
75 | The `makeRadiogroup` decorator will add a radio buttin group input, and automatically add an `allowedValues` property to your schema based on the field's `options` property to restrict the field to allowed values.
76 |
77 | ```js
78 | import { makeRadiogroup } from 'meteor/vulcan:core';
79 |
80 | const schema = {
81 | radioGroupField: makeRadiogroup({
82 | type: Array,
83 | arrayItem: {
84 | type: String
85 | },
86 | optional: true,
87 | canRead: ['guests'],
88 | canCreate: ['members'],
89 | canUpdate: ['owners'],
90 | options: fieldOptions
91 | })
92 | }
93 | ```
94 |
95 | ## Likert
96 |
97 | The `makeLikert` decorator will add a Likert scale input, as well as automatically set the proper `Int` type for the field's array items.
98 |
99 | ```js
100 | import { makeLikert } from 'meteor/vulcan:core';
101 |
102 | const schema = {
103 | likertField: makeLikert({
104 | optional: true,
105 | canRead: ['guests'],
106 | canCreate: ['members'],
107 | canUpdate: ['owners'],
108 | options: [
109 | { value: 'reservation', label: 'Reservation process' },
110 | { value: 'ease_of_use', label: 'Ease of use' },
111 | { value: 'design', label: 'Visual design' },
112 | { value: 'support', label: 'Customer support' },
113 | { value: 'properties', label: 'Number of properties' },
114 | { value: 'accomodations', label: 'Quality of accomodations' },
115 | { value: 'pricing', label: 'Price of accomodations' }
116 | ],
117 | })
118 | }
119 | ```
--------------------------------------------------------------------------------
/source/error-tracking.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Error Tracking
3 | ---
4 |
5 | TODO
--------------------------------------------------------------------------------
/source/errors.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Errors & Messages
3 | ---
4 |
5 | ## Error Properties
6 |
7 | An error object can have the following properties (all optional):
8 |
9 | - `id`: a string identifier, for example `app.not_found`. Can be used as an internationalization key.
10 | - `path`: for field-specific errors inside forms, the path of the field with the issue.
11 | - `properties`: additional data. Will be passed to vulcan-i18n as values.
12 | - `message`: if `id` cannot be used as an i81n key, `message` will be used instead.
13 | - `type`: one of `error`, `warning`, `success`. Can be used to visually differentiate between message types.
14 | - `break`: a Boolean, whether to interrupt the current operation or try to keep going.
15 | - `errors`: if the error object itself contains an `errors` array, it will be interpreted as a "multi-error" that contains multiple related sub-errors.
16 |
17 | ## GraphQL Errors
18 |
19 | When an error is thrown during a GraphQL query or mutation, it can be a good idea to use [Apollo-Errors](https://github.com/thebigredgeek/apollo-errors) instead of "regular" Node errors. This lets you build more complex errors and transmit more data back to the client.
20 |
21 | ```js
22 | import { createError } from 'apollo-errors';
23 |
24 | const FooError = createError('FooError', { message: 'A foo error has occurred' });
25 |
26 | throw new FooError({ data: { something: 'important' } });
27 | ```
28 |
29 | For GraphQL errors, all the error properties listed above should be assigned on the `data` object (including `message`).
30 |
31 | For example, here is how you could throw an error when trying to geocode an address inside a mutation callback:
32 |
33 | ```js
34 | const message = `Could not geocode address “${address}”`;
35 | const GeoDataError = createError('app.geoData_error', { message });
36 | throw new GeoDataError({
37 | data: {
38 | break: false,
39 | id: 'app.geoData_error',
40 | path: 'address',
41 | properties: { address },
42 | message,
43 | },
44 | });
45 | ```
46 |
47 | Note that if `app.geoData_error` has a corresponding internationalization string, the string will be used (and `{ address }` passed as `values` usable inside the string); and if it does not `message` will be used instead.
48 |
49 | ## Parsing Errors
50 |
51 | When catching a server error as a result of an operation, you can call `getErrors()` to retrieve a properly formatted array of GraphQL (or regular) errors.
52 |
53 | ## Displaying Errors
54 |
55 | Inside your Vulcan app, errors can be displayed using the `withMessages` HoC, which gives a component access to the `flash()` function to show a message.
56 |
57 | ## Form Errors
58 |
59 | Form errors are a special use case, and are thrown from inside a form component using `this.props.throwError()`.
60 |
61 | Note that when throwing a GraphQL error meant to be displayed inside a form, you can give it a `path` property to relate it to a specific form field.
--------------------------------------------------------------------------------
/source/example-forum.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Forum Example
3 | ---
4 |
5 | This packages enables the original Telescope forum features and components.
6 |
7 | Note that it doesn't actually contain any code by itself, but instead activates the [forum packages](packages.html) package set.
8 |
9 | ## Settings
10 |
11 | | Setting | Example | Description |
12 | | --- | --- | --- |
13 | | enableNotifications | true | Whether to enable email notifications |
14 | | postInterval | 20 | How long, in seconds, to make users wait between submitting posts |
15 | | commentInterval | 20 | How long, in seconds, to make users wait between commenting |
16 | | maxPostsPerDay | 10 | How many posts a user can submit per day |
17 | | RSSLinksPointTo | "link"/"page" | Whether to point RSS links to the linked site or back to your own site |
18 | | postsPerPage | 10 | How many posts to display per page |
19 | | thumbnailWidth | 800 | Posts thumbnails width |
20 | | thumbnailHeight | 600 | Posts thumbnails height |
21 | | scoreUpdateInterval | 30 | How often, in seconds, to update posts scores. Set to `0` to disable score updating |
22 |
23 | ## Categories
24 |
25 | You can prefill categories from your `settings.json` file:
26 |
27 | ```js
28 | "categories": [
29 | {
30 | "name": "Test Category 1",
31 | "description": "The first test category",
32 | "order": 4,
33 | "slug": "testcat1"
34 | },
35 | {
36 | "name": "Test Category 2",
37 | "description": "The second test category",
38 | "order": 7,
39 | "slug": "testcat2"
40 | },
41 | {
42 | "name": "Test Category 3",
43 | "description": "The third test category",
44 | "order": 10,
45 | "slug": "testcat3"
46 | }
47 | ],
48 | ```
--------------------------------------------------------------------------------
/source/example-instagram.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Instagram Example
3 | ---
4 |
5 | Here's a [video walkthrough](https://www.youtube.com/watch?v=qibyA_ReqEQ) of [the code](https://github.com/VulcanJS/Vulcan-Starter/tree/master/packages/example-instagram) for the `example-instagram` package.
6 |
7 | ### Note: Code Changes
8 |
9 | Unlike the video, the actual package now relies on [default resolvers](/resolvers.html#Default-Resolvers) and [default mutations](/mutations.html#Default-Mutations) to simplify the code, meaning the `resolvers.js` and `mutations.js` files were removed.
10 |
11 | Also, resolvers for the `userId` and `commentsCount` fields are now specified directly in the schema instead of in the `resolvers.js` file.
12 |
13 | ### Image Upload
14 |
15 | This example uses [the `vulcan:forms-upload` package](forms-upload.html), which uploads images to [Cloudinary](http://cloudinary.com).
16 |
--------------------------------------------------------------------------------
/source/example-permissions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Permissions Example
3 | ---
4 |
5 | These videos cover the [Permissions example](https://github.com/VulcanJS/Vulcan/tree/devel/packages/example-permissions/).
6 |
7 | ## Basic Permissions
8 |
9 |
10 |
11 | Learn how to define custom user groups, and then give them custom permissions on a site-wide basis.
12 |
13 | ## Advanced Permissions
14 |
15 |
16 |
17 | Learn how to define permissions related to specific documents.
--------------------------------------------------------------------------------
/source/example-simple.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Simple Example
3 | ---
4 |
5 | The simplest possible VulcanJS app.
6 |
7 | ## Video Tutorial
8 |
9 |
10 |
--------------------------------------------------------------------------------
/source/examples.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Examples
3 | ---
4 |
5 | - [ZensHome](http://zenshome.jp/)
6 | - [LessWrong](http://lesswrong.com/)
7 | - [Huttle](http://huttle.co/)
8 | - [Sidebar](http://sidebar.io)
9 | - [SmartHosts](http://smarthosts.org)
10 | - [Blogpost: The Vulcan.js challenge - 15 days for an app](https://hackernoon.com/the-vulcan-js-challenge-15-days-for-an-app-e3735d1e3d4c)
11 |
12 | If you know about something based on Vulcan.js which not this list and worth mentioning, you can [edit this page](https://github.com/VulcanJS/vulcan-docs/edit/master/source/examples.md) and submit a PR to the docs.
13 |
--------------------------------------------------------------------------------
/source/features-packages.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Forum Packages
3 | ---
4 |
5 | Here's an overview of the various features packages included with Vulcan.
6 |
7 | ## Forms
8 |
9 | - Dependencies: `vulcan:core`
10 |
11 | ## Accounts
12 |
13 | - Dependencies: `vulcan:core`
14 |
15 | ## Email
16 |
17 | - Dependencies: `vulcan:core`
18 | - NPM Dependencies: `handlebars`
19 |
20 | Email package.
21 |
22 | ## Events
23 |
24 | - Dependencies: `vulcan:core`
25 | - NPM Dependencies: `analytics-node`
26 |
27 | Simple internal event tracking.
28 |
29 | #### `sendGoogleAnalyticsRequest()`
30 |
31 | Log a Google Analytics request for the current page.
32 |
33 | #### `requestAnalyticsAsync(hook, document, user)`
34 |
35 | Log a Segment request.
36 |
37 | ## Debug
38 |
39 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`, `vulcan:email`
40 |
41 | Add some special debugging routes.
42 |
43 | ## Forms-Tags
44 |
45 | ## Forms-Upload
46 |
47 | ## Kadira
48 |
--------------------------------------------------------------------------------
/source/file-architecture.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: File Architecture
3 | ---
4 |
5 | ## Package-Based Architecture
6 |
7 | Vulcan's core code follows a **file-based architecture**, which means every feature lives inside its own package which can be enabled or disabled to add or remove said feature; or even over-ridden with your own custom package of the same name.
8 |
9 | ## App Packages
10 |
11 | When building your own Vulcan app, you will typically create a new package that contains the code for your app. Although you are of course free to follow your own structure, we recommend the following file architecture.
12 |
13 | ### Root Level
14 |
15 | First, a Meteor package will contain a `package.js` package manifest that list its contents. On the same level, you will have a `lib` directory that contains the entirety of the package's code. This `lib` directory is then divided into three directories:
16 |
17 | - `/client`: code that will only run inside the browser.
18 | - `/server`: code that will only run on the server.
19 | - `/modules`: code that will run on both.
20 |
21 | Both `client` and `server` should contain a `main.js` entry point that will load client- and server- specific code as well a shared `modules` code.
22 |
23 | ### Client Directory
24 |
25 | Because Vulcan enables server-side rendering, very little code actually runs *only* on the client. This directory will thus be empty save for the `main.js` entry point that should point back to `modules/index.js`.
26 |
27 | ### Modules Directory
28 |
29 | We recommend splitting your code around your collection (a.k.a. models). For example, a forum app could have the following directories:
30 |
31 | - `/modules/posts`
32 | - `/modules/users`
33 | - `/modules/comments`
34 |
35 | Each directory will then contain all or some of the following files:
36 |
37 | - `collection.js`: the main collection/model declaration
38 | - `schema.js`: the schema definition
39 | - `fragments.js`: GraphQL fragments related to the collection
40 | - `helpers.js`: any other code related to the collection
41 |
42 | Additionally, we also recommend always including an `index.js` file to centralize all imports/exports in every directory.
43 |
44 | #### Other Code
45 |
46 | You will usually also end up with some code that is not directly related to any collection, such as components imports, route declarations, or internationalization definitions. You can leave those files in `/modules`.
47 |
48 | ### Server Directory
49 |
50 | The server directory should more or less mirror the `modules` directory in terms of being organized by collection, except it will contain code that only runs on the server, such as mutation callbacks.
--------------------------------------------------------------------------------
/source/forms-components.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Form Components
3 | ---
4 |
5 | ## General Components
6 |
7 | The following components are used to display Vulcan forms.
8 |
9 | #### `SmartForm`
10 |
11 | This component (`FormWrapper.jsx`) wraps the entire form and handles all data loading.
12 |
13 | #### `Form`
14 |
15 | This is the main component responsible for generating and displaying the form.
16 |
17 | #### `FormErrors`
18 |
19 | Used to display errors at the top of the form.
20 |
21 | #### `FormGroup`
22 |
23 | Used to display form groups.
24 |
25 | #### `FormSubmit`
26 |
27 | Used to display the form's submit and cancel buttons.
28 |
29 | #### `FieldErrors`
30 |
31 | Used to display errors beneath a form field.
32 |
33 | #### `FormComponent`
34 |
35 | Used to display an individual form field.
36 |
37 | #### `FormNested` & `FormNestedItem`
38 |
39 | Used to display nested form items along with add/remove controls.
40 |
41 | ## Field Components
42 |
43 | In addition to the components used to display the form's structure, an additional set of components is used to display each individual field, according to its type.
44 |
45 | To select a field, use its lowercase name as the `control` property on your schema (e.g. `control: 'select'`).
46 |
47 | #### `Default`
48 |
49 | Default form input, for text strings.
50 |
51 | #### `Textarea`
52 |
53 | Textarea form input.
54 |
55 | #### `Email`
56 |
57 | Email form input.
58 |
59 | #### `Number`
60 |
61 | Number form input. Will be used automatically for `Number` fields.
62 |
63 | #### `Url`
64 |
65 | URL form input.
66 |
67 | #### `Checkbox`
68 |
69 | Checkbox form input. Will be used automatically for `Boolean` fields.
70 |
71 | #### `Checkboxgroup`
72 |
73 | Checkbox form input.
74 |
75 | #### `Select`
76 |
77 | Select form input.
78 |
79 | #### `Radiogroup`
80 |
81 | Radio group form input
82 |
83 | #### `Date`, `Time`, `Datetime`
84 |
85 | Used to select a date, a time, or both. `Date` will be used automatically for `Date` fields.
--------------------------------------------------------------------------------
/source/forms-custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Custom Form Components
3 | ---
4 |
5 | ## Custom Components
6 |
7 | You'll often need to write your own form components. Fortunately, VulcanJS makes it relatively easy. The main thing you need to figure out is how your custom component will pass its value to the form's data in order to submit it back to the server.
8 |
9 | ### Handling Values
10 |
11 | Vulcan form components are **controlled components**, meaning that they get their value from their parent component. In other words, you can't change their value directly but must instead call the `onChange` event handler, which will itself call `this.context.updateCurrentValues()`.
12 |
13 | You can also call `this.context.updateCurrentValues()` directly. This function takes an object and will update the parent `Form` component's state with its properties:
14 |
15 | ```js
16 | import React, { PureComponent } from 'react';
17 | import PropTypes from 'prop-types';
18 | import { Input } from 'formsy-react-components';
19 |
20 | class MyCustomFormComponent extends PureComponent {
21 |
22 | constructor() {
23 | super();
24 | this.toggleMessage = this.toggleMessage.bind(this);
25 | this.state = {
26 | message: 'foo'
27 | };
28 | }
29 |
30 | toggleMessage() {
31 | this.setState({
32 | message: this.state.message === 'foo' ? 'bar' : 'foo'
33 | });
34 | this.context.updateCurrentValues({message: this.state.message});
35 | }
36 |
37 | render() {
38 |
39 | return (
40 |
47 | );
48 | }
49 | }
50 |
51 | MyCustomFormComponent.contextTypes = {
52 | updateCurrentValues: PropTypes.func,
53 | };
54 |
55 | export default MyCustomFormComponent;
56 | ```
57 |
58 | Note that you'll need to define your custom component's `contextTypes` property to make `updateCurrentValues` available on the component's `context`.
59 |
60 | ### SmartForms API
61 |
62 | Here's an overview of the API methods available on a custom component's `context`:
63 |
64 | #### `updateCurrentValues(object)`
65 |
66 | This takes an object with `name: value` pairs, and will update the form state accordingly. Note that `name` can also be a dotted path, for nested components (e.g. `{ 'addresses.2.zipCode': 12345 }`).
67 |
68 | #### `addToAutofilledValues(object)`
69 |
70 | This adds a set of `name: value` pairs to the form's *autofilled* values. Autofilled values have a lower priority than "current" values. In other words, if someone fills in the `foo` field with `bar` but you then call `addToAutofilledValues({foo: 'baz'})`, the contents of `foo` will not change.
71 |
72 | This is useful when you want the value of one field to affect the contents or another, except when that other field has already been filled out.
73 |
74 | #### `getAutofilledValues()`
75 |
76 | Get just the autofilled values.
77 |
78 | #### `addToDeletedValues(string)`
79 |
80 | This takes a single **field name** and adds it to the list of document properties to be deleted on the server once the form is submitted.
81 |
82 | Note that once a field is added to the form state's `deletedValues`, it will be deleted even if said field contains a value.
83 |
84 | #### `addToSubmitForm(function)`
85 |
86 | Adds a callback that will be called on the `data` object containing all form values when the form is submitted.
87 |
88 | #### `addToFailureForm(function)`
89 |
90 | Adds a callback that will run with the `error` as argument if the form submission fails.
91 |
92 | #### `addToSuccessForm(function)`
93 |
94 | Adds a callback that will run with the `result` as argument if the form submission succeeds.
95 |
96 | #### `throwError(object)`
97 |
98 | Throws an error.
99 |
100 | #### `clearForm()`
101 |
102 | Clears the form.
103 |
104 | #### `getDocument()`
105 |
106 | Gets the entire document currently being inserted or edited.
107 |
108 | #### `setFormState(object)`
109 |
110 | Calls `setState` inside the `Form` component directly. Can be used as a more general method to affect form state whenever the previous API methods are not sufficient. For example, to set the form as disabled:
111 |
112 | ```
113 | this.context.setFormState({disabled: true});
114 | ```
115 |
--------------------------------------------------------------------------------
/source/forms-upload.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Upload Package
3 | ---
4 |
5 | This is a Vulcan package extending `vulcan:forms` to support form components for uploading one or more images.
6 |
7 | 
8 |
9 | Note: although this package only supports Cloudinary currently, PRs to support additional providers (S3, etc.) are warmly encouraged.
10 |
11 | ## Dependencies
12 |
13 | This package depends on the awesome `react-dropzone` ([repo](https://github.com/okonet/react-dropzone)), you need to install the dependency:
14 | ```
15 | npm install react-dropzone isomorphic-fetch
16 | ```
17 |
18 | ## With Cloudinary
19 |
20 | ### Setup
21 |
22 | Create a [Cloudinary account](https://cloudinary.com) if you don't have one.
23 |
24 | The upload to Cloudinary relies on **unsigned upload**:
25 |
26 | > Unsigned upload is an option for performing upload directly from a browser or mobile application with no authentication signature, and without going through your servers at all. However, for security reasons, not all upload parameters can be specified directly when performing unsigned upload calls.
27 |
28 | Unsigned upload options are controlled by [an upload preset](http://cloudinary.com/documentation/upload_images#upload_presets), so in order to use this feature you first need to enable unsigned uploading for your Cloudinary account from the [Upload Settings](https://cloudinary.com/console/settings/upload) page.
29 |
30 | When creating your **preset**, you can define image transformations. I recommend to set something like 200px width & height, fill mode and auto quality. Once created, you will get a preset id.
31 |
32 | It may look like this:
33 |
34 | 
35 |
36 | Make sure that your **preset** is named the same between Cloudinary and the one you define in the schema. Otherwise, the upload will request will fail.
37 |
38 | ### Settings
39 |
40 | Edit your `settings.json` and add inside the `public: { ... }` block the following entries with your own credentials:
41 |
42 | ```json
43 | "public": {
44 | "cloudinary": {
45 | "cloudName": "YOUR_APP_NAME",
46 | }
47 | }
48 | ```
49 |
50 | ### Specifying a Preset
51 |
52 | If you'd like to specify a Cloudinary preset to be used to resize, convert, etc. your images, you can do so in the properties of the **form field**:
53 |
54 | ```js
55 | photos: {
56 | label: 'Photos',
57 | type: Array,
58 | optional: false,
59 | canRead: ['guests'],
60 | canCreate: ['members'],
61 | canUpdate: ['members'],
62 | control: FormsUpload, // use the FormsUpload form component
63 | form: {
64 | options: {
65 | preset: 'myCloudinaryPreset'
66 | },
67 | },
68 | },
69 | ```
70 |
71 | ### Adding images to Posts (as an example)
72 | If you want to add images to a certain collection. Add the custom field, make sure to add this in your fragments.js file:
73 |
74 | ``` extendFragment('PostsList', `
75 | image
76 | `);
77 |
78 | extendFragment('PostsPage', `
79 | image
80 | `);```
81 |
82 | You can now use the uploaded image anywhere in your Vulcan app, using this:
83 | ``````
84 | which will look for the image field from your post.
85 |
--------------------------------------------------------------------------------
/source/forum-packages.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Forum Packages
3 | ---
4 |
5 | Here's an overview of the various forum packages included with Vulcan.
6 |
7 | Note that most of these packages currently require the `vulcan:posts` package. Eventually, the goal is to generalize them so they can work with any Vulcan collection.
8 |
9 | ## Posts
10 |
11 | - Dependencies: `vulcan:core`, `vulcan:events`
12 |
13 | The Vulcan Posts package.
14 |
15 | ## Comments
16 |
17 | - Dependencies: `vulcan:core`, `vulcan:posts`
18 |
19 | The Vulcan Comments package.
20 |
21 | ## Notifications
22 |
23 | - Dependencies: `vulcan:core`, `vulcan:email`
24 |
25 | Email notifications.
26 |
27 | ## Voting
28 |
29 | - Dependencies: `vulcan:core`, `vulcan:posts`
30 |
31 | Enables upvoting and downvoting for the Posts and Comments collections.
32 |
33 | ## Embedly
34 |
35 | - Dependencies: `vulcan:core`, `vulcan:posts`
36 |
37 | Use [Embedly](http://embed.ly) to load extra metadata for posts.
38 |
39 | ## Categories
40 |
41 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`
42 |
43 | Posts categories.
44 |
45 | ## API
46 |
47 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`
48 |
49 | Create a JSON API for posts and comments.
50 |
51 | ## RSS
52 |
53 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`
54 |
55 | Create RSS feeds for posts and comments.
56 |
57 | ## Newsletter
58 |
59 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`, `vulcan:categories`, `vulcan:email`
60 |
61 | Generate and schedule a MailChimp newsletter for posts and comments.
62 |
63 | ## Email Templates
64 |
65 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`, `vulcan:email`
66 |
67 | Templates for email notifications and the newsletter.
68 |
69 | ## Getting Started
70 |
71 | - Dependencies: `vulcan:core`, `vulcan:posts`, `vulcan:comments`, `vulcan:events`
72 |
73 | Dummy content for posts, comments, and users.
--------------------------------------------------------------------------------
/source/fragments.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Fragments
3 | ---
4 |
5 | A fragment is a piece of schema, usually used to define what data you want to query for.
6 |
7 | ## Registering Fragments
8 |
9 | You can register fragments by passing the fragment string to `registerFragment`:
10 |
11 | ```js
12 | import { registerFragment } from 'meteor/vulcan:core';
13 |
14 | registerFragment(`
15 | fragment PostsList on Post {
16 | _id
17 | title
18 | url
19 | slug
20 | }
21 | `);
22 | ```
23 |
24 | ## Using Fragments
25 |
26 | You can get a fragment with:
27 |
28 | ```js
29 | import { getFragment } from 'meteor/vulcan:lib';
30 |
31 | getFragment('PostsList');
32 | ```
33 |
34 | ## Sub-Fragments
35 |
36 | When registering a fragment, you'll often want to use sub-fragments to avoid repeating frequently used properties. For example, the `PostsList` fragment uses the `UserMinimumInfo` fragment:
37 |
38 | ```js
39 | registerFragment(`
40 | fragment PostsList on Post {
41 | # vulcan:posts
42 | _id
43 | title
44 | url
45 | slug
46 | user {
47 | ...UsersMinimumInfo
48 | }
49 | }
50 | `);
51 | ```
52 |
53 | Note that in “regular” Apollo code, you need to include any sub-fragment used by a fragment [as tagged template literal](http://dev.apollodata.com/react/fragments.html#reusing-fragments), but Vulcan takes care of this for you (provided you've previously registered any sub-fragment using `registerFragment`).
54 |
55 | ## Extending Fragments
56 |
57 | You often need to add one or more properties to a fragment without modifying its existing properties. You can do this with `extendFragment`:
58 |
59 | ```js
60 | import { extendFragment } from 'meteor/vulcan:lib';
61 |
62 | extendFragment(
63 | 'PostsList',
64 | `
65 | color # new custom property!
66 | `
67 | );
68 | ```
69 |
70 | This is the same as registering the entire `PostsList` fragment with `color` tacked on at the end.
71 |
72 | ## Replacing Fragments
73 |
74 | To replace a fragment completely, you can just register it again under the same name.
75 |
76 | ```js
77 | import { registerFragment } from 'meteor/vulcan:lib';
78 |
79 | registerFragment(`
80 | fragment UsersMinimumInfo on User {
81 | _id
82 | slug
83 | username
84 | # displayName # remove this
85 | # emailHash # and this
86 | mySuperCustomProperty # but add this
87 | }
88 | `);
89 | ```
90 |
91 | Note that you can replace both “regular” fragments and sub-fragments.
92 |
93 | ## Default Fragments
94 |
95 | Every collection automatically gets a default fragment associated with it called `FooDefaultFragment` (for example `PostsDefaultFragment`).
96 |
97 | This default fragment simply contain all fields where `canRead` is defined (in other words, all public fields). Note that it **does not** follow field resolvers, meaning that the default fragment will e.g. include `userId` but not `user`.
98 |
99 | #### Alternative Approach
100 |
101 | You can use standard Apollo fragments at any point in your Vulcan app (passing them as `fragment` instead of `fragmentName`), but be aware that you will lose the ability to extend and replace fragments. You will also need to [manually specify](http://dev.apollodata.com/react/fragments.html#reusing-fragments) sub-fragments.
102 |
--------------------------------------------------------------------------------
/source/graphql-schema.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: GraphQL Schema
3 | ---
4 |
5 | The first piece of any GraphQL API is the **schema**, which defines what data is made available to the client.
6 |
7 | In Vulcan, this GraphQL schema can be generated automatically from your collection's JSON schema, so you don't have to type things twice. Just pass a [SimpleSchema](https://github.com/aldeed/node-simple-schema)-compatible JSON schema as `createCollection`'s `schema` property.
8 |
9 | ## Viewing the Schema
10 |
11 | You can view your GraphQL schema using the Meteor shell. First, launch the shell from your terminal with:
12 |
13 | ```
14 | meteor shell
15 | ```
16 |
17 | Then, type:
18 |
19 | ```
20 | Vulcan.getGraphQLSchema()
21 | ```
22 |
23 | Note: if `Vulcan` is not defined you can import it first with `import {Vulcan} from 'meteor/vulcan:lib'`.
24 |
25 | ## Custom Schemas
26 |
27 | If you need to manually add a schema, you can also do this using the `addGraphQLSchema` function which will add one or more [GraphQL schemas](http://graphql.org/learn/schema/) to your GraphQL API:
28 |
29 | ```js
30 | import { addGraphQLSchema } from 'meteor/vulcan:core';
31 |
32 | const customSchema = `
33 | input Custom {
34 | _id: String
35 | userId: String
36 | title: String
37 | }
38 | `;
39 | addGraphQLSchema(customSchema);
40 | ```
41 |
--------------------------------------------------------------------------------
/source/head-tags.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Head Tags
3 | ---
4 |
5 | You'll often need to add tags to your app's `` section. There are three ways to do this: using the [Helmet](https://github.com/nfl/react-helmet) library, using the `` component, or using the `Head` object.
6 |
7 | ## Helmet
8 |
9 | You can use Helmet inside a VulcanJS app just like inside any other React app:
10 |
11 | ```js
12 | const Layout = ({children}) =>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {children}
24 |
25 |
26 |
27 |
28 |
29 | registerComponent('Layout', Layout);
30 | ```
31 |
32 | ## Components.HeadTags
33 |
34 | ### Using HeadTags
35 |
36 | Out of the box, a VulcanJS app adds a few default tags such as ``, ``, etc. These are all added using Helmet by `Components.HeadTags` (called from inside the `App` component), using values provided by your `settings.json` file.
37 |
38 | In some cases, you'll want to change these values for a given page. For example, a single post page will want to have a different title than your homepage. You can do that by calling `Components.HeadTags` directly anywhere inside your component tree with one or more of the following props:
39 |
40 | - `title`
41 | - `url`
42 | - `image`
43 | - `description`
44 |
45 | ```js
46 |
47 | ```
48 |
49 | By calling `HeadTags` again, you'll be calling Helmet again, which in turn will override the head tags previously set by the `App` component.
50 |
51 | ### Overriding Headtags
52 |
53 | Since `Components.HeadTags` is a component, you can also override it completely using `replaceComponent('HeadTags', MyComponent)`.
54 |
55 | ## The Head Object
56 |
57 | Finally, there are also some cases where you want to add or remove a tag from *outside* your React components. You can do so using the `Head` object.
58 |
59 | For example. here is how the `vulcan:rss` package adds an RSS feed link to the head:
60 |
61 | ```js
62 | import { Head, Utils } from 'meteor/vulcan:core';
63 |
64 | Head.link.push({
65 | name: 'rss',
66 | rel: 'alternate',
67 | type: 'application/rss+xml',
68 | href: `${Utils.getSiteUrl()}feed.xml`
69 | });
70 | ```
71 |
72 | You can also remove a tag using `removeFromHeadTags`:
73 |
74 | ```js
75 | import { removeFromHeadTags } from 'meteor/vulcan:core';
76 |
77 | removeFromHeadTags('link', 'rss');
78 | ```
79 |
80 | ### Head Components
81 |
82 | You can also use the `Head` object to add React components to every page of your app (this can be useful for adding analytics integrations wrapped inside React components, for example). Just push the components to `Head.components`.
83 |
84 | If you push an array (such as `[myComponent, withCurrentUser]`) the array will be interpreted as a component + HoC array similar to the arguments of `registerComponent`.
--------------------------------------------------------------------------------
/source/images/how-vulcan-works.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VulcanJS/vulcan-docs/3cb0cd142609b9efc00ead23c30de424a304a605/source/images/how-vulcan-works.png
--------------------------------------------------------------------------------
/source/learn.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Learn More
3 | ---
4 |
5 | - [React for Beginners](https://reactforbeginners.com)
6 | - [Meteor Documentation](http://docs.meteor.com/)
7 | - [Apollo Documentation](http://dev.apollodata.com/)
--------------------------------------------------------------------------------
/source/logo/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VulcanJS/vulcan-docs/3cb0cd142609b9efc00ead23c30de424a304a605/source/logo/favicon.png
--------------------------------------------------------------------------------
/source/logo/icon-apollo-white-200x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VulcanJS/vulcan-docs/3cb0cd142609b9efc00ead23c30de424a304a605/source/logo/icon-apollo-white-200x200.png
--------------------------------------------------------------------------------
/source/logo/logo-apollo-space-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
42 |
--------------------------------------------------------------------------------
/source/logo/logo-apollo-space.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
44 |
--------------------------------------------------------------------------------
/source/logo/logo-apollo-subbrands-developers-space.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
38 |
--------------------------------------------------------------------------------
/source/newsletter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Newsletter
3 | ---
4 |
5 |
6 | This package automatically generates and sends a newsletter for posts and comments on a predefined schedule.
7 |
8 | 
9 |
10 | Every `x` days, it builds a digest consisting of the top `y` items posted in the past `x` days that haven't yet been sent out in a newsletter. It then sends the campaign, and sends you a confirmation email.
11 |
12 | Note: this package requires using a third-party email provider ([MailChimp](http://mailchimp.com) and [Sendy](http://sendy.co) are currently supported).
13 |
14 | ## Install
15 |
16 | `meteor add vulcan:newsletter`
17 |
18 | ## Dependencies
19 |
20 | #### Vulcan Dependencies
21 |
22 | - vulcan:core
23 | - vulcan:posts
24 | - vulcan:comments
25 | - vulcan:categories
26 | - vulcan:email
27 |
28 | #### NPM Dependencies
29 |
30 | - [mailchimp](https://github.com/gomfunkel/node-mailchimp)
31 | - [sendy-api](https://github.com/igord/sendy-api)
32 |
33 | ## Settings
34 |
35 | ```
36 | "newsletter": {
37 | "enabled":true,
38 | "enabledInDev":false,
39 | "autoSubscribe":false,
40 | "frequency": [1,2,3,4,5,6,7],
41 | "time": "00:00",
42 | "provider": "mailchimp"
43 | },
44 | "sendy": {
45 | "server": "http://sendy.myapp.com/",
46 | "apiKey": "123foo",
47 | "listId": "456bar",
48 | "fromName": "Bruce Willis",
49 | "fromEmail": "hello@myapp.com",
50 | "replyTo": "hello@myapp.com"
51 | },
52 | "mailchimp": {
53 | "apiKey": "123foo",
54 | "listId": "456bar",
55 | "fromName": "hello@myapp.com",
56 | "fromEmail": "hello@myapp.com"
57 | },
58 | ```
59 |
60 | - `enabled`: enable/disable automated newsletter sending.
61 | - `enabledInDev`: enable/disable newsletter while in development mode.
62 | - `autoSubscribe`: automatically subscribe every new user to your newsletter.
63 | - `frequency`: which days the newsletter should go out. `1` is Monday, `2` is Tuesday, etc.
64 | - `time`: what time the newsletter should go out.
65 | - `provider`: the name of your newsletter provider.
66 |
67 | #### Sendy Settings
68 |
69 | - `server`: The URL to your self-hosted Sendy server (including trailing `/`).
70 | - `apiKey`: Your API key.
71 | - `listId`: List ID.
72 | - `fromName`: "From" name.
73 | - `fromEmail`: "From" email.
74 | - `replyTo`: "Reply to" email.
75 |
76 | #### MailChimp Settings
77 |
78 | - `apiKey`: Your API key.
79 | - `listId`: List ID.
80 |
81 | ## Test Routes
82 |
83 | If you want to preview your email templates, you can do so at the following routes:
84 |
85 | - **Campaign**: [http://localhost:3000/email/newsletter](http://localhost:3000/email/newsletter)
86 | - **Confirmation**: [http://localhost:3000/email/newsletter-confirmation](http://localhost:3000/email/newsletter-confirmation)
87 |
88 | (Replace `http://localhost:3000` with your app's URL)
89 |
90 | ## Mutations
91 |
92 | The package exposes the following GraphQL mutations:
93 |
94 | - `sendNewsletter`: generate and send the next newsletter.
95 | - `testNewsletter`: same as `sendNewsletter`, but without marking posts as sent.
96 | - `addUserNewsletter(userId: String)`: add a user to your subscriber list.
97 | - `addEmailNewsletter(email: String)`: add an email to your subscriber list.
98 | - `removeUserNewsletter(userId: String)`: remove a user from your subscriber list.
99 |
100 | You can call these mutations from any React component using the [`withMutation` HoC](mutations.html#Higher-Order-Components):
101 |
102 | ```
103 | import React, { PropTypes, Component } from 'react';
104 | import { withMutation } from 'meteor/vulcan:core';
105 |
106 | const SendButton = ({ sendNewsletter }) =>
107 |
108 | export default withMutation({name: 'sendNewsletter'})(SendButton);
109 | ```
110 |
111 | ## Providers
112 |
113 | To add a provider, you can start from the `sample.js` template included in the package.
114 |
115 | Any new provider must implement the following methods:
116 |
117 | - `Newsletters.providerName.subscribe(email)`: subscribe an email to your list.
118 | - `Newsletters.providerName.unsubscribe(email)`: unsubscribe an email from your list.
119 | - `Newsletters.providerName.send({ title, subject, text, html, isTest})`: send a newsletter.
120 |
121 | ## NewsletterSubscribe Component
122 |
123 | This package also exports a `NewsletterSubscribe` custom form field component used to add a subscribe/unsubscribe button to the user account page.
124 |
125 | ## Settings
126 |
127 | | Setting | Example | Description |
128 | | --- | --- | --- |
129 | | enabled | false | Enable the automated newsletter |
130 | | enabledInDev | false | Enable the automated newsletter while in development mode |
131 | | frequency | [1,2,3,4,5,6,7] | The days on which to send the newsletter (1 = Monday, 2 = Tuesday, etc.) |
132 | | time | "07:55" | When to send out the newsletter
133 | | autoSubscribe | false | Whether new users should be automatically subscribed to the newsletter |
--------------------------------------------------------------------------------
/source/notifications.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Notifications
3 | ---
4 |
5 | Coming soon…
--------------------------------------------------------------------------------
/source/nutshell.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Vulcan in a Nutshell
3 | ---
4 |
5 | The best way to understand how Vulcan works is to consider its three main aspects: the role of the schema, how Vulcan reads data, and how Vulcan writes data.
6 |
7 | ## Overview
8 |
9 | [](/images/how-vulcan-works.svg)
10 |
11 | ## The Schema
12 |
13 | At its core, a Vulcan schema is just a JavaScript object containing a list of fields such as `name`, `_id`, `createdAt`, `description`, etc. describing a **type of document** (a movie, a post, a photo, a review, and so on).
14 |
15 | The schema is what defines how a [Collection](/schemas.html) (you might also be more familiar with the equivalent term “model”) behaves, and it fulfills many important functions:
16 |
17 | 1. It's used to generate your GraphQL schema, which in turn controls your app's GraphQL API.
18 | 2. It's used to [control permissions](http://docs.vulcanjs.org/groups-permissions.html).
19 | 3. It's used to [generate forms](http://docs.vulcanjs.org/forms.html).
20 |
21 | ## Reading Data (Queries)
22 |
23 | Reading data basically means getting data from your database all the way to the user's browser.
24 |
25 | Let's assume we want to take a list of movies currently stored in our database and display it inside a `Movies.jsx` component. Here's a quick overview of the entire data lifecycle:
26 |
27 | #### Component
28 |
29 | The `Movies.jsx` component expects a `results` prop. But how will it receive it?
30 |
31 | #### Hook/Higher-Order Component
32 |
33 | In order to receive that prop, the component will need to be wrapped with the [`withMulti` higher-order component](/resolvers.html#withMulti). You just need to specify the appropriate [Collection](/schemas.html), and optionally also specify a [fragment](/fragments.html) to define which document fields to load.
34 |
35 | Alternatively, you can do the same thing through the `useMulti` hook.
36 |
37 | #### GraphQL Query
38 |
39 | The `withMulti` HoC (or `useMulti` hook) will trigger an Apollo query to the app's GraphQL endpoint using the [`movies` query resolver](/queries.html).
40 |
41 | This is the same kind of query you would write manually in any regular GraphQL app, but in this case the query is auto-generated by the HoC or hook.
42 |
43 | #### Resolver
44 |
45 | The query triggers a [resolver function](/queries.html). The job of that function is to take the [query arguments](/filtering.html) and output the corresponding data in return, after making sure the current user is authorized to access said data.
46 |
47 | #### Connector
48 |
49 | Finally, the resolver queries the database to retrieve the data. This is done through a [connector](/database.html), a function that translates a generic `find` request into instructions specific to the current database.
50 |
51 | ## Writing Data (Mutations)
52 |
53 | Now let's consider the opposite operation: writing data, such as editing a movie's description using a form.
54 |
55 | #### Component
56 |
57 | First, you should know that the movie update form component can be [automatically generated from your schema](/forms.html), meaning you don't actually need to code it or worry about hooking it up to your GraphQL API.
58 |
59 | #### Hook/Higher-Order Component
60 |
61 | That form is wrapped with the [`withUpdate` HoC](/mutations.html#Higher-Order-Components).
62 |
63 | #### GraphQL Query
64 |
65 | The HoC in turn will call the `updateMovie` mutation on the server (which again can be [automatically generated from default mutations](/mutations.html#Default-Mutations)).
66 |
67 | #### Resolver
68 |
69 | On the server, the `update` resolver takes in the mutation's arguments (an object indicating which document to update, as well as the payload containing the actual changes) and after some permission checks to make sure the current user is authorized to perform the mutation passes them on to the mutator.
70 |
71 | #### Mutator
72 |
73 | `updateMovie` will then call a [boilerplate mutator](/mutations.html#Boilerplate-Mutations) which will perform **validation** based on your schema, and finally call the database connector.
74 |
75 | #### Connector
76 |
77 | The database connector then modifies the document inside your database.
78 |
--------------------------------------------------------------------------------
/source/other-components.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Other Components
3 | ---
4 |
5 | Out of the box, VulcanJS provides a set of useful components you can use to quickly build applications.
6 |
7 | These live in the `vulcan:core` package and can all be called using ``. Most of them are implemented using a [component library](/ui-components) such as React Bootstrap.
8 |
9 |
10 | ### App
11 |
12 | This is the main component that wraps everything else in your app. Its main job is to initialize the internationalization context.
13 |
14 | ### Avatar
15 |
16 | The `Avatar` component takes in a `user` and displays the user's avatar. Additionally, if you pass `link = {true}`, the avatar will be linked to the user's profile.
17 |
18 | ### Card
19 |
20 | The `Card` component is used to display a card for a single document. It takes the following props:
21 |
22 | - `document`: the document to display.
23 | - `collection`: the collection the document belongs to.
24 | - `currentUser`: if passed, an "Edit Document" button will be shown (if the user has edit permissions on the document).
25 | - `fields`: optionally, you can pass a list of field names to limit the fields shown in the card.
26 |
27 | The component will do its best to "guess" how to display each of the document's properties based on their type and the collection schema:
28 |
29 | ```js
30 |
36 | ```
37 |
38 | ### EditButton
39 |
40 | A button that triggers a modal window for editing a document.
41 |
42 | ### Flash
43 |
44 | Adds a zone to dynamically show “flash” messages (errors, successes, notifications, etc.).`
45 |
46 | ### HeadTags
47 |
48 | See [Head Tags](/head-tags.html) section.
49 |
50 | ### Layout
51 |
52 | The default layout is just a placeholder that can be replaced by your own.
53 |
54 | ### Loading
55 |
56 | Show a loading animation.
57 |
58 | ### MutationButton
59 |
60 | TODO
61 |
62 | ### NewButton
63 |
64 | A button that triggers a modal window for creating a new document.
--------------------------------------------------------------------------------
/source/plugins.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Plugins
3 | ---
4 |
5 | ## Plugins
6 |
7 | VulcanJS is open-sourced and community-based, so it features some plugins developped by the community:
8 |
9 | - [ErikDakoda/vulcan-material-ui](https://github.com/ErikDakoda/vulcan-material-ui) : Replacement for Vulcan components using Material-UI.
10 | - [voodooattack/file-scalar](https://github.com/voodooattack/file-scalar) : A `File` scalar for Vulcan.js to facilitate file uploads with GraphQL.
11 | - [VulcanJS/vulcan-places](https://github.com/VulcanJS/vulcan-places) : Google Maps Places integration for Vulcan.
12 | - [Apollinaire/vulcan-crisp](https://github.com/Apollinaire/vulcan-crisp) : Crisp integration
13 | - [OrigenStudio/vulcan-files](https://github.com/OrigenStudio/vulcan-files): File System integration for Vulcan. Upload files to S3 or other storage systems.
14 | - [OrigenStudio/vulcan-sentry](https://github.com/OrigenStudio/vulcan-sentry) Sentry integration for Vulcan.
15 | - [OrigenStudio/vulcan-logrocket](https://github.com/OrigenStudio/vulcan-logrocket) LogRocket integration for Vulcan.
16 | - [OrigenStudio/vulcan-hotjar](https://github.com/OrigenStudio/vulcan-hotjar) Hotjar integration for Vulcan.
17 | - [arctop/vulcan-organizations-manager](https://https://github.com/arctop/vulcan-organizations-manager) Manage and create organizations.
18 | - [LiveForGood/vulcan-google-recaptcha](https://github.com/live-for-good/vulcan-google-recaptcha/) Google reCAPTCHA integration for your Vulcan forms.
19 |
20 | If you have written a package and would like to see it registered here, you can [edit this page](https://github.com/VulcanJS/vulcan-docs/blob/master/source/plugins.md) and submit a PR to the docs.
21 |
22 | ## Tooling
23 |
24 | ### VS Code
25 |
26 | Text editors might have trouble providing IntelliSense for Meteor packages. [The following extension from Matt Black](https://marketplace.visualstudio.com/items?itemName=mattblack.meteor-package-intellisense) provides a script to update your `jsconfig.json` file and thus provide intellisense for Vulcan packages.
27 |
28 | Snippets for common features are also provided by the [VulcanJS snippets extension from Apollinaire Lecocq](https://marketplace.visualstudio.com/items?itemName=Apollinaire.vulcanjs-snippets).
29 |
30 | ### Commande Line Interface
31 |
32 | [A CLI is available](https://www.npmjs.com/package/vulcanjs-cli) for fast generation of new Vulcan packages or modules.
33 |
--------------------------------------------------------------------------------
/source/recipes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Recipes
3 | ---
4 |
5 | Short code snippets teaching you how to do various things in Vulcan.
6 |
7 | ## Using Router Hooks
8 |
9 | The router exposes an `onUpdate` hook that triggers every time the route changes (including when the initial route is triggered).
10 |
11 | Here's how you'd use it in conjunction with Apollo's [imperative API](http://dev.apollodata.com/core/read-and-write.html) to check if the current user has completed their profile:
12 |
13 | ```js
14 | import { addCallback, getFragment } from 'meteor/vulcan:core';
15 | import Users from 'meteor/vulcan:users';
16 | import gql from 'graphql-tag';
17 |
18 | function checkProfileOnUpdate (unusedItem, store, apolloClient) {
19 | const query = gql`
20 | query getCurrentUser {
21 | currentUser {
22 | ...UsersCurrent
23 | }
24 | }
25 | ${getFragment('UsersCurrent')}
26 | `
27 |
28 | const currentUser = apolloClient.readQuery({query}).currentUser;
29 |
30 | if (currentUser && !Users.hasCompletedProfile(currentUser)) {
31 | alert(`Current user hasn't completed their profile!`)
32 | }
33 | }
34 |
35 | addCallback('router.onUpdate', checkProfileOnUpdate);
36 | ```
37 |
38 | ## Load a Random Post
39 |
40 | On the server:
41 |
42 | ```js
43 | const randomResolver = {
44 | Query: {
45 | postsRandom(root, args, context) {
46 | const postCount = context.Posts.find({status: 2}).count();
47 | return context.Posts.find({status: 2}, {limit: 1, skip: _.random(postCount)}).fetch()[0];
48 | }
49 | }
50 | };
51 |
52 | addGraphQLResolvers(randomResolver);
53 | addGraphQLQuery(`postsRandom: Post`);
54 | ```
55 |
56 | On the client:
57 |
58 | ```js
59 | const withRandomPost = graphql(gql`
60 | query postsRandom {
61 | postsRandom {
62 | ...PostsPage
63 | }
64 | }
65 | ${getFragment('PostsPage')}
66 | `);
67 |
68 | export default withRandomPost(PostsItem);
69 | ```
--------------------------------------------------------------------------------
/source/redux.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Using Redux [REVIEW]
3 | ---
4 |
5 | Redux is used transparently by Apollo to cache your data on the client, and it's perfectly fine to use Vulcan without ever worrying about it.
6 |
7 | That being said, if you need to manage state at an application-wide level (for example, you might want to track if a sidebar is open or closed), using Redux explicitly can be very useful.
8 |
9 | ## A Redux Higher-Order Component
10 |
11 | Vulcan doesn't come with any built-in Redux HoCs, but it's not too hard to build one. For example, here's how to create a HoC to manage a sidebar's shown/hidden state.
12 |
13 | First, we'll create a new action and a new reducer in a new `redux.js` file. The action defines a specific type of event that can happen inside your app; while the reducer defines what to do when that action happens.
14 |
15 | ```js
16 | import { addAction, addReducer } from 'meteor/vulcan:core';
17 |
18 | // register messages actions
19 | addAction({
20 | ui: {
21 | toggleSidebar() {
22 | return {
23 | type: 'TOGGLESIDEBAR',
24 | };
25 | },
26 | }
27 | });
28 |
29 | // register messages reducer
30 | addReducer({
31 | ui: (state = {showSidebar: false}, action) => {
32 | switch(action.type) {
33 | case 'TOGGLESIDEBAR':
34 | return {
35 | ...state,
36 | showSidebar: !state.showSidebar
37 | };
38 | default:
39 | return state;
40 | }
41 | },
42 | });
43 | ```
44 |
45 | Next, let's create our higher-order component in a separate `withUI.js` file:
46 |
47 | ```js
48 | import { getActions,} from 'meteor/vulcan:lib';
49 | import { bindActionCreators } from 'redux';
50 | import { connect } from 'react-redux';
51 |
52 | const mapStateToProps = state => ({ ui: state.ui, });
53 | const mapDispatchToProps = dispatch => bindActionCreators(getActions().ui, dispatch);
54 |
55 | const withUI = (component) => connect(mapStateToProps, mapDispatchToProps)(component);
56 |
57 | export default withUI;
58 | ```
59 |
60 | Once we wrap a component with `withUI`, the following props will be available to it:
61 |
62 | - `this.props.ui.showSidebar`: a boolean that indicates if the sidebar is shown or not.
63 | - `this.props.toggleSidebar`: a function that toggles the sidebar on and off.
64 |
--------------------------------------------------------------------------------
/source/relations.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Relations
3 | ---
4 |
5 | A common use case for field resolvers is fetching one or more item associated with the current document, such as the user corresponding to a post's `userId` field; or the array of categories correponding to its `categoriesIds` field.
6 |
7 | While you can achieve this by explicitly adding an API schema field, Vulcan also offers a shortcut syntax using the `relation` property:
8 |
9 | ```
10 | userId: {
11 | type: String,
12 | optional: true,
13 | canRead: ['guests'],
14 | relation: {
15 | fieldName: 'user',
16 | typeName: 'User',
17 | kind: 'hasOne', // one of 'hasOne' or 'hasMany'
18 | }
19 | },
20 | ```
21 |
22 | Since this is a `hasOne` relation, Vulcan will assume that the current field stores the `_id` of the related document to look up; and because we know that the type of the returned item should be `User` we can also easily figure out that we need to search the `Users` collection.
23 |
24 | Similarly, you can also define `hasMany` relations:
25 |
26 | ```
27 | categoriesIds: {
28 | type: Array,
29 | arrayItem: {
30 | type: String,
31 | optional: true,
32 | canRead: ['guests'],
33 | }
34 | optional: true,
35 | canRead: ['guests'],
36 | relation: {
37 | fieldName: 'categories',
38 | typeName: '[Categories]',
39 | kind: 'hasMany'
40 | }
41 | },
42 | ```
43 |
44 | ## Relations, Cards, and Datatables
45 |
46 | As an added bonus, if you define the `userId` field as having a relation to the `User` type, Cards and Datatables will be able to understand this relation and use smarter formatting when displaying this data.
47 |
--------------------------------------------------------------------------------
/source/resolvers.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Resolvers
3 | ---
4 |
5 | A resolver is the function on the server that receives a GraphQL query, decides what to do with it (how to _resolve_ it), and then returns some data.
6 |
7 | Each level of a GraphQL query (in other words, each field) can have its own custom resolver; or if no resolver exists the field will default to look for a value on its parent object.
8 |
9 | ### Implicit Resolvers
10 |
11 | Technically speaking, every single field in the fragment has to resolve to _something_. But if for example you've specified a `Post` resolver and you're asking for a post's `title` and `createdAt` properties, GraphQL will be smart enough to look for `post.title` and `post.createdAt` without you needing to define additional resolvers for these two fields.
12 |
13 | That being said if you wanted `createdAt` to resolve to, say, `formatDate(post.createdAt)` instead of just `post.createdAt`, then you could write a custom resolver [just for that specific field](/field-resolvers.html).
14 |
15 | ### Data Updating
16 |
17 | As long as you use `withMulti` or `useMulti` in conjunction with `withCreate`, `withUpdate`, and `withDelete`, your lists will automatically be updated after any mutation. This includes:
18 |
19 | * Inserting new items in lists after they're inserted.
20 | * Removing items when they're removed.
21 | * Reordering lists when an item is edited in a way that changes a sort.
22 | * Removing an item after it's been edited when it doesn't match a list's filters anymore.
23 |
24 | At this time, it's only possible to benefit from this auto-updating behavior if you're using the three built-in mutation HoCs, although making `withMulti` more flexible towards custom mutations is on the roadmap (PRs welcome!).
25 |
26 | #### Alternative Approach
27 |
28 | You can replace any of Vulcan's generic HoCs with your own tailor-made HoCs (whether it is for queries or mutations) using the `graphql` utility. Note that if you do so, you will need to manually [update your queries](http://dev.apollodata.com/react/cache-updates.html) after each mutation.
29 |
--------------------------------------------------------------------------------
/source/server-queries.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Querying on the Server
3 | ---
4 |
5 | Most of the Data Layer section's contents applies to the client. After all, if you're on the server, you can simply connect to your database directly, so why would you need to worry about the data layer?
6 |
7 | But the truth is that connecting to the database directly can lead to problems. For example, let's assume you have a `user` resolver on your `Post` type that gives you access to the post's author. On the client, you could write:
8 |
9 | ```js
10 | const authorName = post.user.displayName;
11 | ```
12 |
13 | On the server, it's easy enough to obtain that post document:
14 |
15 | ```js
16 | const post = Posts.findOne(documentId);
17 | ```
18 |
19 | But the document you fetch directly from MongoDB won't have a `user` property, let alone a `user.displayName` (since those are provided by the GraphQL resolver).
20 |
21 | Thankfully, VulcanJS features a few special utilities to let you query for data through GraphQL *on the server*
22 |
23 | ### runGraphQL(query, variables)
24 |
25 | `runGraphQL` lets you execute any GraphQL query or mutation against your schema and get its result back on the server.
26 |
27 | ```js
28 | import { runGraphQL } from 'meteor/vulcan:core';
29 |
30 | async function foo = () => {
31 |
32 | const query = `
33 | query OneRoom($documentId: String){
34 | RoomsSingle(documentId: $documentId){
35 | name
36 | description
37 | user{
38 | _id
39 | displayName
40 | }
41 | }
42 | }
43 | `;
44 | return await runGraphQL(query, {documentId: '123foo'});
45 |
46 | }
47 | ```
48 |
49 | (Note that you'll need to be inside an `async` context to use the `await` keyword)
50 |
51 | ### collection.queryOne(inputOrId, options)
52 |
53 | A common need on the server is fetching a single document by its `_id`. You can do so with `collection.queryOne`. The first argument is either a string `_id`, or a full `input` object (as used by `single` queries).
54 |
55 | The second argument is an `options` object with a `fragmentText` property. If no fragment is provided, the query will use the collection's `defaultFragment`.
56 |
57 | ```js
58 | const user = await Users.queryOne(userId);
59 |
60 | // or
61 |
62 | const user = await Users.queryOne(userId, {
63 | fragmentText: `
64 | fragment MyUsersFragment on User {
65 | _id
66 | username
67 | createdAt
68 | posts{
69 | _id
70 | title
71 | }
72 | }
73 | `
74 | });
75 | ```
76 |
77 | ### `queryOne` vs `load`
78 |
79 | It might seem like `queryOne` does the same thing as the [Dataloader layer](/performance.html#Caching-amp-Batching)'s `load`, but there's a crucial difference in how they're used.
80 |
81 | `queryOne` uses your existing GraphQL resolvers behind the scenes, which means it itself can't be used *inside* a resolver (or you'd risk an infinite loop). It's thus meant to be used from outside of your GraphQL resolver tree, from a server script, cron job, migration, etc. or any kind of server code unrelated to the client.
82 |
83 | `load` on the other hand is specifically made to be used inside resolvers and add a caching and batching layer to them.
84 |
--------------------------------------------------------------------------------
/source/settings.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Settings
3 | ---
4 |
5 | You can configure the following settings in your `settings.json` file.
6 |
7 | ## Debugging
8 |
9 | **If you've got the [debug package](/debug.html) enabled, a settings debugging UI is available at [http://0.0.0.0:3000/debug/settings](http://0.0.0.0:3000/debug/settings).**
10 |
11 | ## Overview
12 |
13 | You can call Vulcan.showSettings() in your meteor shell to get a quick overview of all settings, or go to `/settings` if you have the `vulcan:debug` package enabled.
14 |
15 | ### Public Settings
16 |
17 | These settings are defined on the `public` property, meaning they will be shared with the client.
18 |
19 | #### Global Settings
20 |
21 | | Setting | Example | Description |
22 | | --- | --- | --- |
23 | | title | "My Vulcan Site" | Your site's title |
24 | | siteUrl | "http://mysite.com" | Your site's main URL |
25 | | tagline | "The best site ever!" | Your site's tagline or description |
26 | | language | "en" | Your site's language |
27 |
28 | #### Social Settings
29 |
30 | | Setting | Example | Description |
31 | | --- | --- | --- |
32 | | twitterAccount | "TelescopeApp" | Your main twitter account (without the "@")|
33 | | facebookPage | "https://facebook.com/TelescopeApp" | Your Facebook page URL|
34 | | googleAnalyticsId | "UA-123456-9" | Your Google Analytics code |
35 |
36 | ### Server Settings
37 |
38 | These settings are defined at the root of the settings object, (or optionally on the `private` property), and kept on the server.
39 |
40 | #### Global Settings
41 |
42 | | Setting | Example | Description |
43 | | --- | --- | --- |
44 | | mailUrl | "smtp://username:password@smtp.mailgun.org:587/" | The SMTP URL used by your email provider |
45 |
46 | **MailGun** Note:
47 | For `username` use your **Default SMTP Login** and for `password` use **Default Password**.
48 | See screenshot below.
49 |
50 | 
51 |
52 | #### OAuth Settings
53 |
54 | You can use the settings file to store your oAuth configurations:
55 |
56 | ```js
57 | "oAuth": {
58 | "twitter": {
59 | "consumerKey": "foo",
60 | "secret": "bar"
61 | },
62 | "facebook": {
63 | "appId": "foo",
64 | "secret": "bar"
65 | },
66 | "github": {
67 | "clientId": "foo",
68 | "secret": "bar"
69 | }
70 | }
71 | ```
72 | Don't forget to add the matching meteor package, e.g. `accounts-twitter`, `accounts-facebook`, `accounts-github` etc.
73 |
74 | ## registerSetting
75 |
76 | You can optionally register a new setting with `registerSetting(name, defaultValue, description)`. This is optional, but it will let your setting be displayed on the `/settings` route and when calling `Vulcan.showSettings()`.
77 |
78 | Note that `registerSetting` also both works with nested settings objects (e.g. `registerSetting('newsletter.frequency', [1,3,5], 'Newsletter frequency')`).
79 |
80 | ## getSetting
81 |
82 | To retrieve a setting, just call `getSetting(settingName)`. If no setting has been assigned in `settings.json`, the default value assigned through `registerSetting` will be used (if it exists).
83 |
84 | Just like `registerSetting`, `getSetting` also works with nested settings objects (e.g. `getSetting('newsletter.frequency')`).
85 |
--------------------------------------------------------------------------------
/source/sponsorship-tutorial.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Sponsorship Flow Tutorial
3 | ---
4 |
5 | This series of video will take you through a case study on building a flow for submitting, scheduling, and paying for a sponsored link on [Sidebar](http://sidebar.io).
6 |
7 | This tutorial will cover many topics, including:
8 |
9 | - Creating new routes.
10 | - Using custom fields.
11 | - Writing custom query resolvers.
12 | - Writing custom mutations.
13 | - Creating custom views.
14 | - Using the Payments package.
15 | - Creating new email objects.
16 | - Using callbacks.
17 |
18 | Let's get started!
19 |
20 | - [Start the playlist](https://www.youtube.com/watch?v=4MMILATC9Ds&list=PLBoa_Q6hVeSzGqmpl4xUS9913lTP15Zve)
21 | - [Overview](https://docs.google.com/document/d/1dxFlI7QdG4UniCCNAX_DLV6wO2U50zlcwSA4b4N3AJ0/edit?usp=sharing) (Google Docs)
22 | - [Code](https://github.com/SachaG/Sidebar2017)
23 |
24 | Individual videos:
25 |
26 | - [Intro](https://www.youtube.com/watch?v=4MMILATC9Ds&list=PLBoa_Q6hVeSzGqmpl4xUS9913lTP15Zve&index=1) (3:24)
27 | - [Sponsor Page](https://www.youtube.com/watch?v=OQ0zOgwj0fk&list=PLBoa_Q6hVeSzGqmpl4xUS9913lTP15Zve&index=2) (6:09)
28 | - [Submit Link](https://www.youtube.com/watch?v=1N58_LRVZis&index=3&list=PLBoa_Q6hVeSzGqmpl4xUS9913lTP15Zve) (5:24)
29 | - [Pick Date](https://www.youtube.com/watch?v=wkfx1bypuw0&index=4&list=PLBoa_Q6hVeSzGqmpl4xUS9913lTP15Zve) (8:35)
30 | - [Complete Payment](https://www.youtube.com/watch?v=jdDPFZTRt0s&list=PLBoa_Q6hVeSzGqmpl4xUS9913lTP15Zve&index=5) (12:11)
--------------------------------------------------------------------------------
/source/themes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Themes
3 | ---
4 |
5 | Coming soon…
--------------------------------------------------------------------------------
/source/toc.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Table of Contents
3 | ---
4 |
5 | A quick overview of this documentation.
6 |
7 | ---
8 |
9 | | *Section* | *Description* |
10 | |---|---|
11 | | **Schemas** | **How to define model schemas to power your app** |
12 | | [Schemas & Collections](/schemas.html) | The basics of creating a new collection |
13 | | [Schema Properties](/schema-properties.html) | Available schema properties |
14 | | [Field Resolvers](/field-resolvers.html) | How to make schema fields "resolve" into complex objects |
15 | | [Relations](/relations.html) | How to define relations between documents |
16 | | **GraphQL API** | **How to define model schemas to power your app** |
17 | | [GraphQL Schema](/graphql-schema.html) | How to access your generated GraphQL schema |
18 | | [Queries](/queries.html) | Querying for data (read) |
19 | | [Filtering](/filtering.html) | How to narrow down the data you query for |
20 | | [Mutations](/mutations.html) | Mutating data (create, update, delete) |
21 | | [Fragments](/fragments.html) | How to control which fields you receive on the client |
22 | | [Querying on the Server](/server-queries.html) | Using GraphQL queries from the server |
23 | | [Connecting Remotely](/connecting-remotely.html) | Using your GraphQL API from another server |
24 | | **Features** | **Core Vulcan features** |
25 | | [Routing](/routing.html) | How to declare routes |
26 | | [Settings](/settings.html) | Storing and retrieving app-level settings |
27 | | [Groups & Permissions](/groups-permissions.html) | Controlling permissions with groups and functions |
28 | | [Callbacks](/callbacks.html) | How to add callback functions after key operations (create, update, etc.) |
29 | | [Users](/users.html) | Managing users |
30 | | [Internationalization](internationalization.html) | Making your UI and database contents available in multiple languages |
31 | | [Head Tags](/head-tags.html) | Control your app's `meta` tags |
32 | | [Errors & Messages](/errors.html) | Showing users error messages |
33 | | **Components** | **Vulcan's built-in components** |
34 | | [Components](/components.html) | Registering, using, and replacing components |
35 | | [UI Components](/ui-components.html) | Using UI themes (Bootstrap, Material, etc.) with Vulcan |
36 | | [Datatable](/datatable.html) | The Datatable component |
37 | | [Form Components](/form-components.html) | Components used to build forms |
38 | | [Other Components](/other-components.html) | Other built-in components |
39 | | **Forms** | **Vulcan's form generation and form handling** |
40 | | [Forms](/forms.html) | Using “new” and “edit” forms |
41 | | [Custom Forms](/custom-forms.html) | Customizing your forms |
42 | | **Server** | **Server-level concerns** |
43 | | [Performance](/performance.html) | Ensuring your Vulcan app runs smoothly |
44 | | [Database Layer](/database.html) | Accessing the database |
45 | | **Testing** | **Testing your app**|
46 | | [Unit Testing](/unit-testing.html) | Unit Testing with Mocha |
47 | | [Storybook](/storybook.html) | Component testing with Storybook |
--------------------------------------------------------------------------------
/source/tutorials.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tutorials
3 | ---
4 |
5 | - [Interactive Tutorial](https://github.com/VulcanJS/Vulcan-Starter)
6 |
--------------------------------------------------------------------------------
/source/ui-components.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: UI Components
3 | ---
4 |
5 | While Vulcan itself doesn't provide its own UI components (besides core utilites such as `Layout`, `App`, etc.), it does make use of a common component API that can serve as a bridge between your app and UI frameworks such as [React Bootstrap](https://react-bootstrap.github.io/) or [Material UI](https://www.material-ui.com/#/).
6 |
7 | By using this API instead of depending on a framework's components directly, Vulcan plugins and projects can make it possible to easily swap from one UI framework to another.
8 |
9 | ## Form Components
10 |
11 | - Checkbox
12 | - Checkboxgroup
13 | - Date
14 | - Datetime
15 | - Default
16 | - Email
17 | - FormControl
18 | - Number
19 | - Radiogroup
20 | - Select
21 | - Textarea
22 | - Time
23 | - URL
24 |
25 | ## Other Components
26 |
27 | ### Alert
28 |
29 | ### Button
30 |
31 | ### Dropdown
32 |
33 | ### Modal
34 |
35 | ### ModalTrigger
36 |
37 | The `ModalTrigger` component can be used to display its child component inside a modal window when the trigger component is clicked:
38 |
39 | ```js
40 | }>
41 |
42 |
43 | ```
44 |
45 | It accepts the following props:
46 |
47 | - `className`
48 | - `component`: the trigger component.
49 | - `label`: if no trigger component is passed, you can also specify a label for a text link.
50 | - `size`: `large` or `small` (defaults to `large`).
51 | - `title`: the modal popup's title.
52 |
53 | All props are optional, but you should pass at least either `component` or `label`.
54 |
55 | Note that the element passed as `component` needs to accept an `onClick` handler. In some cases, it might be necessary to wrap it inside an extra `
`:
56 |
57 | ```js
58 |
}>
59 |
60 |
61 | ```
62 |
63 | ### Table
64 |
65 | ### Tooltip
--------------------------------------------------------------------------------
/source/unit-testing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Unit Testing
3 | ---
4 |
5 | Currently we are using [Mocha](https://mochajs.org/) for unit and integration testing.
6 | At some point soon we will be migrating to [Jest](https://jestjs.io), but we will make the migration process as smooth as possible.
7 | This is the reason we are using the Jest Expect library instead of Chai.
8 |
9 | You'll find documentation about Meteor testing at the following places:
10 |
11 | * [Meteor Guide: Testing](https://guide.meteor.com/testing.html)
12 | * [MeteorTesting:Mocha]()
13 | * [Mocha Getting Started](https://mochajs.org/#getting-started)
14 | * [Jest Expect API Reference](https://jestjs.io/docs/en/expect)
15 | * [Jest Extended Expect API Reference](https://jestjs.io/docs/en/expect)
16 | * [Chai BDD Expect API Reference](https://www.chaijs.com/api/bdd/)
17 |
18 | ## Getting started
19 |
20 | Vulcan comes pre-configured for unit testing, but for your own project you'll have to install it.
21 |
22 | If you are NOT using a [two repo install](http://docs.vulcanjs.org/index.html#Two-Repo-Install-Optional) copy vulcan/packages/meteor-mocha to your project's packages directory.
23 |
24 | In your app's root directory run these terminal commands:
25 |
26 | ```
27 | meteor add meteortesting:mocha
28 | meteor npm install --save-dev chai
29 | meteor npm install --save-dev chai-as-promised
30 | meteor npm install --save-dev selenium-webdriver@3.6.0
31 | meteor npm install --save-dev chromedriver@2.41.0
32 | meteor npm install --save-dev enzyme
33 | meteor npm install --save-dev enzyme-adapter-react-16
34 | meteor npm install --save-dev jsdom
35 | meteor npm install --save-dev jsdom-global
36 | ```
37 |
38 | Open `.meteor/versions` and make sure the meteortesting packages are at their latest versions,
39 | and if not, update them individually using `meteor update meteortesting:browser-tests` etc.:
40 |
41 | ```
42 | meteortesting:browser-tests@1.2.0
43 | meteortesting:mocha@1.1.3
44 | meteortesting:mocha-core@6.1.2
45 | ```
46 |
47 | Open `package.json` in your app root and add the following line to the scripts section:
48 |
49 | ```json
50 | {
51 | "name": "MyApp",
52 | . . .
53 | "scripts": {
54 | . . .
55 | "unit-test": "TEST_BROWSER_DRIVER=chrome ROOT_URL=http://localhost:60859 METEOR_PACKAGE_DIRS=~/Dev/Erik-Vulcan/packages meteor test-packages ./packages/* --port 60859 --settings settings-dev.json --driver-package meteortesting:mocha --raw-logs"
56 | }
57 | }
58 | ```
59 |
60 | Make sure to replace `~/Dev/Erik-Vulcan` with the actual path to you local copy of Vuclan in your two-repo install.
61 | Before `meteor` you can define additional environment variables that you may need, such as `MAIL_URL` or `MONGO_URL`.
62 |
63 | ## Running the tests
64 |
65 | To start testing initiate unit-test, for example type in your console:
66 |
67 | ```
68 | npm run test-unit
69 | ```
70 |
71 | The unit test suite will then run and later re-run when you make code changes. You can terminate it with **CTRL-C**.
72 |
73 | ## Adding code coverage
74 |
75 | Add the following packages in your terminal:
76 |
77 | ```
78 | meteor add lmieulet:meteor-coverage meteortesting:mocha
79 | meteor npm install --save-dev babel-plugin-istanbul
80 | ```
81 |
82 | In order to instrument your code, you need to add the babel-plugin-istanbul the .babelrc file in the root of your app:
83 |
84 | ```json
85 | {
86 | "env": {
87 | "COVERAGE": {
88 | "plugins": [
89 | "istanbul"
90 | ]
91 | }
92 | },
93 | "presets": [
94 | . . .
95 | ]
96 | }
97 | ```
98 |
99 | You must wrap the istanbul plugin with the env setting to disable the file-instrumentation of your project when you are not running the test coverage script. Just keep in mind that if you follow the here under script, babel will use the istanbul package only when BABEL_ENV=COVERAGE.
100 |
101 | Now, to run the coverage process, just add these new scripts inside your package.json in the root folder of your app:
102 |
103 | ```json
104 | {
105 | "name": "MyApp",
106 | . . .
107 | "scripts": {
108 | . . .
109 | "unit-test": "TEST_BROWSER_DRIVER=chrome ROOT_URL=http://localhost:60859 METEOR_PACKAGE_DIRS=~/Dev/Erik-Vulcan/packages meteor test-packages ./packages/* --port 60859 --driver-package meteortesting:mocha --raw-logs",
110 | "unit-test-coverage": "BABEL_ENV=COVERAGE COVERAGE=1 COVERAGE_VERBOSE=1 COVERAGE_APP_FOLDER=$PWD/ TEST_BROWSER_DRIVER=chrome ROOT_URL=http://localhost:60859 METEOR_PACKAGE_DIRS=~/Dev/Erik-Vulcan/packages meteor test-packages ./packages/* --port 60859 --driver-package meteortesting:mocha --raw-logs"
111 | }
112 | }
113 | ```
114 |
--------------------------------------------------------------------------------
/source/users.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Users
3 | ---
4 |
5 | Vulcan uses the standard [Meteor user accounts system](https://guide.meteor.com/accounts.html), along with the [`std:accounts-ui`](https://github.com/studiointeract/accounts-ui) package for the front-end.
6 |
7 | ### Preloaded Properties
8 |
9 | A number of properties (such as a user's `_id`, `email`, etc.) are preloaded for the current user when the app loads. These properties are controlled by the `UsersCurrent` fragment:
10 |
11 | ```
12 | fragment UsersCurrent on User {
13 | _id
14 | username
15 | createdAt
16 | isAdmin
17 | displayName
18 | email
19 | emailHash
20 | slug
21 | groups
22 | }
23 | ```
24 |
25 | You can extend or replace it just like any other [fragment](fragments.html).
--------------------------------------------------------------------------------
/source/videos.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Videos
3 | ---
4 |
5 | - [Vulcan on YouTube](https://www.youtube.com/channel/UCGIvQQ6zw7ov2cHgD70HFlA).
--------------------------------------------------------------------------------
/source/vulcan.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: From Telescope to Vulcan
3 | ---
4 |
5 | **Vulcan** is the name of the next big evolution of the [Telescope](http://www.telescopeapp.org/) project.
6 |
7 | Over the years, we've realized that Telescope could be used to do a lot more than simply build Product Hunt clones. That's why we've decided to focus our energy on building a more flexible and more generic platform, and we thought this new approach deserved a new name.
8 |
9 | In the near future, this will mean two big changes for Telescope:
10 |
11 | First, all various assets (repo/documentation/homepage/etc.) will progressively transition to the new VulcanJS name and branding. This might be a bit confusing at first, but we'll get there eventually!
12 |
13 | Second, Vulcan will adopt a new “framework-first” approach. Practically speaking, this means you'll start new projects with the `framework-demo` package, and not with the optional `vulcan:posts`, `vulcan:comments`, etc. feature packages. Of course, these packages will still be available if you choose to use them. We just think it's important for new users to understand how the underlying framework works before they start using its more advanced capabilities.
14 |
15 | ### Telescope, Nova, & Vulcan
16 |
17 | Just to clear things up, here's a short timeline of Telescope's various versions:
18 |
19 | #### Telescope Legacy (2012-2016)
20 |
21 | The original Telescope, built with Meteor and Blaze. Focused on being usable out of the box as a Product Hunt/Hacker News clone.
22 |
23 | #### Telescope Nova (2016-2017)
24 |
25 | The Meteor/React refactor of Telescope. Contained fewer features than Legacy, and was focused mainly on extensibility and flexibility. Nova originally used the Meteor pub/sub data layer (“Nova Classic”).
26 |
27 | Later in 2016, Nova was ported to the Apollo data layer while still using Meteor (“Nova Apollo”), and all internal APIs were refactored to eliminate global variables.
28 |
29 | #### Vulcan (2017-???)
30 |
31 | The new name for the Apollo version of Telescope Nova.
32 |
33 | As of March 2017, Vulcan is merely a rebranding of Telescope Nova 1.2, and does not feature any code changes.
34 |
35 | ### Why “Vulcan”?
36 |
37 | [Vulcan](https://en.wikipedia.org/wiki/Vulcan_(mythology) is the Roman god of fire and volcanoes. Since Vulcan uses React and Apollo, we thought this was an appropriate name. After all Apollo is a Roman god too, and the React team has used [volcanoes](https://facebook.github.io/react/blog/2016/09/28/our-first-50000-stars.html) in their imagery before.
38 |
39 | Also, the logo has a hidden reference to Telescope's original space-themed roots. Can you find it?
40 |
--------------------------------------------------------------------------------
/source/why-vulcan.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VulcanJS/vulcan-docs/3cb0cd142609b9efc00ead23c30de424a304a605/source/why-vulcan.md
--------------------------------------------------------------------------------
/talk-outline.md:
--------------------------------------------------------------------------------
1 |
2 | ## Background
3 |
4 | 1. Telescope/Sidebar
5 | 2. Discover Meteor
6 | 3. React rewrite (Nova) + Xavier comes on board
7 | 4. Apollo rewrite (Vulcan)
8 |
9 | ## What Is It?
10 |
11 | A full-stack toolkit of components and packages that helps you quickly build
12 | web apps, both on the fƒront-end and back-end.
13 |
14 | Show some projects:
15 | 1. Crater
16 | 2. Sidebar
17 | 3. Huttle
18 | 4. SmartHosts
19 |
20 | ## Principles
21 |
22 | 1. No Dumb Work
23 | 2. Flexible (Extend, Don't Edit)
24 | 3. Eject Anytime
25 |
26 | ## Technology
27 |
28 | 1. Front-End: React
29 | 2. Data Layer: GraphQL & Redux (Apollo)
30 | 3. Server & Build Tool: Meteor
31 |
32 | 4. The bridge between Meteor's ease of use and Apollo's flexibility
33 | 5. Package-based architecture
34 |
35 | ## Movies Demo + Core Features
36 |
37 | 1. Schema
38 | 2. Data Layer (list pagination, load detailed views)
39 | - Show Redux/Apollo devtools
40 | 3. Forms
41 |
42 | ## Posts Demo + Other Features
43 |
44 | 1. Posts & Comments
45 | 2. Categories
46 | 3. Newsletter
47 |
48 | ## Code + Docs
49 |
50 | 1. Data layer HoCs
51 | 2. Components & theming
52 | 3. Fragments extension
53 | 4. Callback Hooks
54 |
55 | ## Next Steps
56 |
57 | 1. Last 10%
58 | 2. Relaunch with clear messaging
59 | 3. Find more contributors! (you!)
60 |
--------------------------------------------------------------------------------
/themes/meteor/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/themes/meteor/_config.yaml:
--------------------------------------------------------------------------------
1 | logo:
2 | #nav_mobile: images/logo-apollo-space-left.svg
3 | #subbrand: images/logo-apollo-subbrands-developers-space.svg
4 | title: Vulcan Documentation
5 | subtitle: Vulcan Documentation
6 | url: https://docs.vulcanjs.org/
7 | favicon: images/favicon.ico
8 |
9 | nav_links:
10 | 'GitHub':
11 | url: https://github.com/vulcanjs/vulcan
12 | 'Slack':
13 | url: http://slack.vulcanjs.org
14 |
15 |
--------------------------------------------------------------------------------
/themes/meteor/layout/layout.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%- page.title %> | <%- config.title %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | <%- body %>
36 |
37 |
38 |
39 |
64 |
65 |
66 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/themes/meteor/layout/page.ejs:
--------------------------------------------------------------------------------
1 | <% var githubUrl = 'https://github.com/' + config.github_repo +
2 | '/tree/master/' + (config.content_root || 'content') + '/' +
3 | page.path.replace(/\.html$/, '.md'); %>
4 |
5 |